├── .gitignore ├── Cargo.toml ├── vm-lox ├── docs │ ├── 15-a-virtual-machine.md │ └── 14-chunks-of-bytecode.md ├── Cargo.toml └── src │ ├── lib.rs │ ├── common │ ├── mod.rs │ ├── value.rs │ ├── token.rs │ ├── ins.rs │ ├── span.rs │ └── chunk.rs │ ├── pipeline.rs │ ├── main.rs │ ├── vm.rs │ └── scanner.rs ├── tree-lox ├── src │ ├── parser │ │ ├── state.rs │ │ ├── scanner │ │ │ ├── error.rs │ │ │ └── identifier.rs │ │ ├── error.rs │ │ └── scanner.rs │ ├── lib.rs │ ├── main.rs │ ├── interpreter │ │ ├── control_flow.rs │ │ ├── error.rs │ │ └── environment.rs │ ├── ast │ │ ├── mod.rs │ │ ├── stmt.rs │ │ ├── expr.rs │ │ └── dbg.rs │ ├── user │ │ ├── diagnostic_printer.rs │ │ ├── mod.rs │ │ └── repl.rs │ ├── span.rs │ ├── token.rs │ ├── data.rs │ ├── resolver.rs │ ├── interpreter.rs │ └── parser.rs └── Cargo.toml ├── examples ├── closure.lox ├── super.lox ├── functions.lox ├── scope.lox ├── return.lox └── class.lox ├── README.md ├── LICENSE ├── .vscode └── launch.json └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /private 3 | .DS_Store 4 | TODO.md -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["tree-lox", "vm-lox"] 3 | -------------------------------------------------------------------------------- /vm-lox/docs/15-a-virtual-machine.md: -------------------------------------------------------------------------------- 1 | # A Virtual Machine 2 | 3 | -------------------------------------------------------------------------------- /tree-lox/src/parser/state.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Default)] 2 | pub struct ParserOptions { 3 | pub repl_mode: bool, 4 | } 5 | -------------------------------------------------------------------------------- /vm-lox/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vm-lox" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | phf = { version = "0.10.1", features = ["macros"] } 8 | -------------------------------------------------------------------------------- /vm-lox/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] // TODO: remove this 2 | 3 | mod common; 4 | mod pipeline; 5 | mod scanner; 6 | mod vm; 7 | 8 | pub use pipeline::{interpret, Error, Result}; 9 | -------------------------------------------------------------------------------- /tree-lox/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-lox" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ansi_term = "0.12" 8 | anyhow = "1" 9 | lazy_static = "1" 10 | -------------------------------------------------------------------------------- /examples/closure.lox: -------------------------------------------------------------------------------- 1 | fun makeCounter() { 2 | var i = 0; 3 | fun count() { 4 | i = i + 1; 5 | print i; 6 | } 7 | return count; 8 | } 9 | 10 | var c = makeCounter(); 11 | c(); 12 | c(); 13 | c(); -------------------------------------------------------------------------------- /tree-lox/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::new_without_default)] 2 | 3 | pub mod ast; 4 | pub mod data; 5 | pub mod interpreter; 6 | pub mod parser; 7 | pub mod resolver; 8 | pub mod span; 9 | pub mod token; 10 | pub mod user; 11 | -------------------------------------------------------------------------------- /vm-lox/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | mod chunk; 2 | mod ins; 3 | mod span; 4 | mod token; 5 | mod value; 6 | 7 | pub use chunk::Chunk; 8 | pub use ins::Ins; 9 | pub use span::Span; 10 | pub use token::{Token, TokenKind}; 11 | pub use value::Value; 12 | -------------------------------------------------------------------------------- /tree-lox/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{env, io}; 2 | 3 | use tree_lox::user; 4 | 5 | fn main() -> io::Result<()> { 6 | match env::args().nth(1) { 7 | Some(path) => user::run_file(path, None).map(drop), 8 | _ => user::repl::Repl::run(), 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tree-lox/src/interpreter/control_flow.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | pub enum ControlFlow { 4 | Return(R), 5 | Err(E), 6 | } 7 | 8 | impl From for ControlFlow { 9 | fn from(err: E) -> Self { 10 | ControlFlow::Err(err) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/super.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | method() { 3 | print "(A) method"; 4 | } 5 | } 6 | 7 | class B < A { 8 | method() { 9 | print "(B) method"; 10 | } 11 | 12 | test() { 13 | super.method(); 14 | } 15 | } 16 | 17 | class C < B {} 18 | 19 | var c_ins = C(); 20 | c_ins.test(); -------------------------------------------------------------------------------- /examples/functions.lox: -------------------------------------------------------------------------------- 1 | fun fib(n) { 2 | if (n <= 1) return n; 3 | return fib(n - 2) + fib(n - 1); 4 | } 5 | 6 | for (var i = 0; i <= 25; i = i + 1) { 7 | var start_sec = clock(); 8 | var res = fib(i); 9 | var elapsed_sec = clock() - start_sec; 10 | 11 | print "fib(" + show i + ") = " + show res + " (elapsed " + show elapsed_sec + "s)"; 12 | } -------------------------------------------------------------------------------- /examples/scope.lox: -------------------------------------------------------------------------------- 1 | var a = "global a"; 2 | var b = "global b"; 3 | var c = "global c"; 4 | { 5 | var a = "outer a"; 6 | var b = "outer b"; 7 | { 8 | var a = "inner a"; 9 | print a; 10 | print b; 11 | print c; 12 | } 13 | print a; 14 | print b; 15 | print c; 16 | } 17 | print a; 18 | print b; 19 | print c; 20 | -------------------------------------------------------------------------------- /examples/return.lox: -------------------------------------------------------------------------------- 1 | fun loop(until, stop) { 2 | for (var i = 0; i <= until; i = i + 1) { 3 | print "before if: " + show i; 4 | if (i == stop) { 5 | return stop; 6 | } 7 | print "after if: " + show i; 8 | } 9 | print "after for"; 10 | } 11 | 12 | print "call output (1): " + show loop(3, 4); 13 | print "---"; 14 | print "call output (2): " + show loop(3, 2); -------------------------------------------------------------------------------- /examples/class.lox: -------------------------------------------------------------------------------- 1 | class Person { 2 | init(name) { 3 | if (!name) { 4 | print "Must provide a name."; 5 | return; 6 | } 7 | this.name = name; 8 | } 9 | 10 | greet(other) { 11 | print "Hello, " + other.name + "! My name is " + this.name + ", nice to meet you. :)"; 12 | } 13 | } 14 | 15 | var john = Person("John Doe"); 16 | var jane = Person("Jane Janot"); 17 | 18 | var john_greeter = john.greet; 19 | john_greeter(jane); 20 | jane.greet(john); -------------------------------------------------------------------------------- /vm-lox/src/common/value.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | 3 | /// Represents a Lox value. 4 | #[derive(Debug, Clone)] 5 | pub enum Value { 6 | Number(f64), 7 | } 8 | 9 | impl Display for Value { 10 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 11 | match self { 12 | Value::Number(number) => { 13 | if number.floor() == *number { 14 | write!(f, "{:.0}", number) 15 | } else { 16 | write!(f, "{}", number) 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vm-lox/src/pipeline.rs: -------------------------------------------------------------------------------- 1 | use crate::scanner::Scanner; 2 | 3 | /// Represents an error within the Lox interpretation pipeline. 4 | // TODO: impl Error + Display 5 | #[derive(Debug)] 6 | pub enum Error { 7 | CompileError(String), 8 | RuntimeError(String), 9 | } 10 | 11 | /// A specialized Result type for the Lox interpretation pipeline. 12 | pub type Result = std::result::Result; 13 | 14 | /// Runs the Lox interpretation pipeline (scanning, parsing, compiling and interpretation). 15 | pub fn interpret(source: &str) -> Result<()> { 16 | let scanner = Scanner::new(source); 17 | 18 | for token in scanner { 19 | let text = &source[token.span.into_range()]; 20 | println!("{token:?} [{text:?}]"); 21 | } 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crafting Interpreters in Rust – `rs-lox` 2 | 3 | My implementation of the first interpreter presented in "[Crafting Interpreters](https://craftinginterpreters.com/)", by Robert Nystrom. 4 | 5 | This repository holds two implementations for the same language. The first one, `tree-lox` implements a tree-based interpreter and the other, `vm-lox` uses a bytecode virtual machine to interpret the code. 6 | 7 | ### Quick start 8 | 9 | This project can by compiled using Cargo. Use the `--project` flag to choose the implementation. 10 | 11 | Run an existing file: 12 | 13 | ```terminal 14 | $ cargo r -p tree-lox -- script-name.lox 15 | ``` 16 | 17 | Open the REPL: 18 | 19 | ``` 20 | $ cargo r -p tree-lox 21 | ``` 22 | 23 | ### Project overview 24 | 25 | todo 26 | 27 | ### License 28 | 29 | Code licensed under the MIT license. -------------------------------------------------------------------------------- /vm-lox/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, fs, 3 | io::{self, Write}, 4 | path::Path, 5 | }; 6 | 7 | use vm_lox::interpret; 8 | 9 | fn main() -> io::Result<()> { 10 | match env::args().nth(1) { 11 | Some(path) => run_file(&path), 12 | _ => run_repl(), 13 | } 14 | } 15 | 16 | fn run(source: &str) { 17 | if source.is_empty() { 18 | return; 19 | } 20 | 21 | interpret(source).unwrap(); 22 | } 23 | 24 | fn run_file(path: impl AsRef) -> io::Result<()> { 25 | let source = fs::read_to_string(path)?; 26 | run(&source); 27 | Ok(()) 28 | } 29 | 30 | fn run_repl() -> io::Result<()> { 31 | loop { 32 | print!(">>> "); 33 | io::stdout().flush()?; 34 | 35 | let mut source = String::new(); 36 | if io::stdin().read_line(&mut source)? == 0 { 37 | break; 38 | } 39 | 40 | run(source.trim()); 41 | } 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /tree-lox/src/parser/scanner/error.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fmt::{self, Display}, 4 | }; 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub enum ScanError { 8 | UnexpectedChar(char), 9 | 10 | UnterminatedString, 11 | 12 | InvalidNumberLiteral, 13 | } 14 | 15 | impl Display for ScanError { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | use ScanError::*; 18 | match self { 19 | UnexpectedChar(char) => write!(f, "Unexpected character `{}`", char), 20 | UnterminatedString => f.write_str("Unterminated string"), 21 | InvalidNumberLiteral => f.write_str("Unparseable number literal"), 22 | } 23 | } 24 | } 25 | 26 | impl Error for ScanError {} 27 | 28 | impl ScanError { 29 | /// Checks if the error allows REPL continuation (aka. "..." prompt). 30 | pub fn allows_continuation(&self) -> bool { 31 | matches!(self, ScanError::UnterminatedString) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vm-lox/src/common/token.rs: -------------------------------------------------------------------------------- 1 | use crate::common::Span; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct Token { 5 | pub(crate) kind: TokenKind, 6 | pub(crate) span: Span, 7 | } 8 | 9 | #[derive(Debug, Clone, PartialEq)] 10 | pub enum TokenKind { 11 | Identifier(String), 12 | String(String), 13 | Number(f64), 14 | 15 | LeftParen, 16 | RightParen, 17 | LeftBrace, 18 | RightBrace, 19 | Plus, 20 | Minus, 21 | Star, 22 | Slash, 23 | Dot, 24 | Comma, 25 | Semicolon, 26 | Bang, 27 | BangEqual, 28 | Equal, 29 | EqualEqual, 30 | Less, 31 | LessEqual, 32 | Greater, 33 | GreaterEqual, 34 | 35 | Nil, 36 | True, 37 | False, 38 | This, 39 | Super, 40 | Class, 41 | And, 42 | Or, 43 | If, 44 | Else, 45 | Return, 46 | Fun, 47 | For, 48 | While, 49 | Var, 50 | Print, 51 | Typeof, 52 | Show, 53 | 54 | Eof, 55 | 56 | Dummy, 57 | Error(String), 58 | } 59 | -------------------------------------------------------------------------------- /vm-lox/src/common/ins.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug}; 2 | 3 | use crate::common::Value; 4 | 5 | /// Represents a single bytecode instruction. 6 | pub enum Ins { 7 | /// Constant value. 8 | Constant(Value), 9 | 10 | /// Negation. 11 | Negate, 12 | 13 | /// Addition. 14 | Add, 15 | 16 | /// Subtraction. 17 | Subtract, 18 | 19 | /// Multiplication. 20 | Multiply, 21 | 22 | /// Division. 23 | Divide, 24 | 25 | /// Return instruction. 26 | Return, 27 | } 28 | 29 | impl Debug for Ins { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | const PAD: usize = 15; 32 | use Ins::*; 33 | match self { 34 | Constant(value) => write!(f, "{name:PAD$} {value:?}", name = "OP_CONSTANT"), 35 | 36 | Negate => f.write_str("OP_NEGATE"), 37 | Add => f.write_str("OP_ADD"), 38 | Subtract => f.write_str("OP_NEGATE"), 39 | Multiply => f.write_str("OP_MULTIPLY"), 40 | Divide => f.write_str("OP_DIVIDE"), 41 | 42 | Return => f.write_str("OP_RETURN"), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Luiz Felipe Gonçalves 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tree-lox/src/ast/mod.rs: -------------------------------------------------------------------------------- 1 | macro_rules! make_ast_enum { 2 | ( $enum_name:ident, [ $( $variant:ident ),* $( , )? ] ) => { 3 | #[derive(Debug, Clone)] 4 | pub enum $enum_name { 5 | $( $variant($variant), )* 6 | } 7 | impl $enum_name { 8 | /// Returns the span of the inner AST node. 9 | #[inline] 10 | pub fn span(&self) -> Span { 11 | match self { 12 | $( 13 | $enum_name::$variant(inner) => inner.span, 14 | )* 15 | } 16 | } 17 | } 18 | $( 19 | impl From<$variant> for $enum_name { 20 | fn from(val: $variant) -> $enum_name { 21 | $enum_name::$variant(val) 22 | } 23 | } 24 | impl From<$variant> for Box<$enum_name> { 25 | fn from(variant: $variant) -> Box<$enum_name> { 26 | Box::new($enum_name::from(variant)) 27 | } 28 | } 29 | )* 30 | } 31 | } 32 | 33 | pub mod dbg; 34 | pub mod expr; 35 | pub mod stmt; 36 | -------------------------------------------------------------------------------- /tree-lox/src/parser/scanner/identifier.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use lazy_static::lazy_static; 4 | 5 | use crate::token::TokenKind::{self, *}; 6 | 7 | /// Checks if the given char is valid as an identifier's start character. 8 | #[inline] 9 | pub fn is_valid_identifier_start(c: char) -> bool { 10 | c.is_ascii_alphabetic() || c == '_' 11 | } 12 | 13 | /// Checks if the given char can belong to an identifier's tail. 14 | #[inline] 15 | pub fn is_valid_identifier_tail(c: char) -> bool { 16 | c.is_ascii_digit() || is_valid_identifier_start(c) 17 | } 18 | 19 | lazy_static! { 20 | pub static ref LOX_KEYWORDS: HashMap<&'static str, TokenKind> = HashMap::from([ 21 | ("nil", Nil), 22 | ("true", True), 23 | ("false", False), 24 | ("this", This), 25 | ("super", Super), 26 | ("class", Class), 27 | ("and", And), 28 | ("or", Or), 29 | ("if", If), 30 | ("else", Else), 31 | ("return", Return), 32 | ("fun", Fun), 33 | ("for", For), 34 | ("while", While), 35 | ("var", Var), 36 | ("print", Print), 37 | ("typeof", Typeof), 38 | ("show", Show), 39 | ]); 40 | } 41 | -------------------------------------------------------------------------------- /vm-lox/src/common/span.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp::{max, min}, 3 | fmt::{self, Display}, 4 | ops::Range, 5 | }; 6 | 7 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 8 | /// Represents a string fragment. 9 | pub struct Span { 10 | /// Substring start index. 11 | pub lo: usize, 12 | 13 | /// Substring end index (not inclusive). 14 | pub hi: usize, 15 | } 16 | 17 | impl Span { 18 | /// Creates a new span from the given bounds. 19 | pub fn new(lo: usize, hi: usize) -> Span { 20 | Span { 21 | lo: min(lo, hi), 22 | hi: max(lo, hi), 23 | } 24 | } 25 | 26 | /// Creates a new span encompassing `self` and `other`. 27 | pub fn to(&self, other: Span) -> Span { 28 | Span::new(min(self.lo, other.lo), max(self.hi, other.hi)) 29 | } 30 | 31 | /// Returns the span `Range` representation. 32 | pub fn into_range(&self) -> Range { 33 | Range { 34 | start: self.lo, 35 | end: self.hi, 36 | } 37 | } 38 | } 39 | 40 | impl Display for Span { 41 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 42 | if (self.hi - self.lo) <= 1 { 43 | write!(f, "{}", self.lo) 44 | } else { 45 | write!(f, "{}..{}", self.lo, self.hi) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vm-lox/src/common/chunk.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug}; 2 | 3 | use crate::common::Ins; 4 | 5 | /// Represents a chunk of bytecode. A sequence of instructions. 6 | pub struct Chunk { 7 | name: String, 8 | pub(crate) code: Vec, 9 | pub(crate) lines: Vec, 10 | } 11 | 12 | impl Chunk { 13 | /// Creates a new chunk. 14 | pub fn new(name: impl Into) -> Self { 15 | Self { 16 | name: name.into(), 17 | code: Vec::new(), 18 | lines: Vec::new(), 19 | } 20 | } 21 | 22 | /// Writes an instruction to the chunk's bytecode. 23 | pub fn write(&mut self, ins: Ins, line: u32) { 24 | debug_assert_eq!( 25 | self.code.len(), 26 | self.lines.len(), 27 | "Not parallel lengths of code and lines vectors" 28 | ); 29 | 30 | self.code.push(ins); 31 | self.lines.push(line); 32 | } 33 | } 34 | 35 | impl Debug for Chunk { 36 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 37 | writeln!(f, "=== {} ===", self.name)?; 38 | 39 | let mut last_line = 0; 40 | for (ins, &line) in self.code.iter().zip(&self.lines) { 41 | if last_line != line { 42 | write!(f, "{line:>5}")?; 43 | last_line = line; 44 | } else { 45 | f.write_str(" .")?; 46 | } 47 | writeln!(f, " | {ins:?}")?; 48 | } 49 | 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'lox'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=lox", 15 | "--package=lox" 16 | ], 17 | "filter": { 18 | "name": "lox", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [], 23 | "cwd": "${workspaceFolder}" 24 | }, 25 | { 26 | "type": "lldb", 27 | "request": "launch", 28 | "name": "Debug unit tests in library 'lox'", 29 | "cargo": { 30 | "args": [ 31 | "test", 32 | "--no-run", 33 | "--lib", 34 | "--package=lox" 35 | ], 36 | "filter": { 37 | "name": "lox", 38 | "kind": "lib" 39 | } 40 | }, 41 | "args": [], 42 | "cwd": "${workspaceFolder}" 43 | }, 44 | { 45 | "type": "lldb", 46 | "request": "launch", 47 | "name": "Debug unit tests in executable 'lox'", 48 | "cargo": { 49 | "args": [ 50 | "test", 51 | "--no-run", 52 | "--bin=lox", 53 | "--package=lox" 54 | ], 55 | "filter": { 56 | "name": "lox", 57 | "kind": "bin" 58 | } 59 | }, 60 | "args": [], 61 | "cwd": "${workspaceFolder}" 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /tree-lox/src/user/diagnostic_printer.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use crate::span::Span; 4 | use ansi_term::Color::Red; 5 | 6 | pub fn print_span_window(writer: &mut dyn Write, str: &str, span: Span) { 7 | let spanned = SpannedStr::new(str, span); 8 | 9 | // TODO: Handle multi lines 10 | let (before, span, after) = spanned.line_sections(); 11 | writeln!( 12 | writer, 13 | "{:>5} | {}{}{}", 14 | spanned.line_number(), 15 | before, 16 | Red.paint(span), 17 | after 18 | ) 19 | .unwrap(); 20 | } 21 | 22 | struct SpannedStr<'s> { 23 | str: &'s str, 24 | span: Span, 25 | } 26 | 27 | impl<'s> SpannedStr<'s> { 28 | pub fn new(str: &'s str, span: Span) -> SpannedStr<'s> { 29 | Self { str, span } 30 | } 31 | 32 | pub fn line_number(&self) -> usize { 33 | &self.str[..self.span.lo].matches('\n').count() + 1 34 | } 35 | 36 | pub fn line_bounds(&self) -> (usize, usize) { 37 | let Span { lo, hi } = self.span; 38 | let len = self.str.len(); 39 | 40 | let before = &self.str[..lo]; 41 | let start = before.rfind('\n').map(|pos| pos + 1).unwrap_or(0); 42 | 43 | let after = &self.str[hi..]; 44 | let end = after.find('\n').map(|pos| pos + hi + 1).unwrap_or(len); 45 | 46 | (start, end) 47 | } 48 | 49 | pub fn line_sections(&self) -> (&str, &str, &str) { 50 | let (l_start, l_end) = self.line_bounds(); 51 | 52 | let before = &self.str[l_start..self.span.lo]; 53 | let span = &self.str[self.span.range()]; 54 | let after = &self.str[self.span.hi..l_end]; 55 | (before, span, after) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tree-lox/src/ast/stmt.rs: -------------------------------------------------------------------------------- 1 | use crate::{ast::expr, data::LoxIdent, span::Span}; 2 | 3 | make_ast_enum!( 4 | Stmt, 5 | [VarDecl, ClassDecl, FunDecl, If, While, Return, Print, Block, Expr, Dummy] 6 | ); 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct VarDecl { 10 | pub span: Span, 11 | pub name: LoxIdent, 12 | pub init: Option, 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct ClassDecl { 17 | pub span: Span, 18 | pub name: LoxIdent, 19 | pub super_name: Option, 20 | pub methods: Vec, 21 | } 22 | 23 | #[derive(Debug, Clone)] 24 | pub struct FunDecl { 25 | pub span: Span, 26 | pub name: LoxIdent, 27 | pub params: Vec, 28 | pub body: Vec, 29 | } 30 | 31 | #[derive(Debug, Clone)] 32 | pub struct If { 33 | pub span: Span, 34 | pub cond: expr::Expr, 35 | pub then_branch: Box, 36 | pub else_branch: Option>, 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub struct While { 41 | pub span: Span, 42 | pub cond: expr::Expr, 43 | pub body: Box, 44 | } 45 | 46 | #[derive(Debug, Clone)] 47 | pub struct Return { 48 | pub span: Span, 49 | pub return_span: Span, 50 | pub value: Option, 51 | } 52 | 53 | #[derive(Debug, Clone)] 54 | pub struct Print { 55 | pub span: Span, 56 | pub expr: expr::Expr, 57 | pub debug: bool, 58 | } 59 | 60 | #[derive(Debug, Clone)] 61 | pub struct Block { 62 | pub span: Span, 63 | pub stmts: Vec, 64 | } 65 | 66 | #[derive(Debug, Clone)] 67 | pub struct Expr { 68 | pub span: Span, 69 | pub expr: expr::Expr, 70 | } 71 | 72 | /// For error purposes. 73 | #[derive(Debug, Clone)] 74 | pub struct Dummy { 75 | pub span: Span, 76 | } 77 | -------------------------------------------------------------------------------- /tree-lox/src/user/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, io, path::Path}; 2 | 3 | use crate::{ 4 | interpreter::Interpreter, 5 | parser::{Parser, ParserOutcome}, 6 | resolver::Resolver, 7 | user::diagnostic_printer::print_span_window, 8 | }; 9 | 10 | pub mod diagnostic_printer; 11 | pub mod repl; 12 | 13 | fn handle_parser_outcome( 14 | src: &str, 15 | (stmts, errors): &ParserOutcome, 16 | interpreter: &mut Interpreter, 17 | ) -> bool { 18 | let writer = &mut io::stderr(); 19 | 20 | // parser 21 | if !errors.is_empty() { 22 | for error in errors { 23 | eprintln!("{}\n", error); 24 | print_span_window(writer, src, error.primary_span()); 25 | } 26 | return false; 27 | } 28 | 29 | // resolver 30 | let resolver = Resolver::new(interpreter); 31 | let (ok, errors) = resolver.resolve(stmts); 32 | if !ok { 33 | for error in errors { 34 | eprintln!("{}; at position {}\n", error.message, error.span); 35 | print_span_window(writer, src, error.span); 36 | } 37 | return false; 38 | } 39 | 40 | // interpreter 41 | if let Err(error) = interpreter.interpret(stmts) { 42 | eprintln!("{}\n", error); 43 | print_span_window(writer, src, error.primary_span()); 44 | return false; 45 | } 46 | true 47 | } 48 | 49 | pub fn run_file(file: impl AsRef, interpreter: Option<&mut Interpreter>) -> io::Result { 50 | let src = &fs::read_to_string(file)?; 51 | let outcome = Parser::new(src).parse(); 52 | let status = handle_parser_outcome( 53 | src, 54 | &outcome, 55 | interpreter.unwrap_or(&mut Interpreter::new()), 56 | ); 57 | Ok(status) 58 | } 59 | -------------------------------------------------------------------------------- /tree-lox/src/interpreter/error.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fmt::{self, Display}, 4 | }; 5 | 6 | use crate::{data::LoxIdent, span::Span}; 7 | 8 | #[derive(Debug, Clone)] 9 | pub enum RuntimeError { 10 | UnsupportedType { message: String, span: Span }, 11 | 12 | UndefinedVariable { ident: LoxIdent }, 13 | UndefinedProperty { ident: LoxIdent }, 14 | 15 | ZeroDivision { span: Span }, 16 | } 17 | 18 | impl Display for RuntimeError { 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | use RuntimeError::*; 21 | match self { 22 | UnsupportedType { message, span } => { 23 | write!(f, "{}; at position {}", message, span) 24 | } 25 | 26 | UndefinedVariable { ident } => { 27 | write!( 28 | f, 29 | "Undefined variable `{}`; at position {}", 30 | ident.name, ident.span 31 | ) 32 | } 33 | 34 | UndefinedProperty { ident } => { 35 | write!( 36 | f, 37 | "Undefined property `{}` at position {}", 38 | ident.name, ident.span 39 | ) 40 | } 41 | 42 | ZeroDivision { span } => { 43 | write!(f, "Can not divide by zero; at position {}", span) 44 | } 45 | } 46 | } 47 | } 48 | 49 | impl RuntimeError { 50 | /// Returns the span that caused the error. 51 | pub fn primary_span(&self) -> Span { 52 | use RuntimeError::*; 53 | match self { 54 | UnsupportedType { span, .. } | ZeroDivision { span } => *span, 55 | UndefinedVariable { ident } | UndefinedProperty { ident } => ident.span, 56 | } 57 | } 58 | } 59 | 60 | impl Error for RuntimeError {} 61 | -------------------------------------------------------------------------------- /vm-lox/src/vm.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{Chunk, Ins, Value}; 2 | 3 | /// The virtual machine. 4 | pub struct Vm { 5 | stack: Vec, 6 | } 7 | 8 | impl Vm { 9 | pub fn new() -> Vm { 10 | Self { 11 | stack: Vec::with_capacity(256), 12 | } 13 | } 14 | 15 | pub fn interpret(&mut self, chunk: Chunk) -> Result<(), ()> { 16 | for ins in chunk.code { 17 | use Ins::*; 18 | match ins { 19 | Constant(value) => { 20 | self.push(value); 21 | } 22 | Negate => match self.pop() { 23 | Value::Number(number) => self.push(Value::Number(-number)), 24 | }, 25 | Add => arithmetic_binary!(self, +), 26 | Subtract => arithmetic_binary!(self, -), 27 | Multiply => arithmetic_binary!(self, *), 28 | Divide => arithmetic_binary!(self, /), 29 | Return => { 30 | let val = self.pop(); 31 | println!("{val}"); 32 | return Ok(()); 33 | } 34 | } 35 | } 36 | Ok(()) 37 | } 38 | 39 | fn push(&mut self, value: Value) { 40 | if self.stack.len() == self.stack.capacity() { 41 | panic!("Stack overflow"); 42 | } 43 | self.stack.push(value); 44 | } 45 | 46 | fn pop(&mut self) -> Value { 47 | self.stack.pop().unwrap() 48 | } 49 | } 50 | 51 | macro_rules! arithmetic_binary { 52 | ($self:expr, $op:tt) => {{ 53 | let b = $self.pop(); 54 | let a = $self.pop(); 55 | use Value::*; 56 | let out = match (a, b) { 57 | (Number(a), Number(b)) => Number(a $op b), 58 | }; 59 | $self.push(out); 60 | }} 61 | } 62 | use arithmetic_binary; 63 | -------------------------------------------------------------------------------- /tree-lox/src/span.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp::{max, min}, 3 | fmt::{self, Display}, 4 | ops::Range, 5 | }; 6 | 7 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 8 | /// Represents a string fragment. The bounds are over its byte representation. 9 | pub struct Span { 10 | /// Lower bound. 11 | pub lo: usize, 12 | 13 | /// Higher bound. 14 | pub hi: usize, 15 | } 16 | 17 | impl Span { 18 | /// Creates a new span. 19 | pub fn new(lo: usize, hi: usize) -> Span { 20 | Span { 21 | lo: min(lo, hi), 22 | hi: max(lo, hi), 23 | } 24 | } 25 | 26 | /// Creates a new span encompassing `self` and `other`. 27 | pub fn to(&self, other: Span) -> Span { 28 | Span::new(min(self.lo, other.lo), max(self.hi, other.hi)) 29 | } 30 | 31 | /// Checks if the span contains the given position. 32 | pub fn contains_p(&self, position: usize) -> bool { 33 | self.lo <= position && position < self.hi 34 | } 35 | 36 | /// Modifies the given span. Panics if new bounds are invalid. 37 | pub fn updated(&self, lo: isize, hi: isize) -> Span { 38 | let lo = self.lo as isize + lo; 39 | let hi = self.hi as isize + hi; 40 | assert!(lo >= 0, "New lower bound can't be negative."); 41 | assert!(lo <= hi, "Lower bound can not pass the higher."); 42 | Span::new(lo as _, hi as _) 43 | } 44 | 45 | /// Returns the span range. 46 | pub fn range(&self) -> Range { 47 | Range { 48 | start: self.lo, 49 | end: self.hi, 50 | } 51 | } 52 | } 53 | 54 | impl Display for Span { 55 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 56 | if (self.hi - self.lo) <= 1 { 57 | write!(f, "{}", self.lo) 58 | } else { 59 | write!(f, "{}..{}", self.lo, self.hi) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tree-lox/src/parser/error.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fmt::{self, Display}, 4 | }; 5 | 6 | use crate::{ 7 | parser::scanner::error::ScanError, 8 | span::Span, 9 | token::{Token, TokenKind}, 10 | }; 11 | 12 | #[derive(Debug, Clone, PartialEq)] 13 | pub enum ParseError { 14 | Error { 15 | message: String, 16 | span: Span, 17 | }, 18 | 19 | ScanError { 20 | error: ScanError, 21 | span: Span, 22 | }, 23 | 24 | UnexpectedToken { 25 | message: String, 26 | offending: Token, 27 | expected: Option, 28 | }, 29 | } 30 | 31 | impl Display for ParseError { 32 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 33 | use ParseError::*; 34 | match self { 35 | Error { message, span } => { 36 | write!(f, "{}; at position {}", message, span) 37 | } 38 | 39 | ScanError { error, span } => { 40 | write!(f, "{}; at position {}", error, span) 41 | } 42 | 43 | UnexpectedToken { 44 | message, offending, .. 45 | } => { 46 | write!( 47 | f, 48 | "{}; unexpected token `{}`; at position {}", 49 | message, offending, offending.span 50 | )?; 51 | // if let Some(expected) = expected { 52 | // write!(f, "\nInstead expected token of kind `{}`", expected)?; 53 | // } 54 | Ok(()) 55 | } 56 | } 57 | } 58 | } 59 | 60 | impl Error for ParseError {} 61 | 62 | impl ParseError { 63 | /// Returns the span that caused the error. 64 | pub fn primary_span(&self) -> Span { 65 | use ParseError::*; 66 | match self { 67 | Error { span, .. } | ScanError { span, .. } => *span, 68 | UnexpectedToken { offending, .. } => offending.span, 69 | } 70 | } 71 | 72 | /// Checks if the error allows REPL continuation (aka. "..." prompt). 73 | pub fn allows_continuation(&self) -> bool { 74 | use ParseError::*; 75 | match self { 76 | UnexpectedToken { offending, .. } if offending.kind == TokenKind::Eof => true, 77 | ScanError { error, .. } if error.allows_continuation() => true, 78 | _ => false, 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tree-lox/src/ast/expr.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | data::{LoxIdent, LoxValue}, 3 | span::Span, 4 | token::{Token, TokenKind}, 5 | }; 6 | 7 | make_ast_enum!( 8 | Expr, 9 | [Lit, This, Var, Group, Super, Get, Set, Call, Unary, Binary, Logical, Assignment] 10 | ); 11 | 12 | #[derive(Debug, Clone)] 13 | pub struct Lit { 14 | pub span: Span, 15 | pub value: LoxValue, 16 | } 17 | 18 | #[derive(Debug, Clone)] 19 | pub struct This { 20 | pub span: Span, 21 | pub name: LoxIdent, 22 | } 23 | 24 | #[derive(Debug, Clone)] 25 | pub struct Var { 26 | pub span: Span, 27 | pub name: LoxIdent, 28 | } 29 | 30 | #[derive(Debug, Clone)] 31 | pub struct Group { 32 | pub span: Span, 33 | pub expr: Box, 34 | } 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct Super { 38 | pub span: Span, 39 | pub super_ident: LoxIdent, 40 | pub method: LoxIdent, 41 | } 42 | 43 | #[derive(Debug, Clone)] 44 | pub struct Get { 45 | pub span: Span, 46 | pub object: Box, 47 | pub name: LoxIdent, 48 | } 49 | 50 | #[derive(Debug, Clone)] 51 | pub struct Set { 52 | pub span: Span, 53 | pub object: Box, 54 | pub name: LoxIdent, 55 | pub value: Box, 56 | } 57 | 58 | #[derive(Debug, Clone)] 59 | pub struct Call { 60 | pub span: Span, 61 | pub callee: Box, 62 | pub args: Vec, 63 | } 64 | 65 | #[derive(Debug, Clone)] 66 | pub struct Unary { 67 | pub span: Span, 68 | pub operator: Token, 69 | pub operand: Box, 70 | } 71 | 72 | #[derive(Debug, Clone)] 73 | pub struct Binary { 74 | pub span: Span, 75 | pub left: Box, 76 | pub operator: Token, 77 | pub right: Box, 78 | } 79 | 80 | #[derive(Debug, Clone)] 81 | pub struct Logical { 82 | pub span: Span, 83 | pub left: Box, 84 | pub operator: Token, 85 | pub right: Box, 86 | } 87 | 88 | #[derive(Debug, Clone)] 89 | pub struct Assignment { 90 | pub span: Span, 91 | pub name: LoxIdent, 92 | pub value: Box, 93 | } 94 | 95 | // 96 | // Some other utilities. 97 | // 98 | 99 | impl From for Lit { 100 | fn from(token: Token) -> Self { 101 | use LoxValue as L; 102 | use TokenKind as T; 103 | Lit { 104 | span: token.span, 105 | value: match token.kind { 106 | T::String(string) => L::String(string), 107 | T::Number(number) => L::Number(number), 108 | T::Nil => L::Nil, 109 | T::True => L::Boolean(true), 110 | T::False => L::Boolean(false), 111 | unexpected => unreachable!( 112 | "Invalid `Token` ({:?}) to `Literal` conversion.", 113 | unexpected 114 | ), 115 | }, 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /vm-lox/docs/14-chunks-of-bytecode.md: -------------------------------------------------------------------------------- 1 | # Chunks of Bytecode 2 | 3 | ## Why use bytecode? 4 | 5 | In the first implementation, `tree-lox`, the strategy used to interpret the user code was to user a tree-walk interpreter. Though simple to implement, this approach has some drawbacks, such—and most notably—as the poor performance. 6 | 7 | Interpreting the code by walking the AST is slow because such data structure is not memory-efficient to that use case. Each syntax node of the language is represented by a single AST node, which may reference other AST nodes. Since they may be located in different memory locations, they don't have an ideal cache locality. 8 | 9 | By using bytecode, which is a _super condensed_ representation of the source code, most of those problems are attenuated or resolved. The main cost is a slight more complex interpreter implementation. Since the language execution _must_ have a good performance that cost is definitely worth it. 10 | 11 | Bytecode has some similarities with machine code. The main differences are speed (being machine code the fastest), portability and ease of implementation. Compiling to a simple bytecode, designed for a specific language, is tremendously straightforward than compiling to machine code, which of course varies between different architectures. 12 | 13 | ## Starting out 14 | 15 | The original C implementation implements a bytecode chunk using a `Chunk` structure, with a dynamic array of bytes, which would represent the bytecode instructions and instruction arguments. 16 | 17 | In this Rust implementation, a bytecode chunk is implemented using a `Chunk` structure, with a vector of `Ins` (an enum, named after the abbreviation of "instruction"). Of course the variants of such enum should not get too big. This size shall be carefully observed. Using an enum in this case is less memory efficient; however, due to the ease of implementation and its idiomatic characteristic in Rust, such approach was preferred. 18 | 19 | A disassembler is implemented in order to debug the bytecode. The `std::fmt::Debug` crate was used, both in the `Chunk` structure and in the `Ins` enum. 20 | 21 | ## Bytecode constants 22 | 23 | In the C implementation, constants were stored in a separate dynamic array under the `Chunk` struct. The constant instruction is then followed by a index to such array, for example: 24 | 25 | ``` 26 | ... 27 | OP_CONSTANT 5 28 | ... 29 | ``` 30 | 31 | Being `5` the index in the `constants` array. 32 | 33 | In this implementation, since a Rust enum is being used, the `Ins` can directly carry the `Value` (which is also an enum). This makes the implementation a little easier and less error prone. Of course, though, the `Value` enum must now be quite small in memory size. 34 | 35 | The same approach was used to keep track of line numbers, a separate vector in the `Chunk` struct. Direct correspondence with the `code` vector. In the future I might revisit this approach to be more memory efficient. (TODO: lffg) 36 | 37 | -------------------------------------------------------------------------------- /tree-lox/src/interpreter/environment.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, collections::HashMap, rc::Rc}; 2 | 3 | use crate::{ 4 | data::{LoxIdent, LoxValue}, 5 | interpreter::error::RuntimeError, 6 | }; 7 | 8 | #[derive(Debug, Default)] 9 | struct EnvironmentInner { 10 | enclosing: Option, 11 | locals: HashMap, 12 | } 13 | 14 | #[derive(Debug, Clone, Default)] 15 | pub struct Environment { 16 | inner: Rc>, 17 | } 18 | 19 | impl Environment { 20 | /// Creates a new `Environment` with one scope (i.e. the global scope). 21 | pub fn new() -> Self { 22 | Default::default() 23 | } 24 | 25 | /// Creates a new `Environment` enclosing the given `Environment`. 26 | pub fn new_enclosing(enclosing: &Environment) -> Self { 27 | Self { 28 | inner: Rc::new(RefCell::new(EnvironmentInner { 29 | enclosing: Some(enclosing.clone()), 30 | locals: HashMap::new(), 31 | })), 32 | } 33 | } 34 | 35 | /// Returns the enclosed environment. 36 | pub fn enclosed(&self) -> Option { 37 | self.inner.borrow().enclosing.clone() 38 | } 39 | 40 | /// Defines a variable in the innermost scope. 41 | pub fn define(&mut self, ident: impl Into, value: LoxValue) { 42 | self.inner.borrow_mut().locals.insert(ident.into(), value); 43 | } 44 | 45 | /// Assigns a variable. 46 | pub fn assign(&mut self, ident: &LoxIdent, value: LoxValue) -> Result { 47 | let mut inner = self.inner.borrow_mut(); 48 | match inner.locals.get_mut(&ident.name) { 49 | Some(var) => { 50 | *var = value.clone(); 51 | Ok(value) 52 | } 53 | None => match &mut inner.enclosing { 54 | Some(enclosing) => enclosing.assign(ident, value), 55 | None => Err(RuntimeError::UndefinedVariable { 56 | ident: ident.clone(), 57 | }), 58 | }, 59 | } 60 | } 61 | 62 | /// Reads a variable in a distant scope. 63 | pub fn assign_at(&mut self, distance: usize, ident: &LoxIdent, value: LoxValue) -> LoxValue { 64 | // This should never panic due to the semantic verifications that the resolver performs. 65 | *self 66 | .ancestor(distance) 67 | .inner 68 | .borrow_mut() 69 | .locals 70 | .get_mut(&ident.name) 71 | .unwrap() = value.clone(); 72 | value 73 | } 74 | 75 | /// Reads a variable. 76 | pub fn read(&self, ident: &LoxIdent) -> Result { 77 | let inner = self.inner.borrow(); 78 | match inner.locals.get(&ident.name) { 79 | Some(var) => Ok(var.clone()), 80 | None => match &inner.enclosing { 81 | Some(enclosing) => enclosing.read(ident), 82 | None => Err(RuntimeError::UndefinedVariable { 83 | ident: ident.clone(), 84 | }), 85 | }, 86 | } 87 | } 88 | 89 | /// Reads a variable in a distant scope. 90 | pub fn read_at(&self, distance: usize, ident: impl AsRef) -> LoxValue { 91 | // This should never panic due to the semantic verifications that the resolver performs. 92 | self.ancestor(distance) 93 | .inner 94 | .borrow() 95 | .locals 96 | .get(ident.as_ref()) 97 | .unwrap() 98 | .clone() 99 | } 100 | 101 | fn ancestor(&self, distance: usize) -> Environment { 102 | let mut curr = self.clone(); 103 | for _ in 0..distance { 104 | let maybe_enclosing = curr.inner.borrow().enclosing.clone(); 105 | curr = maybe_enclosing.unwrap(); 106 | } 107 | curr 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tree-lox/src/user/repl.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | 3 | use crate::{ 4 | ast, 5 | interpreter::Interpreter, 6 | parser::{error::ParseError, Parser}, 7 | user::{handle_parser_outcome, run_file}, 8 | }; 9 | 10 | pub struct Repl { 11 | interpreter: Interpreter, 12 | current_src: String, 13 | show_lex: bool, 14 | show_ast: bool, 15 | done: bool, 16 | } 17 | 18 | impl Repl { 19 | pub fn run() -> io::Result<()> { 20 | Self::new().start() 21 | } 22 | 23 | fn new() -> Self { 24 | Self { 25 | interpreter: Interpreter::new(), 26 | current_src: "".into(), 27 | show_lex: false, 28 | show_ast: false, 29 | done: false, 30 | } 31 | } 32 | 33 | fn start(mut self) -> io::Result<()> { 34 | eprintln!("Welcome to rs-tree-lox. Enter Ctrl+D or `:exit` to exit.\n"); 35 | 36 | while !self.done { 37 | let (line, is_eof) = self.read_line()?; 38 | 39 | // If previous line started with `:`, interpret it as a command and 40 | // skip this iteration entirely, handling the command. 41 | if let Some(raw_cmd) = line.trim().strip_prefix(':') { 42 | self.handle_command(raw_cmd); 43 | continue; 44 | } 45 | 46 | // Accumulate the input. 47 | self.current_src += &line; 48 | 49 | let mut parser = Parser::new(&self.current_src); 50 | parser.options.repl_mode = true; 51 | let outcome @ (stmts, errors) = &parser.parse(); 52 | 53 | // If the parser produced an error, but the error allows REPL continuation then we 54 | // just continue to ask for successive user inputs. 55 | if !errors.is_empty() && Self::should_continue_repl(errors) && !is_eof { 56 | continue; 57 | } 58 | 59 | // If the user asks so, show them some debug information before any code is interpreted 60 | // or errors are emitted. 61 | if self.show_lex && !self.current_src.trim().is_empty() { 62 | ast::dbg::print_scanned_tokens(&self.current_src); 63 | } 64 | if self.show_ast && !stmts.is_empty() { 65 | ast::dbg::print_program_tree(stmts); 66 | } 67 | 68 | handle_parser_outcome(&self.current_src, outcome, &mut self.interpreter); 69 | self.current_src = "".into(); 70 | } 71 | Ok(()) 72 | } 73 | 74 | fn read_line(&mut self) -> io::Result<(String, bool)> { 75 | let prompt = if self.current_src.is_empty() { 76 | ">>>" 77 | } else { 78 | "..." 79 | }; 80 | print!("{} ", prompt); 81 | io::stdout().flush()?; 82 | 83 | let mut line = String::new(); 84 | let is_eof = io::stdin().read_line(&mut line)? == 0; 85 | self.done = is_eof && self.current_src.is_empty(); 86 | 87 | if is_eof { 88 | println!(); 89 | } 90 | 91 | Ok((line, is_eof)) 92 | } 93 | 94 | fn should_continue_repl(errors: &[ParseError]) -> bool { 95 | !errors.is_empty() && errors.iter().all(ParseError::allows_continuation) 96 | } 97 | 98 | fn handle_command(&mut self, raw_cmd: &str) { 99 | let cmd: Vec<_> = raw_cmd 100 | .split_ascii_whitespace() 101 | .filter(|s| !s.is_empty()) 102 | .collect(); 103 | match *cmd.first().unwrap_or(&"") { 104 | "exit" => self.done = true, 105 | "ast" | "tree" => handle_bool_opt!(self.show_ast), 106 | "lex" => handle_bool_opt!(self.show_lex), 107 | "load" => match run_file(cmd[1], Some(&mut self.interpreter)) { 108 | Ok(status) => { 109 | if status { 110 | println!("ok"); 111 | } 112 | } 113 | Err(error) => eprintln!("{}", error), 114 | }, 115 | 116 | "help" => eprintln!(":exit | :lex | :ast | :help"), 117 | _ => eprintln!("Invalid command. Type `:help` for guidance."), 118 | } 119 | } 120 | } 121 | 122 | macro_rules! handle_bool_opt { 123 | ($self:ident . $option:ident) => {{ 124 | $self.$option = !$self.$option; 125 | let status = if $self.$option { "ON" } else { "OFF" }; 126 | println!("Toggled `{}` option {}.", stringify!($option), status); 127 | }}; 128 | } 129 | use handle_bool_opt; 130 | -------------------------------------------------------------------------------- /tree-lox/src/token.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | 3 | use crate::{parser::scanner::error::ScanError, span::Span}; 4 | 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub struct Token { 7 | pub kind: TokenKind, 8 | pub span: Span, 9 | } 10 | 11 | impl Token { 12 | pub fn new(kind: TokenKind, span: Span) -> Self { 13 | Self { kind, span } 14 | } 15 | 16 | pub fn dummy() -> Self { 17 | Self::new(TokenKind::Dummy, Span::new(0, 0)) 18 | } 19 | } 20 | 21 | #[derive(Debug, PartialEq, Clone)] 22 | pub enum TokenKind { 23 | Identifier(String), 24 | String(String), 25 | Number(f64), 26 | 27 | Comment(String), 28 | Whitespace(String), 29 | 30 | LeftParen, 31 | RightParen, 32 | LeftBrace, 33 | RightBrace, 34 | Plus, 35 | Minus, 36 | Star, 37 | Slash, 38 | Dot, 39 | Comma, 40 | Semicolon, 41 | Bang, 42 | BangEqual, 43 | Equal, 44 | EqualEqual, 45 | Less, 46 | LessEqual, 47 | Greater, 48 | GreaterEqual, 49 | 50 | Nil, 51 | True, 52 | False, 53 | This, 54 | Super, 55 | Class, 56 | And, 57 | Or, 58 | If, 59 | Else, 60 | Return, 61 | Fun, 62 | For, 63 | While, 64 | Var, 65 | Print, 66 | Typeof, 67 | Show, 68 | 69 | Eof, 70 | 71 | Dummy, 72 | Error(ScanError), 73 | } 74 | 75 | impl TokenKind { 76 | pub fn is_keyword(&self) -> bool { 77 | use TokenKind::*; 78 | // All tokens kinds patterns are checked in order to preserve match exhaustiveness. 79 | match self { 80 | Nil | True | False | This | Super | Class | And | Or | If | Else | Return | Fun 81 | | For | While | Var | Print | Typeof | Show => true, 82 | 83 | Identifier(_) | String(_) | Number(_) | Comment(_) | Whitespace(_) | LeftParen 84 | | RightParen | LeftBrace | RightBrace | Plus | Minus | Star | Slash | Dot | Comma 85 | | Semicolon | Bang | BangEqual | Equal | EqualEqual | Less | LessEqual | Greater 86 | | GreaterEqual | Eof | Dummy | Error(_) => false, 87 | } 88 | } 89 | 90 | pub fn get_pair(&self) -> TokenKind { 91 | use TokenKind::*; 92 | match self { 93 | LeftParen => RightParen, 94 | RightParen => LeftParen, 95 | LeftBrace => RightBrace, 96 | RightBrace => LeftBrace, 97 | unexpected => panic!("Kind `{:?}` don't have a pair. This is a bug.", unexpected), 98 | } 99 | } 100 | } 101 | 102 | impl Display for Token { 103 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 104 | self.kind.fmt(f) 105 | } 106 | } 107 | 108 | impl Display for TokenKind { 109 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 110 | use TokenKind::*; 111 | match self { 112 | Identifier(identifier) => identifier.fmt(f), 113 | Number(number) => number.fmt(f), 114 | String(string) => write!(f, "\"{}\"", string), 115 | Comment(comment) => write!(f, "//{}", comment), 116 | Whitespace(whitespace) => whitespace.fmt(f), 117 | LeftParen => f.write_str("("), 118 | RightParen => f.write_str(")"), 119 | LeftBrace => f.write_str("{"), 120 | RightBrace => f.write_str("}"), 121 | Plus => f.write_str("+"), 122 | Minus => f.write_str("-"), 123 | Star => f.write_str("*"), 124 | Slash => f.write_str("/"), 125 | Dot => f.write_str("."), 126 | Comma => f.write_str(","), 127 | Semicolon => f.write_str(";"), 128 | Bang => f.write_str("!"), 129 | BangEqual => f.write_str("!="), 130 | Equal => f.write_str("="), 131 | EqualEqual => f.write_str("=="), 132 | Less => f.write_str("<"), 133 | LessEqual => f.write_str("<="), 134 | Greater => f.write_str(">"), 135 | GreaterEqual => f.write_str(">="), 136 | Nil => f.write_str("nil"), 137 | True => f.write_str("true"), 138 | False => f.write_str("false"), 139 | This => f.write_str("this"), 140 | Super => f.write_str("super"), 141 | Class => f.write_str("class"), 142 | And => f.write_str("and"), 143 | Or => f.write_str("or"), 144 | If => f.write_str("if"), 145 | Else => f.write_str("else"), 146 | Return => f.write_str("return"), 147 | Fun => f.write_str("fun"), 148 | For => f.write_str("for"), 149 | While => f.write_str("while"), 150 | Var => f.write_str("var"), 151 | Print => f.write_str("print"), 152 | Typeof => f.write_str("typeof"), 153 | Show => f.write_str("show"), 154 | 155 | Eof => f.write_str(""), 156 | Dummy => f.write_str(""), 157 | Error(error) => write!(f, "", error), 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ansi_term" 7 | version = "0.12.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 10 | dependencies = [ 11 | "winapi", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.51" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203" 19 | 20 | [[package]] 21 | name = "cfg-if" 22 | version = "1.0.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 25 | 26 | [[package]] 27 | name = "getrandom" 28 | version = "0.2.4" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" 31 | dependencies = [ 32 | "cfg-if", 33 | "libc", 34 | "wasi", 35 | ] 36 | 37 | [[package]] 38 | name = "lazy_static" 39 | version = "1.4.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 42 | 43 | [[package]] 44 | name = "libc" 45 | version = "0.2.117" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" 48 | 49 | [[package]] 50 | name = "phf" 51 | version = "0.10.1" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" 54 | dependencies = [ 55 | "phf_macros", 56 | "phf_shared", 57 | "proc-macro-hack", 58 | ] 59 | 60 | [[package]] 61 | name = "phf_generator" 62 | version = "0.10.0" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" 65 | dependencies = [ 66 | "phf_shared", 67 | "rand", 68 | ] 69 | 70 | [[package]] 71 | name = "phf_macros" 72 | version = "0.10.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" 75 | dependencies = [ 76 | "phf_generator", 77 | "phf_shared", 78 | "proc-macro-hack", 79 | "proc-macro2", 80 | "quote", 81 | "syn", 82 | ] 83 | 84 | [[package]] 85 | name = "phf_shared" 86 | version = "0.10.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" 89 | dependencies = [ 90 | "siphasher", 91 | ] 92 | 93 | [[package]] 94 | name = "ppv-lite86" 95 | version = "0.2.16" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 98 | 99 | [[package]] 100 | name = "proc-macro-hack" 101 | version = "0.5.19" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 104 | 105 | [[package]] 106 | name = "proc-macro2" 107 | version = "1.0.36" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 110 | dependencies = [ 111 | "unicode-xid", 112 | ] 113 | 114 | [[package]] 115 | name = "quote" 116 | version = "1.0.15" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 119 | dependencies = [ 120 | "proc-macro2", 121 | ] 122 | 123 | [[package]] 124 | name = "rand" 125 | version = "0.8.4" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 128 | dependencies = [ 129 | "libc", 130 | "rand_chacha", 131 | "rand_core", 132 | "rand_hc", 133 | ] 134 | 135 | [[package]] 136 | name = "rand_chacha" 137 | version = "0.3.1" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 140 | dependencies = [ 141 | "ppv-lite86", 142 | "rand_core", 143 | ] 144 | 145 | [[package]] 146 | name = "rand_core" 147 | version = "0.6.3" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 150 | dependencies = [ 151 | "getrandom", 152 | ] 153 | 154 | [[package]] 155 | name = "rand_hc" 156 | version = "0.3.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 159 | dependencies = [ 160 | "rand_core", 161 | ] 162 | 163 | [[package]] 164 | name = "siphasher" 165 | version = "0.3.9" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" 168 | 169 | [[package]] 170 | name = "syn" 171 | version = "1.0.86" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 174 | dependencies = [ 175 | "proc-macro2", 176 | "quote", 177 | "unicode-xid", 178 | ] 179 | 180 | [[package]] 181 | name = "tree-lox" 182 | version = "0.1.0" 183 | dependencies = [ 184 | "ansi_term", 185 | "anyhow", 186 | "lazy_static", 187 | ] 188 | 189 | [[package]] 190 | name = "unicode-xid" 191 | version = "0.2.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 194 | 195 | [[package]] 196 | name = "vm-lox" 197 | version = "0.1.0" 198 | dependencies = [ 199 | "phf", 200 | ] 201 | 202 | [[package]] 203 | name = "wasi" 204 | version = "0.10.2+wasi-snapshot-preview1" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 207 | 208 | [[package]] 209 | name = "winapi" 210 | version = "0.3.9" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 213 | dependencies = [ 214 | "winapi-i686-pc-windows-gnu", 215 | "winapi-x86_64-pc-windows-gnu", 216 | ] 217 | 218 | [[package]] 219 | name = "winapi-i686-pc-windows-gnu" 220 | version = "0.4.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 223 | 224 | [[package]] 225 | name = "winapi-x86_64-pc-windows-gnu" 226 | version = "0.4.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 229 | -------------------------------------------------------------------------------- /tree-lox/src/parser/scanner.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | parser::scanner::{ 3 | error::ScanError, 4 | identifier::{is_valid_identifier_start, is_valid_identifier_tail, LOX_KEYWORDS}, 5 | }, 6 | span::Span, 7 | token::{Token, TokenKind}, 8 | }; 9 | 10 | pub mod error; 11 | mod identifier; 12 | 13 | pub struct Scanner<'src> { 14 | src: &'src str, 15 | chars: Vec<(usize, char)>, // Start byte index and char 16 | cursor: usize, 17 | lexme_span_start: usize, 18 | emitted_eof: bool, 19 | } 20 | 21 | impl Iterator for Scanner<'_> { 22 | type Item = Token; 23 | 24 | fn next(&mut self) -> Option { 25 | if self.emitted_eof { 26 | return None; 27 | } 28 | // Ensures the next token starts with a new span. 29 | self.lexme_span_start = self.peek(0).0; 30 | let kind = self.scan_token_kind(); 31 | if kind == TokenKind::Eof { 32 | self.emitted_eof = true; 33 | } 34 | Some(Token { 35 | kind, 36 | span: self.lexme_span(), 37 | }) 38 | } 39 | } 40 | 41 | // The scanner implementation. 42 | impl Scanner<'_> { 43 | /// Tries to scan the current character. 44 | fn scan_token_kind(&mut self) -> TokenKind { 45 | use TokenKind::*; 46 | match self.advance() { 47 | '\0' => Eof, 48 | '(' => LeftParen, 49 | ')' => RightParen, 50 | '{' => LeftBrace, 51 | '}' => RightBrace, 52 | ';' => Semicolon, 53 | ',' => Comma, 54 | '.' => Dot, 55 | '!' => self.take_select('=', BangEqual, Bang), 56 | '=' => self.take_select('=', EqualEqual, Equal), 57 | '>' => self.take_select('=', GreaterEqual, Greater), 58 | '<' => self.take_select('=', LessEqual, Less), 59 | '+' => Plus, 60 | '-' => Minus, 61 | '*' => Star, 62 | '"' => self.string(), 63 | '/' => self.comment_or_slash(), 64 | c if c.is_ascii_digit() => self.number(), 65 | c if c.is_ascii_whitespace() => self.whitespace(), 66 | c if is_valid_identifier_start(c) => self.identifier_or_keyword(), 67 | unexpected => Error(ScanError::UnexpectedChar(unexpected)), 68 | } 69 | } 70 | 71 | /// Tries to scan a string. 72 | fn string(&mut self) -> TokenKind { 73 | while self.current() != '"' && !self.is_at_end() { 74 | self.advance(); 75 | } 76 | if self.is_at_end() { 77 | return TokenKind::Error(ScanError::UnterminatedString); 78 | } 79 | self.advance(); // The closing `"` 80 | TokenKind::String(self.lexme(1, -1).into()) 81 | } 82 | 83 | /// Scans a comment or a slash. 84 | fn comment_or_slash(&mut self) -> TokenKind { 85 | if self.take('/') { 86 | while self.current() != '\n' && !self.is_at_end() { 87 | self.advance(); 88 | } 89 | TokenKind::Comment(self.lexme(2, 0).into()) 90 | } else { 91 | TokenKind::Slash 92 | } 93 | } 94 | 95 | /// Tries to scan a number. 96 | fn number(&mut self) -> TokenKind { 97 | while self.current().is_ascii_digit() { 98 | self.advance(); 99 | } 100 | if self.current() == '.' && self.peek(1).1.is_ascii_digit() { 101 | self.advance(); // The `.` separator 102 | while self.current().is_ascii_digit() { 103 | self.advance(); 104 | } 105 | } 106 | match self.lexme(0, 0).parse() { 107 | Ok(parsed) => TokenKind::Number(parsed), 108 | Err(_) => TokenKind::Error(ScanError::InvalidNumberLiteral), 109 | } 110 | } 111 | 112 | /// Scans a newline or a whitespace. 113 | fn whitespace(&mut self) -> TokenKind { 114 | while self.current().is_ascii_whitespace() { 115 | self.advance(); 116 | } 117 | TokenKind::Whitespace(self.lexme(0, 0).into()) 118 | } 119 | 120 | /// Scans a keyword or an identifier. 121 | fn identifier_or_keyword(&mut self) -> TokenKind { 122 | while is_valid_identifier_tail(self.current()) { 123 | self.advance(); 124 | } 125 | let name = self.lexme(0, 0); 126 | if name == "NaN" { 127 | return TokenKind::Number(f64::NAN); 128 | } 129 | match LOX_KEYWORDS.get(name) { 130 | // Since keyword token kinds have no internal data, the following clone is cheap. 131 | Some(keyword_kind) => keyword_kind.clone(), 132 | None => TokenKind::Identifier(name.into()), 133 | } 134 | } 135 | } 136 | 137 | // The scanner helper methods. 138 | impl<'src> Scanner<'src> { 139 | /// Creates a new scanner. 140 | pub fn new(src: &'src str) -> Self { 141 | Self { 142 | src, 143 | chars: src.char_indices().collect(), 144 | cursor: 0, 145 | lexme_span_start: 0, 146 | emitted_eof: false, 147 | } 148 | } 149 | 150 | /// Peeks a character tuple with the given offset from the cursor. 151 | #[inline] 152 | fn peek(&self, offset: isize) -> (usize, char) { 153 | self.chars 154 | .get((self.cursor as isize + offset) as usize) 155 | .copied() 156 | .unwrap_or((self.src.len(), '\0')) 157 | } 158 | 159 | /// Peeks into the current character (not yet consumed). 160 | #[inline] 161 | fn current(&self) -> char { 162 | self.peek(0).1 163 | } 164 | 165 | /// Returns the current character and advances the `current` cursor. 166 | #[inline] 167 | fn advance(&mut self) -> char { 168 | self.cursor += 1; 169 | self.peek(-1).1 170 | } 171 | 172 | /// Checks if the current character matches the given one. In such case advances and returns 173 | /// true. Otherwise returns false. 174 | #[inline] 175 | fn take(&mut self, expected: char) -> bool { 176 | if self.current() != expected { 177 | return false; 178 | } 179 | self.advance(); 180 | true 181 | } 182 | 183 | /// Checks if the current character matches the given one. In such case, advances and returns 184 | /// `a`, otherwise returns `b`. 185 | #[inline] 186 | fn take_select(&mut self, expected: char, a: T, b: T) -> T { 187 | match self.take(expected) { 188 | true => a, 189 | false => b, 190 | } 191 | } 192 | 193 | /// Returns the current lexme span. 194 | #[inline] 195 | fn lexme_span(&self) -> Span { 196 | Span::new(self.lexme_span_start, self.peek(0).0) 197 | } 198 | 199 | /// Returns a lexme slice. 200 | #[inline] 201 | fn lexme(&self, lo: isize, hi: isize) -> &'src str { 202 | let span = self.lexme_span().updated(lo, hi); 203 | &self.src[span.lo..span.hi] 204 | } 205 | 206 | /// Checks if the scanner has finished. 207 | #[inline] 208 | fn is_at_end(&self) -> bool { 209 | self.cursor >= self.chars.len() 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /tree-lox/src/ast/dbg.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::{ 3 | expr::{self, Expr}, 4 | stmt::{self, Stmt}, 5 | }, 6 | parser::scanner::Scanner, 7 | }; 8 | 9 | /// Prints the scanner result for the given source. 10 | /// Will scan the entire source string and discard the result. 11 | pub fn print_scanned_tokens(src: &str) { 12 | let scanner = Scanner::new(src); 13 | println!("┌─"); 14 | for token in scanner { 15 | println!("│ {:?}", token); 16 | } 17 | println!("└─"); 18 | } 19 | 20 | /// Prints the given program tree. 21 | pub fn print_program_tree(stmts: &[Stmt]) { 22 | println!("┌─"); 23 | TreePrinter::new("│ ").print_stmts(stmts); 24 | println!("└─"); 25 | } 26 | 27 | struct TreePrinter { 28 | prefix: &'static str, 29 | level: usize, 30 | } 31 | 32 | impl TreePrinter { 33 | fn print_stmts(&mut self, stmts: &[Stmt]) { 34 | for (i, stmt) in stmts.iter().enumerate() { 35 | self.print_stmt(stmt); 36 | if i != stmts.len() - 1 { 37 | self.emit(""); 38 | } 39 | } 40 | } 41 | 42 | fn print_stmt(&mut self, stmt: &Stmt) { 43 | use Stmt::*; 44 | match &stmt { 45 | VarDecl(var) => { 46 | self.emit("Var Decl"); 47 | self.nest(|s| { 48 | s.emit(format!("Name = `{}`", var.name)); 49 | if let Some(init) = &var.init { 50 | s.emit("Var Init"); 51 | s.nest(|s| s.print_expr(init)); 52 | } 53 | }); 54 | } 55 | ClassDecl(class) => { 56 | self.emit("Class Decl"); 57 | self.nest(|s| { 58 | s.emit(format!("Name = `{}`", class.name)); 59 | if let Some(super_name) = &class.super_name { 60 | s.emit(format!("Extending `{}`", super_name)); 61 | } 62 | s.emit("Methods"); 63 | s.nest(|s| { 64 | for method in &class.methods { 65 | s.print_fun(method, "Class Method"); 66 | } 67 | }); 68 | }); 69 | } 70 | FunDecl(fun) => self.print_fun(fun, "Fun Stmt"), 71 | If(if_stmt) => { 72 | self.emit("If Stmt"); 73 | self.nest(|s| { 74 | s.emit("Cond Expr"); 75 | s.nest(|s| s.print_expr(&if_stmt.cond)); 76 | s.emit("Then"); 77 | s.nest(|s| s.print_stmt(&if_stmt.then_branch)); 78 | if let Some(else_branch) = &if_stmt.else_branch { 79 | s.emit("Else"); 80 | s.nest(|s| s.print_stmt(else_branch)); 81 | } 82 | }) 83 | } 84 | While(while_stmt) => { 85 | self.emit("While Stmt"); 86 | self.nest(|s| { 87 | s.emit("Cond Expr"); 88 | s.nest(|s| s.print_expr(&while_stmt.cond)); 89 | s.emit("Body"); 90 | s.nest(|s| s.print_stmt(&while_stmt.body)); 91 | }) 92 | } 93 | Return(return_stmt) => { 94 | self.emit("Return Stmt"); 95 | if let Some(value) = &return_stmt.value { 96 | self.nest(|s| s.print_expr(value)); 97 | } 98 | } 99 | Print(print) => { 100 | self.emit("Print Stmt"); 101 | self.nest(|s| { 102 | s.print_expr(&print.expr); 103 | }); 104 | } 105 | Block(block) => { 106 | self.emit("Block Stmt"); 107 | self.nest(|s| s.print_stmts(&block.stmts)); 108 | } 109 | Expr(expr) => { 110 | self.emit("Expr Stmt"); 111 | self.nest(|s| { 112 | s.print_expr(&expr.expr); 113 | }); 114 | } 115 | Dummy(_) => self.emit("Dummy Stmt (INVALID TREE)"), 116 | } 117 | } 118 | 119 | fn print_expr(&mut self, expr: &Expr) { 120 | use Expr::*; 121 | match &expr { 122 | Lit(expr::Lit { value, .. }) => { 123 | self.emit(format!("Literal ({:?} :: {})", value, value.type_name())); 124 | } 125 | This(_) => { 126 | self.emit("This"); 127 | } 128 | Super(_) => { 129 | self.emit("Super"); 130 | } 131 | Var(var) => { 132 | self.emit(format!("Var `{}`", var.name)); 133 | } 134 | Group(group) => { 135 | self.emit("Group"); 136 | self.nest(|s| { 137 | s.print_expr(&group.expr); 138 | }); 139 | } 140 | Get(get) => { 141 | self.emit("Get"); 142 | self.nest(|s| { 143 | s.emit(format!("Property: `{}`", get.name)); 144 | s.emit("From Object"); 145 | s.nest(|s| s.print_expr(&get.object)); 146 | }); 147 | } 148 | Set(set) => { 149 | self.emit("Set"); 150 | self.nest(|s| { 151 | s.emit(format!("Target property: `{}`", set.name)); 152 | s.emit("From Object"); 153 | s.nest(|s| s.print_expr(&set.object)); 154 | s.emit("With Value"); 155 | s.nest(|s| s.print_expr(&set.value)); 156 | }); 157 | } 158 | Call(call) => { 159 | self.emit("Call"); 160 | self.nest(|s| { 161 | s.emit("Callee"); 162 | s.nest(|s| s.print_expr(&call.callee)); 163 | if !call.args.is_empty() { 164 | s.emit("Args"); 165 | s.nest(|s| { 166 | for arg in &call.args { 167 | s.print_expr(arg); 168 | } 169 | }); 170 | } 171 | }) 172 | } 173 | Unary(unary) => { 174 | self.emit(format!("Unary {}", unary.operator)); 175 | self.nest(|s| { 176 | s.print_expr(&unary.operand); 177 | }); 178 | } 179 | #[rustfmt::skip] 180 | Binary(expr::Binary { operator, left, right, .. }) | 181 | Logical(expr::Logical { operator, left, right, .. }) => { 182 | self.emit(format!("Binary {}", operator)); 183 | self.nest(|s| { 184 | s.print_expr(left); 185 | s.print_expr(right); 186 | }); 187 | } 188 | Assignment(assignment) => { 189 | self.emit("Assignment"); 190 | self.nest(|s| { 191 | s.emit(format!("Target: `{}`", assignment.name)); 192 | s.emit("With Value"); 193 | s.nest(|s| s.print_expr(&assignment.value)); 194 | }); 195 | } 196 | } 197 | } 198 | 199 | fn print_fun(&mut self, fun: &stmt::FunDecl, label: &'static str) { 200 | self.emit(label); 201 | self.nest(|s| { 202 | s.emit(format!("Name = `{}`", fun.name)); 203 | s.emit(format!("Params ({})", fun.params.len())); 204 | s.nest(|s| { 205 | for param in &fun.params { 206 | s.emit(¶m.name); 207 | } 208 | }); 209 | s.emit("Body"); 210 | s.nest(|s| s.print_stmts(&fun.body)); 211 | }); 212 | } 213 | 214 | fn new(prefix: &'static str) -> Self { 215 | Self { level: 0, prefix } 216 | } 217 | 218 | fn emit(&self, str: impl Into) { 219 | println!("{}{}{}", self.prefix, " . ".repeat(self.level), str.into()); 220 | } 221 | 222 | fn nest(&mut self, scope: S) { 223 | self.level += 1; 224 | scope(self); 225 | self.level -= 1; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /vm-lox/src/scanner.rs: -------------------------------------------------------------------------------- 1 | use std::str::Chars; 2 | 3 | use crate::common::{Span, Token, TokenKind}; 4 | 5 | pub struct Scanner<'s> { 6 | // Input 7 | source: &'s str, 8 | source_iter: Chars<'s>, 9 | // Lexme location 10 | lexme_start: usize, 11 | lexme_end: usize, 12 | // Is it finished? 13 | done: bool, 14 | } 15 | 16 | // Core implementation. 17 | impl Scanner<'_> { 18 | /// Returns the next kind of token in the source string. 19 | fn scan_kind(&mut self) -> TokenKind { 20 | let current = self.bump(); 21 | 22 | use TokenKind::*; 23 | match current { 24 | '\0' => Eof, 25 | 26 | '(' => LeftParen, 27 | ')' => RightParen, 28 | '{' => LeftBrace, 29 | '}' => RightBrace, 30 | ';' => Semicolon, 31 | ',' => Comma, 32 | '.' => Dot, 33 | '+' => Plus, 34 | '-' => Minus, 35 | '*' => Star, 36 | '/' => Slash, 37 | 38 | '=' => self.next_and('=', EqualEqual, Equal), 39 | '!' => self.next_and('=', BangEqual, Bang), 40 | '<' => self.next_and('=', LessEqual, Less), 41 | '>' => self.next_and('=', GreaterEqual, Greater), 42 | 43 | // Strings 44 | '"' => { 45 | while self.peek_first() != '"' && !self.is_at_end() { 46 | self.bump(); 47 | } 48 | if self.is_at_end() { 49 | return Error("Unterminated string".into()); 50 | } 51 | self.bump(); // The closing `"` 52 | String(self.slice(1, 1).into()) 53 | } 54 | 55 | // Numbers 56 | c if c.is_ascii_digit() => { 57 | while self.peek_first().is_ascii_digit() { 58 | self.bump(); 59 | } 60 | if self.peek_first() == '.' && self.peek_second().is_ascii_digit() { 61 | self.bump(); // The `.` 62 | while self.peek_first().is_ascii_digit() { 63 | self.bump(); 64 | } 65 | } 66 | match self.slice(0, 0).parse() { 67 | Ok(number) => Number(number), 68 | Err(_) => Error("Could not parse number literal value".into()), 69 | } 70 | } 71 | 72 | // Keywords and identifiers 73 | c if is_valid_identifier_start(c) => { 74 | while is_valid_identifier_tail(self.peek_first()) { 75 | self.bump(); 76 | } 77 | let ident_str = self.slice(0, 0); 78 | match KEYWORDS_MAP.get(ident_str) { 79 | Some(keyword_kind) => keyword_kind.clone(), 80 | None => Identifier(ident_str.into()), 81 | } 82 | } 83 | 84 | unexpected => Error(format!("Unexpected token `{unexpected}`.")), 85 | } 86 | } 87 | 88 | /// Returns the next token in the source string. 89 | fn scan_token(&mut self) -> Token { 90 | // Handle ignored sequences (such as whitespaces and comments) so `scan_kind` does not 91 | // have to deal with them. 92 | self.skip_whitespace_and_comment(); 93 | 94 | // Resets the current lexme start bound (the end bound is automatically handled by `bump`). 95 | self.lexme_start = self.lexme_end; 96 | 97 | let kind = self.scan_kind(); 98 | let span = Span::new(self.lexme_start, self.lexme_end); 99 | Token { kind, span } 100 | } 101 | 102 | /// Advances *until* the next token shouldn't be ignored by the scanner. 103 | fn skip_whitespace_and_comment(&mut self) { 104 | loop { 105 | match self.peek_first() { 106 | '/' => { 107 | if self.peek_second() == '/' { 108 | while self.peek_first() != '\n' && !self.is_at_end() { 109 | self.bump(); 110 | } 111 | } else { 112 | // The first slash must not be consumed if there is no second slash that 113 | // indicates the start of a comment token (which is in fact ignored). 114 | // 115 | // In such case, this function is returned since the first slash must be 116 | // accounted for by the "main" scanner implementation, in `scan_kind`. 117 | return; 118 | } 119 | } 120 | 121 | c if c.is_ascii_whitespace() => { 122 | self.bump(); 123 | } 124 | 125 | _ => return, 126 | } 127 | } 128 | } 129 | } 130 | 131 | // Utilities. 132 | impl Scanner<'_> { 133 | /// Advances the underlying character iterator and returns the yielded character. Returns the 134 | /// null character ('\0') if there are no more characters to be produced. 135 | /// 136 | /// Increments the `lexme_end` field according to the returned character's UTF-8 length. 137 | fn bump(&mut self) -> char { 138 | self.source_iter 139 | .next() 140 | .map(|char| { 141 | self.lexme_end += char.len_utf8(); 142 | char 143 | }) 144 | .unwrap_or_else(|| { 145 | if self.done { 146 | panic!("Scanner must not advance past the end of input"); 147 | } 148 | self.done = true; 149 | EOF_CHAR 150 | }) 151 | } 152 | 153 | /// Returns the first next character in the source string without advancing. 154 | fn peek_first(&self) -> char { 155 | self.source_iter.clone().next().unwrap_or(EOF_CHAR) 156 | } 157 | 158 | /// Returns the second next character in the source string without advancing. 159 | fn peek_second(&self) -> char { 160 | let mut iter = self.source_iter.clone(); 161 | iter.next(); 162 | iter.next().unwrap_or(EOF_CHAR) 163 | } 164 | 165 | /// Returns `a` and advances the scanner if the first next character is equal to the expected 166 | /// one. Otherwise returns `b` without advancing. 167 | fn next_and(&mut self, expected: char, a: T, b: T) -> T { 168 | if self.peek_first() == expected { 169 | self.bump(); 170 | a 171 | } else { 172 | b 173 | } 174 | } 175 | 176 | /// Returns a slice over the current lexme bounds. The caller may modify the current bounds: 177 | /// - A `left_modifier` will be *added* to the current *start* bound. 178 | /// - A `right_modifier` will be *subtracted* from the current *end* bound. 179 | /// 180 | /// Such new computed bound must hold that `new_start_bound <= new_end_bound`. 181 | /// 182 | /// While performing the index operation, panics if the computed bound is invalid. 183 | fn slice(&self, left_modifier: usize, right_modifier: usize) -> &str { 184 | let left = self.lexme_start + left_modifier; 185 | let right = self.lexme_end - right_modifier; 186 | debug_assert!(left <= left, "Invalid computed bounds"); 187 | &self.source[left..right] 188 | } 189 | 190 | /// Checks if the source string is finished. 191 | fn is_at_end(&self) -> bool { 192 | self.source_iter.as_str().is_empty() 193 | } 194 | } 195 | 196 | // Public API. 197 | impl<'s> Scanner<'s> { 198 | /// Creates a new scanner from the given source string. 199 | pub fn new(source: &'s str) -> Scanner<'s> { 200 | Scanner { 201 | source, 202 | source_iter: source.chars(), 203 | lexme_start: 0, 204 | lexme_end: 0, 205 | done: false, 206 | } 207 | } 208 | } 209 | 210 | impl Iterator for Scanner<'_> { 211 | type Item = Token; 212 | 213 | fn next(&mut self) -> Option { 214 | if self.done { 215 | return None; 216 | } 217 | Some(self.scan_token()) 218 | } 219 | } 220 | 221 | const EOF_CHAR: char = '\0'; 222 | 223 | static KEYWORDS_MAP: phf::Map<&'static str, TokenKind> = phf::phf_map! { 224 | "nil" => TokenKind::Nil, 225 | "true" => TokenKind::True, 226 | "false" => TokenKind::False, 227 | "this" => TokenKind::This, 228 | "super" => TokenKind::Super, 229 | "class" => TokenKind::Class, 230 | "and" => TokenKind::And, 231 | "or" => TokenKind::Or, 232 | "if" => TokenKind::If, 233 | "else" => TokenKind::Else, 234 | "return" => TokenKind::Return, 235 | "fun" => TokenKind::Fun, 236 | "for" => TokenKind::For, 237 | "while" => TokenKind::While, 238 | "var" => TokenKind::Var, 239 | "print" => TokenKind::Print, 240 | "typeof" => TokenKind::Typeof, 241 | "show" => TokenKind::Show, 242 | }; 243 | 244 | /// Checks if the given char is valid as an identifier's start character. 245 | fn is_valid_identifier_start(c: char) -> bool { 246 | c.is_ascii_alphabetic() || c == '_' 247 | } 248 | 249 | /// Checks if the given char can belong to an identifier's tail. 250 | fn is_valid_identifier_tail(c: char) -> bool { 251 | c.is_ascii_digit() || is_valid_identifier_start(c) 252 | } 253 | -------------------------------------------------------------------------------- /tree-lox/src/data.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::RefCell, 3 | collections::HashMap, 4 | fmt::{self, Debug, Display}, 5 | rc::Rc, 6 | sync::atomic::{self, AtomicUsize}, 7 | }; 8 | 9 | use crate::{ 10 | ast::stmt::FunDecl, 11 | interpreter::{ 12 | control_flow::ControlFlow, environment::Environment, error::RuntimeError, CFResult, 13 | Interpreter, 14 | }, 15 | span::Span, 16 | token::{Token, TokenKind}, 17 | }; 18 | 19 | #[derive(Clone)] 20 | pub enum LoxValue { 21 | Function(Rc), 22 | Class(Rc), 23 | Object(Rc), 24 | Boolean(bool), 25 | Number(f64), 26 | String(String), 27 | Nil, 28 | } 29 | 30 | impl LoxValue { 31 | /// Returns the canonical type name. 32 | pub fn type_name(&self) -> &'static str { 33 | use LoxValue::*; 34 | match self { 35 | Function(_) => "function", 36 | Class(_) => "class", 37 | Object(_) => "object", 38 | Boolean(_) => "boolean", 39 | Number(_) => "number", 40 | String(_) => "string", 41 | Nil => "nil", 42 | } 43 | } 44 | 45 | /// Returns the `Rc` pointer if the given `LoxValue` is a class. Otherwise None. 46 | pub fn as_class(self) -> Option> { 47 | match self { 48 | LoxValue::Class(inner) => Some(inner), 49 | _ => None, 50 | } 51 | } 52 | 53 | /// Returns the `Rc` pointer if the given `LoxValue` is a class. Otherwise None. 54 | pub fn as_object(self) -> Option> { 55 | match self { 56 | LoxValue::Object(inner) => Some(inner), 57 | _ => None, 58 | } 59 | } 60 | } 61 | 62 | impl Display for LoxValue { 63 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 64 | use LoxValue::*; 65 | match self { 66 | Function(fun) => Display::fmt(fun, f), 67 | Class(class) => Display::fmt(class, f), 68 | Object(instance) => Display::fmt(instance, f), 69 | Boolean(boolean) => Display::fmt(boolean, f), 70 | Number(number) => { 71 | if number.floor() == *number { 72 | write!(f, "{:.0}", number) 73 | } else { 74 | Display::fmt(number, f) 75 | } 76 | } 77 | String(string) => f.write_str(string), 78 | Nil => f.write_str("nil"), 79 | } 80 | } 81 | } 82 | 83 | impl Debug for LoxValue { 84 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 85 | use LoxValue::*; 86 | match self { 87 | String(s) => write!(f, "\"{}\"", s), 88 | other => Display::fmt(other, f), 89 | } 90 | } 91 | } 92 | 93 | #[derive(Debug, Clone)] 94 | pub struct LoxIdent { 95 | pub id: LoxIdentId, 96 | pub name: String, 97 | pub span: Span, 98 | } 99 | 100 | // Yep, global state: 101 | static LOX_IDENT_ID_SEQ: AtomicUsize = AtomicUsize::new(0); 102 | 103 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 104 | pub struct LoxIdentId(usize); 105 | 106 | impl LoxIdentId { 107 | pub fn new() -> Self { 108 | LoxIdentId(LOX_IDENT_ID_SEQ.fetch_add(1, atomic::Ordering::SeqCst)) 109 | } 110 | } 111 | 112 | impl LoxIdent { 113 | pub fn new(span: Span, name: impl Into) -> Self { 114 | LoxIdent { 115 | id: LoxIdentId::new(), 116 | name: name.into(), 117 | span, 118 | } 119 | } 120 | } 121 | 122 | impl From for LoxIdent { 123 | fn from(Token { kind, span }: Token) -> Self { 124 | match kind { 125 | TokenKind::Identifier(name) => LoxIdent::new(span, name), 126 | unexpected => unreachable!( 127 | "Invalid `Token` ({:?}) to `LoxIdent` conversion.", 128 | unexpected 129 | ), 130 | } 131 | } 132 | } 133 | 134 | impl AsRef for LoxIdent { 135 | fn as_ref(&self) -> &str { 136 | &self.name 137 | } 138 | } 139 | 140 | impl From for String { 141 | fn from(ident: LoxIdent) -> Self { 142 | ident.name 143 | } 144 | } 145 | 146 | impl Display for LoxIdent { 147 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 148 | f.write_str(&self.name) 149 | } 150 | } 151 | 152 | pub trait LoxCallable: Display + Debug { 153 | fn call(self: Rc, interpreter: &mut Interpreter, args: &[LoxValue]) 154 | -> CFResult; 155 | fn arity(&self) -> usize; 156 | } 157 | 158 | #[derive(Debug, Clone)] 159 | pub struct LoxFunction { 160 | pub decl: Rc, 161 | pub closure: Environment, 162 | pub is_class_init: bool, 163 | } 164 | 165 | impl LoxFunction { 166 | pub fn bind(&self, instance: &Rc) -> Rc { 167 | let mut env = Environment::new_enclosing(&self.closure); 168 | env.define("this", LoxValue::Object(instance.clone())); 169 | Rc::new(LoxFunction { 170 | decl: self.decl.clone(), 171 | closure: env, 172 | is_class_init: self.is_class_init, 173 | }) 174 | } 175 | } 176 | 177 | impl LoxCallable for LoxFunction { 178 | fn call( 179 | self: Rc, 180 | interpreter: &mut Interpreter, 181 | args: &[LoxValue], 182 | ) -> CFResult { 183 | let mut env = Environment::new_enclosing(&self.closure); 184 | for (param, value) in self.decl.params.iter().zip(args) { 185 | env.define(param.clone(), value.clone()); 186 | } 187 | let real_returned_value = match interpreter.eval_block(&self.decl.body, env) { 188 | Ok(()) => LoxValue::Nil, 189 | Err(ControlFlow::Return(value)) => value, 190 | Err(other) => return Err(other), 191 | }; 192 | // If the function being currently executed happens to be the initializer (i.e. "init") of 193 | // some class, the returned value should be simply ignored, since it always returns the 194 | // instance's `this` value implicitly by this implementation (it's a Lox design choice). 195 | // 196 | // Note that if an error arises from the initializer it is not ignored. 197 | if self.is_class_init { 198 | Ok(self.closure.read_at(0, "this")) 199 | } else { 200 | Ok(real_returned_value) 201 | } 202 | } 203 | 204 | fn arity(&self) -> usize { 205 | self.decl.params.len() 206 | } 207 | } 208 | 209 | impl Display for LoxFunction { 210 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 211 | write!(f, "", self.decl.name) 212 | } 213 | } 214 | 215 | pub struct NativeFunction { 216 | pub name: &'static str, 217 | pub fn_ptr: fn(args: &[LoxValue]) -> CFResult, 218 | pub arity: usize, 219 | } 220 | 221 | impl LoxCallable for NativeFunction { 222 | fn call(self: Rc, _: &mut Interpreter, args: &[LoxValue]) -> CFResult { 223 | (self.fn_ptr)(args) 224 | } 225 | 226 | fn arity(&self) -> usize { 227 | self.arity 228 | } 229 | } 230 | 231 | impl Display for NativeFunction { 232 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 233 | write!(f, "", self.name) 234 | } 235 | } 236 | 237 | impl Debug for NativeFunction { 238 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 239 | f.debug_struct("NativeFunction") 240 | .field("name", &self.name) 241 | .field("fn_ptr", &"fn_ptr") 242 | .field("arity", &self.arity) 243 | .finish() 244 | } 245 | } 246 | 247 | #[derive(Debug, Clone)] 248 | pub struct LoxClass { 249 | pub name: LoxIdent, 250 | pub methods: HashMap>, 251 | pub super_class: Option>, 252 | } 253 | 254 | impl LoxClass { 255 | pub fn get_method(&self, ident: impl AsRef) -> Option> { 256 | self.methods 257 | .get(ident.as_ref()) 258 | .cloned() 259 | .or_else(|| self.super_class.as_ref().and_then(|s| s.get_method(ident))) 260 | } 261 | } 262 | 263 | // Class instantiation. 264 | impl LoxCallable for LoxClass { 265 | fn call( 266 | self: Rc, 267 | interpreter: &mut Interpreter, 268 | args: &[LoxValue], 269 | ) -> CFResult { 270 | let instance = Rc::new(LoxInstance { 271 | constructor: self, 272 | properties: RefCell::new(HashMap::new()), 273 | }); 274 | // Run the class' initializer if it's defined. 275 | if let Some(init) = instance.get_bound_method("init") { 276 | init.call(interpreter, args)?; 277 | } 278 | Ok(LoxValue::Object(instance)) 279 | } 280 | 281 | fn arity(&self) -> usize { 282 | match self.get_method("init") { 283 | Some(function) => function.arity(), 284 | None => 0, 285 | } 286 | } 287 | } 288 | 289 | impl Display for LoxClass { 290 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 291 | write!(f, "", self.name) 292 | } 293 | } 294 | 295 | #[derive(Debug, Clone)] 296 | pub struct LoxInstance { 297 | pub constructor: Rc, 298 | properties: RefCell>, 299 | } 300 | 301 | impl LoxInstance { 302 | pub fn get(self: &Rc, ident: &LoxIdent) -> Result { 303 | if let Some(value) = self.properties.borrow().get(&ident.name) { 304 | return Ok(value.clone()); 305 | } 306 | 307 | if let Some(method) = self.get_bound_method(ident) { 308 | return Ok(LoxValue::Function(method)); 309 | } 310 | 311 | Err(RuntimeError::UndefinedProperty { 312 | ident: ident.clone(), 313 | }) 314 | } 315 | 316 | pub fn set(&self, ident: &LoxIdent, value: LoxValue) { 317 | self.properties 318 | .borrow_mut() 319 | .insert(ident.name.clone(), value); 320 | } 321 | 322 | pub fn get_bound_method(self: &Rc, ident: impl AsRef) -> Option> { 323 | self.constructor 324 | .get_method(ident) 325 | .map(|unbound| unbound.bind(self)) 326 | } 327 | } 328 | 329 | impl Display for LoxInstance { 330 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 331 | write!(f, "")?; 339 | Ok(()) 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /tree-lox/src/resolver.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{hash_map::Entry, HashMap}, 3 | mem, 4 | }; 5 | 6 | use crate::{ 7 | ast::{ 8 | expr::Expr, 9 | stmt::{self, Stmt}, 10 | }, 11 | data::LoxIdent, 12 | interpreter::Interpreter, 13 | span::Span, 14 | }; 15 | 16 | #[derive(Debug)] 17 | pub struct Resolver<'i> { 18 | interpreter: &'i mut Interpreter, 19 | state: ResolverState, 20 | scopes: Vec>, 21 | errors: Vec, 22 | } 23 | 24 | impl Resolver<'_> { 25 | pub fn resolve(mut self, stmts: &[Stmt]) -> (bool, Vec) { 26 | self.resolve_stmts(stmts); 27 | (self.errors.is_empty(), self.errors) 28 | } 29 | 30 | // 31 | // Statements 32 | // 33 | 34 | fn resolve_stmts(&mut self, stmts: &[Stmt]) { 35 | for stmt in stmts { 36 | self.resolve_stmt(stmt); 37 | } 38 | } 39 | 40 | fn resolve_stmt(&mut self, stmt: &Stmt) { 41 | use Stmt::*; 42 | match &stmt { 43 | VarDecl(var) => { 44 | self.declare(&var.name); 45 | if let Some(init) = &var.init { 46 | self.resolve_expr(init); 47 | } 48 | self.define(&var.name); 49 | } 50 | ClassDecl(class) => { 51 | let old_class_state = mem::replace(&mut self.state.class, ClassState::Class); 52 | 53 | self.declare(&class.name); 54 | self.define(&class.name); 55 | 56 | if let Some(super_name) = &class.super_name { 57 | if class.name.name == super_name.name { 58 | self.error(super_name.span, "Class can't inherit itself"); 59 | } 60 | 61 | self.state.class = ClassState::SubClass; 62 | 63 | self.resolve_binding(super_name); 64 | 65 | // If there is a super-class, we initialize a new scope and define `super` in 66 | // it so the current class may access it. 67 | self.begin_scope(); 68 | self.initialize("super"); 69 | } 70 | 71 | self.scoped(|this| { 72 | this.initialize("this"); 73 | for method in &class.methods { 74 | let state = if method.name.name == "init" { 75 | FunctionState::Init 76 | } else { 77 | FunctionState::Method 78 | }; 79 | this.resolve_function(method, state); 80 | } 81 | }); 82 | 83 | if class.super_name.is_some() { 84 | self.end_scope(); 85 | } 86 | 87 | self.state.class = old_class_state; 88 | } 89 | FunDecl(fun) => { 90 | self.declare(&fun.name); 91 | self.define(&fun.name); 92 | self.resolve_function(fun, FunctionState::Function); 93 | } 94 | If(if_stmt) => { 95 | self.resolve_expr(&if_stmt.cond); 96 | self.resolve_stmt(&if_stmt.then_branch); 97 | if let Some(else_branch) = &if_stmt.else_branch { 98 | self.resolve_stmt(else_branch); 99 | } 100 | } 101 | While(while_stmt) => { 102 | self.resolve_expr(&while_stmt.cond); 103 | self.resolve_stmt(&while_stmt.body); 104 | } 105 | Return(return_stmt) => { 106 | if self.state.function == FunctionState::None { 107 | self.error(return_stmt.return_span, "Illegal return statement"); 108 | } 109 | if let Some(value) = &return_stmt.value { 110 | if self.state.function == FunctionState::Init { 111 | self.error( 112 | return_stmt.return_span, 113 | "Can't return value from class initializer", 114 | ); 115 | } 116 | self.resolve_expr(value); 117 | } 118 | } 119 | Print(print) => self.resolve_expr(&print.expr), 120 | Block(block) => self.scoped(|this| this.resolve_stmts(&block.stmts)), 121 | Expr(expr) => self.resolve_expr(&expr.expr), 122 | Dummy(_) => unreachable!(), 123 | } 124 | } 125 | 126 | // 127 | // Expressions 128 | // 129 | 130 | fn resolve_expr(&mut self, expr: &Expr) { 131 | use Expr::*; 132 | match &expr { 133 | Lit(_) => (), 134 | This(this) => { 135 | if self.state.class == ClassState::None { 136 | self.error( 137 | this.span, 138 | "Illegal this expression, can't use this outside of a class", 139 | ); 140 | } 141 | self.resolve_binding(&this.name) 142 | } 143 | Super(sup) => { 144 | if self.state.class == ClassState::None { 145 | self.error( 146 | sup.super_ident.span, 147 | "Illegal super expression, can't use super outside of a class", 148 | ) 149 | } 150 | if self.state.class == ClassState::Class { 151 | self.error( 152 | sup.super_ident.span, 153 | "Illegal super expression, can't use super within a class with no superclass", 154 | ) 155 | } 156 | self.resolve_binding(&sup.super_ident); 157 | } 158 | Var(var) => { 159 | if self.query(&var.name, BindingState::Declared) { 160 | self.error( 161 | var.name.span, 162 | "Can't read local variable in its own initializer", 163 | ); 164 | return; 165 | } 166 | self.resolve_binding(&var.name); 167 | } 168 | Group(group) => self.resolve_expr(&group.expr), 169 | Get(get) => { 170 | // Since properties are looked up dynamically by the interpreter (in a similar 171 | // manner to how global variables are handled), the resolver don't need to touch 172 | // their names. 173 | self.resolve_expr(&get.object); 174 | } 175 | Set(set) => { 176 | // Like get, the resolver doesn't need to resolve the set property name since it is 177 | // dynamically looked up by the interpreter. 178 | self.resolve_expr(&set.object); 179 | self.resolve_expr(&set.value); 180 | } 181 | Call(call) => { 182 | self.resolve_expr(&call.callee); 183 | for arg in &call.args { 184 | self.resolve_expr(arg); 185 | } 186 | } 187 | Unary(unary) => self.resolve_expr(&unary.operand), 188 | Binary(binary) => { 189 | self.resolve_expr(&binary.left); 190 | self.resolve_expr(&binary.right); 191 | } 192 | Logical(logical) => { 193 | self.resolve_expr(&logical.left); 194 | self.resolve_expr(&logical.right); 195 | } 196 | Assignment(assignment) => { 197 | self.resolve_expr(&assignment.value); 198 | self.resolve_binding(&assignment.name); 199 | } 200 | } 201 | } 202 | } 203 | 204 | impl<'i> Resolver<'i> { 205 | pub fn new(interpreter: &'i mut Interpreter) -> Resolver<'i> { 206 | Self { 207 | interpreter, 208 | state: ResolverState::default(), 209 | scopes: Vec::new(), 210 | errors: Vec::new(), 211 | } 212 | } 213 | 214 | fn declare(&mut self, ident: &LoxIdent) { 215 | if let Some(top) = self.scopes.last_mut() { 216 | let entry = top.entry(ident.name.clone()); 217 | match entry { 218 | Entry::Vacant(entry) => { 219 | entry.insert(BindingState::Declared); 220 | } 221 | Entry::Occupied(_) => { 222 | self.error(ident.span, "Can't shadow a identifier in the same scope") 223 | } 224 | } 225 | } 226 | } 227 | 228 | fn define(&mut self, ident: &LoxIdent) { 229 | if let Some(top) = self.scopes.last_mut() { 230 | match top.get_mut(&ident.name) { 231 | Some(binding) => *binding = BindingState::Initialized, 232 | None => { 233 | self.error( 234 | ident.span, 235 | format!("Binding `{}` is not defined", ident.name), 236 | ); 237 | } 238 | } 239 | } 240 | } 241 | 242 | fn initialize(&mut self, ident: impl Into) { 243 | self.scopes 244 | .last_mut() 245 | .unwrap() 246 | .insert(ident.into(), BindingState::Initialized); 247 | } 248 | 249 | fn query(&mut self, ident: &LoxIdent, expected: BindingState) -> bool { 250 | self.scopes.last().and_then(|scope| scope.get(&ident.name)) == Some(&expected) 251 | } 252 | 253 | fn resolve_binding(&mut self, ident: &LoxIdent) { 254 | for (depth, scope) in self.scopes.iter().rev().enumerate() { 255 | if scope.contains_key(&ident.name) { 256 | self.interpreter.resolve_local(ident, depth); 257 | return; 258 | } 259 | } 260 | } 261 | 262 | fn resolve_function(&mut self, decl: &stmt::FunDecl, state: FunctionState) { 263 | let old_function_state = mem::replace(&mut self.state.function, state); 264 | 265 | self.scoped(|this| { 266 | for param in &decl.params { 267 | this.declare(param); 268 | this.define(param); 269 | } 270 | this.resolve_stmts(&decl.body); 271 | }); 272 | 273 | self.state.function = old_function_state; 274 | } 275 | 276 | /// One should ideally use `scoped`. Callers of `begin_scope` must also call `end_scope`. 277 | #[inline] 278 | fn begin_scope(&mut self) { 279 | self.scopes.push(HashMap::new()); 280 | } 281 | 282 | #[inline] 283 | fn end_scope(&mut self) { 284 | self.scopes.pop(); 285 | } 286 | 287 | fn scoped(&mut self, inner: I) 288 | where 289 | I: FnOnce(&mut Self), 290 | { 291 | self.begin_scope(); 292 | let res = inner(self); 293 | self.end_scope(); 294 | res 295 | } 296 | 297 | fn error(&mut self, span: Span, message: impl Into) { 298 | let message = message.into(); 299 | self.errors.push(ResolveError { span, message }); 300 | } 301 | } 302 | 303 | #[derive(Debug, Default)] 304 | struct ResolverState { 305 | function: FunctionState, 306 | class: ClassState, 307 | } 308 | 309 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 310 | enum FunctionState { 311 | None, 312 | Init, // Class init 313 | Method, // Class method 314 | Function, 315 | } 316 | 317 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 318 | enum ClassState { 319 | None, 320 | Class, 321 | SubClass, 322 | } 323 | 324 | macro_rules! impl_default_for_state { 325 | ($($name:ident),+) => { 326 | $( 327 | impl Default for $name { 328 | fn default() -> Self { 329 | Self::None 330 | } 331 | } 332 | )+ 333 | } 334 | } 335 | 336 | impl_default_for_state!(FunctionState, ClassState); 337 | 338 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 339 | enum BindingState { 340 | Declared, 341 | Initialized, 342 | } 343 | 344 | #[derive(Debug)] 345 | pub struct ResolveError { 346 | pub message: String, 347 | pub span: Span, 348 | } 349 | -------------------------------------------------------------------------------- /tree-lox/src/interpreter.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, mem, rc::Rc}; 2 | 3 | use crate::{ 4 | ast::{ 5 | expr::{self, Expr}, 6 | stmt::{self, Stmt}, 7 | }, 8 | data::{LoxClass, LoxFunction, LoxIdent, LoxIdentId, LoxInstance, LoxValue, NativeFunction}, 9 | interpreter::{control_flow::ControlFlow, environment::Environment, error::RuntimeError}, 10 | span::Span, 11 | token::TokenKind, 12 | }; 13 | 14 | pub mod control_flow; 15 | pub mod environment; 16 | pub mod error; 17 | 18 | #[derive(Debug)] 19 | pub struct Interpreter { 20 | locals: HashMap, 21 | globals: Environment, 22 | env: Environment, 23 | } 24 | 25 | // The interpreter implementation. 26 | impl Interpreter { 27 | // Note that `CFResult` must not be exposed to the interpreter caller. 28 | // It is an implementation detail. 29 | pub fn interpret(&mut self, stmts: &[Stmt]) -> Result<(), RuntimeError> { 30 | match self.eval_stmts(stmts) { 31 | Ok(()) => Ok(()), 32 | Err(ControlFlow::Err(err)) => Err(err), 33 | Err(ControlFlow::Return(_)) => unreachable!(), 34 | } 35 | } 36 | 37 | // 38 | // Statements 39 | // 40 | 41 | fn eval_stmts(&mut self, stmts: &[Stmt]) -> CFResult<()> { 42 | for stmt in stmts { 43 | self.eval_stmt(stmt)?; 44 | } 45 | Ok(()) 46 | } 47 | 48 | fn eval_stmt(&mut self, stmt: &Stmt) -> CFResult<()> { 49 | use Stmt::*; 50 | match &stmt { 51 | VarDecl(var) => self.eval_var_stmt(var), 52 | ClassDecl(class) => self.eval_class_stmt(class), 53 | FunDecl(fun) => self.eval_fun_stmt(fun), 54 | If(if_stmt) => self.eval_if_stmt(if_stmt), 55 | While(while_stmt) => self.eval_while_stmt(while_stmt), 56 | Return(return_stmt) => self.eval_return_stmt(return_stmt), 57 | Print(print) => self.eval_print_stmt(print), 58 | Block(block) => self.eval_block(&block.stmts, Environment::new_enclosing(&self.env)), 59 | Expr(expr) => self.eval_expr(&expr.expr).map(drop), 60 | Dummy(_) => unreachable!(), 61 | } 62 | } 63 | 64 | fn eval_var_stmt(&mut self, var: &stmt::VarDecl) -> CFResult<()> { 65 | let value = match &var.init { 66 | Some(expr) => self.eval_expr(expr)?, 67 | None => LoxValue::Nil, 68 | }; 69 | self.env.define(var.name.clone(), value); 70 | Ok(()) 71 | } 72 | 73 | fn eval_class_stmt(&mut self, class: &stmt::ClassDecl) -> CFResult<()> { 74 | let super_class = class 75 | .super_name 76 | .as_ref() 77 | .map(|name| { 78 | let maybe_class = self.lookup_variable(name)?; 79 | if let LoxValue::Class(class) = maybe_class { 80 | Ok(class) 81 | } else { 82 | Err(ControlFlow::from(RuntimeError::UnsupportedType { 83 | message: "Superclass must be a class".into(), 84 | span: name.span, 85 | })) 86 | } 87 | }) 88 | .transpose()?; 89 | 90 | if let Some(super_class) = super_class.clone() { 91 | self.env = Environment::new_enclosing(&self.env); 92 | self.env.define("super", LoxValue::Class(super_class)); 93 | } 94 | 95 | let methods = class 96 | .methods 97 | .iter() 98 | .cloned() 99 | .map(|decl| { 100 | ( 101 | decl.name.name.clone(), 102 | Rc::new(LoxFunction { 103 | is_class_init: decl.name.name == "init", 104 | decl: Rc::new(decl), 105 | closure: self.env.clone(), 106 | }), 107 | ) 108 | }) 109 | .collect(); 110 | 111 | if super_class.is_some() { 112 | self.env = self.env.enclosed().unwrap(); 113 | } 114 | 115 | self.env.define( 116 | class.name.clone(), 117 | LoxValue::Class(Rc::new(LoxClass { 118 | name: class.name.clone(), 119 | methods, 120 | super_class, 121 | })), 122 | ); 123 | Ok(()) 124 | } 125 | 126 | fn eval_fun_stmt(&mut self, fun: &stmt::FunDecl) -> CFResult<()> { 127 | self.env.define( 128 | fun.name.clone(), 129 | LoxValue::Function(Rc::new(LoxFunction { 130 | decl: Rc::new(fun.clone()), 131 | closure: self.env.clone(), 132 | is_class_init: false, 133 | })), 134 | ); 135 | Ok(()) 136 | } 137 | 138 | fn eval_if_stmt(&mut self, if_stmt: &stmt::If) -> CFResult<()> { 139 | let cond_value = self.eval_expr(&if_stmt.cond)?; 140 | if lox_is_truthy(&cond_value) { 141 | self.eval_stmt(&if_stmt.then_branch)?; 142 | } else if let Some(else_branch) = &if_stmt.else_branch { 143 | self.eval_stmt(else_branch)?; 144 | } 145 | Ok(()) 146 | } 147 | 148 | fn eval_while_stmt(&mut self, while_stmt: &stmt::While) -> CFResult<()> { 149 | while lox_is_truthy(&self.eval_expr(&while_stmt.cond)?) { 150 | self.eval_stmt(&while_stmt.body)?; 151 | } 152 | Ok(()) 153 | } 154 | 155 | fn eval_return_stmt(&mut self, return_stmt: &stmt::Return) -> CFResult<()> { 156 | let value = return_stmt 157 | .value 158 | .as_ref() 159 | .map(|expr| self.eval_expr(expr)) 160 | .transpose()? 161 | .unwrap_or(LoxValue::Nil); 162 | Err(ControlFlow::Return(value)) 163 | } 164 | 165 | fn eval_print_stmt(&mut self, print: &stmt::Print) -> CFResult<()> { 166 | let val = self.eval_expr(&print.expr)?; 167 | match print.debug { 168 | true => println!("{:?}", val), 169 | false => println!("{}", val), 170 | } 171 | Ok(()) 172 | } 173 | 174 | pub(crate) fn eval_block(&mut self, stmts: &[Stmt], new_env: Environment) -> CFResult<()> { 175 | let old_env = mem::replace(&mut self.env, new_env); 176 | let result = self.eval_stmts(stmts); 177 | self.env = old_env; 178 | result 179 | } 180 | 181 | // 182 | // Expressions 183 | // 184 | 185 | fn eval_expr(&mut self, expr: &Expr) -> CFResult { 186 | use Expr::*; 187 | match &expr { 188 | Lit(lit) => self.eval_lit_expr(lit), 189 | This(this) => self.lookup_variable(&this.name), 190 | Super(sup) => self.eval_super_expr(sup), 191 | Var(var) => self.lookup_variable(&var.name), 192 | Group(group) => self.eval_group_expr(group), 193 | Get(get) => self.eval_get_expr(get), 194 | Set(set) => self.eval_set_expr(set), 195 | Call(call) => self.eval_call_expr(call), 196 | Unary(unary) => self.eval_unary_expr(unary), 197 | Binary(binary) => self.eval_binary_expr(binary), 198 | Logical(logical) => self.eval_logical_expr(logical), 199 | Assignment(assignment) => self.eval_assignment_expr(assignment), 200 | } 201 | } 202 | 203 | fn eval_lit_expr(&mut self, lit: &expr::Lit) -> CFResult { 204 | Ok(lit.value.clone()) 205 | } 206 | 207 | fn eval_group_expr(&mut self, group: &expr::Group) -> CFResult { 208 | self.eval_expr(&group.expr) 209 | } 210 | 211 | fn eval_super_expr(&mut self, sup: &expr::Super) -> CFResult { 212 | // This unwrap should never fail due to resolver's semantic verifications. 213 | let distance = self.locals.get(&sup.super_ident.id).unwrap(); 214 | 215 | // This unwrap should never fail due to the fact that "super" should always be a `Class`. 216 | let super_class = self.env.read_at(*distance, "super").as_class().unwrap(); 217 | 218 | // The environment where "this" is defined is always bound immediately inside the 219 | // environment that defined "super" (the "this env" encloses the "super env"). 220 | // 221 | // This unwrap should never fail due to the fact that "this" should always be an `Object`. 222 | let this = self.env.read_at(distance - 1, "this").as_object().unwrap(); 223 | 224 | match super_class.get_method(&sup.method) { 225 | Some(method) => Ok(LoxValue::Function(method.bind(&this))), 226 | None => Err(ControlFlow::from(RuntimeError::UndefinedProperty { 227 | ident: sup.method.clone(), 228 | })), 229 | } 230 | } 231 | 232 | fn eval_get_expr(&mut self, get: &expr::Get) -> CFResult { 233 | let maybe_object = self.eval_expr(&get.object)?; 234 | let instance = Self::ensure_object(maybe_object, get.name.span)?; 235 | Ok(instance.get(&get.name)?) 236 | } 237 | 238 | fn eval_set_expr(&mut self, set: &expr::Set) -> CFResult { 239 | let maybe_object = self.eval_expr(&set.object)?; 240 | let instance = Self::ensure_object(maybe_object, set.name.span)?; 241 | let value = self.eval_expr(&set.value)?; 242 | instance.set(&set.name, value.clone()); 243 | Ok(value) 244 | } 245 | 246 | fn eval_call_expr(&mut self, call: &expr::Call) -> CFResult { 247 | use LoxValue::*; 248 | let callee = self.eval_expr(&call.callee)?; 249 | let args = call 250 | .args 251 | .iter() 252 | .map(|expr| self.eval_expr(expr)) 253 | .collect::, _>>()?; 254 | 255 | let callable = match callee { 256 | Function(callable) => callable, 257 | Class(class) => class, 258 | _ => { 259 | return Err(ControlFlow::from(RuntimeError::UnsupportedType { 260 | message: format!( 261 | "Type `{}` is not callable, can only call functions and classes", 262 | callee.type_name() 263 | ), 264 | span: call.span, 265 | })) 266 | } 267 | }; 268 | 269 | if callable.arity() != args.len() { 270 | return Err(ControlFlow::from(RuntimeError::UnsupportedType { 271 | message: format!( 272 | "Expected {} arguments, but got {}", 273 | callable.arity(), 274 | args.len() 275 | ), 276 | span: call.span, 277 | })); 278 | } 279 | 280 | callable.call(self, &args) 281 | } 282 | 283 | fn eval_unary_expr(&mut self, unary: &expr::Unary) -> CFResult { 284 | let operand = self.eval_expr(&unary.operand)?; 285 | match &unary.operator.kind { 286 | TokenKind::Minus => match operand { 287 | LoxValue::Number(number) => Ok(LoxValue::Number(-number)), 288 | unexpected => Err(RuntimeError::UnsupportedType { 289 | message: format!( 290 | "Bad type for unary `-` operator: `{}`", 291 | unexpected.type_name() 292 | ), 293 | span: unary.operator.span, 294 | } 295 | .into()), 296 | }, 297 | TokenKind::Bang => Ok(LoxValue::Boolean(!lox_is_truthy(&operand))), 298 | TokenKind::Show => Ok(LoxValue::String(operand.to_string())), 299 | TokenKind::Typeof => Ok(LoxValue::String(operand.type_name().into())), 300 | unexpected => unreachable!("Invalid unary operator ({:?}).", unexpected), 301 | } 302 | } 303 | 304 | fn eval_binary_expr(&mut self, binary: &expr::Binary) -> CFResult { 305 | use LoxValue::*; 306 | let left = self.eval_expr(&binary.left)?; 307 | let right = self.eval_expr(&binary.right)?; 308 | match &binary.operator.kind { 309 | TokenKind::Plus => match (left, right) { 310 | (Number(left), Number(right)) => Ok(Number(left + right)), 311 | (String(left), String(right)) => Ok(String(left + &right)), 312 | (left, right) => Err(RuntimeError::UnsupportedType { 313 | message: format!( 314 | "Binary `+` operator can only operate over two numbers or two strings. \ 315 | Got types `{}` and `{}`", 316 | left.type_name(), 317 | right.type_name() 318 | ), 319 | span: binary.operator.span, 320 | } 321 | .into()), 322 | }, 323 | 324 | TokenKind::Minus => bin_number_operator!(left - right, binary.operator), 325 | TokenKind::Star => bin_number_operator!(left * right, binary.operator), 326 | TokenKind::Slash => { 327 | if let Number(right_num) = right { 328 | if right_num == 0.0 { 329 | return Err(RuntimeError::ZeroDivision { 330 | span: binary.operator.span, 331 | } 332 | .into()); 333 | } 334 | } 335 | bin_number_operator!(left / right, binary.operator) 336 | } 337 | 338 | TokenKind::EqualEqual => Ok(LoxValue::Boolean(lox_is_equal(&left, &right))), 339 | TokenKind::BangEqual => Ok(LoxValue::Boolean(!lox_is_equal(&left, &right))), 340 | 341 | TokenKind::Greater => bin_comparison_operator!(left > right, binary.operator), 342 | TokenKind::GreaterEqual => bin_comparison_operator!(left >= right, binary.operator), 343 | TokenKind::Less => bin_comparison_operator!(left < right, binary.operator), 344 | TokenKind::LessEqual => bin_comparison_operator!(left <= right, binary.operator), 345 | 346 | unexpected => unreachable!("Invalid binary operator ({:?}).", unexpected), 347 | } 348 | } 349 | 350 | fn eval_logical_expr(&mut self, logical: &expr::Logical) -> CFResult { 351 | let left = self.eval_expr(&logical.left)?; 352 | match &logical.operator.kind { 353 | TokenKind::And if !lox_is_truthy(&left) => Ok(left), 354 | TokenKind::Or if lox_is_truthy(&left) => Ok(left), 355 | _ => self.eval_expr(&logical.right), 356 | } 357 | } 358 | 359 | fn eval_assignment_expr(&mut self, assignment: &expr::Assignment) -> CFResult { 360 | let value = self.eval_expr(&assignment.value)?; 361 | if let Some(distance) = self.locals.get(&assignment.name.id) { 362 | Ok(self.env.assign_at(*distance, &assignment.name, value)) 363 | } else { 364 | Ok(self.globals.assign(&assignment.name, value)?) 365 | } 366 | } 367 | } 368 | 369 | impl Interpreter { 370 | pub fn new() -> Self { 371 | let mut globals = Environment::new(); 372 | 373 | def_native!( 374 | globals.clock / 0, 375 | fn clock(_: &[LoxValue]) -> CFResult { 376 | use std::time::{SystemTime, UNIX_EPOCH}; 377 | let start = SystemTime::now(); 378 | let since_the_epoch = start.duration_since(UNIX_EPOCH).unwrap().as_secs_f64(); 379 | Ok(LoxValue::Number(since_the_epoch)) 380 | } 381 | ); 382 | 383 | Self { 384 | env: globals.clone(), 385 | globals, 386 | locals: HashMap::new(), 387 | } 388 | } 389 | 390 | pub fn resolve_local(&mut self, ident: &LoxIdent, depth: usize) { 391 | self.locals.insert(ident.id, depth); 392 | } 393 | 394 | fn lookup_variable(&self, ident: &LoxIdent) -> CFResult { 395 | if let Some(distance) = self.locals.get(&ident.id) { 396 | Ok(self.env.read_at(*distance, ident)) 397 | } else { 398 | Ok(self.globals.read(ident)?) 399 | } 400 | } 401 | 402 | fn ensure_object(value: LoxValue, error_span: Span) -> CFResult> { 403 | if let LoxValue::Object(instance) = value { 404 | Ok(instance) 405 | } else { 406 | Err(RuntimeError::UnsupportedType { 407 | message: "Only objects (instances of some class) have properties".into(), 408 | span: error_span, 409 | } 410 | .into()) 411 | } 412 | } 413 | } 414 | 415 | /// Control flow result 416 | pub type CFResult = Result>; 417 | 418 | /// Tries to convert a `LoxValue` to a Rust bool. 419 | /// * Truthy lox values: all numbers (incl. 0), all strings (incl. "") and `true`. 420 | /// * Falsy lox values: `false` and `nil`. 421 | fn lox_is_truthy(value: &LoxValue) -> bool { 422 | use LoxValue::*; 423 | match value { 424 | Boolean(inner) => *inner, 425 | Function(_) | Class(_) | Object(_) | Number(_) | String(_) => true, 426 | Nil => false, 427 | } 428 | } 429 | 430 | /// Checks if two `LoxValue`s are equal. No type coercion is performed so both types must be equal. 431 | fn lox_is_equal(a: &LoxValue, b: &LoxValue) -> bool { 432 | use LoxValue::*; 433 | match (a, b) { 434 | (Function(a), Function(b)) => 435 | { 436 | #[allow(clippy::vtable_address_comparisons)] 437 | Rc::ptr_eq(a, b) 438 | } 439 | (Class(a), Class(b)) => Rc::ptr_eq(a, b), 440 | (Object(a), Object(b)) => Rc::ptr_eq(a, b), 441 | (Boolean(a), Boolean(b)) => a == b, 442 | (Number(a), Number(b)) => a == b, 443 | (String(a), String(b)) => a == b, 444 | (Nil, Nil) => true, 445 | // This is not exhaustive, pay close attention if a new `LoxValue` variant is introduced. 446 | _ => false, 447 | } 448 | } 449 | 450 | macro_rules! bin_number_operator { 451 | ( $left:tt $op:tt $right:tt, $op_token:expr ) => { 452 | match ($left, $right) { 453 | (Number(left), Number(right)) => Ok(Number(left $op right)), 454 | (left, right) => Err(RuntimeError::UnsupportedType { 455 | message: format!( 456 | "Binary `{}` operator can only operate over two numbers. \ 457 | Got types `{}` and `{}`", 458 | stringify!($op), 459 | left.type_name(), 460 | right.type_name() 461 | ), 462 | span: $op_token.span 463 | } 464 | .into()), 465 | } 466 | }; 467 | } 468 | use bin_number_operator; 469 | 470 | macro_rules! bin_comparison_operator { 471 | ( $left:tt $op:tt $right:tt, $op_token:expr ) => { 472 | match ($left, $right) { 473 | (Number(left), Number(right)) => Ok(LoxValue::Boolean(left $op right)), 474 | (String(left), String(right)) => Ok(LoxValue::Boolean(left $op right)), 475 | (left, right) => Err(RuntimeError::UnsupportedType { 476 | message: format!( 477 | "Binary `{}` operator can only compare two numbers or two strings. \ 478 | Got types `{}` and `{}`", 479 | stringify!($op), 480 | left.type_name(), 481 | right.type_name() 482 | ), 483 | span: $op_token.span, 484 | } 485 | .into()), 486 | } 487 | }; 488 | } 489 | use bin_comparison_operator; 490 | 491 | macro_rules! def_native { 492 | ($globals:ident . $name:ident / $arity:expr , $fn:item) => { 493 | $fn 494 | $globals.define( 495 | LoxIdent::new(Span::new(0, 0), stringify!($name)), 496 | LoxValue::Function(Rc::new(NativeFunction { 497 | name: stringify!($name), 498 | fn_ptr: $name, 499 | arity: $arity 500 | })), 501 | ); 502 | }; 503 | } 504 | use def_native; 505 | -------------------------------------------------------------------------------- /tree-lox/src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Borrow, mem}; 2 | 3 | use crate::{ 4 | ast::{ 5 | expr::{self, Expr}, 6 | stmt::{self, Stmt}, 7 | }, 8 | data::{LoxIdent, LoxValue}, 9 | parser::{error::ParseError, scanner::Scanner, state::ParserOptions}, 10 | span::Span, 11 | token::{Token, TokenKind}, 12 | }; 13 | 14 | pub mod error; 15 | pub mod scanner; 16 | pub mod state; 17 | 18 | /// Parse result 19 | type PResult = Result; 20 | 21 | pub type ParserOutcome = (Vec, Vec); 22 | 23 | pub struct Parser<'src> { 24 | scanner: Scanner<'src>, 25 | current_token: Token, 26 | prev_token: Token, 27 | diagnostics: Vec, 28 | pub options: ParserOptions, 29 | } 30 | 31 | // The parser implementation. 32 | // 33 | // # Grammar: 34 | // 35 | // ----------------------------------------------------------------------------- 36 | // 37 | // program ::= decl* EOF ; 38 | // 39 | // decl ::= var_decl 40 | // | fun_decl 41 | // | stmt ; 42 | // 43 | // var_decl ::= "var" IDENTIFIER ( "=" expr )? ";" ; 44 | // class_decl ::= "class" IDENTIFIER ( "<" IDENTIFIER )? "{" fn* "}" ; 45 | // fun_decl ::= "fun" fn ; 46 | // 47 | // fn ::= IDENTIFIER "(" params? ")" block_stmt ; 48 | // params ::= IDENTIFIER ( "," IDENTIFIER )* ; 49 | // 50 | // stmt ::= if_stmt 51 | // | for_stmt 52 | // | while_stmt 53 | // | return_stmt 54 | // | print_stmt 55 | // | block_stmt 56 | // | expr_stmt ; 57 | // 58 | // if_stmt ::= "if" "(" expr ")" statement ( "else" statement )? ; 59 | // for_stmt ::= "for" 60 | // "(" ( var_decl | expr_stmt | ";" ) expr? ";" expr? ")" 61 | // statement ; 62 | // while_stmt ::= "while" "(" expr ")" statement ; 63 | // return_stmt ::= "return" expr? ";" ; 64 | // print_stmt ::= "print" expr ";" ; 65 | // block_stmt ::= "{" declaration* "}" ; 66 | // expr_stmt ::= expr ";" ; 67 | // 68 | // expr ::= assignment ; 69 | // assignment ::= ( call_or_get "." )? IDENTIFIER "=" assignment 70 | // | logic_or ; 71 | // logic_or ::= logic_and ( "or" logic_and )* ; 72 | // logic_and ::= equality ( "and" equality )* ; 73 | // equality ::= comparison ( ( "==" | "!=" ) comparison )* ; 74 | // comparison ::= term ( ( ">" | ">=" | "<" | "<=" ) term )* ; 75 | // term ::= factor ( ( "+" | "-" ) factor )* ; 76 | // factor ::= unary ( ( "*" | "/" ) unary )* ; 77 | // unary ::= ( "show" | "typeof" | "!" | "-" ) unary 78 | // | call_or_get ; 79 | // call_or_get ::= primary ( "(" arguments? ")" | "." IDENTIFIER )* ; 80 | // arguments ::= expr ( "," expr )* ; 81 | // primary ::= IDENTIFIER 82 | // | NUMBER | STRING 83 | // | "true" | "false" 84 | // | "nil" 85 | // | "(" expr ")" 86 | // | "super" "." IDENTIFIER ; 87 | // 88 | // ----------------------------------------------------------------------------- 89 | // 90 | // Each production has a correspondent method in the following implementation. 91 | impl Parser<'_> { 92 | pub fn parse(mut self) -> ParserOutcome { 93 | (self.parse_program(), self.diagnostics) 94 | } 95 | 96 | fn parse_program(&mut self) -> Vec { 97 | let mut stmts = Vec::new(); 98 | while !self.is_at_end() { 99 | stmts.push(self.parse_decl()); 100 | } 101 | stmts 102 | } 103 | 104 | // 105 | // Declarations 106 | // 107 | 108 | fn parse_decl(&mut self) -> Stmt { 109 | use TokenKind::*; 110 | let result = match self.current_token.kind { 111 | Var => self.parse_var_decl(), 112 | Class => self.parse_class_decl(), 113 | Fun => self.parse_fun_decl(), 114 | _ => self.parse_stmt(), 115 | }; 116 | 117 | match result { 118 | Ok(stmt) => stmt, 119 | Err(error) => { 120 | self.diagnostics.push(error); 121 | self.synchronize(); 122 | let lo = self.current_token.span.lo; 123 | Stmt::from(stmt::Dummy { 124 | span: Span::new(lo, lo), 125 | }) 126 | } 127 | } 128 | } 129 | 130 | fn parse_var_decl(&mut self) -> PResult { 131 | use TokenKind::*; 132 | let var_span = self.consume(Var, S_MUST)?.span; 133 | 134 | let name = self.consume_ident("Expected variable name")?; 135 | let init = self.take(Equal).then(|| self.parse_expr()).transpose()?; 136 | 137 | let semicolon_span = self 138 | .consume(Semicolon, "Expected `;` after variable declaration")? 139 | .span; 140 | 141 | Ok(Stmt::from(stmt::VarDecl { 142 | span: var_span.to(semicolon_span), 143 | name, 144 | init, 145 | })) 146 | } 147 | 148 | fn parse_class_decl(&mut self) -> PResult { 149 | use TokenKind::*; 150 | let class_span = self.consume(Class, S_MUST)?.span; 151 | 152 | let name = self.consume_ident("Expected class name")?; 153 | 154 | let super_name = self 155 | .take(Less) 156 | .then(|| self.consume_ident("Expected superclass name")) 157 | .transpose()?; 158 | 159 | let (methods, class_body_span) = self.paired_spanned( 160 | LeftBrace, 161 | "Expected `{` before class body", 162 | "Expected `}` after class body", 163 | |this| { 164 | let mut methods = Vec::new(); 165 | while !this.is(RightBrace) && !this.is_at_end() { 166 | methods.push(this.parse_fn_params_and_body("method", None)?); 167 | } 168 | Ok(methods) 169 | }, 170 | )?; 171 | 172 | Ok(Stmt::from(stmt::ClassDecl { 173 | span: class_span.to(class_body_span), 174 | name, 175 | super_name, 176 | methods, 177 | })) 178 | } 179 | 180 | fn parse_fun_decl(&mut self) -> PResult { 181 | use TokenKind::*; 182 | let fun_span = self.consume(Fun, S_MUST)?.span; 183 | let fun = self.parse_fn_params_and_body("function", Some(fun_span))?; 184 | Ok(Stmt::from(fun)) 185 | } 186 | 187 | fn parse_fn_params_and_body( 188 | &mut self, 189 | kind: &'static str, 190 | start_span: Option, 191 | ) -> PResult { 192 | use TokenKind::*; 193 | let name = self.consume_ident(format!("Expected {} name", kind))?; 194 | 195 | let params = self.paired( 196 | LeftParen, 197 | format!("Expected `(` after {} name", kind), 198 | format!("Expected `)` after {} parameter list", kind), 199 | |this| { 200 | let mut params = Vec::new(); 201 | if !this.is(RightParen) { 202 | loop { 203 | let param = this.consume_ident("Expected parameter name")?; 204 | params.push(param); 205 | if !this.take(Comma) { 206 | break; 207 | } 208 | } 209 | } 210 | Ok(params) 211 | }, 212 | )?; 213 | 214 | let (body, body_span) = self.parse_block()?; 215 | Ok(stmt::FunDecl { 216 | span: start_span.unwrap_or(name.span).to(body_span), 217 | name, 218 | params, 219 | body, 220 | }) 221 | } 222 | 223 | // 224 | // Statements 225 | // 226 | 227 | fn parse_stmt(&mut self) -> PResult { 228 | use TokenKind::*; 229 | match self.current_token.kind { 230 | If => self.parse_if_stmt(), 231 | For => self.parse_for_stmt(), 232 | While => self.parse_while_stmt(), 233 | Return => self.parse_return_stmt(), 234 | Print => self.parse_print_stmt(), 235 | LeftBrace => { 236 | let (stmts, span) = self.parse_block()?; 237 | Ok(Stmt::from(stmt::Block { span, stmts })) 238 | } 239 | _ => self.parse_expr_stmt(), 240 | } 241 | } 242 | 243 | fn parse_if_stmt(&mut self) -> PResult { 244 | use TokenKind::*; 245 | let if_token_span = self.consume(If, S_MUST)?.span; 246 | 247 | let cond = self.paired( 248 | LeftParen, 249 | "Expected `if` condition group opening", 250 | "Expected `if` condition group to be closed", 251 | |this| this.parse_expr(), 252 | )?; 253 | let then_branch = self.parse_stmt()?; 254 | let else_branch = self.take(Else).then(|| self.parse_stmt()).transpose()?; 255 | 256 | Ok(Stmt::from(stmt::If { 257 | span: if_token_span.to(else_branch 258 | .as_ref() 259 | .map(|it| it.span()) 260 | .unwrap_or_else(|| then_branch.span())), 261 | cond, 262 | then_branch: then_branch.into(), 263 | else_branch: else_branch.map(|it| it.into()), 264 | })) 265 | } 266 | 267 | // In this implementation, all `for` statements are translated to `while` statements by the 268 | // parser. Hence there is not even a `StmtKind::For` kind since it is a syntactic sugar. E.g.: 269 | // 270 | // ``` 271 | // for (var i = 1; i <= 10; i = i + 1) { print show i; } 272 | // ``` 273 | // 274 | // Is translated to: 275 | // 276 | // ``` 277 | // { 278 | // var i = 1; 279 | // while (i <= 10) { 280 | // { print show i; } 281 | // i = i + 1; 282 | // } 283 | // } 284 | // ``` 285 | fn parse_for_stmt(&mut self) -> PResult { 286 | use TokenKind::*; 287 | let for_token_span = self.consume(For, S_MUST)?.span; 288 | 289 | let (init, cond, incr) = self.paired( 290 | LeftParen, 291 | "Expected `for` clauses group opening", 292 | "Expected `for` clauses group to be closed", 293 | |this| { 294 | let init = match this.current_token.kind { 295 | Semicolon => { 296 | this.advance(); 297 | None 298 | } 299 | Var => Some(this.parse_var_decl()?), 300 | _ => Some(this.parse_expr_stmt()?), 301 | }; 302 | let cond = match this.current_token.kind { 303 | // If there is no condition in the for clauses, the parser creates a synthetic 304 | // `true` literal expression. This must be placed here to capture the curr span. 305 | Semicolon => { 306 | let lo = this.current_token.span.lo; 307 | Expr::from(expr::Lit { 308 | span: Span::new(lo, lo), 309 | value: LoxValue::Boolean(true), 310 | }) 311 | } 312 | _ => this.parse_expr()?, 313 | }; 314 | this.consume(Semicolon, "Expected `;` after `for` condition")?; 315 | let incr = match this.current_token.kind { 316 | RightParen => None, 317 | _ => Some(this.parse_expr()?), 318 | }; 319 | Ok((init, cond, incr)) 320 | }, 321 | )?; 322 | let mut body = self.parse_stmt()?; 323 | 324 | // Desugar `for` increment: 325 | if let Some(incr) = incr { 326 | body = Stmt::from(stmt::Block { 327 | span: body.span(), 328 | stmts: Vec::from([ 329 | body, 330 | Stmt::from(stmt::Expr { 331 | span: incr.span(), 332 | expr: incr, 333 | }), 334 | ]), 335 | }); 336 | } 337 | 338 | // Create the while: 339 | body = Stmt::from(stmt::While { 340 | span: for_token_span.to(body.span()), 341 | cond, 342 | body: body.into(), 343 | }); 344 | 345 | // Desugar `for` initializer: 346 | if let Some(init) = init { 347 | body = Stmt::from(stmt::Block { 348 | span: body.span(), 349 | stmts: Vec::from([init, body]), 350 | }); 351 | } 352 | 353 | Ok(body) 354 | } 355 | 356 | fn parse_while_stmt(&mut self) -> PResult { 357 | use TokenKind::*; 358 | let while_token_span = self.consume(While, S_MUST)?.span; 359 | 360 | let cond = self.paired( 361 | LeftParen, 362 | "Expected `while` condition group opening", 363 | "Expected `while` condition group to be closed", 364 | |this| this.parse_expr(), 365 | )?; 366 | let body = self.parse_stmt()?; 367 | 368 | Ok(Stmt::from(stmt::While { 369 | span: while_token_span.to(body.span()), 370 | cond, 371 | body: body.into(), 372 | })) 373 | } 374 | 375 | fn parse_return_stmt(&mut self) -> PResult { 376 | let return_span = self.consume(TokenKind::Return, S_MUST)?.span; 377 | 378 | let value = (!self.is(TokenKind::Semicolon)) 379 | .then(|| self.parse_expr()) 380 | .transpose()?; 381 | 382 | let semicolon_span = self 383 | .consume(TokenKind::Semicolon, "Expected `;` after return")? 384 | .span; 385 | 386 | Ok(Stmt::from(stmt::Return { 387 | span: return_span.to(semicolon_span), 388 | value, 389 | return_span, 390 | })) 391 | } 392 | 393 | fn parse_print_stmt(&mut self) -> PResult { 394 | let print_token_span = self.consume(TokenKind::Print, S_MUST)?.span; 395 | 396 | let expr = self.parse_expr()?; 397 | let semicolon_span = self 398 | .consume(TokenKind::Semicolon, "Expected `;` after value")? 399 | .span; 400 | 401 | Ok(Stmt::from(stmt::Print { 402 | span: print_token_span.to(semicolon_span), 403 | expr, 404 | debug: false, 405 | })) 406 | } 407 | 408 | fn parse_block(&mut self) -> PResult<(Vec, Span)> { 409 | self.paired_spanned( 410 | TokenKind::LeftBrace, 411 | "Expected block to be opened", 412 | "Expected block to be closed", 413 | |this| { 414 | let mut stmts = Vec::new(); 415 | while !this.is(TokenKind::RightBrace) && !this.is_at_end() { 416 | stmts.push(this.parse_decl()); 417 | } 418 | Ok(stmts) 419 | }, 420 | ) 421 | } 422 | 423 | fn parse_expr_stmt(&mut self) -> PResult { 424 | let expr = self.parse_expr()?; 425 | 426 | // If the parser is running in the REPL mode and the next token is of kind `Eof`, it will 427 | // emit a `Print` statement in order to show the given expression's value. 428 | if self.options.repl_mode && self.is_at_end() { 429 | return Ok(Stmt::from(stmt::Print { 430 | span: expr.span(), 431 | expr, 432 | debug: true, 433 | })); 434 | } 435 | 436 | let semicolon_span = self 437 | .consume(TokenKind::Semicolon, "Expected `;` after expression")? 438 | .span; 439 | 440 | Ok(Stmt::from(stmt::Expr { 441 | span: expr.span().to(semicolon_span), 442 | expr, 443 | })) 444 | } 445 | 446 | // 447 | // Expressions 448 | // 449 | 450 | fn parse_expr(&mut self) -> PResult { 451 | self.parse_assignment() 452 | } 453 | 454 | fn parse_assignment(&mut self) -> PResult { 455 | // The parser does not yet know if `left` should be used as an expression (i.e. an rvalue) 456 | // or as an "assignment target" (i.e. an lvalue). 457 | let left = self.parse_or()?; 458 | 459 | if self.take(TokenKind::Equal) { 460 | // Since assignments are right associative, we use right recursion to parse its value. 461 | // The right-most assignment value should be evaluated first (down in the parse tree), 462 | // so it should be parsed last. This semantic can be coded with this kind of recursion. 463 | let value = self.parse_assignment()?; 464 | let span = left.span().to(value.span()); 465 | 466 | // Now the parser knows that `left` must be an lvalue, otherwise it is an error. 467 | match left { 468 | Expr::Var(expr::Var { name, .. }) => Ok(Expr::from(expr::Assignment { 469 | span, 470 | name, 471 | value: value.into(), 472 | })), 473 | Expr::Get(expr::Get { name, object, .. }) => Ok(Expr::from(expr::Set { 474 | span, 475 | object, 476 | name, 477 | value: value.into(), 478 | })), 479 | _ => Err(ParseError::Error { 480 | message: "Invalid assignment target".into(), 481 | span: left.span(), 482 | }), 483 | } 484 | } else { 485 | Ok(left) 486 | } 487 | } 488 | 489 | fn parse_or(&mut self) -> PResult { 490 | bin_expr!( 491 | self, 492 | parse_as = Logical, 493 | token_kinds = Or, 494 | next_production = parse_and 495 | ) 496 | } 497 | 498 | fn parse_and(&mut self) -> PResult { 499 | bin_expr!( 500 | self, 501 | parse_as = Logical, 502 | token_kinds = And, 503 | next_production = parse_equality 504 | ) 505 | } 506 | 507 | fn parse_equality(&mut self) -> PResult { 508 | bin_expr!( 509 | self, 510 | parse_as = Binary, 511 | token_kinds = EqualEqual | BangEqual, 512 | next_production = parse_comparison 513 | ) 514 | } 515 | 516 | fn parse_comparison(&mut self) -> PResult { 517 | bin_expr!( 518 | self, 519 | parse_as = Binary, 520 | token_kinds = Greater | GreaterEqual | Less | LessEqual, 521 | next_production = parse_term 522 | ) 523 | } 524 | 525 | fn parse_term(&mut self) -> PResult { 526 | bin_expr!( 527 | self, 528 | parse_as = Binary, 529 | token_kinds = Plus | Minus, 530 | next_production = parse_factor 531 | ) 532 | } 533 | 534 | fn parse_factor(&mut self) -> PResult { 535 | bin_expr!( 536 | self, 537 | parse_as = Binary, 538 | token_kinds = Star | Slash, 539 | next_production = parse_unary 540 | ) 541 | } 542 | 543 | fn parse_unary(&mut self) -> PResult { 544 | use TokenKind::*; 545 | if let Bang | Minus | Typeof | Show = self.current_token.kind { 546 | let operator = self.advance().clone(); 547 | let operand = self.parse_unary()?; 548 | return Ok(Expr::from(expr::Unary { 549 | span: operator.span.to(operand.span()), 550 | operator, 551 | operand: operand.into(), 552 | })); 553 | } 554 | self.parse_call_or_get() 555 | } 556 | 557 | fn parse_call_or_get(&mut self) -> PResult { 558 | use TokenKind::*; 559 | let mut expr = self.parse_primary()?; 560 | 561 | loop { 562 | expr = match self.current_token.kind { 563 | LeftParen => self.finish_call_parsing(expr)?, 564 | Dot => { 565 | self.advance(); // Consumes the `.` 566 | let name = self.consume_ident("Expect property name after `.`")?; 567 | Expr::from(expr::Get { 568 | span: expr.span().to(name.span), 569 | object: expr.into(), 570 | name, 571 | }) 572 | } 573 | _ => break, 574 | }; 575 | } 576 | 577 | Ok(expr) 578 | } 579 | 580 | fn finish_call_parsing(&mut self, curr_expr: Expr) -> PResult { 581 | use TokenKind::*; 582 | let (args, call_span) = self.paired_spanned( 583 | LeftParen, 584 | S_MUST, 585 | "Expected `)` to close call syntax", 586 | |this| { 587 | let mut args = Vec::new(); 588 | if !this.is(RightParen) { 589 | loop { 590 | args.push(this.parse_expr()?); 591 | if !this.take(Comma) { 592 | break; 593 | } 594 | } 595 | } 596 | Ok(args) 597 | }, 598 | )?; 599 | 600 | if args.len() >= 255 { 601 | self.diagnostics.push(ParseError::Error { 602 | message: "Call can't have more than 255 arguments".into(), 603 | span: call_span, 604 | }) 605 | } 606 | 607 | Ok(Expr::from(expr::Call { 608 | span: curr_expr.span().to(call_span), 609 | callee: curr_expr.into(), 610 | args, 611 | })) 612 | } 613 | 614 | fn parse_primary(&mut self) -> PResult { 615 | use TokenKind::*; 616 | match &self.current_token.kind { 617 | String(_) | Number(_) | True | False | Nil => { 618 | let token = self.advance(); 619 | Ok(Expr::from(expr::Lit::from(token.clone()))) 620 | } 621 | Identifier(_) => { 622 | let name = self.consume_ident(S_MUST)?; 623 | Ok(Expr::from(expr::Var { 624 | span: name.span, 625 | name, 626 | })) 627 | } 628 | This => { 629 | let span = self.advance().span; 630 | Ok(Expr::from(expr::This { 631 | span, 632 | name: LoxIdent::new(span, "this"), 633 | })) 634 | } 635 | Super => { 636 | let super_span = self.advance().span; 637 | self.consume(Dot, "Expected `.` after `super`")?; 638 | let method = self.consume_ident("Expected superclass method name")?; 639 | Ok(Expr::from(expr::Super { 640 | span: super_span.to(method.span), 641 | super_ident: LoxIdent::new(super_span, "super"), 642 | method, 643 | })) 644 | } 645 | LeftParen => { 646 | let (expr, span) = self.paired_spanned( 647 | LeftParen, 648 | S_MUST, 649 | "Expected group to be closed", 650 | |this| this.parse_expr(), 651 | )?; 652 | Ok(Expr::from(expr::Group { 653 | span, 654 | expr: expr.into(), 655 | })) 656 | } 657 | _ => Err(self.unexpected("Expected any expression", None)), 658 | } 659 | } 660 | } 661 | 662 | // The parser helper methods. 663 | impl<'src> Parser<'src> { 664 | /// Creates a new parser. 665 | pub fn new(src: &'src str) -> Self { 666 | let mut parser = Self { 667 | scanner: Scanner::new(src), 668 | current_token: Token::dummy(), 669 | prev_token: Token::dummy(), 670 | diagnostics: Vec::new(), 671 | options: ParserOptions::default(), 672 | }; 673 | parser.advance(); // The first advancement. 674 | parser 675 | } 676 | 677 | /// Advances the parser and returns a reference to the `prev_token` field. 678 | fn advance(&mut self) -> &Token { 679 | let next = loop { 680 | let maybe_next = self.scanner.next().expect("Cannot advance past Eof."); 681 | // Report and ignore tokens with the `Error` kind: 682 | if let TokenKind::Error(error) = maybe_next.kind { 683 | self.diagnostics.push(ParseError::ScanError { 684 | error, 685 | span: maybe_next.span, 686 | }); 687 | continue; 688 | } 689 | // Handle other common ignored kinds: 690 | if let TokenKind::Comment(_) | TokenKind::Whitespace(_) = maybe_next.kind { 691 | continue; 692 | } 693 | break maybe_next; 694 | }; 695 | self.prev_token = mem::replace(&mut self.current_token, next); 696 | &self.prev_token 697 | } 698 | 699 | /// Checks if the current token matches the kind of the given one. 700 | #[inline] 701 | fn is(&mut self, expected: impl Borrow) -> bool { 702 | mem::discriminant(&self.current_token.kind) == mem::discriminant(expected.borrow()) 703 | } 704 | 705 | /// Checks if the current token matches the kind of the given one. In such case advances and 706 | /// returns true. Otherwise returns false. 707 | fn take(&mut self, expected: TokenKind) -> bool { 708 | let res = self.is(expected); 709 | if res { 710 | self.advance(); 711 | } 712 | res 713 | } 714 | 715 | /// Checks if the current token matches the kind of the given one. In such case advances and 716 | /// returns `Ok(_)` with the consumed token. Otherwise returns an expectation error with the 717 | /// provided message. 718 | fn consume(&mut self, expected: TokenKind, msg: impl Into) -> PResult<&Token> { 719 | if self.is(&expected) { 720 | Ok(self.advance()) 721 | } else { 722 | Err(self.unexpected(msg, Some(expected))) 723 | } 724 | } 725 | 726 | /// Checks if the current token is an identifier. In such case advances and returns `Ok(_)` with 727 | /// the parsed identifier. Otherwise returns an expectation error with the provided message. 728 | fn consume_ident(&mut self, msg: impl Into) -> PResult { 729 | let expected = TokenKind::Identifier("".into()); 730 | if self.is(&expected) { 731 | Ok(LoxIdent::from(self.advance().clone())) 732 | } else { 733 | Err(self.unexpected(msg, Some(expected))) 734 | } 735 | } 736 | 737 | /// Pair invariant. 738 | fn paired( 739 | &mut self, 740 | delim_start: TokenKind, 741 | delim_start_expectation: impl Into, 742 | delim_end_expectation: impl Into, 743 | inner: I, 744 | ) -> PResult 745 | where 746 | I: FnOnce(&mut Self) -> PResult, 747 | { 748 | self.paired_spanned( 749 | delim_start, 750 | delim_start_expectation, 751 | delim_end_expectation, 752 | inner, 753 | ) 754 | .map(|(ret, _)| ret) 755 | } 756 | 757 | /// Pair invariant (2), also returning the full span. 758 | fn paired_spanned( 759 | &mut self, 760 | delim_start: TokenKind, 761 | delim_start_expectation: impl Into, 762 | delim_end_expectation: impl Into, 763 | inner: I, 764 | ) -> PResult<(R, Span)> 765 | where 766 | I: FnOnce(&mut Self) -> PResult, 767 | { 768 | let start_span = self 769 | .consume(delim_start.clone(), delim_start_expectation)? 770 | .span; 771 | let ret = inner(self)?; 772 | let end_span = match self.consume(delim_start.get_pair(), delim_end_expectation) { 773 | Ok(token) => token.span, 774 | Err(error) => { 775 | return Err(error); 776 | } 777 | }; 778 | Ok((ret, start_span.to(end_span))) 779 | } 780 | 781 | /// Returns an `ParseError::UnexpectedToken`. 782 | #[inline(always)] 783 | fn unexpected(&self, message: impl Into, expected: Option) -> ParseError { 784 | ParseError::UnexpectedToken { 785 | message: message.into(), 786 | expected, 787 | offending: self.current_token.clone(), 788 | } 789 | } 790 | 791 | /// Synchronizes the parser state with the current token. 792 | /// A synchronization is needed in order to match the parser state to the current token. 793 | /// 794 | /// When an error is encountered in a `parse_*` method, a `ParseError` is returned. These kind 795 | /// of errors are forwarded using the `?` operator, which, in practice, unwinds the parser 796 | /// stack frame. Hence the question mark operator should not be used in synchronization points. 797 | /// Such synchronization points call this method. 798 | /// 799 | /// The synchronization process discards all tokens until it reaches a grammar rule which marks 800 | /// a synchronization point. 801 | /// 802 | /// In this implementation, synchronizations are manually performed in statement boundaries: 803 | /// * If the previous token is a semicolon, the parser is *probably* (exceptions exists, such 804 | /// as a semicolon in a for loop) starting a new statement. 805 | /// * If the next token marks the start of a new statement. 806 | /// 807 | /// Before synchronize one must not forget to emit the raised parse error. 808 | fn synchronize(&mut self) { 809 | use TokenKind::*; 810 | while !self.is_at_end() { 811 | match &self.current_token.kind { 812 | Semicolon => { 813 | self.advance(); 814 | return; 815 | } 816 | Class | For | Fun | If | Print | Return | Var | While => { 817 | return; 818 | } 819 | _ => self.advance(), 820 | }; 821 | } 822 | } 823 | 824 | /// Checks if the parser has finished. 825 | #[inline] 826 | fn is_at_end(&self) -> bool { 827 | self.current_token.kind == TokenKind::Eof 828 | } 829 | } 830 | 831 | /// (String Must) Indicates the parser to emit a parser error (i.e. the parser is bugged) message. 832 | const S_MUST: &str = "Parser bug. Unexpected token"; 833 | 834 | /// Parses a binary expression. 835 | macro_rules! bin_expr { 836 | ($self:expr, parse_as = $ast_kind:ident, token_kinds = $( $kind:ident )|+, next_production = $next:ident) => {{ 837 | let mut expr = $self.$next()?; 838 | while let $( TokenKind::$kind )|+ = $self.current_token.kind { 839 | let operator = $self.advance().clone(); 840 | let right = $self.$next()?; 841 | expr = Expr::from(expr::$ast_kind { 842 | span: expr.span().to(right.span()), 843 | left: expr.into(), 844 | operator, 845 | right: right.into(), 846 | }); 847 | } 848 | Ok(expr) 849 | }}; 850 | } 851 | use bin_expr; 852 | --------------------------------------------------------------------------------