diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/compiler.rs | 26 | ||||
| -rw-r--r-- | src/lib.rs | 198 | ||||
| -rw-r--r-- | src/tokenizer.rs | 8 | ||||
| -rw-r--r-- | src/vm.rs | 11 |
4 files changed, 210 insertions, 33 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index 2caa4e9..d12ee4b 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -161,6 +161,7 @@ macro_rules! push_scope { }; } +/// Helper function for adding operations to the given block. fn add_op(compiler: &Compiler, block: &mut Block, op: Op) -> usize { block.add(op, compiler.line()) } @@ -207,6 +208,7 @@ impl Compiler { &mut self.frame_mut().stack } + /// Used to recover from a panic so the rest of the code can be parsed. fn clear_panic(&mut self) { if self.panic { self.panic = false; @@ -255,6 +257,15 @@ impl Compiler { t } + /// The line of the current token. + fn line(&self) -> usize { + if self.curr < self.tokens.len() { + self.tokens[self.curr].1 + } else { + self.tokens[self.tokens.len() - 1].1 + } + } + fn precedence(&self, token: Token) -> Prec { match token { Token::Star | Token::Slash => Prec::Factor, @@ -277,14 +288,6 @@ impl Compiler { } } - fn line(&self) -> usize { - if self.curr < self.tokens.len() { - self.tokens[self.curr].1 - } else { - self.tokens[self.tokens.len() - 1].1 - } - } - fn prefix(&mut self, token: Token, block: &mut Block) -> bool { match token { Token::Identifier(_) => self.variable_expression(block), @@ -303,7 +306,6 @@ impl Compiler { return true; } - fn infix(&mut self, token: Token, block: &mut Block) -> bool { match token { Token::Minus @@ -428,6 +430,7 @@ impl Compiler { block.add_from(op, self.line()); } + /// Entry point for all expression parsing. fn expression(&mut self, block: &mut Block) { match self.peek_four() { (Token::Fn, ..) => self.function(block), @@ -514,6 +517,7 @@ impl Compiler { add_op(self, block, Op::Call(arity)); } + // TODO(ed): de-complexify fn function(&mut self, block: &mut Block) { expect!(self, Token::Fn, "Expected 'fn' at start of function."); @@ -807,6 +811,7 @@ impl Compiler { fn parse_type(&mut self) -> Result<Type, ()> { match self.peek() { + Token::Fn => { self.eat(); let mut params = Vec::new(); @@ -839,6 +844,7 @@ impl Compiler { let f = Type::Function(params, Box::new(return_type)); Ok(f) } + Token::LeftParen => { self.eat(); let mut element = Vec::new(); @@ -855,6 +861,7 @@ impl Compiler { } } } + Token::Identifier(x) => { self.eat(); match x.as_str() { @@ -866,7 +873,6 @@ impl Compiler { } } _ => Err(()), - } } @@ -281,69 +281,232 @@ impl Blob { } } +/// +/// Ops are opperations that the virtual +/// machine carries out when running the +/// "byte-code". +/// #[derive(Debug, Clone)] pub enum Op { + /// This instruction should never be run. + /// Finding it in a program is a critical error. Illegal, + /// Pops one value from the stack. + /// + /// {A, B} - Pop - {A} Pop, + /// Assumes the value on the top of the + /// stack has an upvalue, and closes that + /// upvalue. + /// + /// {A, B} - Pop - {A} PopUpvalue, + /// Copies the value on the top of the stack + /// and puts it on top of the stack. + /// + /// {A, B} - Copy - {A, B, B} Copy, + /// Adds the given value to the top of the stack. + /// Also links upvalues if the value is a function. + /// + /// {A} - Constant(B) - {A, B} Constant(Value), + /// Creates a new [Tuple] with the given size and place it on the top + /// of the stack. + /// + /// {A, B, C} - Tuple(3) - {D(A, B, C)} Tuple(usize), + /// Indexes something indexable, currently only Tuples, + /// and adds that element to the stack. + /// + /// {T, I} - Index - {T[I]} Index, + /// Looks up a field by the given name + /// and replaces the parent with it. + /// Currently only expects [Value::Blob]. + /// + /// {O} - Get(F) - {O.F} Get(String), + /// Looks up a field by the given name + /// and replaces the current value in the object. + /// Currently only expects [Value::Blob]. + /// + /// {O} - Set(F) - {} Set(String), + /// Adds the two top elements on the stack, + /// using the function [op::add]. The result + /// is the pushed. + /// + /// {A, B} - Add - {A + B} Add, + /// Sub the two top elements on the stack, + /// using the function [op::sub]. The result + /// is the pushed. + /// + /// {A, B} - Sub - {A - B} Sub, + /// Multiples the two top elements on the stack, + /// using the function [op::mul]. The result + /// is the pushed. + /// + /// {A, B} - Mul - {A - B} Mul, + /// Divides the two top elements on the stack, + /// using the function [op::div]. The result + /// is the pushed. + /// + /// {A, B} - Div - {A / B} Div, + /// Negates the top element on the stack. + /// + /// {A} - Neg - {-A} Neg, + /// Performs a boolean and on the + /// top 2 stack elements using [op::and]. + /// + /// {A, B} - And - {A && B} And, + /// Performs a boolean or on the + /// top 2 stack elements using [op::or]. + /// + /// {A, B} - Or - {A || B} Or, + /// Performs a boolean not on the + /// top stack element using [op::not]. + /// + /// {A} - Not - {!A} Not, + /// Sets the instruction pointer + /// to the given value. + /// + /// Does not affect the stack. Jmp(usize), + /// Sets the instruction pointer + /// to the given value, if the + /// topmost value is false, also + /// pops this value. + /// + /// {A} - JmpFalse(n) - {} JmpFalse(usize), - Equal, // == - Less, // < - Greater, // > - + /// Compares the two topmost elements + /// on the stack for equality, and pushes + /// the result. Compares using [op::eq]. + /// + /// {A, B} - Equal - {A == B} + Equal, + /// Compares the two topmost elements + /// on the stack for order, and pushes the result. + /// Compares using [op::less]. + /// + /// {A, B} - Less - {A < B} + Less, + /// Compares the two topmost elements + /// on the stack for order, and pushes the result. + /// Compares using [op::less]. + /// + /// {A, B} - Greater - {B < A} + Greater, + + /// Pops the top value of the stack, and + /// crashes the program if it is false. + /// + /// {A} - Assert - {} Assert, + /// This instruction should not be executed. + /// If it is the program crashes. + /// + /// Does not affect the stack. Unreachable, + /// Reads the value counted from the + /// bottom of the stack and adds it + /// to the top. + /// + /// {A, B} - ReadLocal(0) - {A, B, A} ReadLocal(usize), + /// Sets the value at the given index + /// of the stack, to the topmost value. + /// Pops the topsmost element. + /// + /// {A, B} - AssignLocal(0) - {B} AssignLocal(usize), + /// Reads the upvalue, and adds it + /// to the top of the stack. + /// + /// {} - ReadUpvalue(0) - {A} ReadUpvalue(usize), + /// Sets the given upvalue, and pops + /// the topmost element. + /// + /// {A} - AssignUpvalue(0) - {} AssignUpvalue(usize), + /// A helper instruction for the typechecker, + /// makes sure the top value on the stack + /// is of the given type, and is ment to signal + /// that the "variable" is added. + /// + /// Does not affect the stack. Define(Type), + /// Calls "something" with the given number + /// of arguments. The callable value is + /// then replaced with the result. + /// + /// Callable things are: [Value::Blob], [Value::Function], + /// and [Value::ExternFunction]. + /// + /// {F, A, B} - Call(2) - {F(A, B)} Call(usize), + /// Prints and pops the top value on the stack. + /// + /// {A} - Print - {} Print, + /// Pops the current stackframe and replaces + /// slot 0 with the top value. Also pops + /// upvalues. + /// + /// {F, A, B} - Return - {..., B} Return, + + /// Temporarily stops execution and returns + /// to the call site. + /// + /// Does not affect the stack. Yield, } +/// +/// Module with all the operators that can be applied +/// to values. +/// +/// Broken out because they need to be recursive. mod op { use super::Value; use std::rc::Rc; - fn tuple_op(a: &Rc<Vec<Value>>, b: &Rc<Vec<Value>>, f: fn (&Value, &Value) -> Value) -> Value { + fn tuple_bin_op(a: &Rc<Vec<Value>>, b: &Rc<Vec<Value>>, f: fn (&Value, &Value) -> Value) -> Value { Value::Tuple(Rc::new(a.iter().zip(b.iter()).map(|(a, b)| f(a, b)).collect())) } + fn tuple_un_op(a: &Rc<Vec<Value>>, f: fn (&Value) -> Value) -> Value { + Value::Tuple(Rc::new(a.iter().map(f).collect())) + } + pub fn neg(value: &Value) -> Value { match value { Value::Float(a) => Value::Float(-*a), Value::Int(a) => Value::Int(-*a), - Value::Tuple(a) => Value::Tuple(Rc::new(a.iter().map(neg).collect())), + Value::Tuple(a) => tuple_un_op(a, neg), _ => Value::Nil, } } @@ -351,7 +514,7 @@ mod op { pub fn not(value: &Value) -> Value { match value { Value::Bool(a) => Value::Bool(!*a), - Value::Tuple(a) => Value::Tuple(Rc::new(a.iter().map(not).collect())), + Value::Tuple(a) => tuple_un_op(a, not), _ => Value::Nil, } } @@ -362,7 +525,7 @@ mod op { (Value::Float(a), Value::Float(b)) => Value::Float(a + b), (Value::Int(a), Value::Int(b)) => Value::Int(a + b), (Value::String(a), Value::String(b)) => Value::String(Rc::from(format!("{}{}", a, b))), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, add), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, add), _ => Value::Nil, } } @@ -375,7 +538,7 @@ mod op { match (a, b) { (Value::Float(a), Value::Float(b)) => Value::Float(a * b), (Value::Int(a), Value::Int(b)) => Value::Int(a * b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, mul), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, mul), _ => Value::Nil, } } @@ -384,7 +547,7 @@ mod op { match (a, b) { (Value::Float(a), Value::Float(b)) => Value::Float(a / b), (Value::Int(a), Value::Int(b)) => Value::Int(a / b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, div), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, div), _ => Value::Nil, } } @@ -395,7 +558,7 @@ mod op { (Value::Int(a), Value::Int(b)) => Value::Bool(a == b), (Value::String(a), Value::String(b)) => Value::Bool(a == b), (Value::Bool(a), Value::Bool(b)) => Value::Bool(a == b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, eq), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, eq), _ => Value::Nil, } } @@ -406,7 +569,7 @@ mod op { (Value::Int(a), Value::Int(b)) => Value::Bool(a < b), (Value::String(a), Value::String(b)) => Value::Bool(a < b), (Value::Bool(a), Value::Bool(b)) => Value::Bool(a < b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, less), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, less), _ => Value::Nil, } } @@ -418,7 +581,7 @@ mod op { pub fn and(a: &Value, b: &Value) -> Value { match (a, b) { (Value::Bool(a), Value::Bool(b)) => Value::Bool(*a && *b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, and), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, and), _ => Value::Nil, } } @@ -426,7 +589,7 @@ mod op { pub fn or(a: &Value, b: &Value) -> Value { match (a, b) { (Value::Bool(a), Value::Bool(b)) => Value::Bool(*a || *b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, or), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, or), _ => Value::Nil, } } @@ -459,6 +622,7 @@ impl Block { } } + // Used to create empty functions. fn empty_with_type(ty: &Type) -> Self { let mut block = Block::new("/empty/", Path::new(""), 0); block.ty = ty.clone(); @@ -836,4 +1000,10 @@ a.a <=> 1 a.a -= 1 a.a <=> 0" ); + + test_multiple!( + newline_regression, + simple: "a := 1 // blargh \na += 1 // blargh \n a <=> 2 // HARGH", + expressions: "1 + 1 // blargh \n 2 // blargh \n // HARGH \n", + ); } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 28172a3..9574af1 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -123,7 +123,7 @@ pub enum Token { #[token("\n")] Newline, - #[regex(r"//[^\n]*\n", logos::skip)] + #[regex(r"//[^\n]*", logos::skip)] Comment, #[regex(r"[ \t\r]", logos::skip)] @@ -229,8 +229,8 @@ mod tests { #[test] fn comment() { - lex_once("// a\n1"); - assert_eq!(lex("1// a\n2").len(), 2); - assert_eq!(lex("1\n// a\n2").len(), 3); // newline is also a token + assert_eq!(lex("// a\n1").len(), 2); + assert_eq!(lex("1// a\n2").len(), 3); + assert_eq!(lex("1\n// a\n2").len(), 4); // newline is also a token } } @@ -122,10 +122,6 @@ impl VM { (b, a) // this matches the order they were on the stack } - fn _peek_up(&self, amount: usize) -> Option<&Value> { - self.stack.get(self.stack.len() - amount) - } - fn frame(&self) -> &Frame { let last = self.frames.len() - 1; &self.frames[last] @@ -141,8 +137,8 @@ impl VM { self.frame().block.borrow().ops[ip].clone() } + /// Stop the program, violently fn crash_and_burn(&self) -> ! { - println!("\n\n !!!POPPING EMPTY STACK - DUMPING EVERYTHING!!!\n"); self.print_stack(); println!("\n"); self.frame().block.borrow().debug_print(); @@ -162,6 +158,7 @@ impl VM { } } + /// Runs a single operation on the VM fn eval_op(&mut self, op: Op) -> Result<OpResult, Error> { match op { Op::Illegal => { @@ -427,6 +424,7 @@ impl VM { self.frame().block.borrow().ops[self.frame().ip]); } + // Initalizes the VM for running. Run cannot be called before this. pub(crate) fn init(&mut self, prog: &Prog) { let block = Rc::clone(&prog.blocks[0]); self.blobs = prog.blobs.clone(); @@ -443,6 +441,7 @@ impl VM { }); } + /// Simulates the program. pub fn run(&mut self) -> Result<OpResult, Error> { if self.print_blocks { println!("\n [[{}]]\n", "RUNNING".red()); @@ -461,6 +460,7 @@ impl VM { } } + /// Checks the current operation for type errors. fn check_op(&mut self, op: Op) -> Result<(), Error> { match op { Op::Unreachable => {} @@ -702,6 +702,7 @@ impl VM { errors } + // Checks the program for type errors. pub(crate) fn typecheck(&mut self, prog: &Prog) -> Result<(), Vec<Error>> { let mut errors = Vec::new(); |
