cl 0.0.2: MAJOR ERGONOMIC BOOST
Broke frontend into its own library, "cl-frontend" - Frontend is pretty :D - Included sample fibonacci implementation Deprecated conlang::ast::Visitor in favor of bespoke traits - Rust traits are super cool. - The Interpreter is currently undergoing a major rewrite Added preliminary type-path support to the parser - Currently incomplete: type paths must end in Never..? Pretty printer is now even prettier - conlang::ast now exports all relevant AST nodes, since there are no namespace collisions any more
This commit is contained in:
		| @@ -1,10 +1,10 @@ | ||||
| [workspace] | ||||
| members = ["libconlang"] | ||||
| members = ["libconlang", "cl-frontend"] | ||||
| resolver = "2" | ||||
|  | ||||
| [workspace.package] | ||||
| repository = "https://git.soft.fish/j/Conlang" | ||||
| version = "0.0.1" | ||||
| version = "0.0.2" | ||||
| authors = ["John Breaux <j@soft.fish>"] | ||||
| edition = "2021" | ||||
| license = "MIT" | ||||
|   | ||||
							
								
								
									
										13
									
								
								cl-frontend/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								cl-frontend/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| [package] | ||||
| name = "cl-frontend" | ||||
| repository.workspace = true | ||||
| version.workspace = true | ||||
| authors.workspace = true | ||||
| edition.workspace = true | ||||
| license.workspace = true | ||||
| publish.workspace = true | ||||
|  | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
|  | ||||
| [dependencies] | ||||
| conlang = { path = "../libconlang" } | ||||
							
								
								
									
										483
									
								
								cl-frontend/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								cl-frontend/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,483 @@ | ||||
| //! Utilities for cl-frontend | ||||
|  | ||||
| pub mod args { | ||||
|     use crate::cli::Mode; | ||||
|     use std::{ | ||||
|         io::{stdin, IsTerminal}, | ||||
|         ops::Deref, | ||||
|         path::{Path, PathBuf}, | ||||
|     }; | ||||
|  | ||||
|     #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | ||||
|     pub struct Args { | ||||
|         pub path: Option<PathBuf>, // defaults None | ||||
|         pub repl: bool,            // defaults true if stdin is terminal | ||||
|         pub mode: Mode,            // defaults Interpret | ||||
|     } | ||||
|     const HELP: &str = "[( --repl | --no-repl )] [( -f | --file ) <filename>]"; | ||||
|  | ||||
|     impl Args { | ||||
|         pub fn new() -> Self { | ||||
|             Args { path: None, repl: stdin().is_terminal(), mode: Mode::Interpret } | ||||
|         } | ||||
|         pub fn parse(mut self) -> Option<Self> { | ||||
|             let mut args = std::env::args(); | ||||
|             let name = args.next().unwrap_or_default(); | ||||
|             let mut unknown = false; | ||||
|             while let Some(arg) = args.next() { | ||||
|                 match arg.deref() { | ||||
|                     "--repl" => self.repl = true, | ||||
|                     "--no-repl" => self.repl = false, | ||||
|                     "-f" | "--file" => self.path = args.next().map(PathBuf::from), | ||||
|                     "-m" | "--mode" => { | ||||
|                         self.mode = args.next().unwrap_or_default().parse().unwrap_or_default() | ||||
|                     } | ||||
|                     arg => { | ||||
|                         eprintln!("Unknown argument: {arg}"); | ||||
|                         unknown = true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if unknown { | ||||
|                 println!("Usage: {name} {HELP}"); | ||||
|                 None? | ||||
|             } | ||||
|             Some(self) | ||||
|         } | ||||
|         /// Returns the path to a file, if one was specified | ||||
|         pub fn path(&self) -> Option<&Path> { | ||||
|             self.path.as_deref() | ||||
|         } | ||||
|         /// Returns whether to start a REPL session or not | ||||
|         pub fn repl(&self) -> bool { | ||||
|             self.repl | ||||
|         } | ||||
|         /// Returns the repl Mode | ||||
|         pub fn mode(&self) -> Mode { | ||||
|             self.mode | ||||
|         } | ||||
|     } | ||||
|     impl Default for Args { | ||||
|         fn default() -> Self { | ||||
|             Self::new() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub mod program { | ||||
|     use std::io::{Result as IOResult, Write}; | ||||
|  | ||||
|     use conlang::{ | ||||
|         ast::preamble::{expression::Expr, *}, | ||||
|         interpreter::{error::IResult, Interpreter}, | ||||
|         lexer::Lexer, | ||||
|         parser::{error::PResult, Parser}, | ||||
|         pretty_printer::{PrettyPrintable, Printer}, | ||||
|         resolver::{error::TyResult, Resolve, Resolver}, | ||||
|         token::Token, | ||||
|     }; | ||||
|  | ||||
|     pub struct Tokenized { | ||||
|         tokens: Vec<Token>, | ||||
|     } | ||||
|  | ||||
|     pub enum Parsed { | ||||
|         Program(Start), | ||||
|         Expr(Expr), | ||||
|     } | ||||
|  | ||||
|     impl TryFrom<Tokenized> for Parsed { | ||||
|         type Error = conlang::parser::error::Error; | ||||
|         fn try_from(value: Tokenized) -> Result<Self, Self::Error> { | ||||
|             let mut parser = Parser::new(value.tokens); | ||||
|             let ast = parser.parse()?; | ||||
|             //if let Ok(ast) = ast { | ||||
|             //return | ||||
|             Ok(Self::Program(ast)) | ||||
|             //} | ||||
|  | ||||
|             //Ok(Self::Expr(parser.reset().parse_expr()?)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub struct Program<Variant> { | ||||
|         data: Variant, | ||||
|     } | ||||
|  | ||||
|     impl Program<Tokenized> { | ||||
|         pub fn new(input: &str) -> Result<Self, Vec<conlang::lexer::error::Error>> { | ||||
|             let mut tokens = vec![]; | ||||
|             let mut errors = vec![]; | ||||
|             for token in Lexer::new(input) { | ||||
|                 match token { | ||||
|                     Ok(token) => tokens.push(token), | ||||
|                     Err(error) => errors.push(error), | ||||
|                 } | ||||
|             } | ||||
|             if errors.is_empty() { | ||||
|                 Ok(Self { data: Tokenized { tokens } }) | ||||
|             } else { | ||||
|                 Err(errors) | ||||
|             } | ||||
|         } | ||||
|         pub fn tokens(&self) -> &[Token] { | ||||
|             &self.data.tokens | ||||
|         } | ||||
|         pub fn parse(self) -> PResult<Program<Parsed>> { | ||||
|             Ok(Program { data: Parsed::try_from(self.data)? }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl Program<Parsed> { | ||||
|         pub fn resolve(&mut self, resolver: &mut Resolver) -> TyResult<()> { | ||||
|             match &mut self.data { | ||||
|                 Parsed::Program(start) => start.resolve(resolver), | ||||
|                 Parsed::Expr(expr) => expr.resolve(resolver), | ||||
|             } | ||||
|             .map(|ty| println!("{ty}")) | ||||
|         } | ||||
|         /// Runs the [Program] in the specified [Interpreter] | ||||
|         pub fn run(&self, interpreter: &mut Interpreter) -> IResult<()> { | ||||
|             match &self.data { | ||||
|                 Parsed::Program(start) => interpreter.interpret(start), | ||||
|                 Parsed::Expr(expr) => { | ||||
|                     for value in interpreter.eval(expr)? { | ||||
|                         println!("{value}") | ||||
|                     } | ||||
|                     Ok(()) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl PrettyPrintable for Program<Parsed> { | ||||
|         fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|             match &self.data { | ||||
|                 Parsed::Program(value) => value.visit(p), | ||||
|                 Parsed::Expr(value) => value.visit(p), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub mod cli { | ||||
|     use conlang::{ | ||||
|         interpreter::Interpreter, pretty_printer::PrettyPrintable, resolver::Resolver, token::Token, | ||||
|     }; | ||||
|  | ||||
|     use crate::{ | ||||
|         args::Args, | ||||
|         program::{Parsed, Program, Tokenized}, | ||||
|     }; | ||||
|     use std::{ | ||||
|         convert::Infallible, | ||||
|         error::Error, | ||||
|         io::{stdin, stdout, Write}, | ||||
|         path::{Path, PathBuf}, | ||||
|         str::FromStr, | ||||
|     }; | ||||
|  | ||||
|     // ANSI color escape sequences | ||||
|     const ANSI_RED: &str = "\x1b[31m"; | ||||
|     const ANSI_GREEN: &str = "\x1b[32m"; | ||||
|     const ANSI_CYAN: &str = "\x1b[36m"; | ||||
|     const ANSI_BRIGHT_BLUE: &str = "\x1b[94m"; | ||||
|     const ANSI_BRIGHT_MAGENTA: &str = "\x1b[95m"; | ||||
|     // const ANSI_BRIGHT_CYAN: &str = "\x1b[96m"; | ||||
|     const ANSI_RESET: &str = "\x1b[0m"; | ||||
|     const ANSI_OUTPUT: &str = "\x1b[38;5;117m"; | ||||
|  | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub enum CLI { | ||||
|         Repl(Repl), | ||||
|         File { mode: Mode, path: PathBuf }, | ||||
|         Stdin { mode: Mode }, | ||||
|     } | ||||
|     impl From<Args> for CLI { | ||||
|         fn from(value: Args) -> Self { | ||||
|             let Args { path, repl, mode } = value; | ||||
|             match (repl, path) { | ||||
|                 (_, Some(path)) => Self::File { mode, path }, | ||||
|                 (true, None) => Self::Repl(Repl { mode, ..Default::default() }), | ||||
|                 (false, None) => Self::Stdin { mode }, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     impl CLI { | ||||
|         pub fn run(&mut self) -> Result<(), Box<dyn Error>> { | ||||
|             use std::{fs, io}; | ||||
|             match self { | ||||
|                 CLI::Repl(repl) => repl.repl(), | ||||
|                 CLI::File { mode, ref path } => { | ||||
|                     // read file | ||||
|                     Self::no_repl(*mode, Some(path), &fs::read_to_string(path)?) | ||||
|                 } | ||||
|                 CLI::Stdin { mode } => { | ||||
|                     Self::no_repl(*mode, None, &io::read_to_string(io::stdin())?) | ||||
|                 } | ||||
|             } | ||||
|             Ok(()) | ||||
|         } | ||||
|         fn no_repl(mode: Mode, path: Option<&Path>, code: &str) { | ||||
|             let program = Program::new(code); | ||||
|             match (mode, program) { | ||||
|                 (Mode::Tokenize, Ok(program)) => { | ||||
|                     for token in program.tokens() { | ||||
|                         if let Some(path) = path { | ||||
|                             print!("{}:", path.display()); | ||||
|                         } | ||||
|                         print_token(token) | ||||
|                     } | ||||
|                 } | ||||
|                 (Mode::Beautify, Ok(program)) => Self::beautify(program), | ||||
|                 (Mode::Resolve, Ok(program)) => Self::resolve(program, Default::default()), | ||||
|                 (Mode::Interpret, Ok(program)) => Self::interpret(program, Default::default()), | ||||
|                 (_, Err(errors)) => { | ||||
|                     for error in errors { | ||||
|                         if let Some(path) = path { | ||||
|                             eprint!("{}:", path.display()); | ||||
|                         } | ||||
|                         eprintln!("{error}") | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         fn beautify(program: Program<Tokenized>) { | ||||
|             match program.parse() { | ||||
|                 Ok(program) => program.print(), | ||||
|                 Err(e) => eprintln!("{e}"), | ||||
|             }; | ||||
|         } | ||||
|         fn resolve(program: Program<Tokenized>, mut resolver: Resolver) { | ||||
|             let mut program = match program.parse() { | ||||
|                 Ok(program) => program, | ||||
|                 Err(e) => { | ||||
|                     eprintln!("{e}"); | ||||
|                     return; | ||||
|                 } | ||||
|             }; | ||||
|             if let Err(e) = program.resolve(&mut resolver) { | ||||
|                 eprintln!("{e}"); | ||||
|             } | ||||
|         } | ||||
|         fn interpret(program: Program<Tokenized>, mut interpreter: Interpreter) { | ||||
|             let program = match program.parse() { | ||||
|                 Ok(program) => program, | ||||
|                 Err(e) => { | ||||
|                     eprintln!("{e}"); | ||||
|                     return; | ||||
|                 } | ||||
|             }; | ||||
|             if let Err(e) = program.run(&mut interpreter) { | ||||
|                 eprintln!("{e}"); | ||||
|                 return; | ||||
|             } | ||||
|             if let Err(e) = interpreter.call("main", &[]) { | ||||
|                 eprintln!("{e}"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// The CLI's operating mode | ||||
|     #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] | ||||
|     pub enum Mode { | ||||
|         Tokenize, | ||||
|         Beautify, | ||||
|         Resolve, | ||||
|         #[default] | ||||
|         Interpret, | ||||
|     } | ||||
|     impl Mode { | ||||
|         pub fn ansi_color(self) -> &'static str { | ||||
|             match self { | ||||
|                 Mode::Tokenize => ANSI_BRIGHT_BLUE, | ||||
|                 Mode::Beautify => ANSI_BRIGHT_MAGENTA, | ||||
|                 Mode::Resolve => ANSI_GREEN, | ||||
|                 Mode::Interpret => ANSI_CYAN, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     impl FromStr for Mode { | ||||
|         type Err = Infallible; | ||||
|         fn from_str(s: &str) -> Result<Self, Infallible> { | ||||
|             Ok(match s { | ||||
|                 "i" | "interpret" => Mode::Interpret, | ||||
|                 "b" | "beautify" | "p" | "pretty" => Mode::Beautify, | ||||
|                 "r" | "resolve" | "typecheck" => Mode::Resolve, | ||||
|                 "t" | "tokenize" => Mode::Tokenize, | ||||
|                 _ => Mode::Interpret, | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Implements the interactive interpreter | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct Repl { | ||||
|         prompt_again: &'static str, // " ?>" | ||||
|         prompt_begin: &'static str, // "cl>" | ||||
|         prompt_error: &'static str, // "! >" | ||||
|         interpreter: Interpreter, | ||||
|         resolver: Resolver, | ||||
|         mode: Mode, | ||||
|     } | ||||
|  | ||||
|     impl Default for Repl { | ||||
|         fn default() -> Self { | ||||
|             Self { | ||||
|                 prompt_begin: "cl>", | ||||
|                 prompt_again: " ?>", | ||||
|                 prompt_error: "! >", | ||||
|                 interpreter: Default::default(), | ||||
|                 resolver: Default::default(), | ||||
|                 mode: Default::default(), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Prompt functions | ||||
|     impl Repl { | ||||
|         pub fn prompt_begin(&self) { | ||||
|             print!( | ||||
|                 "{}{} {ANSI_RESET}", | ||||
|                 self.mode.ansi_color(), | ||||
|                 self.prompt_begin | ||||
|             ); | ||||
|             let _ = stdout().flush(); | ||||
|         } | ||||
|         pub fn prompt_again(&self) { | ||||
|             print!( | ||||
|                 "{}{} {ANSI_RESET}", | ||||
|                 self.mode.ansi_color(), | ||||
|                 self.prompt_again | ||||
|             ); | ||||
|             let _ = stdout().flush(); | ||||
|         } | ||||
|         pub fn prompt_error(&self, err: &impl Error) { | ||||
|             println!("{ANSI_RED}{} {err}{ANSI_RESET}", self.prompt_error) | ||||
|         } | ||||
|         pub fn begin_output(&self) { | ||||
|             print!("{ANSI_OUTPUT}") | ||||
|         } | ||||
|         fn reprompt(&self, buf: &mut String) { | ||||
|             self.prompt_begin(); | ||||
|             buf.clear(); | ||||
|         } | ||||
|     } | ||||
|     /// The actual REPL | ||||
|     impl Repl { | ||||
|         /// Constructs a new [Repl] with the provided [Mode] | ||||
|         pub fn new(mode: Mode) -> Self { | ||||
|             Self { mode, ..Default::default() } | ||||
|         } | ||||
|         /// Runs the main REPL loop | ||||
|         pub fn repl(&mut self) { | ||||
|             let mut buf = String::new(); | ||||
|             self.prompt_begin(); | ||||
|             while let Ok(len) = stdin().read_line(&mut buf) { | ||||
|                 // Exit the loop | ||||
|                 if len == 0 { | ||||
|                     println!(); | ||||
|                     break; | ||||
|                 } | ||||
|                 self.begin_output(); | ||||
|                 // Process mode-change commands | ||||
|                 if self.command(&buf) { | ||||
|                     self.reprompt(&mut buf); | ||||
|                     continue; | ||||
|                 } | ||||
|                 // Lex the buffer, or reset and output the error | ||||
|                 let code = match Program::new(&buf) { | ||||
|                     Ok(code) => code, | ||||
|                     Err(e) => { | ||||
|                         for error in e { | ||||
|                             eprintln!("{error}"); | ||||
|                         } | ||||
|                         self.reprompt(&mut buf); | ||||
|                         continue; | ||||
|                     } | ||||
|                 }; | ||||
|                 // Tokenize mode doesn't require valid parse, so it gets processed first | ||||
|                 if self.mode == Mode::Tokenize { | ||||
|                     self.tokenize(&code); | ||||
|                     self.reprompt(&mut buf); | ||||
|                     continue; | ||||
|                 } | ||||
|                 // Parse code and dispatch to the proper function | ||||
|                 match (len, code.parse()) { | ||||
|                     // If the code is OK, run it and print any errors | ||||
|                     (_, Ok(mut code)) => { | ||||
|                         self.dispatch(&mut code); | ||||
|                         buf.clear(); | ||||
|                     } | ||||
|                     // If the user types two newlines, print syntax errors | ||||
|                     (1, Err(e)) => { | ||||
|                         self.prompt_error(&e); | ||||
|                         buf.clear(); | ||||
|                     } | ||||
|                     // Otherwise, ask for more input | ||||
|                     _ => { | ||||
|                         self.prompt_again(); | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
|                 self.prompt_begin() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fn help(&self) { | ||||
|             println!( | ||||
|                 "Commands:\n- $tokens\n    Tokenize Mode:\n    Outputs information derived by the Lexer\n- $pretty\n    Beautify Mode:\n    Pretty-prints the input\n- $type\n    Resolve Mode:\n    Attempts variable resolution and type-checking on the input\n- $run\n    Interpret Mode:\n    Interprets the input using Conlang\'s work-in-progress interpreter\n- $mode\n    Prints the current mode\n- $help\n    Prints this help message" | ||||
|             ); | ||||
|         } | ||||
|         fn command(&mut self, line: &str) -> bool { | ||||
|             match line.trim() { | ||||
|                 "$pretty" => self.mode = Mode::Beautify, | ||||
|                 "$tokens" => self.mode = Mode::Tokenize, | ||||
|                 "$type" => self.mode = Mode::Resolve, | ||||
|                 "$run" => self.mode = Mode::Interpret, | ||||
|                 "$mode" => print!("{:?} Mode", self.mode), | ||||
|                 "$help" => self.help(), | ||||
|                 _ => return false, | ||||
|             } | ||||
|             println!(); | ||||
|             true | ||||
|         } | ||||
|         /// Dispatches calls to repl functions based on the program | ||||
|         fn dispatch(&mut self, code: &mut Program<Parsed>) { | ||||
|             match self.mode { | ||||
|                 Mode::Tokenize => (), | ||||
|                 Mode::Beautify => self.beautify(code), | ||||
|                 Mode::Resolve => self.typecheck(code), | ||||
|                 Mode::Interpret => self.interpret(code), | ||||
|             } | ||||
|         } | ||||
|         fn tokenize(&mut self, code: &Program<Tokenized>) { | ||||
|             for token in code.tokens() { | ||||
|                 print_token(token); | ||||
|             } | ||||
|         } | ||||
|         fn interpret(&mut self, code: &Program<Parsed>) { | ||||
|             if let Err(e) = code.run(&mut self.interpreter) { | ||||
|                 self.prompt_error(&e) | ||||
|             } | ||||
|         } | ||||
|         fn typecheck(&mut self, code: &mut Program<Parsed>) { | ||||
|             if let Err(e) = code.resolve(&mut self.resolver) { | ||||
|                 self.prompt_error(&e) | ||||
|             } | ||||
|         } | ||||
|         fn beautify(&mut self, code: &Program<Parsed>) { | ||||
|             code.print() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn print_token(t: &Token) { | ||||
|         println!( | ||||
|             "{:02}:{:02}: {:#19} │{}│", | ||||
|             t.line(), | ||||
|             t.col(), | ||||
|             t.ty(), | ||||
|             t.data(), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										9
									
								
								cl-frontend/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								cl-frontend/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use cl_frontend::{args::Args, cli::CLI}; | ||||
| use std::error::Error; | ||||
|  | ||||
| fn main() -> Result<(), Box<dyn Error>> { | ||||
|     // parse args | ||||
|     let args = Args::new().parse().unwrap_or_default(); | ||||
|     let mut cli = CLI::from(args); | ||||
|     cli.run() | ||||
| } | ||||
							
								
								
									
										26
									
								
								grammar.ebnf
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								grammar.ebnf
									
									
									
									
									
								
							| @@ -1,7 +1,14 @@ | ||||
| (* Conlang Expression Grammar *) | ||||
| Start       = Program ; | ||||
| Program     = Stmt* EOI ; | ||||
|  | ||||
| (* TODO:  | ||||
| - Replace Program with Module | ||||
|     Module = Decl* EOI ; | ||||
| - Move Fn and Let into "Decl": | ||||
|     Decl   = Fn | Let ; | ||||
| - allow Decl | ExprStmt in Stmt: | ||||
|     Stmt   = Decl | Expr ';' ; | ||||
| *) | ||||
| (* literal *) | ||||
| Literal     = STRING | CHARACTER | FLOAT | INTEGER | Bool ; | ||||
| Bool        = "true" | "false" ; | ||||
| @@ -10,11 +17,22 @@ Identifier  = IDENTIFIER ; | ||||
| (* # Statements *) | ||||
| (* statement *) | ||||
| Stmt        = Fn | Let | Expr ';' ; | ||||
| Let         = "let" "mut"? Identifier (':' Type)? ('=' Expr)? ';' ; | ||||
| Let         = "let" Name ('=' Expr)? ';' ; | ||||
| Fn          = "fn" Identifier '(' Params? ')' Block ; | ||||
| (* TODO: Type system *) | ||||
| Params      = (Param ',')* Param? ; | ||||
| Param       = Identifier (*':' Type *) ; | ||||
| Params      = (Name ',')* Name? ; | ||||
| Name        = "mut"? Identifier (':' Type )? ; | ||||
|  | ||||
| (* # Type Expressions *) | ||||
| (* types *) | ||||
| TypeExpr    = Never | Empty | TypeTuple | PathExpr ; | ||||
| Never       = '!' ; | ||||
| Empty       = '(' ')' ; | ||||
| TypeTuple   = '(' (TypeExpr ',')* TypeExpr? ')' ; | ||||
|  | ||||
| PathExpr    = '::'? PathPart ; | ||||
| PathPart    = "super" | Identifier ; | ||||
|  | ||||
|  | ||||
| (* # Expressions *) | ||||
| (* expression *) | ||||
|   | ||||
| @@ -1,60 +0,0 @@ | ||||
| //! This example grabs input from stdin, lexes it, parses it, and prints the AST | ||||
| #![allow(unused_imports)] | ||||
| use conlang::{lexer::Lexer, parser::Parser, pretty_printer::PrettyPrintable, token::Token}; | ||||
| use std::{ | ||||
|     error::Error, | ||||
|     io::{stdin, stdout, IsTerminal, Read, Write}, | ||||
|     path::{Path, PathBuf}, | ||||
| }; | ||||
|  | ||||
| fn main() -> Result<(), Box<dyn Error>> { | ||||
|     let conf = Config::new(); | ||||
|     if conf.paths.is_empty() { | ||||
|         take_stdin()?; | ||||
|     } else { | ||||
|         for path in conf.paths.iter().map(PathBuf::as_path) { | ||||
|             parse(&std::fs::read_to_string(path)?, Some(path)); | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| struct Config { | ||||
|     paths: Vec<PathBuf>, | ||||
| } | ||||
|  | ||||
| impl Config { | ||||
|     fn new() -> Self { | ||||
|         Config { paths: std::env::args().skip(1).map(PathBuf::from).collect() } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn take_stdin() -> Result<(), Box<dyn Error>> { | ||||
|     const PROMPT: &str = "> "; | ||||
|     if stdin().is_terminal() { | ||||
|         print!("{PROMPT}"); | ||||
|         stdout().flush()?; | ||||
|         for line in stdin().lines() { | ||||
|             let line = line?; | ||||
|             if !line.is_empty() { | ||||
|                 parse(&line, None); | ||||
|                 println!(); | ||||
|             } | ||||
|             print!("{PROMPT}"); | ||||
|             stdout().flush()?; | ||||
|         } | ||||
|     } else { | ||||
|         parse(&std::io::read_to_string(stdin())?, None) | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn parse(file: &str, path: Option<&Path>) { | ||||
|     use conlang::parser::error::Error; | ||||
|     match Parser::from(Lexer::new(file)).parse() { | ||||
|         Ok(ast) => ast.print(), | ||||
|         Err(e) if e.start().is_some() => print!("{:?}:{}", path.unwrap_or(Path::new("-")), e), | ||||
|         Err(e) => print!("{e}"), | ||||
|     } | ||||
|     println!(); | ||||
| } | ||||
| @@ -1,123 +0,0 @@ | ||||
| //! This example grabs input from stdin or a file, lexes it, parses it, and interprets it | ||||
| use conlang::{ | ||||
|     ast::{expression::Expr, Start}, | ||||
|     interpreter::Interpreter, | ||||
|     lexer::Lexer, | ||||
|     parser::Parser, | ||||
| }; | ||||
| use std::{ | ||||
|     error::Error, | ||||
|     io::{stdin, stdout, IsTerminal, Write}, | ||||
|     path::{Path, PathBuf}, | ||||
| }; | ||||
|  | ||||
| fn main() -> Result<(), Box<dyn Error>> { | ||||
|     let conf = Config::new(); | ||||
|     if conf.paths.is_empty() { | ||||
|         take_stdin()?; | ||||
|     } else { | ||||
|         for path in conf.paths.iter().map(PathBuf::as_path) { | ||||
|             run_file(&std::fs::read_to_string(path)?, Some(path))?; | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| struct Config { | ||||
|     paths: Vec<PathBuf>, | ||||
| } | ||||
|  | ||||
| impl Config { | ||||
|     fn new() -> Self { | ||||
|         Config { paths: std::env::args().skip(1).map(PathBuf::from).collect() } | ||||
|     } | ||||
| } | ||||
|  | ||||
| macro_rules! prompt { | ||||
|     ($($t:tt)*) => {{ | ||||
|         let mut out = stdout().lock(); | ||||
|         out.write_all(b"\x1b[36m")?; | ||||
|         write!(out, $($t)*)?; | ||||
|         out.write_all(b"\x1b[0m")?; | ||||
|         out.flush() | ||||
|     }}; | ||||
| } | ||||
|  | ||||
| fn take_stdin() -> Result<(), Box<dyn Error>> { | ||||
|     const CONLANG: &str = "cl>"; | ||||
|     const MOREPLS: &str = " ?>"; | ||||
|     if stdin().is_terminal() { | ||||
|         let mut interpreter = Interpreter::new(); | ||||
|         let mut buf = String::new(); | ||||
|         prompt!("{CONLANG} ")?; | ||||
|         while let Ok(len) = stdin().read_line(&mut buf) { | ||||
|             let code = Program::parse(&buf); | ||||
|             match (len, code) { | ||||
|                 // Exit the loop | ||||
|                 (0, _) => { | ||||
|                     println!(); | ||||
|                     break; | ||||
|                 } | ||||
|                 // If the code is OK, run it and print any errors | ||||
|                 (_, Ok(code)) => { | ||||
|                     let _ = code.run(&mut interpreter).map_err(|e| eprintln!("{e}")); | ||||
|                     buf.clear(); | ||||
|                 } | ||||
|                 // If the user types two newlines, print syntax errors | ||||
|                 (1, Err(e)) => { | ||||
|                     eprintln!("{e}"); | ||||
|                     buf.clear(); | ||||
|                 } | ||||
|                 // Otherwise, ask for more input | ||||
|                 _ => { | ||||
|                     prompt!("{MOREPLS} ")?; | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             prompt!("{CONLANG} ")?; | ||||
|         } | ||||
|     } else { | ||||
|         run_file(&std::io::read_to_string(stdin())?, None)? | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn run_file(file: &str, path: Option<&Path>) -> Result<(), Box<dyn Error>> { | ||||
|     let mut interpreter = Interpreter::new(); | ||||
|     match Parser::from(Lexer::new(file)).parse() { | ||||
|         Ok(ast) => { | ||||
|             interpreter.interpret(&ast)?; | ||||
|             println!("{}", interpreter.call("main", &[])?) | ||||
|         }, | ||||
|         Err(e) if e.start().is_some() => print!("{:?}:{}", path.unwrap_or(Path::new("-")), e), | ||||
|         Err(e) => print!("{e}"), | ||||
|     } | ||||
|     println!(); | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| enum Program { | ||||
|     Program(Start), | ||||
|     Expr(Expr), | ||||
| } | ||||
|  | ||||
| impl Program { | ||||
|     fn parse(input: &str) -> conlang::parser::error::PResult<Self> { | ||||
|         let ast = Parser::from(Lexer::new(input)).parse(); | ||||
|         if let Ok(ast) = ast { | ||||
|             return Ok(Self::Program(ast)); | ||||
|         } | ||||
|         Ok(Self::Expr(Parser::from(Lexer::new(input)).parse_expr()?)) | ||||
|     } | ||||
|     fn run(&self, interpreter: &mut Interpreter) -> conlang::interpreter::error::IResult<()> { | ||||
|         match self { | ||||
|             Program::Program(start) => interpreter.interpret(start), | ||||
|             Program::Expr(expr) => { | ||||
|                 for value in interpreter.eval(expr)? { | ||||
|                     println!("{value}") | ||||
|                 } | ||||
|                 Ok(()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -11,19 +11,16 @@ | ||||
| //! See [statement], [literal], and [expression] for more information. | ||||
|  | ||||
| pub mod preamble { | ||||
|     #![allow(deprecated)] | ||||
|     //! Common imports for working with the [ast](super) | ||||
|     pub use super::{ | ||||
|         expression::{ | ||||
|             self, | ||||
|             call::*, | ||||
|             control, | ||||
|             math::{self, operator}, | ||||
|             tuple::*, | ||||
|         }, | ||||
|         literal, | ||||
|         expression::{call::*, control::*, math::*, tuple::*, *}, | ||||
|         literal::*, | ||||
|         path::*, | ||||
|         statement::*, | ||||
|         types::*, | ||||
|         visitor::Visitor, | ||||
|         Identifier, Program, Start, | ||||
|         *, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -43,47 +40,9 @@ pub struct Program(pub Vec<statement::Stmt>); | ||||
| /// # Syntax | ||||
| /// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier) | ||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||
| pub struct Identifier(pub String); | ||||
|  | ||||
| pub mod todo { | ||||
|     //! temporary storage for pending expression work.  \ | ||||
|     //! when an item is in progress, remove it from todo. | ||||
|     //! | ||||
|     //! # General TODOs: | ||||
|     //! - [x] Implement support for storing items in the AST | ||||
|     //! - [ ] Keep track of the source location of each node | ||||
|     //! - [ ] Implement paths | ||||
|     //! - [x] Implement functions | ||||
|     //! - [ ] Implement structs | ||||
|     //! - [ ] Implement enums | ||||
|     //! - [ ] Implement implementation | ||||
|     //! - [ ] Store token spans in AST | ||||
|     pub mod path { | ||||
|         //! Path support | ||||
|         //! - [ ] Add namespace syntax (i.e. `::crate::foo::bar` | `foo::bar::Baz` | `foo::bar::*`) | ||||
|         //! | ||||
|         //! Path resolution will be vital to the implementation of structs, enums, impl blocks, | ||||
|         //! traits, modules, etc. | ||||
|     } | ||||
|  | ||||
|     pub mod structure { | ||||
|         //! Struct support | ||||
|         //! - [ ] Add struct declaration expression (returns a struct declaration) | ||||
|         //! - [ ] Add struct value expression (returns a struct value) | ||||
|         //! - [ ] Add struct update syntax (yippee!!) | ||||
|     } | ||||
|  | ||||
|     pub mod enumeration { | ||||
|         //! Enum support | ||||
|         //! - [ ] Add enum declaration expression (returns an enum declaration) | ||||
|         //! - [ ] Add enum value expression (returns an enum value) | ||||
|     } | ||||
|  | ||||
|     pub mod implementation { | ||||
|         //! Impl block support | ||||
|         //! - [ ] Add impl block expression? Statement? | ||||
|         //! - [ ] Add member function call expression | ||||
|     } | ||||
| pub struct Identifier { | ||||
|     pub name: String, | ||||
|     pub index: Option<usize>, | ||||
| } | ||||
|  | ||||
| pub mod literal { | ||||
| @@ -150,6 +109,7 @@ pub mod statement { | ||||
|  | ||||
|     use super::{ | ||||
|         expression::{Block, Expr}, | ||||
|         types::TypeExpr, | ||||
|         Identifier, | ||||
|     }; | ||||
|  | ||||
| @@ -164,7 +124,7 @@ pub mod statement { | ||||
|         Let(Let), | ||||
|         /// Contains a function declaration | ||||
|         /// # Syntax | ||||
|         /// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'` [`Block`] | ||||
|         /// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` `Args...` `')'` [`Block`] | ||||
|         Fn(FnDecl), | ||||
|         /// Contains an expression statement | ||||
|         /// # Syntax | ||||
| @@ -177,26 +137,104 @@ pub mod statement { | ||||
|     /// [`Let`] := `let` [`Identifier`] (`:`) `Type`)? (`=` [`Expr`])? `;` | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct Let { | ||||
|         pub name: Identifier, | ||||
|         pub mutable: bool, | ||||
|         pub ty: Option<Identifier>, | ||||
|         pub name: Name, | ||||
|         pub init: Option<Expr>, | ||||
|     } | ||||
|  | ||||
|     /// Contains a function declaration | ||||
|     /// # Syntax | ||||
|     /// [`FnDecl`] := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'` | ||||
|     /// [`FnDecl`] := `"fn"` [`Identifier`] `'('` `Args...` `')'` | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct FnDecl { | ||||
|         pub name: Identifier, | ||||
|         pub args: Vec<Identifier>, | ||||
|         pub name: Name, | ||||
|         pub args: Vec<Name>, | ||||
|         pub body: Block, | ||||
|         // TODO: Store type information | ||||
|     } | ||||
|  | ||||
|     /// Contains the name, mutability, and type information for a [Let] or [FnDecl] | ||||
|     /// # Syntax | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct Name { | ||||
|         pub name: Identifier, | ||||
|         /// The mutability of the [Name]. Functions are never mutable. | ||||
|         pub mutable: bool, | ||||
|         /// The [type](TypeExpr) | ||||
|         pub ty: Option<TypeExpr>, | ||||
|     } | ||||
|  | ||||
|     // TODO: Create closure, transmute fndecl into a name and closure | ||||
| } | ||||
|  | ||||
| pub mod path { | ||||
|     //! Paths | ||||
|     //! | ||||
|     //! A Path Expression refers to an item, either local or module-scoped. | ||||
|  | ||||
|     use super::Identifier; | ||||
|  | ||||
|     /// A path to an item in a module | ||||
|     /// # Syntax | ||||
|     /// [`Path`]` := "::"? `[`PathPart`]` ("::" `[`PathPart`]`)*` | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct Path { | ||||
|         pub absolute: bool, | ||||
|         pub parts: Vec<PathPart>, | ||||
|     } | ||||
|  | ||||
|     /// A component of a [`TypePath`] | ||||
|     /// # Syntax | ||||
|     /// [`PathPart`]` := "super" | `[`Identifier`] | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub enum PathPart { | ||||
|         PathSuper, | ||||
|         PathSelf, | ||||
|         PathIdent(Identifier), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub mod types { | ||||
|     //! # Types | ||||
|     //! | ||||
|     //! The [Type Expresson](TypeExpr) powers Conlang's type checker. | ||||
|     //! | ||||
|     //! # Syntax | ||||
|     //! [`TypeExpr`]` := `[`TupleType`]` | `[`TypePath`]` | `[`Never`] | ||||
|  | ||||
|     pub use super::path::Path as TypePath; | ||||
|  | ||||
|     /// Contains a [Type Expression](self) | ||||
|     /// | ||||
|     /// # Syntax | ||||
|     /// [`TypeExpr`]` := `[`TupleType`]` | `[`TypePath`]` | `[`Empty`]` | `[`Never`] | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub enum TypeExpr { | ||||
|         TupleType(TupleType), | ||||
|         TypePath(TypePath), | ||||
|         Empty(Empty), | ||||
|         Never(Never), | ||||
|     } | ||||
|  | ||||
|     /// A [TupleType] represents the [TypeExpr] of a Tuple value | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct TupleType { | ||||
|         pub types: Vec<TypeExpr>, | ||||
|     } | ||||
|  | ||||
|     /// The empty type. You get nothing! You lose! | ||||
|     /// # Syntax | ||||
|     /// [`Empty`]` := '(' ')'` | ||||
|     #[derive(Clone, Copy, Debug, Default)] | ||||
|     pub struct Empty; | ||||
|  | ||||
|     /// The never type. This type can never be constructed, and can only appear if a block of code | ||||
|     /// doesn't terminate | ||||
|     /// # Syntax | ||||
|     /// [`Never`]` := '!'` | ||||
|     #[derive(Clone, Copy, Debug, Default)] | ||||
|     pub struct Never; | ||||
| } | ||||
|  | ||||
| pub mod expression { | ||||
|     //! # Expressions | ||||
|     //! | ||||
| @@ -219,17 +257,19 @@ pub mod expression { | ||||
|     //! | 9 | [`control::Flow`] | Branch expressions (`if`, `while`, `for`, `return`, `break`, `continue`) | ||||
|     //! | 9 |         [`Group`] | Group expressions `(` [Expr]? `)` /* Can evaluate to Empty! */ | ||||
|     //! | 9 |         [`Block`] | Block expressions `{` [Expr] `}` | ||||
|     //! | 9 |       [`Primary`] | Contains an [Identifier], [Literal](literal::Literal), [Block], [Group], or [Flow](control::Flow) | ||||
|     //! | 9 |       [`Primary`] | Contains an [Identifier], [Literal], [Block], [Group], or [Flow] | ||||
|     //! | ||||
|     //! ## Syntax | ||||
|     //! [`Expr`]`  := `[`math::Operation`]  \ | ||||
|     //! [`Block`]` := '{' `[`Expr`]` '}'`   \ | ||||
|     //! [`Group`]` := '(' `[`Expr`]`? ')'`  \ | ||||
|     //! [`Primary`]` := `[`Identifier`]` | `[`Literal`](literal::Literal)` | `[`Block`]` | | ||||
|     //! `[`Group`]` | `[`control::Flow`] | ||||
|     //! [`Primary`]` := `[`Identifier`]` | `[`Literal`]` | `[`Block`]` | | ||||
|     //! `[`Group`]` | `[`Flow`] | ||||
|     //! | ||||
|     //! See [control] and [math] for their respective production rules. | ||||
|     use super::{statement::Stmt, *}; | ||||
|     use super::{literal::Literal, statement::Stmt, *}; | ||||
|     use control::Flow; | ||||
|     use tuple::Group; | ||||
|  | ||||
|     /// Contains an expression | ||||
|     /// | ||||
| @@ -241,18 +281,18 @@ pub mod expression { | ||||
|     /// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest | ||||
|     /// derivation) | ||||
|     /// # Syntax | ||||
|     /// [`Primary`]` := `[`IDENTIFIER`](Identifier)` | ||||
|     /// | `[`Literal`](literal::Literal)` | ||||
|     /// | `[`Block`]` | ||||
|     /// | `[`Group`](tuple::Group)` | ||||
|     /// | `[`Branch`](control::Flow) | ||||
|     /// [`Primary`]` := `[`Identifier`]` | ||||
|     /// | `[`Literal`]` | ||||
|     /// | `[`Block`]` | ||||
|     /// | `[`Group`]` | ||||
|     /// | `[`Branch`](Flow) | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub enum Primary { | ||||
|         Identifier(Identifier), | ||||
|         Literal(literal::Literal), | ||||
|         Literal(Literal), | ||||
|         Block(Block), | ||||
|         Group(tuple::Group), | ||||
|         Branch(control::Flow), | ||||
|         Group(Group), | ||||
|         Branch(Flow), | ||||
|     } | ||||
|  | ||||
|     /// Contains a Block Expression | ||||
| @@ -260,6 +300,7 @@ pub mod expression { | ||||
|     /// [`Block`] := `'{'` [`Expr`] `'}'` | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct Block { | ||||
|         pub let_count: Option<usize>, | ||||
|         pub statements: Vec<Stmt>, | ||||
|         pub expr: Option<Box<Expr>>, | ||||
|     } | ||||
| @@ -352,7 +393,7 @@ pub mod expression { | ||||
|         //! [`Shift`][2]`   := `[`Term`][2]`    (`[`ShiftOp`][5]`   `[`Term`][2]`   )*`  \ | ||||
|         //! [`Term`][2]`    := `[`Factor`][2]`  (`[`TermOp`][5]`    `[`Factor`][2]` )*`  \ | ||||
|         //! [`Factor`][2]`  := `[`Unary`][1]`   (`[`FactorOp`][5]`  `[`Unary`][1]`  )*`  \ | ||||
|         //! [`Unary`][1]`   := (`[`UnaryOp`][4]`)* `[`FnCall`][7] | ||||
|         //! [`Unary`][1]`   := (`[`UnaryOp`][4]`)* `[`Call`] | ||||
|         //! | ||||
|         //! [1]: Operation::Unary | ||||
|         //! [2]: Operation::Binary | ||||
| @@ -716,6 +757,7 @@ pub mod visitor { | ||||
|     }; | ||||
|  | ||||
|     /// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super) | ||||
|     #[deprecated] | ||||
|     pub trait Visitor<R> { | ||||
|         /// Visit the start of an AST | ||||
|         fn visit(&mut self, start: &Start) -> R { | ||||
| @@ -733,9 +775,9 @@ pub mod visitor { | ||||
|             } | ||||
|         } | ||||
|         /// Visit a [Let statement](Let) | ||||
|         fn visit_let(&mut self, stmt: &Let) -> R; | ||||
|         fn visit_let(&mut self, decl: &Let) -> R; | ||||
|         /// Visit a [Fn declaration](FnDecl) | ||||
|         fn visit_fn_decl(&mut self, function: &FnDecl) -> R; | ||||
|         fn visit_fn_decl(&mut self, decl: &FnDecl) -> R; | ||||
|  | ||||
|         /// Visit an [Expression](Expr) | ||||
|         fn visit_expr(&mut self, expr: &Expr) -> R { | ||||
| @@ -781,10 +823,11 @@ pub mod visitor { | ||||
|         /// Visit a [Unary] Operation | ||||
|         fn visit_unary(&mut self, unary: &Unary) -> R; | ||||
|         // Math operators | ||||
|         /// Visit an [Assignment](Assign) [operator](operator::Assign) | ||||
|         fn visit_assign_op(&mut self, op: &operator::Assign) -> R; | ||||
|         /// Visit a [Binary](Operation::Binary) [operator](operator::Binary) | ||||
|         /// Visit a [Binary] [operator](operator::Binary) | ||||
|         fn visit_binary_op(&mut self, op: &operator::Binary) -> R; | ||||
|         /// Visit a [Unary](Operation::Unary) [operator](operator::Unary) | ||||
|         /// Visit a [Unary] [operator](operator::Unary) | ||||
|         fn visit_unary_op(&mut self, op: &operator::Unary) -> R; | ||||
|  | ||||
|         /// Visit a [Primary] expression | ||||
| @@ -796,7 +839,7 @@ pub mod visitor { | ||||
|                 Primary::Literal(v) => self.visit_literal(v), | ||||
|                 Primary::Block(v) => self.visit_block(v), | ||||
|                 Primary::Group(v) => self.visit_group(v), | ||||
|                 Primary::Branch(v) => self.visit_branch_expr(v), | ||||
|                 Primary::Branch(v) => self.visit_branch(v), | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -804,8 +847,8 @@ pub mod visitor { | ||||
|         /// | ||||
|         /// [`Flow`]` := `[`While`]` | `[`If`]` | `[`For`]` | ||||
|         /// | `[`Continue`]` | `[`Return`]` | `[`Break`] | ||||
|         fn visit_branch_expr(&mut self, expr: &Flow) -> R { | ||||
|             match expr { | ||||
|         fn visit_branch(&mut self, flow: &Flow) -> R { | ||||
|             match flow { | ||||
|                 Flow::While(e) => self.visit_while(e), | ||||
|                 Flow::If(e) => self.visit_if(e), | ||||
|                 Flow::For(e) => self.visit_for(e), | ||||
| @@ -859,3 +902,46 @@ pub mod visitor { | ||||
|         fn visit_empty(&mut self) -> R; | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub mod todo { | ||||
|     //! temporary storage for pending expression work.  \ | ||||
|     //! when an item is in progress, remove it from todo. | ||||
|     //! | ||||
|     //! # General TODOs: | ||||
|     //! - [ ] REMOVE VISITOR TRAIT | ||||
|     //! - [ ] | ||||
|     //! - [x] Implement support for storing items in the AST | ||||
|     //! - [ ] Keep track of the source location of each node | ||||
|     //! - [ ] Implement paths | ||||
|     //! - [x] Implement functions | ||||
|     //! - [ ] Implement structs | ||||
|     //! - [ ] Implement enums | ||||
|     //! - [ ] Implement implementation | ||||
|     //! - [ ] Store token spans in AST | ||||
|     pub mod path { | ||||
|         //! Path support | ||||
|         //! - [ ] Add namespace syntax (i.e. `::crate::foo::bar` | `foo::bar::Baz` | `foo::bar::*`) | ||||
|         //! | ||||
|         //! Path resolution will be vital to the implementation of structs, enums, impl blocks, | ||||
|         //! traits, modules, etc. | ||||
|     } | ||||
|  | ||||
|     pub mod structure { | ||||
|         //! Struct support | ||||
|         //! - [ ] Add struct declaration expression (returns a struct declaration) | ||||
|         //! - [ ] Add struct value expression (returns a struct value) | ||||
|         //! - [ ] Add struct update syntax (yippee!!) | ||||
|     } | ||||
|  | ||||
|     pub mod enumeration { | ||||
|         //! Enum support | ||||
|         //! - [ ] Add enum declaration expression (returns an enum declaration) | ||||
|         //! - [ ] Add enum value expression (returns an enum value) | ||||
|     } | ||||
|  | ||||
|     pub mod implementation { | ||||
|         //! Impl block support | ||||
|         //! - [ ] Add impl block expression? Statement? | ||||
|         //! - [ ] Add member function call expression | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| //! Interprets an AST as a program | ||||
| #![allow(deprecated)] // TODO: REMOVE | ||||
|  | ||||
| use crate::ast::preamble::*; | ||||
| use error::{Error, IResult}; | ||||
| @@ -9,7 +10,7 @@ use temp_type_impl::ConValue; | ||||
| pub trait Callable: std::fmt::Debug { | ||||
|     /// Calls this [Callable] in the provided [Interpreter], with [ConValue] args  \ | ||||
|     /// The Callable is responsible for checking the argument count and validating types | ||||
|     fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<()>; | ||||
|     fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue>; | ||||
|     /// Returns the common name of this identifier. | ||||
|     fn name(&self) -> &str; | ||||
| } | ||||
| @@ -105,7 +106,7 @@ pub mod temp_type_impl { | ||||
|                 _ => "", | ||||
|             } | ||||
|         } | ||||
|         fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<()> { | ||||
|         fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> { | ||||
|             match self { | ||||
|                 Self::Function(func) => func.call(interpreter, args), | ||||
|                 Self::BuiltIn(func) => func.call(interpreter, args), | ||||
| @@ -347,7 +348,7 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|     } | ||||
|  | ||||
|     fn visit_let(&mut self, stmt: &Let) -> IResult<()> { | ||||
|         let Let { name: Identifier(name), init, .. } = stmt; | ||||
|         let Let { name: Name { name: Identifier { name, .. }, .. }, init, .. } = stmt; | ||||
|         if let Some(init) = init { | ||||
|             self.visit_expr(init)?; | ||||
|             let init = self.pop()?; | ||||
| @@ -394,7 +395,8 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|             let (ConValue::Tuple(args), callee) = self.pop_two()? else { | ||||
|                 Err(Error::TypeError)? | ||||
|             }; | ||||
|             callee.call(self, &args)?; | ||||
|             let return_value = callee.call(self, &args)?; | ||||
|             self.push(return_value); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| @@ -404,7 +406,8 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|         let math::Assign { target, operator, init } = assign; | ||||
|         self.visit_operation(init)?; | ||||
|         let init = self.pop()?; | ||||
|         let resolved = self.scope.get_mut(&target.0)?; | ||||
|         let resolved = self.scope.get_mut(&target.name)?; | ||||
|  | ||||
|         if let Assign::Assign = operator { | ||||
|             use std::mem::discriminant as variant; | ||||
|             // runtime typecheck | ||||
| @@ -418,9 +421,11 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|             self.push(ConValue::Empty); | ||||
|             return Ok(()); | ||||
|         } | ||||
|  | ||||
|         let Some(target) = resolved.as_mut() else { | ||||
|             Err(Error::NotInitialized(target.0.to_owned()))? | ||||
|             Err(Error::NotInitialized(target.name.to_owned()))? | ||||
|         }; | ||||
|  | ||||
|         match operator { | ||||
|             Assign::AddAssign => target.add_assign(init)?, | ||||
|             Assign::SubAssign => target.sub_assign(init)?, | ||||
| @@ -434,12 +439,13 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|             Assign::ShrAssign => target.shr_assign(init)?, | ||||
|             _ => (), | ||||
|         } | ||||
|  | ||||
|         self.push(ConValue::Empty); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn visit_binary(&mut self, bin: &math::Binary) -> IResult<()> { | ||||
|         use math::Binary; | ||||
|         let Binary { first, other } = bin; | ||||
|  | ||||
|         self.visit_operation(first)?; | ||||
| @@ -469,15 +475,19 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn visit_unary(&mut self, unary: &math::Unary) -> IResult<()> { | ||||
|         let math::Unary { operand, operators } = unary; | ||||
|         let Unary { operand, operators } = unary; | ||||
|  | ||||
|         self.visit_operation(operand)?; | ||||
|  | ||||
|         for op in operators.iter().rev() { | ||||
|             self.visit_unary_op(op)?; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @@ -488,6 +498,7 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|     fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> { | ||||
|         use operator::Binary; | ||||
|         let (second, first) = self.pop_two()?; | ||||
|  | ||||
|         let out = match op { | ||||
|             Binary::Mul => first * second, | ||||
|             Binary::Div => first / second, | ||||
| @@ -511,12 +522,14 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|             Binary::GreaterEq => first.gt_eq(&second), | ||||
|             Binary::Greater => first.gt(&second), | ||||
|         }?; | ||||
|  | ||||
|         self.push(out); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn visit_unary_op(&mut self, op: &operator::Unary) -> IResult<()> { | ||||
|         let operand = self.pop()?; | ||||
|  | ||||
|         self.push(match op { | ||||
|             operator::Unary::RefRef => todo!(), | ||||
|             operator::Unary::Ref => todo!(), | ||||
| @@ -530,10 +543,11 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|             } | ||||
|             operator::Unary::Tilde => todo!(), | ||||
|         }); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn visit_if(&mut self, expr: &control::If) -> IResult<()> { | ||||
|     fn visit_if(&mut self, expr: &If) -> IResult<()> { | ||||
|         self.visit_expr(&expr.cond)?; | ||||
|         if self.pop()?.truthy()? { | ||||
|             self.visit_block(&expr.body)?; | ||||
| @@ -545,7 +559,7 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn visit_while(&mut self, expr: &control::While) -> IResult<()> { | ||||
|     fn visit_while(&mut self, expr: &While) -> IResult<()> { | ||||
|         while { | ||||
|             self.visit_expr(&expr.cond)?; | ||||
|             self.pop()?.truthy()? | ||||
| @@ -581,7 +595,7 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|             _ => Err(Error::NotIterable)?, | ||||
|         }; | ||||
|         for loop_var in bounds.0..=bounds.1 { | ||||
|             self.scope.insert(&expr.var.0, Some(loop_var.into())); | ||||
|             self.scope.insert(&expr.var.name, Some(loop_var.into())); | ||||
|             let Err(out) = self.visit_block(&expr.body) else { | ||||
|                 self.pop()?; | ||||
|                 continue; | ||||
| @@ -627,7 +641,7 @@ impl Visitor<IResult<()>> for Interpreter { | ||||
|     } | ||||
|  | ||||
|     fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> { | ||||
|         let value = self.resolve(&ident.0)?; | ||||
|         let value = self.resolve(&ident.name)?; | ||||
|         self.push(value); | ||||
|         Ok(()) | ||||
|     } | ||||
| @@ -670,8 +684,17 @@ impl Default for Interpreter { | ||||
|  | ||||
| pub mod function { | ||||
|     //! Represents a block of code which lives inside the Interpreter | ||||
|     use super::{Callable, ConValue, Error, FnDecl, IResult, Identifier, Interpreter}; | ||||
|     use crate::ast::visitor::Visitor; | ||||
|     use super::{ | ||||
|         // scope::Environment, | ||||
|         Callable, | ||||
|         ConValue, | ||||
|         Error, | ||||
|         FnDecl, | ||||
|         IResult, | ||||
|         Identifier, | ||||
|         Interpreter, | ||||
|     }; | ||||
|     use crate::ast::{preamble::Name, visitor::Visitor}; | ||||
|     /// Represents a block of code which persists inside the Interpreter | ||||
|     #[derive(Clone, Debug)] | ||||
|     pub struct Function { | ||||
| @@ -679,19 +702,23 @@ pub mod function { | ||||
|         declaration: Box<FnDecl>, | ||||
|         // /// Stores the enclosing scope of the function | ||||
|         // TODO: Capture variables | ||||
|         //environment: Box<Environment>, | ||||
|     } | ||||
|  | ||||
|     impl Function { | ||||
|         pub fn new(declaration: &FnDecl) -> Self { | ||||
|             Self { declaration: declaration.clone().into() } | ||||
|             Self { | ||||
|                 declaration: declaration.clone().into(), | ||||
|                 //environment: Box::new(Environment::new()), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl Callable for Function { | ||||
|         fn name(&self) -> &str { | ||||
|             &self.declaration.name.0 | ||||
|             &self.declaration.name.name.name | ||||
|         } | ||||
|         fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<()> { | ||||
|         fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> { | ||||
|             // Check arg mapping | ||||
|             if args.len() != self.declaration.args.len() { | ||||
|                 return Err(Error::ArgNumber { | ||||
| @@ -701,17 +728,19 @@ pub mod function { | ||||
|             } | ||||
|             // TODO: Isolate cross-function scopes! | ||||
|             interpreter.scope.enter(); | ||||
|             for (Identifier(arg), value) in self.declaration.args.iter().zip(args) { | ||||
|                 interpreter.scope.insert(arg, Some(value.clone())); | ||||
|             for (Name { name: Identifier { name, .. }, .. }, value) in | ||||
|                 self.declaration.args.iter().zip(args) | ||||
|             { | ||||
|                 interpreter.scope.insert(name, Some(value.clone())); | ||||
|             } | ||||
|             match interpreter.visit_block(&self.declaration.body) { | ||||
|                 Err(Error::Return(value)) => interpreter.push(value), | ||||
|                 Err(Error::Break(value)) => Err(Error::BadBreak(value))?, | ||||
|                 Err(e) => Err(e)?, | ||||
|                 Ok(_) => (), | ||||
|                 Ok(()) => (), | ||||
|             } | ||||
|             interpreter.scope.exit()?; | ||||
|             Ok(()) | ||||
|             interpreter.pop() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -719,14 +748,12 @@ pub mod function { | ||||
| pub mod builtin { | ||||
|     mod builtin_imports { | ||||
|         pub use crate::interpreter::{ | ||||
|             error::{Error, IResult}, | ||||
|             temp_type_impl::ConValue, | ||||
|             BuiltIn, Callable, Interpreter, | ||||
|             error::IResult, temp_type_impl::ConValue, BuiltIn, Callable, Interpreter, | ||||
|         }; | ||||
|     } | ||||
|     use super::BuiltIn; | ||||
|     /// Builtins to load when a new interpreter is created | ||||
|     pub const DEFAULT_BUILTINS: &[&dyn BuiltIn] = &[&print::Print, &dbg::Dbg]; | ||||
|     pub const DEFAULT_BUILTINS: &[&dyn BuiltIn] = &[&print::Print, &dbg::Dbg, &dump::Dump]; | ||||
|  | ||||
|     mod print { | ||||
|         //! Implements the unstable `print(...)` builtin | ||||
| @@ -738,11 +765,12 @@ pub mod builtin { | ||||
|         #[rustfmt::skip] | ||||
|         impl Callable for Print { | ||||
|             fn name(&self) -> &'static str { "print" } | ||||
|             fn call(&self, inter: &mut Interpreter, args: &[ConValue]) -> IResult<()> { | ||||
|                 for arg in args { print!("{arg}") } | ||||
|             fn call(&self, _inter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> { | ||||
|                 for arg in args { | ||||
|                     print!("{arg}") | ||||
|                 } | ||||
|                 println!(); | ||||
|                 inter.push(ConValue::Empty); | ||||
|                 Ok(()) | ||||
|                 Ok(ConValue::Empty) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -755,10 +783,27 @@ pub mod builtin { | ||||
|         #[rustfmt::skip] | ||||
|         impl Callable for Dbg { | ||||
|             fn name(&self) -> &str { "dbg" } | ||||
|             fn call(&self, inter: &mut Interpreter, args: &[ConValue]) -> IResult<()> { | ||||
|             fn call(&self, _inter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> { | ||||
|                 println!("{args:?}"); | ||||
|                 inter.push(args); | ||||
|                 Ok(()) | ||||
|                 Ok(args.into()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     mod dump { | ||||
|         use super::builtin_imports::*; | ||||
|         #[derive(Clone, Debug)] | ||||
|         pub struct Dump; | ||||
|         impl BuiltIn for Dump {} | ||||
|         impl Callable for Dump { | ||||
|             fn call(&self, interpreter: &mut Interpreter, _args: &[ConValue]) -> IResult<ConValue> { | ||||
|                 println!("Scope:\n{}", interpreter.scope); | ||||
|                 println!("Stack:{:#?}", interpreter.stack); | ||||
|                 Ok(ConValue::Empty) | ||||
|             } | ||||
|  | ||||
|             fn name(&self) -> &str { | ||||
|                 "dump" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -774,7 +819,7 @@ pub mod scope { | ||||
|         temp_type_impl::ConValue, | ||||
|         FnDecl, | ||||
|     }; | ||||
|     use std::collections::HashMap; | ||||
|     use std::{collections::HashMap, fmt::Display}; | ||||
|  | ||||
|     /// Implements a nested lexical scope | ||||
|     #[derive(Clone, Debug, Default)] | ||||
| @@ -783,6 +828,19 @@ pub mod scope { | ||||
|         vars: HashMap<String, Option<ConValue>>, | ||||
|     } | ||||
|  | ||||
|     impl Display for Environment { | ||||
|         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|             for (var, val) in &self.vars { | ||||
|                 writeln!(f, "{var}: {}", if val.is_some() { "..." } else { "None" })?; | ||||
|             } | ||||
|             "--- Frame ---\n".fmt(f)?; | ||||
|             if let Some(outer) = &self.outer { | ||||
|                 outer.fmt(f)?; | ||||
|             } | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl Environment { | ||||
|         pub fn new() -> Self { | ||||
|             let mut out = Self::default(); | ||||
| @@ -835,8 +893,10 @@ pub mod scope { | ||||
|         } | ||||
|         /// A convenience function for registering a [FnDecl] as a [Function] | ||||
|         pub fn insert_fn(&mut self, decl: &FnDecl) { | ||||
|             self.vars | ||||
|                 .insert(decl.name.0.clone(), Some(Function::new(decl).into())); | ||||
|             self.vars.insert( | ||||
|                 decl.name.name.name.clone(), | ||||
|                 Some(Function::new(decl).into()), | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,18 +1,37 @@ | ||||
| //! Parses [tokens](super::token) into an [AST](super::ast) | ||||
|  | ||||
| use super::{ast::preamble::*, lexer::Lexer, token::preamble::*}; | ||||
| use error::{Error, Reason::*, *}; | ||||
| use error::{Error, *}; | ||||
|  | ||||
| pub mod error { | ||||
|     use super::{Token, Type}; | ||||
|     use std::fmt::Display; | ||||
|  | ||||
|     pub trait WrapError { | ||||
|         /// Wraps this error in a parent [Error] | ||||
|         fn wrap(self, parent: Error) -> Self; | ||||
|     } | ||||
|     impl WrapError for Error { | ||||
|         fn wrap(self, parent: Error) -> Self { | ||||
|             Self { child: Some(self.into()), ..parent } | ||||
|         } | ||||
|     } | ||||
|     impl<T> WrapError for Result<T, Error> { | ||||
|         fn wrap(self, parent: Error) -> Self { | ||||
|             self.map_err(|e| e.wrap(parent)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// The reason for the [Error] | ||||
|     #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] | ||||
|     pub enum Reason { | ||||
|         Expected(Type), | ||||
|         Unexpected(Type), | ||||
|         NotPathSegment(Type), | ||||
|         NotIdentifier, | ||||
|         NotStatement, | ||||
|         NotLet, | ||||
|         NotFnDecl, | ||||
|         NotOperator, | ||||
|         NotLiteral, | ||||
|         NotString, | ||||
| @@ -37,7 +56,11 @@ pub mod error { | ||||
|             match self { | ||||
|                 Self::Expected(t) => write!(f, "Expected {t}"), | ||||
|                 Self::Unexpected(t) => write!(f, "Unexpected {t} in bagging area"), | ||||
|                 Self::NotPathSegment(t) => write!(f, "{t} not a path segment"), | ||||
|                 Self::NotIdentifier => "Not an identifier".fmt(f), | ||||
|                 Self::NotStatement => "Not a statement".fmt(f), | ||||
|                 Self::NotLet => "Not a let statement".fmt(f), | ||||
|                 Self::NotFnDecl => "Not a valid function declaration".fmt(f), | ||||
|                 Self::NotOperator => "Not an operator".fmt(f), | ||||
|                 Self::NotLiteral => "Not a literal".fmt(f), | ||||
|                 Self::NotString => "Not a string".fmt(f), | ||||
| @@ -67,11 +90,15 @@ pub mod error { | ||||
|     #[derive(Clone, Debug, Default, PartialEq)] | ||||
|     pub struct Error { | ||||
|         reason: Reason, | ||||
|         child: Option<Box<Self>>, | ||||
|         start: Option<Token>, | ||||
|     } | ||||
|     impl std::error::Error for Error {} | ||||
|     impl Display for Error { | ||||
|         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|             if let Some(child) = &self.child { | ||||
|                 write!(f, "{child}: ")?; | ||||
|             } | ||||
|             if let Some(token) = &self.start { | ||||
|                 write!(f, "{}:{}: ", token.line(), token.col())?; | ||||
|             } | ||||
| @@ -84,7 +111,7 @@ pub mod error { | ||||
|         #[doc = concat!("[`", stringify!($reason), "`]")] | ||||
|         #[allow(dead_code)] | ||||
|         pub(crate) fn $fn($($($p : $t),*)?) -> Self { | ||||
|             Self { reason: $reason$(($($p)*))?, start: None } | ||||
|             Self { reason: $reason$(($($p)*))?, child: None, start: None } | ||||
|         } | ||||
|     )*} | ||||
|     impl Error { | ||||
| @@ -104,14 +131,14 @@ pub mod error { | ||||
|         pub fn reason(&self) -> Reason { | ||||
|             self.reason | ||||
|         } | ||||
|         /// Modifies the [Reason] of this error | ||||
|         pub fn with_reason(self, reason: Reason) -> Self { | ||||
|             Self { reason, ..self } | ||||
|         } | ||||
|         error_impl! { | ||||
|             expected(e: Type): Expected, | ||||
|             unexpected(e: Type): Unexpected, | ||||
|             not_path_segment(e: Type): NotPathSegment, | ||||
|             not_identifier: NotIdentifier, | ||||
|             not_statement: NotStatement, | ||||
|             not_let: NotLet, | ||||
|             not_fn_decl: NotFnDecl, | ||||
|             not_operator: NotOperator, | ||||
|             not_literal: NotLiteral, | ||||
|             not_string: NotString, | ||||
| @@ -160,20 +187,25 @@ impl Parser { | ||||
|     pub fn new(tokens: Vec<Token>) -> Self { | ||||
|         Self { tokens, panic_stack: vec![], errors: vec![], cursor: 0 } | ||||
|     } | ||||
|     /// Resets the parser, so it can be reused | ||||
|     pub fn reset(&mut self) -> &mut Self { | ||||
|         *self = Self::new(std::mem::take(&mut self.tokens)); | ||||
|         self | ||||
|     } | ||||
|     /// Parses the [start of an AST](Start) | ||||
|     pub fn parse(&mut self) -> PResult<Start> { | ||||
|         self.consume_comments(); | ||||
|         Ok(Start(self.program()?)) | ||||
|     } | ||||
|     /// Parses only one expression | ||||
|     pub fn parse_expr(&mut self) -> PResult<expression::Expr> { | ||||
|     pub fn parse_expr(&mut self) -> PResult<Expr> { | ||||
|         self.expr() | ||||
|     } | ||||
|     /// Peeks at the current token | ||||
|     pub fn peek(&self) -> PResult<&Token> { | ||||
|         self.tokens | ||||
|             .get(self.cursor) | ||||
|             .ok_or(Error::end_of_file().maybe_token(self.tokens.last().cloned())) | ||||
|             .ok_or_else(|| Error::end_of_file().maybe_token(self.tokens.last().cloned())) | ||||
|     } | ||||
|     /// Consumes any number of consecutive comments | ||||
|     fn consume_comments(&mut self) -> &mut Self { | ||||
| @@ -212,9 +244,7 @@ impl Parser { | ||||
|     /// Advances forward until a token with type [`t`](Type) is encountered | ||||
|     fn advance_until(&mut self, t: Type) -> PResult<&mut Self> { | ||||
|         while self.matches(t).is_err() { | ||||
|             self.check_eof() | ||||
|                 .map_err(|e| e.with_reason(Expected(t)))? | ||||
|                 .consume(); | ||||
|             self.check_eof().wrap(Error::expected(t))?.consume(); | ||||
|         } | ||||
|         Ok(self) | ||||
|     } | ||||
| @@ -244,10 +274,11 @@ impl Parser { | ||||
|     fn matches(&mut self, t: Type) -> PResult<&Token> { | ||||
|         let token = self.check_eof()?.peek().expect("self should not be eof"); | ||||
|         if token.ty() != t { | ||||
|             Err(Error::expected(t).token(token.clone()))? | ||||
|         } | ||||
|             Err(Error::expected(t).token(token.clone())) | ||||
|         } else { | ||||
|             Ok(token) | ||||
|         } | ||||
|     } | ||||
|     /// Consumes, without returning, a token with the given [Keyword], or returns an error. | ||||
|     /// | ||||
|     /// Useful if you only want to check the existence of a [Keyword] | ||||
| @@ -282,7 +313,7 @@ impl Parser { | ||||
|     /// Parses an [Identifier] | ||||
|     fn identifier(&mut self) -> PResult<Identifier> { | ||||
|         let out = match self.matches(Type::Identifier)?.data() { | ||||
|             Data::Identifier(id) => Identifier(id.to_string()), | ||||
|             Data::Identifier(id) => Identifier { name: id.to_string(), index: None }, | ||||
|             _ => Err(Error::not_identifier())?, | ||||
|         }; | ||||
|         self.consume(); | ||||
| @@ -363,30 +394,25 @@ impl Parser { | ||||
|     fn stmt(&mut self) -> PResult<Stmt> { | ||||
|         let token = self.peek()?; | ||||
|         match token.ty() { | ||||
|             Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let), | ||||
|             Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn), | ||||
|             Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let).wrap(Error::not_let()), | ||||
|             Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn).wrap(Error::not_fn_decl()), | ||||
|             _ => { | ||||
|                 let out = Stmt::Expr(self.expr()?); | ||||
|                 self.consume_type(Type::Semi)?; | ||||
|                 Ok(out) | ||||
|             } | ||||
|         } | ||||
|         .wrap(Error::not_statement()) | ||||
|     } | ||||
|     /// Parses a [Let] statement | ||||
|     fn let_stmt(&mut self) -> PResult<Let> { | ||||
|         let out = Let { | ||||
|             mutable: self.consume().keyword(Keyword::Mut).is_ok(), | ||||
|             name: self.identifier()?, | ||||
|             ty: self | ||||
|                 .consume_type(Type::Colon) | ||||
|                 .and_then(Self::identifier) | ||||
|                 .ok(), | ||||
|             init: self.consume_type(Type::Eq).and_then(Self::expr).ok(), | ||||
|         }; | ||||
|         self.keyword(Keyword::Let)?; | ||||
|         let out = | ||||
|             Let { name: self.name()?, init: self.consume_type(Type::Eq).and_then(Self::expr).ok() }; | ||||
|         self.consume_type(Type::Semi)?; | ||||
|         Ok(out) | ||||
|     } | ||||
|     /// Parses a [Function] statement | ||||
|     /// Parses a [function declaration](FnDecl) statement | ||||
|     fn fn_decl(&mut self) -> PResult<FnDecl> { | ||||
|         self.keyword(Keyword::Fn)?; | ||||
|         let name = self.identifier()?; | ||||
| @@ -394,37 +420,98 @@ impl Parser { | ||||
|         let args = self.params()?; | ||||
|         self.consume_type(Type::RParen)?; | ||||
|         // TODO: Parse type-expressions and store return types in the AST | ||||
|         if self.consume_type(Type::Arrow).is_ok() { | ||||
|             self.expr()?; | ||||
|         let ty = if self.consume_type(Type::Arrow).is_ok() { | ||||
|             Some(self.type_expr()?) | ||||
|         } else { | ||||
|             None | ||||
|         }; | ||||
|         Ok(FnDecl { name: Name { name, mutable: false, ty }, args, body: self.block()? }) | ||||
|     } | ||||
|         Ok(FnDecl { name, args, body: self.block()? }) | ||||
|     } | ||||
|  | ||||
|     fn params(&mut self) -> PResult<Vec<Identifier>> { | ||||
|     /// Parses a [parameter](Name) list for [FnDecl] | ||||
|     fn params(&mut self) -> PResult<Vec<Name>> { | ||||
|         let mut args = vec![]; | ||||
|         while let Ok(ident) = self.identifier() { | ||||
|             args.push(ident); | ||||
|             if self.consume_type(Type::Colon).is_ok() { | ||||
|                 // TODO: Parse type-expressions and make this mandatory | ||||
|                 self.expr()?; | ||||
|             } | ||||
|         while let Ok(name) = self.name() { | ||||
|             args.push(name); | ||||
|             if self.consume_type(Type::Comma).is_err() { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         Ok(args) | ||||
|     } | ||||
|     /// Parses a [Name]; the object of a let statement, or a single function parameter. | ||||
|     fn name(&mut self) -> PResult<Name> { | ||||
|         Ok(Name { | ||||
|             mutable: self.keyword(Keyword::Mut).is_ok(), | ||||
|             name: self.identifier()?, | ||||
|             ty: self | ||||
|                 .consume_type(Type::Colon) | ||||
|                 .and_then(|this| this.type_expr()) | ||||
|                 .ok(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| /// Path Expressions | ||||
| impl Parser { | ||||
|     fn path(&mut self) -> PResult<path::Path> { | ||||
|         let absolute = self.consume_type(Type::ColonColon).is_ok(); | ||||
|         let mut parts = vec![]; | ||||
|         while let Ok(id) = self.path_part() { | ||||
|             parts.push(id); | ||||
|             if self.consume_type(Type::ColonColon).is_err() { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         Ok(Path { absolute, parts }) | ||||
|     } | ||||
|  | ||||
|     fn path_part(&mut self) -> PResult<PathPart> { | ||||
|         match self.peek()?.ty() { | ||||
|             Type::Identifier => self.identifier().map(PathPart::PathIdent), | ||||
|             Type::Keyword(Keyword::Super) => { | ||||
|                 self.keyword(Keyword::Super).map(|_| PathPart::PathSuper) | ||||
|             } | ||||
|             Type::Keyword(Keyword::SelfKw) => { | ||||
|                 self.keyword(Keyword::SelfKw).map(|_| PathPart::PathSelf) | ||||
|             } | ||||
|             e => Err(Error::not_path_segment(e)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| /// Type Expressions | ||||
| impl Parser { | ||||
|     /// Parses a [Type Expression](TypeExpr) | ||||
|     fn type_expr(&mut self) -> PResult<TypeExpr> { | ||||
|         match self.peek()?.ty() { | ||||
|             Type::LParen => self.type_tuple().map(TypeExpr::TupleType), | ||||
|             Type::Bang => self.type_never().map(TypeExpr::Never), | ||||
|             _ => self.path().map(TypeExpr::TypePath), | ||||
|         } | ||||
|     } | ||||
|     fn type_tuple(&mut self) -> PResult<TupleType> { | ||||
|         self.consume_type(Type::LParen)?; | ||||
|         let mut types = vec![]; | ||||
|         while let Ok(ty) = self.type_expr() { | ||||
|             types.push(ty); | ||||
|             if self.consume_type(Type::Comma).is_err() { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         self.consume_type(Type::RParen)?; | ||||
|         Ok(TupleType { types }) | ||||
|     } | ||||
|     fn type_never(&mut self) -> PResult<Never> { | ||||
|         self.consume_type(Type::Bang).map(|_| Never) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Expressions | ||||
| impl Parser { | ||||
|     /// Parses an [expression](expression::Expr) | ||||
|     fn expr(&mut self) -> PResult<expression::Expr> { | ||||
|         use expression::Expr; | ||||
|     /// Parses an [expression](Expr) | ||||
|     fn expr(&mut self) -> PResult<Expr> { | ||||
|         Ok(Expr(self.assign()?)) | ||||
|     } | ||||
|     /// Parses a [block expression](expression::Block) | ||||
|     fn block(&mut self) -> PResult<expression::Block> { | ||||
|         use expression::{Block, Expr}; | ||||
|     /// Parses a [block expression](Block) | ||||
|     fn block(&mut self) -> PResult<Block> { | ||||
|         let mut statements = vec![]; | ||||
|         let mut expr: Option<Box<Expr>> = None; | ||||
|         self.consume_type(Type::LCurly)?; | ||||
| @@ -440,11 +527,10 @@ impl Parser { | ||||
|                 Err(_) => statements.push(self.stmt()?), | ||||
|             } | ||||
|         } | ||||
|         Ok(Block { statements, expr }) | ||||
|         Ok(Block { statements, expr, let_count: None }) | ||||
|     } | ||||
|     /// Parses a [primary expression](expression::Primary) | ||||
|     fn primary(&mut self) -> PResult<expression::Primary> { | ||||
|         use expression::Primary; | ||||
|     /// Parses a [primary expression](Primary) | ||||
|     fn primary(&mut self) -> PResult<Primary> { | ||||
|         let token = self.peek()?; | ||||
|         match token.ty() { | ||||
|             Type::Identifier => self.identifier().map(Primary::Identifier), | ||||
| @@ -465,7 +551,7 @@ impl Parser { | ||||
|     /// Parses a [call expression](Call) | ||||
|     fn call(&mut self) -> PResult<Call> { | ||||
|         let callee = self.primary()?; | ||||
|         let Ok(Type::LParen) = self.peek().map(Token::ty) else { | ||||
|         if self.matches(Type::LParen).is_err() { | ||||
|             return Ok(Call::Primary(callee)); | ||||
|         }; | ||||
|         let mut args = vec![]; | ||||
| @@ -529,9 +615,8 @@ impl Parser { | ||||
| /// fn function_name(&mut self) -> PResult<ret::Value> {  ... } | ||||
| /// ``` | ||||
| macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$( | ||||
|     #[doc = concat!("Parses a(n) [", stringify!($f), " operation](math::Operation::Binary) expression")] | ||||
|     fn $f (&mut self) -> PResult<math::Operation> { | ||||
|         use math::{Operation, Binary}; | ||||
|     #[doc = concat!("Parses a(n) [", stringify!($f), " operation](Operation::Binary) expression")] | ||||
|     fn $f (&mut self) -> PResult<Operation> { | ||||
|         let (first, mut other) = (self.$a()?, vec![]); | ||||
|         while let Ok(op) = self.$b() { | ||||
|             other.push((op, self.$a()?)); | ||||
| @@ -543,9 +628,7 @@ macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$( | ||||
| )*} | ||||
| /// # [Arithmetic and Logical Subexpressions](math) | ||||
| impl Parser { | ||||
|     fn assign(&mut self) -> PResult<math::Operation> { | ||||
|         use expression::Primary; | ||||
|         use math::{Assign, Operation}; | ||||
|     fn assign(&mut self) -> PResult<Operation> { | ||||
|         let next = self.compare()?; | ||||
|         let Ok(operator) = self.assign_op() else { | ||||
|             return Ok(next); | ||||
| @@ -569,9 +652,8 @@ impl Parser { | ||||
|         term    = factor,  term_op; | ||||
|         factor  = unary,   factor_op; | ||||
|     } | ||||
|     /// Parses a [unary operation](math::Operation::Unary) expression | ||||
|     fn unary(&mut self) -> PResult<math::Operation> { | ||||
|         use math::{Operation, Unary}; | ||||
|     /// Parses a [unary operation](Operation::Unary) expression | ||||
|     fn unary(&mut self) -> PResult<Operation> { | ||||
|         let mut operators = vec![]; | ||||
|         while let Ok(op) = self.unary_op() { | ||||
|             operators.push(op) | ||||
| @@ -584,15 +666,15 @@ impl Parser { | ||||
|             operand: self.primary_operation()?.into(), | ||||
|         })) | ||||
|     } | ||||
|     /// Parses a [primary operation](math::Operation::Primary) expression | ||||
|     fn primary_operation(&mut self) -> PResult<math::Operation> { | ||||
|         Ok(math::Operation::Call(self.call()?)) | ||||
|     /// Parses a [primary operation](Operation::Primary) expression | ||||
|     fn primary_operation(&mut self) -> PResult<Operation> { | ||||
|         Ok(Operation::Call(self.call()?)) | ||||
|     } | ||||
| } | ||||
| macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) { | ||||
|     $($(#[$m])* fn $f(&mut self) -> PResult<operator::Binary> { | ||||
|         use operator::Binary; | ||||
|         let token = self.peek()?; | ||||
|         let token = self.peek().wrap(Error::not_operator())?; | ||||
|         let out = Ok(match token.ty() { | ||||
|             $($type => Binary::$op,)* | ||||
|             _ => Err(Error::not_operator().token(token.clone()))?, | ||||
| @@ -689,9 +771,8 @@ impl Parser { | ||||
| } | ||||
| /// # [Control Flow](control) | ||||
| impl Parser { | ||||
|     /// Parses a [control flow](control::Flow) expression | ||||
|     fn flow(&mut self) -> PResult<control::Flow> { | ||||
|         use control::Flow; | ||||
|     /// Parses a [control flow](Flow) expression | ||||
|     fn flow(&mut self) -> PResult<Flow> { | ||||
|         use Keyword::{Break, Continue, For, If, Return, While}; | ||||
|         let token = self.peek()?; | ||||
|         match token.ty() { | ||||
| @@ -703,55 +784,47 @@ impl Parser { | ||||
|             Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue), | ||||
|             e => Err(Error::unexpected(e).token(token.clone()))?, | ||||
|         } | ||||
|         .map_err(|e| e.with_reason(IncompleteBranch)) | ||||
|         .wrap(Error::not_branch()) | ||||
|     } | ||||
|     /// Parses an [if](control::If) expression | ||||
|     fn parse_if(&mut self) -> PResult<control::If> { | ||||
|     /// Parses an [if](If) expression | ||||
|     fn parse_if(&mut self) -> PResult<If> { | ||||
|         self.keyword(Keyword::If)?; | ||||
|         Ok(control::If { | ||||
|             cond: self.expr()?.into(), | ||||
|             body: self.block()?, | ||||
|             else_: self.parse_else()?, | ||||
|         }) | ||||
|         Ok(If { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? }) | ||||
|     } | ||||
|     /// Parses a [while](control::While) expression | ||||
|     fn parse_while(&mut self) -> PResult<control::While> { | ||||
|     /// Parses a [while](While) expression | ||||
|     fn parse_while(&mut self) -> PResult<While> { | ||||
|         self.keyword(Keyword::While)?; | ||||
|         Ok(control::While { | ||||
|             cond: self.expr()?.into(), | ||||
|             body: self.block()?, | ||||
|             else_: self.parse_else()?, | ||||
|         }) | ||||
|         Ok(While { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? }) | ||||
|     } | ||||
|     /// Parses a [for](control::For) expression | ||||
|     fn parse_for(&mut self) -> PResult<control::For> { | ||||
|     /// Parses a [for](For) expression | ||||
|     fn parse_for(&mut self) -> PResult<For> { | ||||
|         self.keyword(Keyword::For)?; | ||||
|         Ok(control::For { | ||||
|         Ok(For { | ||||
|             var: self.identifier()?, | ||||
|             iter: { self.keyword(Keyword::In)?.expr()?.into() }, | ||||
|             body: self.block()?, | ||||
|             else_: self.parse_else()?, | ||||
|         }) | ||||
|     } | ||||
|     /// Parses an [else](control::Else) sub-expression | ||||
|     fn parse_else(&mut self) -> PResult<Option<control::Else>> { | ||||
|     /// Parses an [else](Else) sub-expression | ||||
|     fn parse_else(&mut self) -> PResult<Option<Else>> { | ||||
|         // it's fine for `else` to be missing entirely | ||||
|         self.keyword(Keyword::Else) | ||||
|             .ok() | ||||
|             .map(|p| Ok(control::Else { expr: p.expr()?.into() })) | ||||
|             .map(|p| Ok(Else { expr: p.expr()?.into() })) | ||||
|             .transpose() | ||||
|     } | ||||
|     /// Parses a [break](control::Break) expression | ||||
|     fn parse_break(&mut self) -> PResult<control::Break> { | ||||
|         Ok(control::Break { expr: self.keyword(Keyword::Break)?.expr()?.into() }) | ||||
|     /// Parses a [break](Break) expression | ||||
|     fn parse_break(&mut self) -> PResult<Break> { | ||||
|         Ok(Break { expr: self.keyword(Keyword::Break)?.expr()?.into() }) | ||||
|     } | ||||
|     /// Parses a [return](control::Return) expression | ||||
|     fn parse_return(&mut self) -> PResult<control::Return> { | ||||
|         Ok(control::Return { expr: self.keyword(Keyword::Return)?.expr()?.into() }) | ||||
|     /// Parses a [return](Return) expression | ||||
|     fn parse_return(&mut self) -> PResult<Return> { | ||||
|         Ok(Return { expr: self.keyword(Keyword::Return)?.expr()?.into() }) | ||||
|     } | ||||
|     /// Parses a [continue](control::Continue) expression | ||||
|     fn parse_continue(&mut self) -> PResult<control::Continue> { | ||||
|     /// Parses a [continue](Continue) expression | ||||
|     fn parse_continue(&mut self) -> PResult<Continue> { | ||||
|         self.keyword(Keyword::Continue)?; | ||||
|         Ok(control::Continue) | ||||
|         Ok(Continue) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| //! A [Printer] pretty-prints a Conlang [syntax tree](crate::ast) | ||||
|  | ||||
| use super::ast::preamble::*; | ||||
| use std::{ | ||||
|     fmt::Display, | ||||
| @@ -7,17 +8,14 @@ use std::{ | ||||
| /// Prettily prints this node | ||||
| pub trait PrettyPrintable { | ||||
|     /// Prettily prints this node | ||||
|     fn print(&self); | ||||
|     /// Prettily writes this node into the given [Writer](Write) | ||||
|     fn write(&self, into: impl Write) -> IOResult<()>; | ||||
| } | ||||
| impl PrettyPrintable for Start { | ||||
|     fn print(&self) { | ||||
|         let _ = Printer::default().visit(self); | ||||
|         let _ = self.visit(&mut Printer::default()); | ||||
|     } | ||||
|     /// Prettily writes this node into the given [Writer](Write) | ||||
|     fn write(&self, into: impl Write) -> IOResult<()> { | ||||
|         Printer::from(into).visit(self) | ||||
|         self.visit(&mut Printer::from(into)) | ||||
|     } | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()>; | ||||
| } | ||||
|  | ||||
| /// Prints a Conlang [syntax tree](crate::ast) into a [Writer](Write) | ||||
| @@ -26,6 +24,18 @@ pub struct Printer<W: Write> { | ||||
|     level: u32, | ||||
|     writer: W, | ||||
| } | ||||
|  | ||||
| impl<W: Write> Write for Printer<W> { | ||||
|     #[inline] | ||||
|     fn write(&mut self, buf: &[u8]) -> IOResult<usize> { | ||||
|         self.writer.write(buf) | ||||
|     } | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> IOResult<()> { | ||||
|         self.writer.flush() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'t> Default for Printer<StdoutLock<'t>> { | ||||
|     fn default() -> Self { | ||||
|         Self { level: 0, writer: stdout().lock() } | ||||
| @@ -67,82 +77,154 @@ impl<W: Write> Printer<W> { | ||||
| macro visit_operator($self:ident.$op:expr) { | ||||
|     $self.space()?.put($op)?.space().map(drop) | ||||
| } | ||||
| impl<W: Write> Visitor<IOResult<()>> for Printer<W> { | ||||
|     fn visit_program(&mut self, prog: &Program) -> IOResult<()> { | ||||
|         // delegate to the walker | ||||
|         for stmt in &prog.0 { | ||||
|             self.visit_statement(stmt)?; | ||||
|  | ||||
| impl PrettyPrintable for Start { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Self(program) = self; | ||||
|         program.visit(p) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Program { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Self(module) = self; | ||||
|         for decl in module { | ||||
|             decl.visit(p)?; | ||||
|             p.newline()?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn visit_statement(&mut self, stmt: &Stmt) -> IOResult<()> { | ||||
|         match stmt { | ||||
|             Stmt::Let(stmt) => self.visit_let(stmt)?, | ||||
|             Stmt::Fn(function) => self.visit_fn_decl(function)?, | ||||
|             Stmt::Expr(e) => { | ||||
|                 self.visit_expr(e)?; | ||||
|                 self.put(';').map(drop)? | ||||
| } | ||||
| impl PrettyPrintable for Stmt { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             Stmt::Let(value) => value.visit(p), | ||||
|             Stmt::Fn(value) => value.visit(p), | ||||
|             Stmt::Expr(value) => { | ||||
|                 value.visit(p)?; | ||||
|                 p.put(';')?.newline().map(drop) | ||||
|             } | ||||
|         } | ||||
|         self.newline().map(drop) | ||||
|     } | ||||
|     fn visit_let(&mut self, stmt: &Let) -> IOResult<()> { | ||||
|         let Let { name, mutable, ty, init } = stmt; | ||||
|         self.put("let")?.space()?; | ||||
| } | ||||
| impl PrettyPrintable for Let { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Let { name: Name { name, mutable, ty }, init } = self; | ||||
|         p.put("let")?.space()?; | ||||
|         if *mutable { | ||||
|             self.put("mut")?.space()?; | ||||
|             p.put("mut")?.space()?; | ||||
|         } | ||||
|         self.visit_identifier(name)?; | ||||
|         name.visit(p)?; | ||||
|         if let Some(ty) = ty { | ||||
|             self.put(':')?.space()?.visit_identifier(ty)?; | ||||
|             ty.visit(p.put(':')?.space()?)? | ||||
|         } | ||||
|         if let Some(init) = init { | ||||
|             self.space()?.put('=')?.space()?.visit_expr(init)?; | ||||
|             init.visit(p.space()?.put('=')?.space()?)?; | ||||
|         } | ||||
|         self.put(';').map(drop) | ||||
|         p.put(';').map(drop) | ||||
|     } | ||||
|  | ||||
|     fn visit_fn_decl(&mut self, function: &FnDecl) -> IOResult<()> { | ||||
|         let FnDecl { name, args, body } = function; | ||||
|         self.put("fn")?.space()?; | ||||
|         self.visit_identifier(name)?; | ||||
|         self.space()?.put('(')?; | ||||
| } | ||||
| impl PrettyPrintable for FnDecl { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let FnDecl { name, args, body } = self; | ||||
|         p.put("fn")?.space()?; | ||||
|         name.visit(p)?; | ||||
|         p.space()?.put('(')?; | ||||
|         for (idx, arg) in args.iter().enumerate() { | ||||
|             if idx > 0 { | ||||
|                 self.put(',')?.space()?; | ||||
|                 p.put(',')?.space()?; | ||||
|             } | ||||
|             self.visit_identifier(arg)?; | ||||
|             arg.visit(p)?; | ||||
|         } | ||||
|         self.put(')')?.space()?; | ||||
|         self.visit_block(body) | ||||
|         p.put(')')?.space()?; | ||||
|         body.visit(p) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Name { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         if self.mutable { | ||||
|             p.put("mut")?.space()?; | ||||
|         } | ||||
|         self.name.visit(p)?; | ||||
|         if let Some(ty) = &self.ty { | ||||
|             ty.visit(p.put(':')?.space()?)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for TypeExpr { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             TypeExpr::TupleType(_tt) => todo!(), | ||||
|             TypeExpr::TypePath(t) => t.visit(p), | ||||
|             TypeExpr::Empty(_) => p.put("()").map(drop), | ||||
|             TypeExpr::Never(_) => p.put('!').map(drop), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Path { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Path { absolute, parts } = self; | ||||
|         if *absolute { | ||||
|             p.put("::")?; | ||||
|         } | ||||
|         for (idx, part) in parts.iter().enumerate() { | ||||
|             if idx != 0 { p.put("::")?;} | ||||
|             part.visit(p)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for PathPart { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             PathPart::PathSuper => p.put("super").map(drop), | ||||
|             PathPart::PathSelf => p.put("self").map(drop), | ||||
|             PathPart::PathIdent(id) =>id.visit(p), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Block { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Block { let_count: _, statements, expr } = self; | ||||
|         p.put('{')?.indent().newline()?; | ||||
|         for stmt in statements { | ||||
|             stmt.visit(p)?; | ||||
|         } | ||||
|         for expr in expr { | ||||
|             expr.visit(p)?; | ||||
|         } | ||||
|         p.dedent().newline()?.put('}').map(drop) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Expr { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Expr(expr) = self; | ||||
|         expr.visit(p) | ||||
|     } | ||||
| } | ||||
|  | ||||
|     fn visit_assign(&mut self, assign: &math::Assign) -> IOResult<()> { | ||||
|         let math::Assign { target, operator, init } = assign; | ||||
|         self.visit_identifier(target)?; | ||||
|         self.visit_assign_op(operator)?; | ||||
|         self.visit_operation(init) | ||||
| impl PrettyPrintable for Operation { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             Operation::Assign(value) => value.visit(p), | ||||
|             Operation::Binary(value) => value.visit(p), | ||||
|             Operation::Unary(value) => value.visit(p), | ||||
|             Operation::Call(value) => value.visit(p), | ||||
|         } | ||||
|     fn visit_binary(&mut self, binary: &math::Binary) -> IOResult<()> { | ||||
|         let math::Binary { first, other } = binary; | ||||
|         self.put('(')?.visit_operation(first)?; | ||||
|         for (op, other) in other { | ||||
|             self.visit_binary_op(op)?; | ||||
|             self.visit_operation(other)?; | ||||
|     } | ||||
|         self.put(')').map(drop) | ||||
| } | ||||
| impl PrettyPrintable for Assign { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Assign { target, operator, init } = self; | ||||
|         target.visit(p)?; | ||||
|         operator.visit(p)?; | ||||
|         init.visit(p) | ||||
|     } | ||||
|     fn visit_unary(&mut self, unary: &math::Unary) -> IOResult<()> { | ||||
|         let math::Unary { operators, operand } = unary; | ||||
|         for op in operators { | ||||
|             self.visit_unary_op(op)?; | ||||
|         } | ||||
|         self.visit_operation(operand) | ||||
|     } | ||||
|  | ||||
|     fn visit_assign_op(&mut self, op: &operator::Assign) -> IOResult<()> { | ||||
| } | ||||
| impl PrettyPrintable for operator::Assign { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         use operator::Assign; | ||||
|         visit_operator!(self.match op { | ||||
|         visit_operator!(p.match self { | ||||
|             Assign::Assign => "=", | ||||
|             Assign::AddAssign => "+=", | ||||
|             Assign::SubAssign => "-=", | ||||
| @@ -156,9 +238,23 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> { | ||||
|             Assign::ShrAssign => ">>=", | ||||
|         }) | ||||
|     } | ||||
|     fn visit_binary_op(&mut self, op: &operator::Binary) -> IOResult<()> { | ||||
| } | ||||
| impl PrettyPrintable for Binary { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Binary { first, other } = self; | ||||
|         p.put('(')?; | ||||
|         first.visit(p)?; | ||||
|         for (op, other) in other { | ||||
|             op.visit(p)?; | ||||
|             other.visit(p)? | ||||
|         } | ||||
|         p.put(')').map(drop) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for operator::Binary { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         use operator::Binary; | ||||
|         visit_operator!(self.match op { | ||||
|         visit_operator!(p.match self { | ||||
|             Binary::Mul => "*", | ||||
|             Binary::Div => "/", | ||||
|             Binary::Rem => "%", | ||||
| @@ -182,9 +278,20 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> { | ||||
|             Binary::Greater => ">", | ||||
|         }) | ||||
|     } | ||||
|     fn visit_unary_op(&mut self, op: &operator::Unary) -> IOResult<()> { | ||||
| } | ||||
| impl PrettyPrintable for Unary { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Unary { operators, operand } = self; | ||||
|         for op in operators { | ||||
|             op.visit(p)?; | ||||
|         } | ||||
|         operand.visit(p) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for operator::Unary { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         use operator::Unary; | ||||
|         self.put(match op { | ||||
|         p.put(match self { | ||||
|             Unary::RefRef => "&&", | ||||
|             Unary::Deref => "*", | ||||
|             Unary::Ref => "&", | ||||
| @@ -196,108 +303,155 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> { | ||||
|         }) | ||||
|         .map(drop) | ||||
|     } | ||||
|     fn visit_if(&mut self, expr: &control::If) -> IOResult<()> { | ||||
|         self.put("while")?.space()?.visit_expr(&expr.cond)?; | ||||
|         self.space()?.visit_block(&expr.body)?; | ||||
|         match &expr.else_ { | ||||
|             Some(e) => self.visit_else(e), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     fn visit_while(&mut self, expr: &control::While) -> IOResult<()> { | ||||
|         self.put("while")?.space()?.visit_expr(&expr.cond)?; | ||||
|         self.space()?.visit_block(&expr.body)?; | ||||
|         match &expr.else_ { | ||||
|             Some(e) => self.visit_else(e), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     fn visit_for(&mut self, expr: &control::For) -> IOResult<()> { | ||||
|         self.put("for")?.space()?.visit_identifier(&expr.var)?; | ||||
|         self.space()?.put("in")?.space()?.visit_expr(&expr.iter)?; | ||||
|         self.space()?.visit_block(&expr.body)?; | ||||
|         match &expr.else_ { | ||||
|             Some(e) => self.visit_else(e), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     fn visit_else(&mut self, else_: &control::Else) -> IOResult<()> { | ||||
|         self.space()?.put("else")?.space()?.visit_expr(&else_.expr) | ||||
|     } | ||||
|     fn visit_continue(&mut self, _: &control::Continue) -> IOResult<()> { | ||||
|         self.put("continue").map(drop) | ||||
|     } | ||||
|     fn visit_break(&mut self, brk: &control::Break) -> IOResult<()> { | ||||
|         self.put("break")?.space()?.visit_expr(&brk.expr) | ||||
|     } | ||||
|     fn visit_return(&mut self, ret: &control::Return) -> IOResult<()> { | ||||
|         self.put("return")?.space()?.visit_expr(&ret.expr) | ||||
|     } | ||||
| } | ||||
|  | ||||
|     fn visit_identifier(&mut self, ident: &Identifier) -> IOResult<()> { | ||||
|         self.put(&ident.0).map(drop) | ||||
| impl PrettyPrintable for Call { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             Call::FnCall(value) => value.visit(p), | ||||
|             Call::Primary(value) => value.visit(p), | ||||
|         } | ||||
|     fn visit_string_literal(&mut self, string: &str) -> IOResult<()> { | ||||
|         self.put("\"")?.put(string)?.put("\"").map(drop) | ||||
|     } | ||||
|     fn visit_char_literal(&mut self, char: &char) -> IOResult<()> { | ||||
|         self.put("'")?.put(char)?.put("'").map(drop) | ||||
|     } | ||||
|     fn visit_bool_literal(&mut self, bool: &bool) -> IOResult<()> { | ||||
|         self.put(bool).map(drop) | ||||
|     } | ||||
|     fn visit_float_literal(&mut self, float: &literal::Float) -> IOResult<()> { | ||||
|         self.put(float.sign)? | ||||
|             .put(float.exponent)? | ||||
|             .put(float.mantissa) | ||||
|             .map(drop) | ||||
|     } | ||||
|     fn visit_int_literal(&mut self, int: &u128) -> IOResult<()> { | ||||
|         self.put(int).map(drop) | ||||
|     } | ||||
|     fn visit_empty(&mut self) -> IOResult<()> { | ||||
|         self.put("()").map(drop) | ||||
|     } | ||||
|  | ||||
|     fn visit_block(&mut self, block: &expression::Block) -> IOResult<()> { | ||||
|         self.put('{')?.indent().newline()?; | ||||
|         for stmt in &block.statements { | ||||
|             self.visit_statement(stmt)?; | ||||
|         } | ||||
|         for expr in &block.expr { | ||||
|             self.visit_expr(expr)?; | ||||
|         } | ||||
|         self.dedent().newline()?.put('}').map(drop) | ||||
|     } | ||||
|  | ||||
|     fn visit_tuple(&mut self, tuple: &Tuple) -> IOResult<()> { | ||||
|         for (idx, expr) in tuple.elements.iter().enumerate() { | ||||
|             if idx > 0 { | ||||
|                 self.put(',')?.space()?; | ||||
|             } | ||||
|             self.visit_expr(expr)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn visit_group(&mut self, expr: &Group) -> IOResult<()> { | ||||
|         self.put('(')?; | ||||
|         match expr { | ||||
|             Group::Tuple(tuple) => self.space()?.visit_tuple(tuple), | ||||
|             Group::Single(expr) => self.space()?.visit_expr(expr), | ||||
|             Group::Empty => self.visit_empty(), | ||||
|         }?; | ||||
|         self.space()?.put(')').map(drop) | ||||
|     } | ||||
|  | ||||
|     fn visit_fn_call(&mut self, call: &FnCall) -> IOResult<()> { | ||||
|         let FnCall { callee, args } = call; | ||||
|         self.visit_primary(callee)?; | ||||
| } | ||||
| impl PrettyPrintable for FnCall { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let FnCall { callee, args } = self; | ||||
|         callee.visit(p)?; | ||||
|         for arg_list in args { | ||||
|             self.put('(')?; | ||||
|             self.visit_tuple(arg_list)?; | ||||
|             self.put(')')?; | ||||
|             p.put('(')?; | ||||
|             arg_list.visit(p)?; | ||||
|             p.put(')')?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Primary { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             Primary::Identifier(value) => value.visit(p), | ||||
|             Primary::Literal(value) => value.visit(p), | ||||
|             Primary::Block(value) => value.visit(p), | ||||
|             Primary::Group(value) => value.visit(p), | ||||
|             Primary::Branch(value) => value.visit(p), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Group { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         p.put('(')?; | ||||
|         match self { | ||||
|             Group::Tuple(tuple) => tuple.visit(p.space()?)?, | ||||
|             Group::Single(expr) => expr.visit(p.space()?)?, | ||||
|             Group::Empty => (), | ||||
|         }; | ||||
|         p.space()?.put(')').map(drop) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Tuple { | ||||
|     /// Writes a *non-delimited* [Tuple] to the [Printer] | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Tuple { elements } = self; | ||||
|         for (idx, expr) in elements.iter().enumerate() { | ||||
|             if idx > 0 { | ||||
|                 p.put(',')?.space()?; | ||||
|             } | ||||
|             expr.visit(p)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl PrettyPrintable for Identifier { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Identifier { name, index: _ } = self; // TODO: Pretty-print variable number as well | ||||
|         p.put(name).map(drop) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Literal { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             Literal::String(value) => write!(p, "\"{value}\""), | ||||
|             Literal::Char(value) => write!(p, "'{value}'"), | ||||
|             Literal::Bool(value) => write!(p, "{value}"), | ||||
|             Literal::Float(value) => value.visit(p), | ||||
|             Literal::Int(value) => write!(p, "{value}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Float { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Float { sign, exponent, mantissa } = self; | ||||
|         p.put(sign)?.put(exponent)?.put(mantissa).map(drop) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl PrettyPrintable for Flow { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         match self { | ||||
|             Flow::While(value) => value.visit(p), | ||||
|             Flow::If(value) => value.visit(p), | ||||
|             Flow::For(value) => value.visit(p), | ||||
|             Flow::Continue(value) => value.visit(p), | ||||
|             Flow::Return(value) => value.visit(p), | ||||
|             Flow::Break(value) => value.visit(p), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for While { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let While { cond, body, else_ } = self; | ||||
|         cond.visit(p.put("while")?.space()?)?; | ||||
|         body.visit(p.space()?)?; | ||||
|         match else_ { | ||||
|             Some(e) => e.visit(p), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for If { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let If { cond, body, else_ } = self; | ||||
|         cond.visit(p.put("if")?.space()?)?; | ||||
|         body.visit(p.space()?)?; | ||||
|         match else_ { | ||||
|             Some(e) => e.visit(p), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for For { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let For { var, iter, body, else_ } = self; | ||||
|         var.visit(p.put("for")?.space()?)?; | ||||
|         iter.visit(p.space()?.put("in")?.space()?)?; | ||||
|         body.visit(p.space()?)?; | ||||
|         match else_ { | ||||
|             Some(e) => e.visit(p), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Else { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Else { expr } = self; | ||||
|         expr.visit(p.space()?.put("else")?.space()?) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Continue { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let control::Continue = self; // using pattern destructuring, rather than assigning to "Continue" | ||||
|         p.put("continue").map(drop) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Break { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Break { expr } = self; | ||||
|         expr.visit(p.put("break")?.space()?) | ||||
|     } | ||||
| } | ||||
| impl PrettyPrintable for Return { | ||||
|     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||
|         let Return { expr } = self; | ||||
|         expr.visit(p.put("return")?.space()?) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -87,6 +87,9 @@ pub enum Keyword { | ||||
|     Let, | ||||
|     Mut, | ||||
|     Return, | ||||
|     SelfKw, | ||||
|     SelfTy, | ||||
|     Super, | ||||
|     True, | ||||
|     While, | ||||
| } | ||||
| @@ -174,6 +177,9 @@ impl Display for Keyword { | ||||
|             Self::Let => "let".fmt(f), | ||||
|             Self::Mut => "mut".fmt(f), | ||||
|             Self::Return => "return".fmt(f), | ||||
|             Self::SelfKw => "self".fmt(f), | ||||
|             Self::SelfTy => "Self".fmt(f), | ||||
|             Self::Super => "super".fmt(f), | ||||
|             Self::True => "true".fmt(f), | ||||
|             Self::While => "while".fmt(f), | ||||
|         } | ||||
| @@ -195,6 +201,9 @@ impl FromStr for Keyword { | ||||
|             "let" => Self::Let, | ||||
|             "mut" => Self::Mut, | ||||
|             "return" => Self::Return, | ||||
|             "self" => Self::SelfKw, | ||||
|             "Self" => Self::SelfTy, | ||||
|             "super" => Self::Super, | ||||
|             "true" => Self::True, | ||||
|             "while" => Self::While, | ||||
|             _ => Err(())?, | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| // Calculate Fibonacci numbers | ||||
|  | ||||
| fn main() -> i128 { | ||||
|     print("fib(10):"); | ||||
|     fib(10) | ||||
|     print("fib(10): ", fib(10)); | ||||
| } | ||||
|  | ||||
| /// Implements the classic recursive definition of fib() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user