stella_checker 0.2.3

Rust-based, speedy Lua type checker
Documentation
mod ast;
mod checker;
mod cli;
mod context;
mod diagnostics;
mod emit;
mod formatting;
mod lexer;
mod modules;
mod parser;
mod stdlib;
mod types;
mod utils;

use std::{
  fs::{self, File},
  io::Write,
  path::Path,
};

use checker::Checker;
use parser::parser::Parser;
use rlua::Lua;
use stella_checker::utils::highlight_text_with_red;

const OUTPUT_DIRECTORY: &str = "build";

fn main() {
  let matches = cli::command_line();

  match matches.subcommand() {
    Some(("check", matches)) => {
      let path_name = matches.get_one::<String>("file").unwrap();
      run_check(path_name);
    }

    Some(("compile", matches)) => {
      let path_name = matches.get_one::<String>("file").unwrap();
      let result = run_compile(path_name);
      if result.is_err() {
        std::process::exit(1);
      }
    }
    Some(("run", matches)) => {
      let path_name = matches.get_one::<String>("file").unwrap();
      run(path_name);
    }
    _ => panic!("No subcommand provided."),
  }
}

fn run_check(path_name: &str) {
  let raw = std::fs::read_to_string(path_name).unwrap();
  let mut parser = Parser::new(&raw, path_name);
  let program = parser.parse_program();
  let mut checker = Checker::new(path_name, &raw);
  let type_result = checker.check(&program);
  if type_result.is_err() || checker.diagnostics.error_count > 0 {
    checker.diagnostics.emit_all(&raw, path_name);
    return;
  }
  let type_ = type_result.unwrap();
  println!("Result Type: {}", type_);
}

fn run_compile(path_name: &str) -> Result<(), std::io::Error> {
  let raw = std::fs::read_to_string(path_name).unwrap();
  let mut parser = Parser::new(&raw, path_name);
  let program = parser.parse_program();
  let mut checker = Checker::new(path_name, &raw);
  let type_result = checker.check(&program);

  if type_result.is_err() || checker.diagnostics.error_count > 0 {
    checker.diagnostics.emit_all(&raw, path_name);
    std::process::exit(1);
  }

  let output = create_output_directory(path_name);
  let raw = program.emit();
  println!("raw: {}", raw);
  let path = Path::new(&output);
  if let Some(parent) = path.parent() {
    fs::create_dir_all(parent)?;
  }
  let mut file = File::create(path)?;
  file.write_all(raw.as_bytes())?;
  Ok(())
}

fn run(path_name: &str) {
  let raw = std::fs::read_to_string(path_name).unwrap();
  let mut parser = Parser::new(&raw, path_name);
  let program = parser.parse_program();
  let mut checker = Checker::new(path_name, &raw);
  let type_result = checker.check(&program);

  if type_result.is_err() || checker.diagnostics.error_count > 0 {
    checker.diagnostics.emit_all(&raw, path_name);
    return;
  }

  let mut raw = program.emit();
  raw = raw.trim().to_string();

  println!("raw: {}", raw);
  let lua = Lua::new();
  let result = lua.load(raw).exec();
  match result {
    Ok(_) => {}
    Err(err) => {
      println!("{}", highlight_text_with_red(remove_location(err.to_string().as_str()).as_str()));
    }
  }
}

fn create_output_directory(path_name: &str) -> String {
  let output_path = std::path::Path::new(OUTPUT_DIRECTORY);
  if !output_path.exists() {
    std::fs::create_dir(output_path).expect("failed to create output directory");
  }
  let out_path = format!("{}/{}", OUTPUT_DIRECTORY, path_name.replace("./", ""));
  return out_path;
}

fn remove_location(error_message: &str) -> String {
  if let Some(start) = error_message.find("[string \"") {
    if let Some(end) = error_message[start..].find(": ") {
      let clean_message = format!("{}{}", &error_message[..start], &error_message[start + end + 1..].trim());
      return clean_message.trim().to_string();
    }
  }
  error_message.to_string()
}