diff --git a/Cargo.toml b/Cargo.toml index a23c32b..cacb295 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,10 +5,10 @@ authors = ["Wafelack "] edition = "2018" repository = "https://github.com/wafelack/orion" readme = "https://github.com/wafelack/orion/blob/master/README.md" -description = "Orion is a high level, purely functional Lisp dialect." +description = "LISP inspired purely functional programming language." documentation = "https://github.com/wafelack/orion/blob/master/DOCS.md" license = "GPL-3.0" -keywords = ["lisp", "functional", "fp", "language"] +keywords = ["lisp-like", "functional", "fp", "language"] [[bin]] diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..0a606f2 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,177 @@ +use clap::{App, Arg}; +use rustyline::{error::ReadlineError, Editor}; +use crate::{Result, print_err, error, OrionError, lexer::Lexer, parser::Parser, compiler::Compiler, vm::VM}; + +fn repl(no_prelude: bool, debug: bool, quiet: bool) -> Result<()> { + println!( + ";; Orion REPL v{}.\n +;; Copyright (C) 2021 Wafelack +;; This program comes with ABSOLUTELY NO WARRANTY. +;; This is free software, and you are welcome to redistribute it +;; under certain conditions.", +env!("CARGO_PKG_VERSION") +); + // let mut interpreter = Interpreter::new(vec![], no_prelude, quiet)?; + + let mut rl = Editor::<()>::new(); + let mut i = 0; + + loop { + i += 1; + let line = rl.readline("orion> "); + + match line { + Ok(line) => { + rl.add_history_entry(line.as_str()); + if line == "(quit)" { + return Ok(()); + } + + let tokens = match Lexer::new(line.trim(), "REPL").line(i).proc_tokens() { + Ok(t) => t, + Err(e) => { + print_err(e); + continue; + } + }; + + if debug { + println!("Tokens\n======"); + tokens.iter().for_each(|t| { + println!("{}", t.display()); + }); + println!(); + } + + let ast = match Parser::new(tokens, "REPL").parse() { + Ok(ast) => ast, + Err(e) => { + print_err(e); + continue; + } + }; + + if debug { + println!("Syntax Tree\n==========="); + ast.iter().for_each(|e| println!("{}", e.get_type())); + } + + // interpreter.update_ast(ast); + + if debug { + println!("\nStdout\n======"); + } + + let bytecode = Compiler::new(ast, "REPL").compile()?; + println!("[INSTRUCTIONS]"); + bytecode + .instructions + .iter() + .for_each(|i| println!("{:?}", i)); + println!("\n[SYMBOLS]"); + bytecode + .symbols + .iter() + .enumerate() + .for_each(|(idx, var)| println!("0x{:04x}: {}", idx, var)); + println!("\n[CONSTANTS]"); + bytecode + .constants + .iter() + .enumerate() + .for_each(|(idx, constant)| println!("0x{:04x}: {:?}", idx, constant)); + println!("\n[CHUNKS]"); + bytecode.chunks.iter().enumerate().for_each(|(idx, chunk)| { + println!("{}: {{", idx); + println!(" reference: ["); + chunk.reference.iter().for_each(|sym| { + println!(" 0x{:04x}", sym); + }); + println!(" ]\n instructions: ["); + chunk.instructions.iter().for_each(|instr| { + println!(" {:?}", instr); + }); + println!(" ]\n}}"); + }); + + let mut vm = VM::<256>::new(bytecode); + println!("-------------------------------"); + println!("[STDOUT]"); + let memory = vm.eval()?; + println!("\n[STACK]"); + vm.stack.into_iter().enumerate().for_each(|(idx, v)| { + println!("0x{:02x}: {:?}", idx, v); + }); + println!("\n[MEMORY]"); + memory.into_iter().enumerate().for_each(|(idx, v)| { + println!("0x{:02x}: {:?}", idx, v); + }); + + /* + let start = Instant::now(); + /* match interpreter.interpret(true) { + Ok(_) => {}, + Err(e) => { + print_err(e); + continue; + } + } */ + let elapsed = start.elapsed(); + if debug { + println!("\nDone in {}ms.", elapsed.as_millis()); + } */ + } + Err(ReadlineError::Interrupted) => { + println!(";; User break"); + } + Err(ReadlineError::Eof) => return Ok(()), + Err(_) => { + eprintln!("An error occured, please retry."); + continue; + } + } + } +} +macro_rules! get_app { + ($name:literal, $version:expr) => { + App::new($name) + .version($version) + .long_version(format!( +"{} +Copyright (C) 2021 Wafelack +License GPLv3+: GNU GPL version 3 or later +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law.", $version).as_str()) + .about("LISP inspired purely functional programming language.") + .after_help("Report bugs to: \nOrion home page: \nRepository: ") + .help_message("Print help information.") + .version_message("Print version information.") + .author(env!("CARGO_PKG_AUTHORS")) + .arg(Arg::with_name("file") + .index(1) + .takes_value(true) + .value_name("FILE") + .help("The source file to compile.")) + .arg(Arg::with_name("compile-only") + .short("c") + .long("compile-only") + .help("Compile, but do not run.")) + .arg(Arg::with_name("output") + .short("o") + .long("output") + .takes_value(true) + .value_name("FILE") + .help("Place the output into FILE.")) + .arg(Arg::with_name("debug-level") + .short("d") + .long("debug") + .value_name("LEVEL") + .takes_value(true) + .help("Set the debug level. Defaults to 0.")) + } +} +pub fn cli() -> Result<()> { + let matches = get_app!("Orion", env!("CARGO_PKG_VERSION")).get_matches(); + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 2afac66..9d3df41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,18 +24,12 @@ mod errors; mod lexer; mod parser; mod vm; +mod cli; // mod builtins; -use crate::{ - compiler::Compiler, - lexer::Lexer, - parser::Parser, - vm::{Value, VM}, -}; -use clap::{App, Arg}; +use crate::cli::cli; pub use errors::{OrionError, Result}; -use rustyline::{error::ReadlineError, Editor}; use std::{fs, path::Path, process::exit, time::Instant}; #[macro_export] @@ -82,215 +76,9 @@ fn print_err(e: OrionError) { ); } -fn repl(no_prelude: bool, debug: bool, quiet: bool) -> Result<()> { - println!( - ";; Orion REPL v{}.\n -;; Copyright (C) 2021 Wafelack -;; This program comes with ABSOLUTELY NO WARRANTY. -;; This is free software, and you are welcome to redistribute it -;; under certain conditions.", - env!("CARGO_PKG_VERSION") - ); - // let mut interpreter = Interpreter::new(vec![], no_prelude, quiet)?; - - let mut rl = Editor::<()>::new(); - let mut i = 0; - - loop { - i += 1; - let line = rl.readline("orion> "); - - match line { - Ok(line) => { - rl.add_history_entry(line.as_str()); - if line == "(quit)" { - return Ok(()); - } - - let tokens = match Lexer::new(line.trim(), "REPL").line(i).proc_tokens() { - Ok(t) => t, - Err(e) => { - print_err(e); - continue; - } - }; - - if debug { - println!("Tokens\n======"); - tokens.iter().for_each(|t| { - println!("{}", t.display()); - }); - println!(); - } - - let ast = match Parser::new(tokens, "REPL").parse() { - Ok(ast) => ast, - Err(e) => { - print_err(e); - continue; - } - }; - - if debug { - println!("Syntax Tree\n==========="); - ast.iter().for_each(|e| println!("{}", e.get_type())); - } - - // interpreter.update_ast(ast); - - if debug { - println!("\nStdout\n======"); - } - - let bytecode = Compiler::new(ast, "REPL").compile()?; - println!("[INSTRUCTIONS]"); - bytecode - .instructions - .iter() - .for_each(|i| println!("{:?}", i)); - println!("\n[SYMBOLS]"); - bytecode - .symbols - .iter() - .enumerate() - .for_each(|(idx, var)| println!("0x{:04x}: {}", idx, var)); - println!("\n[CONSTANTS]"); - bytecode - .constants - .iter() - .enumerate() - .for_each(|(idx, constant)| println!("0x{:04x}: {:?}", idx, constant)); - println!("\n[CHUNKS]"); - bytecode.chunks.iter().enumerate().for_each(|(idx, chunk)| { - println!("{}: {{", idx); - println!(" reference: ["); - chunk.reference.iter().for_each(|sym| { - println!(" 0x{:04x}", sym); - }); - println!(" ]\n instructions: ["); - chunk.instructions.iter().for_each(|instr| { - println!(" {:?}", instr); - }); - println!(" ]\n}}"); - }); - - let mut vm = VM::<256>::new(bytecode); - println!("-------------------------------"); - println!("[STDOUT]"); - let memory = vm.eval()?; - println!("\n[STACK]"); - vm.stack.into_iter().enumerate().for_each(|(idx, v)| { - println!("0x{:02x}: {:?}", idx, v); - }); - println!("\n[MEMORY]"); - memory.into_iter().enumerate().for_each(|(idx, v)| { - println!("0x{:02x}: {:?}", idx, v); - }); - - /* - let start = Instant::now(); - /* match interpreter.interpret(true) { - Ok(_) => {}, - Err(e) => { - print_err(e); - continue; - } - } */ - let elapsed = start.elapsed(); - if debug { - println!("\nDone in {}ms.", elapsed.as_millis()); - } */ - } - Err(ReadlineError::Interrupted) => { - println!(";; User break"); - } - Err(ReadlineError::Eof) => return Ok(()), - Err(_) => { - eprintln!("An error occured, please retry."); - continue; - } - } - } -} - -fn try_main() -> Result<()> { - let matches = App::new("orion") - .author(env!("CARGO_PKG_AUTHORS")) - .version(env!("CARGO_PKG_VERSION")) - .about("Orion is a purely functional lisp dialect.") - .arg( - Arg::with_name("file") - .takes_value(true) - .index(1) - .help("The source file to pass to the interpreter"), - ) - .arg( - Arg::with_name("no-load-prelude") - .short("np") - .long("no-load-prelude") - .help("Do not load the prelude file"), - ) - .arg( - Arg::with_name("debug") - .short("d") - .long("debug") - .help("Display debug information."), - ) - .arg( - Arg::with_name("quiet") - .short("q") - .long("quiet") - .help("Do not display messages."), - ) - .get_matches(); - - if let Some(path) = matches.value_of("file") { - if Path::new(path).exists() { - let content = match fs::read_to_string(path) { - Ok(c) => c, - Err(e) => return error!(=> "fatal: Failed to read file: {}.", e), - }; - - let tokens = Lexer::new(content, path).proc_tokens()?; - if matches.is_present("debug") { - println!("Tokens\n======"); - tokens.iter().for_each(|t| { - println!("{}", t.display()); - }); - println!(); - } - - let ast = Parser::new(tokens, path).parse()?; - - if matches.is_present("debug") { - println!("Syntax Tree\n==========="); - ast.iter().for_each(|e| println!("{}", e.get_type())); - } - if matches.is_present("debug") { - println!("\nStdout\n======"); - } - let start = Instant::now(); - // Interpreter::new(ast, matches.is_present("no-load-prelude"), matches.is_present("quiet"))?.interpret(false)?; - let elapsed = start.elapsed(); - if matches.is_present("debug") { - println!("\nDone in {}ms.", elapsed.as_millis()); - } - Ok(()) - } else { - error!(=> "fatal: File not found: {}.", path) - } - } else { - repl( - matches.is_present("no-load-prelude"), - matches.is_present("debug"), - matches.is_present("quiet"), - )?; - Ok(()) - } -} fn main() { - match try_main() { + match cli() { Ok(()) => {} Err(e) => { print_err(e);