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] | [workspace] | ||||||
| members = ["libconlang"] | members = ["libconlang", "cl-frontend"] | ||||||
| resolver = "2" | resolver = "2" | ||||||
|  |  | ||||||
| [workspace.package] | [workspace.package] | ||||||
| repository = "https://git.soft.fish/j/Conlang" | repository = "https://git.soft.fish/j/Conlang" | ||||||
| version = "0.0.1" | version = "0.0.2" | ||||||
| authors = ["John Breaux <j@soft.fish>"] | authors = ["John Breaux <j@soft.fish>"] | ||||||
| edition = "2021" | edition = "2021" | ||||||
| license = "MIT" | 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 *) | (* Conlang Expression Grammar *) | ||||||
| Start       = Program ; | Start       = Program ; | ||||||
| Program     = Stmt* EOI ; | 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 *) | ||||||
| Literal     = STRING | CHARACTER | FLOAT | INTEGER | Bool ; | Literal     = STRING | CHARACTER | FLOAT | INTEGER | Bool ; | ||||||
| Bool        = "true" | "false" ; | Bool        = "true" | "false" ; | ||||||
| @@ -10,11 +17,22 @@ Identifier  = IDENTIFIER ; | |||||||
| (* # Statements *) | (* # Statements *) | ||||||
| (* statement *) | (* statement *) | ||||||
| Stmt        = Fn | Let | Expr ';' ; | Stmt        = Fn | Let | Expr ';' ; | ||||||
| Let         = "let" "mut"? Identifier (':' Type)? ('=' Expr)? ';' ; | Let         = "let" Name ('=' Expr)? ';' ; | ||||||
| Fn          = "fn" Identifier '(' Params? ')' Block ; | Fn          = "fn" Identifier '(' Params? ')' Block ; | ||||||
| (* TODO: Type system *) | (* TODO: Type system *) | ||||||
| Params      = (Param ',')* Param? ; | Params      = (Name ',')* Name? ; | ||||||
| Param       = Identifier (*':' Type *) ; | Name        = "mut"? Identifier (':' Type )? ; | ||||||
|  |  | ||||||
|  | (* # Type Expressions *) | ||||||
|  | (* types *) | ||||||
|  | TypeExpr    = Never | Empty | TypeTuple | PathExpr ; | ||||||
|  | Never       = '!' ; | ||||||
|  | Empty       = '(' ')' ; | ||||||
|  | TypeTuple   = '(' (TypeExpr ',')* TypeExpr? ')' ; | ||||||
|  |  | ||||||
|  | PathExpr    = '::'? PathPart ; | ||||||
|  | PathPart    = "super" | Identifier ; | ||||||
|  |  | ||||||
|  |  | ||||||
| (* # Expressions *) | (* # Expressions *) | ||||||
| (* expression *) | (* 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. | //! See [statement], [literal], and [expression] for more information. | ||||||
|  |  | ||||||
| pub mod preamble { | pub mod preamble { | ||||||
|  |     #![allow(deprecated)] | ||||||
|     //! Common imports for working with the [ast](super) |     //! Common imports for working with the [ast](super) | ||||||
|     pub use super::{ |     pub use super::{ | ||||||
|         expression::{ |         expression::{call::*, control::*, math::*, tuple::*, *}, | ||||||
|             self, |         literal::*, | ||||||
|             call::*, |         path::*, | ||||||
|             control, |  | ||||||
|             math::{self, operator}, |  | ||||||
|             tuple::*, |  | ||||||
|         }, |  | ||||||
|         literal, |  | ||||||
|         statement::*, |         statement::*, | ||||||
|  |         types::*, | ||||||
|         visitor::Visitor, |         visitor::Visitor, | ||||||
|         Identifier, Program, Start, |         *, | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -43,47 +40,9 @@ pub struct Program(pub Vec<statement::Stmt>); | |||||||
| /// # Syntax | /// # Syntax | ||||||
| /// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier) | /// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier) | ||||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||||
| pub struct Identifier(pub String); | pub struct Identifier { | ||||||
|  |     pub name: String, | ||||||
| pub mod todo { |     pub index: Option<usize>, | ||||||
|     //! 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 mod literal { | pub mod literal { | ||||||
| @@ -150,6 +109,7 @@ pub mod statement { | |||||||
|  |  | ||||||
|     use super::{ |     use super::{ | ||||||
|         expression::{Block, Expr}, |         expression::{Block, Expr}, | ||||||
|  |         types::TypeExpr, | ||||||
|         Identifier, |         Identifier, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -164,7 +124,7 @@ pub mod statement { | |||||||
|         Let(Let), |         Let(Let), | ||||||
|         /// Contains a function declaration |         /// Contains a function declaration | ||||||
|         /// # Syntax |         /// # Syntax | ||||||
|         /// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'` [`Block`] |         /// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` `Args...` `')'` [`Block`] | ||||||
|         Fn(FnDecl), |         Fn(FnDecl), | ||||||
|         /// Contains an expression statement |         /// Contains an expression statement | ||||||
|         /// # Syntax |         /// # Syntax | ||||||
| @@ -177,26 +137,104 @@ pub mod statement { | |||||||
|     /// [`Let`] := `let` [`Identifier`] (`:`) `Type`)? (`=` [`Expr`])? `;` |     /// [`Let`] := `let` [`Identifier`] (`:`) `Type`)? (`=` [`Expr`])? `;` | ||||||
|     #[derive(Clone, Debug)] |     #[derive(Clone, Debug)] | ||||||
|     pub struct Let { |     pub struct Let { | ||||||
|         pub name: Identifier, |         pub name: Name, | ||||||
|         pub mutable: bool, |  | ||||||
|         pub ty: Option<Identifier>, |  | ||||||
|         pub init: Option<Expr>, |         pub init: Option<Expr>, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Contains a function declaration |     /// Contains a function declaration | ||||||
|     /// # Syntax |     /// # Syntax | ||||||
|     /// [`FnDecl`] := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'` |     /// [`FnDecl`] := `"fn"` [`Identifier`] `'('` `Args...` `')'` | ||||||
|     #[derive(Clone, Debug)] |     #[derive(Clone, Debug)] | ||||||
|     pub struct FnDecl { |     pub struct FnDecl { | ||||||
|         pub name: Identifier, |         pub name: Name, | ||||||
|         pub args: Vec<Identifier>, |         pub args: Vec<Name>, | ||||||
|         pub body: Block, |         pub body: Block, | ||||||
|         // TODO: Store type information |         // 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 |     // 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 { | pub mod expression { | ||||||
|     //! # Expressions |     //! # Expressions | ||||||
|     //! |     //! | ||||||
| @@ -219,17 +257,19 @@ pub mod expression { | |||||||
|     //! | 9 | [`control::Flow`] | Branch expressions (`if`, `while`, `for`, `return`, `break`, `continue`) |     //! | 9 | [`control::Flow`] | Branch expressions (`if`, `while`, `for`, `return`, `break`, `continue`) | ||||||
|     //! | 9 |         [`Group`] | Group expressions `(` [Expr]? `)` /* Can evaluate to Empty! */ |     //! | 9 |         [`Group`] | Group expressions `(` [Expr]? `)` /* Can evaluate to Empty! */ | ||||||
|     //! | 9 |         [`Block`] | Block expressions `{` [Expr] `}` |     //! | 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 |     //! ## Syntax | ||||||
|     //! [`Expr`]`  := `[`math::Operation`]  \ |     //! [`Expr`]`  := `[`math::Operation`]  \ | ||||||
|     //! [`Block`]` := '{' `[`Expr`]` '}'`   \ |     //! [`Block`]` := '{' `[`Expr`]` '}'`   \ | ||||||
|     //! [`Group`]` := '(' `[`Expr`]`? ')'`  \ |     //! [`Group`]` := '(' `[`Expr`]`? ')'`  \ | ||||||
|     //! [`Primary`]` := `[`Identifier`]` | `[`Literal`](literal::Literal)` | `[`Block`]` | |     //! [`Primary`]` := `[`Identifier`]` | `[`Literal`]` | `[`Block`]` | | ||||||
|     //! `[`Group`]` | `[`control::Flow`] |     //! `[`Group`]` | `[`Flow`] | ||||||
|     //! |     //! | ||||||
|     //! See [control] and [math] for their respective production rules. |     //! 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 |     /// Contains an expression | ||||||
|     /// |     /// | ||||||
| @@ -241,18 +281,18 @@ pub mod expression { | |||||||
|     /// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest |     /// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest | ||||||
|     /// derivation) |     /// derivation) | ||||||
|     /// # Syntax |     /// # Syntax | ||||||
|     /// [`Primary`]` := `[`IDENTIFIER`](Identifier)` |     /// [`Primary`]` := `[`Identifier`]` | ||||||
|     /// | `[`Literal`](literal::Literal)` |     /// | `[`Literal`]` | ||||||
|     /// | `[`Block`]` |     /// | `[`Block`]` | ||||||
|     /// | `[`Group`](tuple::Group)` |     /// | `[`Group`]` | ||||||
|     /// | `[`Branch`](control::Flow) |     /// | `[`Branch`](Flow) | ||||||
|     #[derive(Clone, Debug)] |     #[derive(Clone, Debug)] | ||||||
|     pub enum Primary { |     pub enum Primary { | ||||||
|         Identifier(Identifier), |         Identifier(Identifier), | ||||||
|         Literal(literal::Literal), |         Literal(Literal), | ||||||
|         Block(Block), |         Block(Block), | ||||||
|         Group(tuple::Group), |         Group(Group), | ||||||
|         Branch(control::Flow), |         Branch(Flow), | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Contains a Block Expression |     /// Contains a Block Expression | ||||||
| @@ -260,6 +300,7 @@ pub mod expression { | |||||||
|     /// [`Block`] := `'{'` [`Expr`] `'}'` |     /// [`Block`] := `'{'` [`Expr`] `'}'` | ||||||
|     #[derive(Clone, Debug)] |     #[derive(Clone, Debug)] | ||||||
|     pub struct Block { |     pub struct Block { | ||||||
|  |         pub let_count: Option<usize>, | ||||||
|         pub statements: Vec<Stmt>, |         pub statements: Vec<Stmt>, | ||||||
|         pub expr: Option<Box<Expr>>, |         pub expr: Option<Box<Expr>>, | ||||||
|     } |     } | ||||||
| @@ -352,7 +393,7 @@ pub mod expression { | |||||||
|         //! [`Shift`][2]`   := `[`Term`][2]`    (`[`ShiftOp`][5]`   `[`Term`][2]`   )*`  \ |         //! [`Shift`][2]`   := `[`Term`][2]`    (`[`ShiftOp`][5]`   `[`Term`][2]`   )*`  \ | ||||||
|         //! [`Term`][2]`    := `[`Factor`][2]`  (`[`TermOp`][5]`    `[`Factor`][2]` )*`  \ |         //! [`Term`][2]`    := `[`Factor`][2]`  (`[`TermOp`][5]`    `[`Factor`][2]` )*`  \ | ||||||
|         //! [`Factor`][2]`  := `[`Unary`][1]`   (`[`FactorOp`][5]`  `[`Unary`][1]`  )*`  \ |         //! [`Factor`][2]`  := `[`Unary`][1]`   (`[`FactorOp`][5]`  `[`Unary`][1]`  )*`  \ | ||||||
|         //! [`Unary`][1]`   := (`[`UnaryOp`][4]`)* `[`FnCall`][7] |         //! [`Unary`][1]`   := (`[`UnaryOp`][4]`)* `[`Call`] | ||||||
|         //! |         //! | ||||||
|         //! [1]: Operation::Unary |         //! [1]: Operation::Unary | ||||||
|         //! [2]: Operation::Binary |         //! [2]: Operation::Binary | ||||||
| @@ -704,10 +745,10 @@ pub mod expression { | |||||||
|  |  | ||||||
| pub mod visitor { | pub mod visitor { | ||||||
|     //! A [`Visitor`] visits every kind of node in the [Abstract Syntax Tree](super) |     //! A [`Visitor`] visits every kind of node in the [Abstract Syntax Tree](super) | ||||||
|     //!  |     //! | ||||||
|     //! This trait is mostly here to ensure that every branch of the tree is accounted for. |     //! This trait is mostly here to ensure that every branch of the tree is accounted for. | ||||||
|     //!  |     //! | ||||||
|     //! Default implementations are provided for  |     //! Default implementations are provided for | ||||||
|     use super::{ |     use super::{ | ||||||
|         expression::{call::*, control::*, math::*, tuple::*, Block, *}, |         expression::{call::*, control::*, math::*, tuple::*, Block, *}, | ||||||
|         literal::*, |         literal::*, | ||||||
| @@ -716,6 +757,7 @@ pub mod visitor { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     /// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super) |     /// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super) | ||||||
|  |     #[deprecated] | ||||||
|     pub trait Visitor<R> { |     pub trait Visitor<R> { | ||||||
|         /// Visit the start of an AST |         /// Visit the start of an AST | ||||||
|         fn visit(&mut self, start: &Start) -> R { |         fn visit(&mut self, start: &Start) -> R { | ||||||
| @@ -733,9 +775,9 @@ pub mod visitor { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         /// Visit a [Let statement](Let) |         /// 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) |         /// 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) |         /// Visit an [Expression](Expr) | ||||||
|         fn visit_expr(&mut self, expr: &Expr) -> R { |         fn visit_expr(&mut self, expr: &Expr) -> R { | ||||||
| @@ -781,10 +823,11 @@ pub mod visitor { | |||||||
|         /// Visit a [Unary] Operation |         /// Visit a [Unary] Operation | ||||||
|         fn visit_unary(&mut self, unary: &Unary) -> R; |         fn visit_unary(&mut self, unary: &Unary) -> R; | ||||||
|         // Math operators |         // Math operators | ||||||
|  |         /// Visit an [Assignment](Assign) [operator](operator::Assign) | ||||||
|         fn visit_assign_op(&mut self, op: &operator::Assign) -> R; |         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; |         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; |         fn visit_unary_op(&mut self, op: &operator::Unary) -> R; | ||||||
|  |  | ||||||
|         /// Visit a [Primary] expression |         /// Visit a [Primary] expression | ||||||
| @@ -796,7 +839,7 @@ pub mod visitor { | |||||||
|                 Primary::Literal(v) => self.visit_literal(v), |                 Primary::Literal(v) => self.visit_literal(v), | ||||||
|                 Primary::Block(v) => self.visit_block(v), |                 Primary::Block(v) => self.visit_block(v), | ||||||
|                 Primary::Group(v) => self.visit_group(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`]` |         /// [`Flow`]` := `[`While`]` | `[`If`]` | `[`For`]` | ||||||
|         /// | `[`Continue`]` | `[`Return`]` | `[`Break`] |         /// | `[`Continue`]` | `[`Return`]` | `[`Break`] | ||||||
|         fn visit_branch_expr(&mut self, expr: &Flow) -> R { |         fn visit_branch(&mut self, flow: &Flow) -> R { | ||||||
|             match expr { |             match flow { | ||||||
|                 Flow::While(e) => self.visit_while(e), |                 Flow::While(e) => self.visit_while(e), | ||||||
|                 Flow::If(e) => self.visit_if(e), |                 Flow::If(e) => self.visit_if(e), | ||||||
|                 Flow::For(e) => self.visit_for(e), |                 Flow::For(e) => self.visit_for(e), | ||||||
| @@ -859,3 +902,46 @@ pub mod visitor { | |||||||
|         fn visit_empty(&mut self) -> R; |         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 | //! Interprets an AST as a program | ||||||
|  | #![allow(deprecated)] // TODO: REMOVE | ||||||
|  |  | ||||||
| use crate::ast::preamble::*; | use crate::ast::preamble::*; | ||||||
| use error::{Error, IResult}; | use error::{Error, IResult}; | ||||||
| @@ -9,7 +10,7 @@ use temp_type_impl::ConValue; | |||||||
| pub trait Callable: std::fmt::Debug { | pub trait Callable: std::fmt::Debug { | ||||||
|     /// Calls this [Callable] in the provided [Interpreter], with [ConValue] args  \ |     /// Calls this [Callable] in the provided [Interpreter], with [ConValue] args  \ | ||||||
|     /// The Callable is responsible for checking the argument count and validating types |     /// 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. |     /// Returns the common name of this identifier. | ||||||
|     fn name(&self) -> &str; |     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 { |             match self { | ||||||
|                 Self::Function(func) => func.call(interpreter, args), |                 Self::Function(func) => func.call(interpreter, args), | ||||||
|                 Self::BuiltIn(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<()> { |     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 { |         if let Some(init) = init { | ||||||
|             self.visit_expr(init)?; |             self.visit_expr(init)?; | ||||||
|             let init = self.pop()?; |             let init = self.pop()?; | ||||||
| @@ -394,7 +395,8 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|             let (ConValue::Tuple(args), callee) = self.pop_two()? else { |             let (ConValue::Tuple(args), callee) = self.pop_two()? else { | ||||||
|                 Err(Error::TypeError)? |                 Err(Error::TypeError)? | ||||||
|             }; |             }; | ||||||
|             callee.call(self, &args)?; |             let return_value = callee.call(self, &args)?; | ||||||
|  |             self.push(return_value); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -404,7 +406,8 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|         let math::Assign { target, operator, init } = assign; |         let math::Assign { target, operator, init } = assign; | ||||||
|         self.visit_operation(init)?; |         self.visit_operation(init)?; | ||||||
|         let init = self.pop()?; |         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 { |         if let Assign::Assign = operator { | ||||||
|             use std::mem::discriminant as variant; |             use std::mem::discriminant as variant; | ||||||
|             // runtime typecheck |             // runtime typecheck | ||||||
| @@ -418,9 +421,11 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|             self.push(ConValue::Empty); |             self.push(ConValue::Empty); | ||||||
|             return Ok(()); |             return Ok(()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let Some(target) = resolved.as_mut() else { |         let Some(target) = resolved.as_mut() else { | ||||||
|             Err(Error::NotInitialized(target.0.to_owned()))? |             Err(Error::NotInitialized(target.name.to_owned()))? | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         match operator { |         match operator { | ||||||
|             Assign::AddAssign => target.add_assign(init)?, |             Assign::AddAssign => target.add_assign(init)?, | ||||||
|             Assign::SubAssign => target.sub_assign(init)?, |             Assign::SubAssign => target.sub_assign(init)?, | ||||||
| @@ -434,12 +439,13 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|             Assign::ShrAssign => target.shr_assign(init)?, |             Assign::ShrAssign => target.shr_assign(init)?, | ||||||
|             _ => (), |             _ => (), | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         self.push(ConValue::Empty); |         self.push(ConValue::Empty); | ||||||
|  |  | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn visit_binary(&mut self, bin: &math::Binary) -> IResult<()> { |     fn visit_binary(&mut self, bin: &math::Binary) -> IResult<()> { | ||||||
|         use math::Binary; |  | ||||||
|         let Binary { first, other } = bin; |         let Binary { first, other } = bin; | ||||||
|  |  | ||||||
|         self.visit_operation(first)?; |         self.visit_operation(first)?; | ||||||
| @@ -469,15 +475,19 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn visit_unary(&mut self, unary: &math::Unary) -> IResult<()> { |     fn visit_unary(&mut self, unary: &math::Unary) -> IResult<()> { | ||||||
|         let math::Unary { operand, operators } = unary; |         let Unary { operand, operators } = unary; | ||||||
|  |  | ||||||
|         self.visit_operation(operand)?; |         self.visit_operation(operand)?; | ||||||
|  |  | ||||||
|         for op in operators.iter().rev() { |         for op in operators.iter().rev() { | ||||||
|             self.visit_unary_op(op)?; |             self.visit_unary_op(op)?; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -488,6 +498,7 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|     fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> { |     fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> { | ||||||
|         use operator::Binary; |         use operator::Binary; | ||||||
|         let (second, first) = self.pop_two()?; |         let (second, first) = self.pop_two()?; | ||||||
|  |  | ||||||
|         let out = match op { |         let out = match op { | ||||||
|             Binary::Mul => first * second, |             Binary::Mul => first * second, | ||||||
|             Binary::Div => first / second, |             Binary::Div => first / second, | ||||||
| @@ -511,12 +522,14 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|             Binary::GreaterEq => first.gt_eq(&second), |             Binary::GreaterEq => first.gt_eq(&second), | ||||||
|             Binary::Greater => first.gt(&second), |             Binary::Greater => first.gt(&second), | ||||||
|         }?; |         }?; | ||||||
|  |  | ||||||
|         self.push(out); |         self.push(out); | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn visit_unary_op(&mut self, op: &operator::Unary) -> IResult<()> { |     fn visit_unary_op(&mut self, op: &operator::Unary) -> IResult<()> { | ||||||
|         let operand = self.pop()?; |         let operand = self.pop()?; | ||||||
|  |  | ||||||
|         self.push(match op { |         self.push(match op { | ||||||
|             operator::Unary::RefRef => todo!(), |             operator::Unary::RefRef => todo!(), | ||||||
|             operator::Unary::Ref => todo!(), |             operator::Unary::Ref => todo!(), | ||||||
| @@ -530,10 +543,11 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|             } |             } | ||||||
|             operator::Unary::Tilde => todo!(), |             operator::Unary::Tilde => todo!(), | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn visit_if(&mut self, expr: &control::If) -> IResult<()> { |     fn visit_if(&mut self, expr: &If) -> IResult<()> { | ||||||
|         self.visit_expr(&expr.cond)?; |         self.visit_expr(&expr.cond)?; | ||||||
|         if self.pop()?.truthy()? { |         if self.pop()?.truthy()? { | ||||||
|             self.visit_block(&expr.body)?; |             self.visit_block(&expr.body)?; | ||||||
| @@ -545,7 +559,7 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn visit_while(&mut self, expr: &control::While) -> IResult<()> { |     fn visit_while(&mut self, expr: &While) -> IResult<()> { | ||||||
|         while { |         while { | ||||||
|             self.visit_expr(&expr.cond)?; |             self.visit_expr(&expr.cond)?; | ||||||
|             self.pop()?.truthy()? |             self.pop()?.truthy()? | ||||||
| @@ -581,7 +595,7 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|             _ => Err(Error::NotIterable)?, |             _ => Err(Error::NotIterable)?, | ||||||
|         }; |         }; | ||||||
|         for loop_var in bounds.0..=bounds.1 { |         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 { |             let Err(out) = self.visit_block(&expr.body) else { | ||||||
|                 self.pop()?; |                 self.pop()?; | ||||||
|                 continue; |                 continue; | ||||||
| @@ -627,7 +641,7 @@ impl Visitor<IResult<()>> for Interpreter { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> { |     fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> { | ||||||
|         let value = self.resolve(&ident.0)?; |         let value = self.resolve(&ident.name)?; | ||||||
|         self.push(value); |         self.push(value); | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -670,8 +684,17 @@ impl Default for Interpreter { | |||||||
|  |  | ||||||
| pub mod function { | pub mod function { | ||||||
|     //! Represents a block of code which lives inside the Interpreter |     //! Represents a block of code which lives inside the Interpreter | ||||||
|     use super::{Callable, ConValue, Error, FnDecl, IResult, Identifier, Interpreter}; |     use super::{ | ||||||
|     use crate::ast::visitor::Visitor; |         // 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 |     /// Represents a block of code which persists inside the Interpreter | ||||||
|     #[derive(Clone, Debug)] |     #[derive(Clone, Debug)] | ||||||
|     pub struct Function { |     pub struct Function { | ||||||
| @@ -679,19 +702,23 @@ pub mod function { | |||||||
|         declaration: Box<FnDecl>, |         declaration: Box<FnDecl>, | ||||||
|         // /// Stores the enclosing scope of the function |         // /// Stores the enclosing scope of the function | ||||||
|         // TODO: Capture variables |         // TODO: Capture variables | ||||||
|  |         //environment: Box<Environment>, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     impl Function { |     impl Function { | ||||||
|         pub fn new(declaration: &FnDecl) -> Self { |         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 { |     impl Callable for Function { | ||||||
|         fn name(&self) -> &str { |         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 |             // Check arg mapping | ||||||
|             if args.len() != self.declaration.args.len() { |             if args.len() != self.declaration.args.len() { | ||||||
|                 return Err(Error::ArgNumber { |                 return Err(Error::ArgNumber { | ||||||
| @@ -701,17 +728,19 @@ pub mod function { | |||||||
|             } |             } | ||||||
|             // TODO: Isolate cross-function scopes! |             // TODO: Isolate cross-function scopes! | ||||||
|             interpreter.scope.enter(); |             interpreter.scope.enter(); | ||||||
|             for (Identifier(arg), value) in self.declaration.args.iter().zip(args) { |             for (Name { name: Identifier { name, .. }, .. }, value) in | ||||||
|                 interpreter.scope.insert(arg, Some(value.clone())); |                 self.declaration.args.iter().zip(args) | ||||||
|  |             { | ||||||
|  |                 interpreter.scope.insert(name, Some(value.clone())); | ||||||
|             } |             } | ||||||
|             match interpreter.visit_block(&self.declaration.body) { |             match interpreter.visit_block(&self.declaration.body) { | ||||||
|                 Err(Error::Return(value)) => interpreter.push(value), |                 Err(Error::Return(value)) => interpreter.push(value), | ||||||
|                 Err(Error::Break(value)) => Err(Error::BadBreak(value))?, |                 Err(Error::Break(value)) => Err(Error::BadBreak(value))?, | ||||||
|                 Err(e) => Err(e)?, |                 Err(e) => Err(e)?, | ||||||
|                 Ok(_) => (), |                 Ok(()) => (), | ||||||
|             } |             } | ||||||
|             interpreter.scope.exit()?; |             interpreter.scope.exit()?; | ||||||
|             Ok(()) |             interpreter.pop() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -719,14 +748,12 @@ pub mod function { | |||||||
| pub mod builtin { | pub mod builtin { | ||||||
|     mod builtin_imports { |     mod builtin_imports { | ||||||
|         pub use crate::interpreter::{ |         pub use crate::interpreter::{ | ||||||
|             error::{Error, IResult}, |             error::IResult, temp_type_impl::ConValue, BuiltIn, Callable, Interpreter, | ||||||
|             temp_type_impl::ConValue, |  | ||||||
|             BuiltIn, Callable, Interpreter, |  | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|     use super::BuiltIn; |     use super::BuiltIn; | ||||||
|     /// Builtins to load when a new interpreter is created |     /// 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 { |     mod print { | ||||||
|         //! Implements the unstable `print(...)` builtin |         //! Implements the unstable `print(...)` builtin | ||||||
| @@ -738,11 +765,12 @@ pub mod builtin { | |||||||
|         #[rustfmt::skip] |         #[rustfmt::skip] | ||||||
|         impl Callable for Print { |         impl Callable for Print { | ||||||
|             fn name(&self) -> &'static str { "print" } |             fn name(&self) -> &'static str { "print" } | ||||||
|             fn call(&self, inter: &mut Interpreter, args: &[ConValue]) -> IResult<()> { |             fn call(&self, _inter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> { | ||||||
|                 for arg in args { print!("{arg}") } |                 for arg in args { | ||||||
|  |                     print!("{arg}") | ||||||
|  |                 } | ||||||
|                 println!(); |                 println!(); | ||||||
|                 inter.push(ConValue::Empty); |                 Ok(ConValue::Empty) | ||||||
|                 Ok(()) |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -755,10 +783,27 @@ pub mod builtin { | |||||||
|         #[rustfmt::skip] |         #[rustfmt::skip] | ||||||
|         impl Callable for Dbg { |         impl Callable for Dbg { | ||||||
|             fn name(&self) -> &str { "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:?}"); |                 println!("{args:?}"); | ||||||
|                 inter.push(args); |                 Ok(args.into()) | ||||||
|                 Ok(()) |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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, |         temp_type_impl::ConValue, | ||||||
|         FnDecl, |         FnDecl, | ||||||
|     }; |     }; | ||||||
|     use std::collections::HashMap; |     use std::{collections::HashMap, fmt::Display}; | ||||||
|  |  | ||||||
|     /// Implements a nested lexical scope |     /// Implements a nested lexical scope | ||||||
|     #[derive(Clone, Debug, Default)] |     #[derive(Clone, Debug, Default)] | ||||||
| @@ -783,6 +828,19 @@ pub mod scope { | |||||||
|         vars: HashMap<String, Option<ConValue>>, |         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 { |     impl Environment { | ||||||
|         pub fn new() -> Self { |         pub fn new() -> Self { | ||||||
|             let mut out = Self::default(); |             let mut out = Self::default(); | ||||||
| @@ -835,8 +893,10 @@ pub mod scope { | |||||||
|         } |         } | ||||||
|         /// A convenience function for registering a [FnDecl] as a [Function] |         /// A convenience function for registering a [FnDecl] as a [Function] | ||||||
|         pub fn insert_fn(&mut self, decl: &FnDecl) { |         pub fn insert_fn(&mut self, decl: &FnDecl) { | ||||||
|             self.vars |             self.vars.insert( | ||||||
|                 .insert(decl.name.0.clone(), Some(Function::new(decl).into())); |                 decl.name.name.name.clone(), | ||||||
|  |                 Some(Function::new(decl).into()), | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,18 +1,37 @@ | |||||||
| //! Parses [tokens](super::token) into an [AST](super::ast) | //! Parses [tokens](super::token) into an [AST](super::ast) | ||||||
|  |  | ||||||
| use super::{ast::preamble::*, lexer::Lexer, token::preamble::*}; | use super::{ast::preamble::*, lexer::Lexer, token::preamble::*}; | ||||||
| use error::{Error, Reason::*, *}; | use error::{Error, *}; | ||||||
|  |  | ||||||
| pub mod error { | pub mod error { | ||||||
|     use super::{Token, Type}; |     use super::{Token, Type}; | ||||||
|     use std::fmt::Display; |     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] |     /// The reason for the [Error] | ||||||
|     #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] |     #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] | ||||||
|     pub enum Reason { |     pub enum Reason { | ||||||
|         Expected(Type), |         Expected(Type), | ||||||
|         Unexpected(Type), |         Unexpected(Type), | ||||||
|  |         NotPathSegment(Type), | ||||||
|         NotIdentifier, |         NotIdentifier, | ||||||
|  |         NotStatement, | ||||||
|  |         NotLet, | ||||||
|  |         NotFnDecl, | ||||||
|         NotOperator, |         NotOperator, | ||||||
|         NotLiteral, |         NotLiteral, | ||||||
|         NotString, |         NotString, | ||||||
| @@ -37,7 +56,11 @@ pub mod error { | |||||||
|             match self { |             match self { | ||||||
|                 Self::Expected(t) => write!(f, "Expected {t}"), |                 Self::Expected(t) => write!(f, "Expected {t}"), | ||||||
|                 Self::Unexpected(t) => write!(f, "Unexpected {t} in bagging area"), |                 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::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::NotOperator => "Not an operator".fmt(f), | ||||||
|                 Self::NotLiteral => "Not a literal".fmt(f), |                 Self::NotLiteral => "Not a literal".fmt(f), | ||||||
|                 Self::NotString => "Not a string".fmt(f), |                 Self::NotString => "Not a string".fmt(f), | ||||||
| @@ -67,11 +90,15 @@ pub mod error { | |||||||
|     #[derive(Clone, Debug, Default, PartialEq)] |     #[derive(Clone, Debug, Default, PartialEq)] | ||||||
|     pub struct Error { |     pub struct Error { | ||||||
|         reason: Reason, |         reason: Reason, | ||||||
|  |         child: Option<Box<Self>>, | ||||||
|         start: Option<Token>, |         start: Option<Token>, | ||||||
|     } |     } | ||||||
|     impl std::error::Error for Error {} |     impl std::error::Error for Error {} | ||||||
|     impl Display for Error { |     impl Display for Error { | ||||||
|         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |         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 { |             if let Some(token) = &self.start { | ||||||
|                 write!(f, "{}:{}: ", token.line(), token.col())?; |                 write!(f, "{}:{}: ", token.line(), token.col())?; | ||||||
|             } |             } | ||||||
| @@ -84,7 +111,7 @@ pub mod error { | |||||||
|         #[doc = concat!("[`", stringify!($reason), "`]")] |         #[doc = concat!("[`", stringify!($reason), "`]")] | ||||||
|         #[allow(dead_code)] |         #[allow(dead_code)] | ||||||
|         pub(crate) fn $fn($($($p : $t),*)?) -> Self { |         pub(crate) fn $fn($($($p : $t),*)?) -> Self { | ||||||
|             Self { reason: $reason$(($($p)*))?, start: None } |             Self { reason: $reason$(($($p)*))?, child: None, start: None } | ||||||
|         } |         } | ||||||
|     )*} |     )*} | ||||||
|     impl Error { |     impl Error { | ||||||
| @@ -104,14 +131,14 @@ pub mod error { | |||||||
|         pub fn reason(&self) -> Reason { |         pub fn reason(&self) -> Reason { | ||||||
|             self.reason |             self.reason | ||||||
|         } |         } | ||||||
|         /// Modifies the [Reason] of this error |  | ||||||
|         pub fn with_reason(self, reason: Reason) -> Self { |  | ||||||
|             Self { reason, ..self } |  | ||||||
|         } |  | ||||||
|         error_impl! { |         error_impl! { | ||||||
|             expected(e: Type): Expected, |             expected(e: Type): Expected, | ||||||
|             unexpected(e: Type): Unexpected, |             unexpected(e: Type): Unexpected, | ||||||
|  |             not_path_segment(e: Type): NotPathSegment, | ||||||
|             not_identifier: NotIdentifier, |             not_identifier: NotIdentifier, | ||||||
|  |             not_statement: NotStatement, | ||||||
|  |             not_let: NotLet, | ||||||
|  |             not_fn_decl: NotFnDecl, | ||||||
|             not_operator: NotOperator, |             not_operator: NotOperator, | ||||||
|             not_literal: NotLiteral, |             not_literal: NotLiteral, | ||||||
|             not_string: NotString, |             not_string: NotString, | ||||||
| @@ -160,20 +187,25 @@ impl Parser { | |||||||
|     pub fn new(tokens: Vec<Token>) -> Self { |     pub fn new(tokens: Vec<Token>) -> Self { | ||||||
|         Self { tokens, panic_stack: vec![], errors: vec![], cursor: 0 } |         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) |     /// Parses the [start of an AST](Start) | ||||||
|     pub fn parse(&mut self) -> PResult<Start> { |     pub fn parse(&mut self) -> PResult<Start> { | ||||||
|         self.consume_comments(); |         self.consume_comments(); | ||||||
|         Ok(Start(self.program()?)) |         Ok(Start(self.program()?)) | ||||||
|     } |     } | ||||||
|     /// Parses only one expression |     /// Parses only one expression | ||||||
|     pub fn parse_expr(&mut self) -> PResult<expression::Expr> { |     pub fn parse_expr(&mut self) -> PResult<Expr> { | ||||||
|         self.expr() |         self.expr() | ||||||
|     } |     } | ||||||
|     /// Peeks at the current token |     /// Peeks at the current token | ||||||
|     pub fn peek(&self) -> PResult<&Token> { |     pub fn peek(&self) -> PResult<&Token> { | ||||||
|         self.tokens |         self.tokens | ||||||
|             .get(self.cursor) |             .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 |     /// Consumes any number of consecutive comments | ||||||
|     fn consume_comments(&mut self) -> &mut Self { |     fn consume_comments(&mut self) -> &mut Self { | ||||||
| @@ -212,9 +244,7 @@ impl Parser { | |||||||
|     /// Advances forward until a token with type [`t`](Type) is encountered |     /// Advances forward until a token with type [`t`](Type) is encountered | ||||||
|     fn advance_until(&mut self, t: Type) -> PResult<&mut Self> { |     fn advance_until(&mut self, t: Type) -> PResult<&mut Self> { | ||||||
|         while self.matches(t).is_err() { |         while self.matches(t).is_err() { | ||||||
|             self.check_eof() |             self.check_eof().wrap(Error::expected(t))?.consume(); | ||||||
|                 .map_err(|e| e.with_reason(Expected(t)))? |  | ||||||
|                 .consume(); |  | ||||||
|         } |         } | ||||||
|         Ok(self) |         Ok(self) | ||||||
|     } |     } | ||||||
| @@ -244,9 +274,10 @@ impl Parser { | |||||||
|     fn matches(&mut self, t: Type) -> PResult<&Token> { |     fn matches(&mut self, t: Type) -> PResult<&Token> { | ||||||
|         let token = self.check_eof()?.peek().expect("self should not be eof"); |         let token = self.check_eof()?.peek().expect("self should not be eof"); | ||||||
|         if token.ty() != t { |         if token.ty() != t { | ||||||
|             Err(Error::expected(t).token(token.clone()))? |             Err(Error::expected(t).token(token.clone())) | ||||||
|  |         } else { | ||||||
|  |             Ok(token) | ||||||
|         } |         } | ||||||
|         Ok(token) |  | ||||||
|     } |     } | ||||||
|     /// Consumes, without returning, a token with the given [Keyword], or returns an error. |     /// Consumes, without returning, a token with the given [Keyword], or returns an error. | ||||||
|     /// |     /// | ||||||
| @@ -282,7 +313,7 @@ impl Parser { | |||||||
|     /// Parses an [Identifier] |     /// Parses an [Identifier] | ||||||
|     fn identifier(&mut self) -> PResult<Identifier> { |     fn identifier(&mut self) -> PResult<Identifier> { | ||||||
|         let out = match self.matches(Type::Identifier)?.data() { |         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())?, |             _ => Err(Error::not_identifier())?, | ||||||
|         }; |         }; | ||||||
|         self.consume(); |         self.consume(); | ||||||
| @@ -363,30 +394,25 @@ impl Parser { | |||||||
|     fn stmt(&mut self) -> PResult<Stmt> { |     fn stmt(&mut self) -> PResult<Stmt> { | ||||||
|         let token = self.peek()?; |         let token = self.peek()?; | ||||||
|         match token.ty() { |         match token.ty() { | ||||||
|             Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let), |             Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let).wrap(Error::not_let()), | ||||||
|             Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn), |             Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn).wrap(Error::not_fn_decl()), | ||||||
|             _ => { |             _ => { | ||||||
|                 let out = Stmt::Expr(self.expr()?); |                 let out = Stmt::Expr(self.expr()?); | ||||||
|                 self.consume_type(Type::Semi)?; |                 self.consume_type(Type::Semi)?; | ||||||
|                 Ok(out) |                 Ok(out) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         .wrap(Error::not_statement()) | ||||||
|     } |     } | ||||||
|     /// Parses a [Let] statement |     /// Parses a [Let] statement | ||||||
|     fn let_stmt(&mut self) -> PResult<Let> { |     fn let_stmt(&mut self) -> PResult<Let> { | ||||||
|         let out = Let { |         self.keyword(Keyword::Let)?; | ||||||
|             mutable: self.consume().keyword(Keyword::Mut).is_ok(), |         let out = | ||||||
|             name: self.identifier()?, |             Let { name: self.name()?, init: self.consume_type(Type::Eq).and_then(Self::expr).ok() }; | ||||||
|             ty: self |  | ||||||
|                 .consume_type(Type::Colon) |  | ||||||
|                 .and_then(Self::identifier) |  | ||||||
|                 .ok(), |  | ||||||
|             init: self.consume_type(Type::Eq).and_then(Self::expr).ok(), |  | ||||||
|         }; |  | ||||||
|         self.consume_type(Type::Semi)?; |         self.consume_type(Type::Semi)?; | ||||||
|         Ok(out) |         Ok(out) | ||||||
|     } |     } | ||||||
|     /// Parses a [Function] statement |     /// Parses a [function declaration](FnDecl) statement | ||||||
|     fn fn_decl(&mut self) -> PResult<FnDecl> { |     fn fn_decl(&mut self) -> PResult<FnDecl> { | ||||||
|         self.keyword(Keyword::Fn)?; |         self.keyword(Keyword::Fn)?; | ||||||
|         let name = self.identifier()?; |         let name = self.identifier()?; | ||||||
| @@ -394,37 +420,98 @@ impl Parser { | |||||||
|         let args = self.params()?; |         let args = self.params()?; | ||||||
|         self.consume_type(Type::RParen)?; |         self.consume_type(Type::RParen)?; | ||||||
|         // TODO: Parse type-expressions and store return types in the AST |         // TODO: Parse type-expressions and store return types in the AST | ||||||
|         if self.consume_type(Type::Arrow).is_ok() { |         let ty = if self.consume_type(Type::Arrow).is_ok() { | ||||||
|             self.expr()?; |             Some(self.type_expr()?) | ||||||
|         } |         } else { | ||||||
|         Ok(FnDecl { name, args, body: self.block()? }) |             None | ||||||
|  |         }; | ||||||
|  |         Ok(FnDecl { name: Name { name, mutable: false, ty }, args, body: self.block()? }) | ||||||
|     } |     } | ||||||
|  |     /// Parses a [parameter](Name) list for [FnDecl] | ||||||
|     fn params(&mut self) -> PResult<Vec<Identifier>> { |     fn params(&mut self) -> PResult<Vec<Name>> { | ||||||
|         let mut args = vec![]; |         let mut args = vec![]; | ||||||
|         while let Ok(ident) = self.identifier() { |         while let Ok(name) = self.name() { | ||||||
|             args.push(ident); |             args.push(name); | ||||||
|             if self.consume_type(Type::Colon).is_ok() { |  | ||||||
|                 // TODO: Parse type-expressions and make this mandatory |  | ||||||
|                 self.expr()?; |  | ||||||
|             } |  | ||||||
|             if self.consume_type(Type::Comma).is_err() { |             if self.consume_type(Type::Comma).is_err() { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         Ok(args) |         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 | /// Expressions | ||||||
| impl Parser { | impl Parser { | ||||||
|     /// Parses an [expression](expression::Expr) |     /// Parses an [expression](Expr) | ||||||
|     fn expr(&mut self) -> PResult<expression::Expr> { |     fn expr(&mut self) -> PResult<Expr> { | ||||||
|         use expression::Expr; |  | ||||||
|         Ok(Expr(self.assign()?)) |         Ok(Expr(self.assign()?)) | ||||||
|     } |     } | ||||||
|     /// Parses a [block expression](expression::Block) |     /// Parses a [block expression](Block) | ||||||
|     fn block(&mut self) -> PResult<expression::Block> { |     fn block(&mut self) -> PResult<Block> { | ||||||
|         use expression::{Block, Expr}; |  | ||||||
|         let mut statements = vec![]; |         let mut statements = vec![]; | ||||||
|         let mut expr: Option<Box<Expr>> = None; |         let mut expr: Option<Box<Expr>> = None; | ||||||
|         self.consume_type(Type::LCurly)?; |         self.consume_type(Type::LCurly)?; | ||||||
| @@ -440,11 +527,10 @@ impl Parser { | |||||||
|                 Err(_) => statements.push(self.stmt()?), |                 Err(_) => statements.push(self.stmt()?), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         Ok(Block { statements, expr }) |         Ok(Block { statements, expr, let_count: None }) | ||||||
|     } |     } | ||||||
|     /// Parses a [primary expression](expression::Primary) |     /// Parses a [primary expression](Primary) | ||||||
|     fn primary(&mut self) -> PResult<expression::Primary> { |     fn primary(&mut self) -> PResult<Primary> { | ||||||
|         use expression::Primary; |  | ||||||
|         let token = self.peek()?; |         let token = self.peek()?; | ||||||
|         match token.ty() { |         match token.ty() { | ||||||
|             Type::Identifier => self.identifier().map(Primary::Identifier), |             Type::Identifier => self.identifier().map(Primary::Identifier), | ||||||
| @@ -465,7 +551,7 @@ impl Parser { | |||||||
|     /// Parses a [call expression](Call) |     /// Parses a [call expression](Call) | ||||||
|     fn call(&mut self) -> PResult<Call> { |     fn call(&mut self) -> PResult<Call> { | ||||||
|         let callee = self.primary()?; |         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)); |             return Ok(Call::Primary(callee)); | ||||||
|         }; |         }; | ||||||
|         let mut args = vec![]; |         let mut args = vec![]; | ||||||
| @@ -526,12 +612,11 @@ impl Parser { | |||||||
| /// ``` | /// ``` | ||||||
| /// becomes | /// becomes | ||||||
| /// ```rust,ignore | /// ```rust,ignore | ||||||
| /// fn function_name(&mut self) -> PResult<ret::Value> { ... } | /// fn function_name(&mut self) -> PResult<ret::Value> {  ... } | ||||||
| /// ``` | /// ``` | ||||||
| macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$( | macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$( | ||||||
|     #[doc = concat!("Parses a(n) [", stringify!($f), " operation](math::Operation::Binary) expression")] |     #[doc = concat!("Parses a(n) [", stringify!($f), " operation](Operation::Binary) expression")] | ||||||
|     fn $f (&mut self) -> PResult<math::Operation> { |     fn $f (&mut self) -> PResult<Operation> { | ||||||
|         use math::{Operation, Binary}; |  | ||||||
|         let (first, mut other) = (self.$a()?, vec![]); |         let (first, mut other) = (self.$a()?, vec![]); | ||||||
|         while let Ok(op) = self.$b() { |         while let Ok(op) = self.$b() { | ||||||
|             other.push((op, self.$a()?)); |             other.push((op, self.$a()?)); | ||||||
| @@ -543,9 +628,7 @@ macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$( | |||||||
| )*} | )*} | ||||||
| /// # [Arithmetic and Logical Subexpressions](math) | /// # [Arithmetic and Logical Subexpressions](math) | ||||||
| impl Parser { | impl Parser { | ||||||
|     fn assign(&mut self) -> PResult<math::Operation> { |     fn assign(&mut self) -> PResult<Operation> { | ||||||
|         use expression::Primary; |  | ||||||
|         use math::{Assign, Operation}; |  | ||||||
|         let next = self.compare()?; |         let next = self.compare()?; | ||||||
|         let Ok(operator) = self.assign_op() else { |         let Ok(operator) = self.assign_op() else { | ||||||
|             return Ok(next); |             return Ok(next); | ||||||
| @@ -569,9 +652,8 @@ impl Parser { | |||||||
|         term    = factor,  term_op; |         term    = factor,  term_op; | ||||||
|         factor  = unary,   factor_op; |         factor  = unary,   factor_op; | ||||||
|     } |     } | ||||||
|     /// Parses a [unary operation](math::Operation::Unary) expression |     /// Parses a [unary operation](Operation::Unary) expression | ||||||
|     fn unary(&mut self) -> PResult<math::Operation> { |     fn unary(&mut self) -> PResult<Operation> { | ||||||
|         use math::{Operation, Unary}; |  | ||||||
|         let mut operators = vec![]; |         let mut operators = vec![]; | ||||||
|         while let Ok(op) = self.unary_op() { |         while let Ok(op) = self.unary_op() { | ||||||
|             operators.push(op) |             operators.push(op) | ||||||
| @@ -584,15 +666,15 @@ impl Parser { | |||||||
|             operand: self.primary_operation()?.into(), |             operand: self.primary_operation()?.into(), | ||||||
|         })) |         })) | ||||||
|     } |     } | ||||||
|     /// Parses a [primary operation](math::Operation::Primary) expression |     /// Parses a [primary operation](Operation::Primary) expression | ||||||
|     fn primary_operation(&mut self) -> PResult<math::Operation> { |     fn primary_operation(&mut self) -> PResult<Operation> { | ||||||
|         Ok(math::Operation::Call(self.call()?)) |         Ok(Operation::Call(self.call()?)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) { | macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) { | ||||||
|     $($(#[$m])* fn $f(&mut self) -> PResult<operator::Binary> { |     $($(#[$m])* fn $f(&mut self) -> PResult<operator::Binary> { | ||||||
|         use operator::Binary; |         use operator::Binary; | ||||||
|         let token = self.peek()?; |         let token = self.peek().wrap(Error::not_operator())?; | ||||||
|         let out = Ok(match token.ty() { |         let out = Ok(match token.ty() { | ||||||
|             $($type => Binary::$op,)* |             $($type => Binary::$op,)* | ||||||
|             _ => Err(Error::not_operator().token(token.clone()))?, |             _ => Err(Error::not_operator().token(token.clone()))?, | ||||||
| @@ -689,9 +771,8 @@ impl Parser { | |||||||
| } | } | ||||||
| /// # [Control Flow](control) | /// # [Control Flow](control) | ||||||
| impl Parser { | impl Parser { | ||||||
|     /// Parses a [control flow](control::Flow) expression |     /// Parses a [control flow](Flow) expression | ||||||
|     fn flow(&mut self) -> PResult<control::Flow> { |     fn flow(&mut self) -> PResult<Flow> { | ||||||
|         use control::Flow; |  | ||||||
|         use Keyword::{Break, Continue, For, If, Return, While}; |         use Keyword::{Break, Continue, For, If, Return, While}; | ||||||
|         let token = self.peek()?; |         let token = self.peek()?; | ||||||
|         match token.ty() { |         match token.ty() { | ||||||
| @@ -703,55 +784,47 @@ impl Parser { | |||||||
|             Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue), |             Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue), | ||||||
|             e => Err(Error::unexpected(e).token(token.clone()))?, |             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 |     /// Parses an [if](If) expression | ||||||
|     fn parse_if(&mut self) -> PResult<control::If> { |     fn parse_if(&mut self) -> PResult<If> { | ||||||
|         self.keyword(Keyword::If)?; |         self.keyword(Keyword::If)?; | ||||||
|         Ok(control::If { |         Ok(If { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? }) | ||||||
|             cond: self.expr()?.into(), |  | ||||||
|             body: self.block()?, |  | ||||||
|             else_: self.parse_else()?, |  | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
|     /// Parses a [while](control::While) expression |     /// Parses a [while](While) expression | ||||||
|     fn parse_while(&mut self) -> PResult<control::While> { |     fn parse_while(&mut self) -> PResult<While> { | ||||||
|         self.keyword(Keyword::While)?; |         self.keyword(Keyword::While)?; | ||||||
|         Ok(control::While { |         Ok(While { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? }) | ||||||
|             cond: self.expr()?.into(), |  | ||||||
|             body: self.block()?, |  | ||||||
|             else_: self.parse_else()?, |  | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
|     /// Parses a [for](control::For) expression |     /// Parses a [for](For) expression | ||||||
|     fn parse_for(&mut self) -> PResult<control::For> { |     fn parse_for(&mut self) -> PResult<For> { | ||||||
|         self.keyword(Keyword::For)?; |         self.keyword(Keyword::For)?; | ||||||
|         Ok(control::For { |         Ok(For { | ||||||
|             var: self.identifier()?, |             var: self.identifier()?, | ||||||
|             iter: { self.keyword(Keyword::In)?.expr()?.into() }, |             iter: { self.keyword(Keyword::In)?.expr()?.into() }, | ||||||
|             body: self.block()?, |             body: self.block()?, | ||||||
|             else_: self.parse_else()?, |             else_: self.parse_else()?, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|     /// Parses an [else](control::Else) sub-expression |     /// Parses an [else](Else) sub-expression | ||||||
|     fn parse_else(&mut self) -> PResult<Option<control::Else>> { |     fn parse_else(&mut self) -> PResult<Option<Else>> { | ||||||
|         // it's fine for `else` to be missing entirely |         // it's fine for `else` to be missing entirely | ||||||
|         self.keyword(Keyword::Else) |         self.keyword(Keyword::Else) | ||||||
|             .ok() |             .ok() | ||||||
|             .map(|p| Ok(control::Else { expr: p.expr()?.into() })) |             .map(|p| Ok(Else { expr: p.expr()?.into() })) | ||||||
|             .transpose() |             .transpose() | ||||||
|     } |     } | ||||||
|     /// Parses a [break](control::Break) expression |     /// Parses a [break](Break) expression | ||||||
|     fn parse_break(&mut self) -> PResult<control::Break> { |     fn parse_break(&mut self) -> PResult<Break> { | ||||||
|         Ok(control::Break { expr: self.keyword(Keyword::Break)?.expr()?.into() }) |         Ok(Break { expr: self.keyword(Keyword::Break)?.expr()?.into() }) | ||||||
|     } |     } | ||||||
|     /// Parses a [return](control::Return) expression |     /// Parses a [return](Return) expression | ||||||
|     fn parse_return(&mut self) -> PResult<control::Return> { |     fn parse_return(&mut self) -> PResult<Return> { | ||||||
|         Ok(control::Return { expr: self.keyword(Keyword::Return)?.expr()?.into() }) |         Ok(Return { expr: self.keyword(Keyword::Return)?.expr()?.into() }) | ||||||
|     } |     } | ||||||
|     /// Parses a [continue](control::Continue) expression |     /// Parses a [continue](Continue) expression | ||||||
|     fn parse_continue(&mut self) -> PResult<control::Continue> { |     fn parse_continue(&mut self) -> PResult<Continue> { | ||||||
|         self.keyword(Keyword::Continue)?; |         self.keyword(Keyword::Continue)?; | ||||||
|         Ok(control::Continue) |         Ok(Continue) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| //! A [Printer] pretty-prints a Conlang [syntax tree](crate::ast) | //! A [Printer] pretty-prints a Conlang [syntax tree](crate::ast) | ||||||
|  |  | ||||||
| use super::ast::preamble::*; | use super::ast::preamble::*; | ||||||
| use std::{ | use std::{ | ||||||
|     fmt::Display, |     fmt::Display, | ||||||
| @@ -7,17 +8,14 @@ use std::{ | |||||||
| /// Prettily prints this node | /// Prettily prints this node | ||||||
| pub trait PrettyPrintable { | pub trait PrettyPrintable { | ||||||
|     /// Prettily prints this node |     /// 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) { |     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<()> { |     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) | /// Prints a Conlang [syntax tree](crate::ast) into a [Writer](Write) | ||||||
| @@ -26,6 +24,18 @@ pub struct Printer<W: Write> { | |||||||
|     level: u32, |     level: u32, | ||||||
|     writer: W, |     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>> { | impl<'t> Default for Printer<StdoutLock<'t>> { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { level: 0, writer: stdout().lock() } |         Self { level: 0, writer: stdout().lock() } | ||||||
| @@ -67,82 +77,154 @@ impl<W: Write> Printer<W> { | |||||||
| macro visit_operator($self:ident.$op:expr) { | macro visit_operator($self:ident.$op:expr) { | ||||||
|     $self.space()?.put($op)?.space().map(drop) |     $self.space()?.put($op)?.space().map(drop) | ||||||
| } | } | ||||||
| impl<W: Write> Visitor<IOResult<()>> for Printer<W> { |  | ||||||
|     fn visit_program(&mut self, prog: &Program) -> IOResult<()> { | impl PrettyPrintable for Start { | ||||||
|         // delegate to the walker |     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||||
|         for stmt in &prog.0 { |         let Self(program) = self; | ||||||
|             self.visit_statement(stmt)?; |         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(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|     fn visit_statement(&mut self, stmt: &Stmt) -> IOResult<()> { | } | ||||||
|         match stmt { | impl PrettyPrintable for Stmt { | ||||||
|             Stmt::Let(stmt) => self.visit_let(stmt)?, |     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||||
|             Stmt::Fn(function) => self.visit_fn_decl(function)?, |         match self { | ||||||
|             Stmt::Expr(e) => { |             Stmt::Let(value) => value.visit(p), | ||||||
|                 self.visit_expr(e)?; |             Stmt::Fn(value) => value.visit(p), | ||||||
|                 self.put(';').map(drop)? |             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; | impl PrettyPrintable for Let { | ||||||
|         self.put("let")?.space()?; |     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 { |         if *mutable { | ||||||
|             self.put("mut")?.space()?; |             p.put("mut")?.space()?; | ||||||
|         } |         } | ||||||
|         self.visit_identifier(name)?; |         name.visit(p)?; | ||||||
|         if let Some(ty) = ty { |         if let Some(ty) = ty { | ||||||
|             self.put(':')?.space()?.visit_identifier(ty)?; |             ty.visit(p.put(':')?.space()?)? | ||||||
|         } |         } | ||||||
|         if let Some(init) = init { |         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<()> { | impl PrettyPrintable for FnDecl { | ||||||
|         let FnDecl { name, args, body } = function; |     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||||
|         self.put("fn")?.space()?; |         let FnDecl { name, args, body } = self; | ||||||
|         self.visit_identifier(name)?; |         p.put("fn")?.space()?; | ||||||
|         self.space()?.put('(')?; |         name.visit(p)?; | ||||||
|  |         p.space()?.put('(')?; | ||||||
|         for (idx, arg) in args.iter().enumerate() { |         for (idx, arg) in args.iter().enumerate() { | ||||||
|             if idx > 0 { |             if idx > 0 { | ||||||
|                 self.put(',')?.space()?; |                 p.put(',')?.space()?; | ||||||
|             } |             } | ||||||
|             self.visit_identifier(arg)?; |             arg.visit(p)?; | ||||||
|         } |         } | ||||||
|         self.put(')')?.space()?; |         p.put(')')?.space()?; | ||||||
|         self.visit_block(body) |         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<()> { | impl PrettyPrintable for Operation { | ||||||
|         let math::Assign { target, operator, init } = assign; |     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||||
|         self.visit_identifier(target)?; |         match self { | ||||||
|         self.visit_assign_op(operator)?; |             Operation::Assign(value) => value.visit(p), | ||||||
|         self.visit_operation(init) |             Operation::Binary(value) => value.visit(p), | ||||||
|     } |             Operation::Unary(value) => value.visit(p), | ||||||
|     fn visit_binary(&mut self, binary: &math::Binary) -> IOResult<()> { |             Operation::Call(value) => value.visit(p), | ||||||
|         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) |  | ||||||
|     } |     } | ||||||
|     fn visit_unary(&mut self, unary: &math::Unary) -> IOResult<()> { | } | ||||||
|         let math::Unary { operators, operand } = unary; | impl PrettyPrintable for Assign { | ||||||
|         for op in operators { |     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||||
|             self.visit_unary_op(op)?; |         let Assign { target, operator, init } = self; | ||||||
|         } |         target.visit(p)?; | ||||||
|         self.visit_operation(operand) |         operator.visit(p)?; | ||||||
|  |         init.visit(p) | ||||||
|     } |     } | ||||||
|  | } | ||||||
|     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; |         use operator::Assign; | ||||||
|         visit_operator!(self.match op { |         visit_operator!(p.match self { | ||||||
|             Assign::Assign => "=", |             Assign::Assign => "=", | ||||||
|             Assign::AddAssign => "+=", |             Assign::AddAssign => "+=", | ||||||
|             Assign::SubAssign => "-=", |             Assign::SubAssign => "-=", | ||||||
| @@ -156,9 +238,23 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> { | |||||||
|             Assign::ShrAssign => ">>=", |             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; |         use operator::Binary; | ||||||
|         visit_operator!(self.match op { |         visit_operator!(p.match self { | ||||||
|             Binary::Mul => "*", |             Binary::Mul => "*", | ||||||
|             Binary::Div => "/", |             Binary::Div => "/", | ||||||
|             Binary::Rem => "%", |             Binary::Rem => "%", | ||||||
| @@ -182,9 +278,20 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> { | |||||||
|             Binary::Greater => ">", |             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; |         use operator::Unary; | ||||||
|         self.put(match op { |         p.put(match self { | ||||||
|             Unary::RefRef => "&&", |             Unary::RefRef => "&&", | ||||||
|             Unary::Deref => "*", |             Unary::Deref => "*", | ||||||
|             Unary::Ref => "&", |             Unary::Ref => "&", | ||||||
| @@ -196,108 +303,155 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> { | |||||||
|         }) |         }) | ||||||
|         .map(drop) |         .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<()> { | impl PrettyPrintable for Call { | ||||||
|         self.put(&ident.0).map(drop) |     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||||
|     } |         match self { | ||||||
|     fn visit_string_literal(&mut self, string: &str) -> IOResult<()> { |             Call::FnCall(value) => value.visit(p), | ||||||
|         self.put("\"")?.put(string)?.put("\"").map(drop) |             Call::Primary(value) => value.visit(p), | ||||||
|     } |  | ||||||
|     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<()> { | impl PrettyPrintable for FnCall { | ||||||
|         for (idx, expr) in tuple.elements.iter().enumerate() { |     fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> { | ||||||
|             if idx > 0 { |         let FnCall { callee, args } = self; | ||||||
|                 self.put(',')?.space()?; |         callee.visit(p)?; | ||||||
|             } |  | ||||||
|             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)?; |  | ||||||
|         for arg_list in args { |         for arg_list in args { | ||||||
|             self.put('(')?; |             p.put('(')?; | ||||||
|             self.visit_tuple(arg_list)?; |             arg_list.visit(p)?; | ||||||
|             self.put(')')?; |             p.put(')')?; | ||||||
|         } |         } | ||||||
|         Ok(()) |         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, |     Let, | ||||||
|     Mut, |     Mut, | ||||||
|     Return, |     Return, | ||||||
|  |     SelfKw, | ||||||
|  |     SelfTy, | ||||||
|  |     Super, | ||||||
|     True, |     True, | ||||||
|     While, |     While, | ||||||
| } | } | ||||||
| @@ -174,6 +177,9 @@ impl Display for Keyword { | |||||||
|             Self::Let => "let".fmt(f), |             Self::Let => "let".fmt(f), | ||||||
|             Self::Mut => "mut".fmt(f), |             Self::Mut => "mut".fmt(f), | ||||||
|             Self::Return => "return".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::True => "true".fmt(f), | ||||||
|             Self::While => "while".fmt(f), |             Self::While => "while".fmt(f), | ||||||
|         } |         } | ||||||
| @@ -195,6 +201,9 @@ impl FromStr for Keyword { | |||||||
|             "let" => Self::Let, |             "let" => Self::Let, | ||||||
|             "mut" => Self::Mut, |             "mut" => Self::Mut, | ||||||
|             "return" => Self::Return, |             "return" => Self::Return, | ||||||
|  |             "self" => Self::SelfKw, | ||||||
|  |             "Self" => Self::SelfTy, | ||||||
|  |             "super" => Self::Super, | ||||||
|             "true" => Self::True, |             "true" => Self::True, | ||||||
|             "while" => Self::While, |             "while" => Self::While, | ||||||
|             _ => Err(())?, |             _ => Err(())?, | ||||||
|   | |||||||
| @@ -1,8 +1,7 @@ | |||||||
| // Calculate Fibonacci numbers | // Calculate Fibonacci numbers | ||||||
|  |  | ||||||
| fn main() -> i128 { | fn main() -> i128 { | ||||||
|     print("fib(10):"); |     print("fib(10): ", fib(10)); | ||||||
|     fib(10) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Implements the classic recursive definition of fib() | /// Implements the classic recursive definition of fib() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user