#![allow(dead_code)]
mod args;
mod fmt;
mod io;
mod runtime;
mod semantic;
mod symbol;
mod syntax;
mod term;
#[cfg(test)]
mod tests;
use std::os::unix::ffi::OsStrExt;
use term::color;
use args::{Args, Command};
use runtime::{Panic, SourcePos, Runtime};
#[derive(Debug)]
enum ExitStatus {
Success,
InvalidArgs,
StaticError,
Panic,
}
impl From<ExitStatus> for i32 {
fn from(status: ExitStatus) -> Self {
match status {
ExitStatus::Success => 0,
ExitStatus::InvalidArgs => 1,
ExitStatus::StaticError => 2,
ExitStatus::Panic => 127,
}
}
}
fn main() -> ! {
let command = match args::parse(std::env::args_os()) {
Ok(command) => command,
Err(error) => {
eprint!("{}", error);
std::process::exit(ExitStatus::InvalidArgs.into())
}
};
let exit_status = match command {
Command::Run(args) => run(args),
Command::Help(msg) | Command::Version(msg) => {
println!("{}", msg);
ExitStatus::Success
},
};
std::process::exit(exit_status.into())
}
fn run(args: Args) -> ExitStatus {
let mut interner = symbol::Interner::new();
let (source, path) = match args.script_path {
Some(path) => {
let path = interner.get_or_intern(path.as_os_str().as_bytes());
let source = syntax::Source::from_path(path, &mut interner);
(source, path)
},
None => {
let path = interner.get_or_intern("<stdin>");
let source = syntax::Source::from_reader(path, std::io::stdin().lock());
(source, path)
},
};
let source = match source {
Ok(source) => source,
Err(error) => {
eprintln!(
"{}",
fmt::Show(
Panic::io(error, SourcePos::file(path)),
&interner
)
);
return ExitStatus::Panic;
}
};
let syntactic_analysis = syntax::Analysis::analyze(&source, &mut interner);
let has_syntax_errors = !syntactic_analysis.is_ok();
if has_syntax_errors {
eprint!("{}", fmt::Show(
syntactic_analysis.errors,
syntax::AnalysisDisplayContext {
max_errors: Some(20),
interner: &interner,
}
));
}
if args.print_lexemes {
println!("{}", color::Fg(color::Yellow, "--------------------------------------------------"));
let cursor = syntax::lexer::Cursor::from(&source);
let results: Vec<_> = syntax::lexer::Lexer::new(cursor, &mut interner).collect();
for result in results {
match result {
Ok(token) => println!("{}", fmt::Show(&token, &interner)),
Err(error) => println!("{}: {}", color::Fg(color::Red, "Error"), error)
}
}
println!("{}", color::Fg(color::Yellow, "--------------------------------------------------"));
}
if args.print_ast {
println!("{}", color::Fg(color::Yellow, "--------------------------------------------------"));
println!(
"{}",
fmt::Show(
&syntactic_analysis.ast,
syntax::ast::fmt::Context::from(&interner)
)
);
println!("{}", color::Fg(color::Yellow, "--------------------------------------------------"));
}
let program = match semantic::Analyzer::analyze(syntactic_analysis.ast, &mut interner) {
Ok(program) => program,
Err(errors) => {
eprint!("{}", fmt::Show(
errors,
semantic::ErrorsDisplayContext {
max_errors: Some(20),
interner: &interner,
}
));
return ExitStatus::StaticError;
}
};
if args.print_program {
println!("{}", color::Fg(color::Yellow, "--------------------------------------------------"));
println!(
"{}",
fmt::Show(
&program,
semantic::program::fmt::Context::from(&interner)
)
);
println!("{}", color::Fg(color::Yellow, "--------------------------------------------------"));
}
if has_syntax_errors {
return ExitStatus::StaticError;
}
if args.check {
return ExitStatus::Success;
}
let program = Box::leak(Box::new(program));
let mut runtime = Runtime::new(
args.script_args.into_vec(), interner
);
match runtime.eval(program) {
Ok(_) => ExitStatus::Success,
Err(panic) => {
eprintln!("{}", fmt::Show(panic, runtime.interner()));
ExitStatus::Panic
}
}
}