diff options
| author | Edvard Thörnros <edvard.thornros@gmail.com> | 2021-02-11 18:42:04 +0100 |
|---|---|---|
| committer | Edvard Thörnros <edvard.thornros@gmail.com> | 2021-02-11 18:42:04 +0100 |
| commit | 4e6f4ff865a132df3e7a6f39f481f78c6fc1df47 (patch) | |
| tree | a9f1ea95995b8b24758ee4d368313ec0e9809c3e | |
| parent | 04b0c68a792cc9be4dea92bb8750d7990bd43cc8 (diff) | |
| parent | b09c97154886e1ca9e0a418f8969870a31f39077 (diff) | |
| download | sylt-4e6f4ff865a132df3e7a6f39f481f78c6fc1df47.tar.gz | |
Merge remote-tracking branch 'origin/main' into break-continue
| -rw-r--r-- | src/compiler.rs | 149 | ||||
| -rw-r--r-- | src/lib.rs | 59 | ||||
| -rw-r--r-- | src/tokenizer.rs | 2 | ||||
| -rw-r--r-- | src/vm.rs | 22 |
4 files changed, 199 insertions, 33 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index e0b65c1..f2daa6e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -126,6 +126,7 @@ struct Variable { active: bool, upvalue: bool, captured: bool, + mutable: bool, } enum LoopOp { @@ -437,7 +438,7 @@ impl Compiler { Token::Bool(_) => self.value(block), Token::String(_) => self.value(block), - Token::Not => self.unary(block), + Token::Bang => self.unary(block), _ => { return false; }, } @@ -537,7 +538,7 @@ impl Compiler { fn unary(&mut self, block: &mut Block) { let op = match self.eat() { Token::Minus => Op::Neg, - Token::Not => Op::Not, + Token::Bang => Op::Not, _ => { error!(self, "Invalid unary operator"); Op::Neg }, }; self.parse_precedence(block, Prec::Factor); @@ -627,27 +628,67 @@ impl Compiler { } fn call(&mut self, block: &mut Block) { - expect!(self, Token::LeftParen, "Expected '(' at start of function call."); - let mut arity = 0; - loop { - match self.peek() { - Token::EOF => { - error!(self, "Unexpected EOF in function call."); - break; - } - Token::RightParen => { - self.eat(); - break; + match self.peek() { + Token::LeftParen => { + self.eat(); + loop { + match self.peek() { + Token::EOF => { + error!(self, "Unexpected EOF in function call."); + break; + } + Token::RightParen => { + self.eat(); + break; + } + _ => { + self.expression(block); + arity += 1; + if !matches!(self.peek(), Token::RightParen) { + expect!(self, Token::Comma, "Expected ',' after argument."); + } + } + } + if self.panic { + break; + } } - _ => { - self.expression(block); - arity += 1; - if !matches!(self.peek(), Token::RightParen) { - expect!(self, Token::Comma, "Expected ',' after argument."); + }, + + Token::Bang => { + self.eat(); + loop { + match self.peek() { + Token::EOF => { + error!(self, "Unexpected EOF in function call."); + break; + } + Token::Newline => { + break; + } + _ => { + if !parse_branch!(self, block, self.expression(block)) { + break; + } + arity += 1; + if matches!(self.peek(), Token::Comma) { + self.eat(); + } + } } + if self.panic { + break; + } + } + if !self.panic { + println!("LINE {} -- ", self.line()); } } + + _ => { + error!(self, "Invalid function call. Expected '!' or '('."); + } } add_op(self, block, Op::Call(arity)); @@ -766,18 +807,17 @@ impl Compiler { break; } } - Token::LeftParen => { - self.call(block); + _ => { + if !parse_branch!(self, block, self.call(block)) { + break + } } - _ => { break } } } } else if let Some(blob) = self.find_blob(&name) { let string = self.add_constant(Value::Blob(blob)); add_op(self, block, Op::Constant(string)); - if self.peek() == Token::LeftParen { - self.call(block); - } + parse_branch!(self, block, self.call(block)); } else if let Some(slot) = self.find_extern_function(&name) { let string = self.add_constant(Value::ExternFunction(slot)); add_op(self, block, Op::Constant(string)); @@ -807,6 +847,32 @@ impl Compiler { scope, active: false, upvalue: false, + mutable: true, + }); + Ok(slot) + } + + fn define_constant(&mut self, name: &str, typ: Type, _block: &mut Block) -> Result<usize, ()> { + if let Some(var) = self.find_variable(&name) { + if var.scope == self.frame().scope { + error!(self, format!("Multiple definitions of {} in this block.", name)); + return Err(()); + } + } + + let slot = self.stack().len(); + let scope = self.frame().scope; + self.stack_mut().push(Variable { + name: String::from(name), + captured: false, + outer_upvalue: false, + outer_slot: 0, + slot, + typ, + scope, + active: false, + upvalue: false, + mutable: false, }); Ok(slot) } @@ -822,6 +888,15 @@ impl Compiler { } } + fn constant_statement(&mut self, name: &str, typ: Type, block: &mut Block) { + let slot = self.define_constant(name, typ.clone(), block); + self.expression(block); + + if let Ok(slot) = slot { + self.stack_mut()[slot].active = true; + } + } + fn assign(&mut self, block: &mut Block) { let name = match self.eat() { Token::Identifier(name) => name, @@ -846,8 +921,16 @@ impl Compiler { }; if let Some(var) = self.find_variable(&name) { + if !var.mutable { + // TODO(ed): Maybe a better error than "SyntaxError". + error!(self, format!("Cannot assign to constant '{}'", var.name)); + } if let Some(op) = op { - add_op(self, block, Op::Copy); + if var.upvalue { + add_op(self, block, Op::ReadUpvalue(var.slot)); + } else { + add_op(self, block, Op::ReadLocal(var.slot)); + } self.expression(block); add_op(self, block, op); } else { @@ -1110,15 +1193,14 @@ impl Compiler { add_op(self, block, Op::Set(field)); return; } - Token::LeftParen => { - self.call(block); - } Token::Newline => { return; } _ => { - error!(self, "Unexpected token when parsing blob-field."); - return; + if !parse_branch!(self, block, self.call(block)) { + error!(self, "Unexpected token when parsing blob-field."); + return; + } } } } @@ -1174,6 +1256,12 @@ impl Compiler { self.definition_statement(&name, Type::Unknown, block); } + (Token::Identifier(name), Token::ColonColon, ..) => { + self.eat(); + self.eat(); + self.constant_statement(&name, Type::Unknown, block); + } + (Token::Blob, Token::Identifier(_), ..) => { self.blob_statement(block); } @@ -1246,6 +1334,7 @@ impl Compiler { active: false, captured: false, upvalue: false, + mutable: true, }); let mut block = Block::new(name, file, 0); @@ -813,6 +813,16 @@ mod tests { assert_errs!(run_string("<!>\n", true, Vec::new()), [ErrorKind::Unreachable]); } + #[test] + fn assign_to_constant() { + assert_errs!(run_string("a :: 2\na = 2", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]); + } + + #[test] + fn assign_to_constant_upvalue() { + assert_errs!(run_string("a :: 2\nq :: fn { a = 2 }\n", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]); + } + macro_rules! test_multiple { ($mod:ident, $( $fn:ident : $prog:literal ),+ $( , )? ) => { mod $mod { @@ -856,7 +866,7 @@ mod tests { test_multiple!( if_, compare_constants_equality: "if 1 == 2 { - <!> + <!> }", compare_constants_unequality: "if 1 != 1 { <!> @@ -1054,6 +1064,14 @@ a.a <=> 0" ); test_multiple!( + fancy_call, + not: "f := fn {}\n f!\n", + one_arg: "f := fn a:int { a <=> 1 }\n f! 1\n", + two_arg: "f := fn a:int, b:int { b <=> 3 }\n f! 1, 1 + 2\n", + three_arg: "f := fn a:int, b:int, c:int { c <=> 13 }\n f! 1, 1 + 2, 1 + 4 * 3\n", + ); + + 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", @@ -1114,6 +1132,45 @@ for i := 0, i < 4, i += 1 { } a <=> 3 ", + ); + + test_multiple!( + read_constants, + simple: " +a :: 1 +a <=> 1 +b := 2 +{ + a <=> 1 +}", + ); + test_multiple!( + assignment_op_regression, + simple_add: " +a := 0 +b := 99999 +a += 1 +a <=> 1 +", + + simple_sub: " +a := 0 +b := 99999 +a -= 1 +a <=> -1 +", + + strange: " +a := 0 +{ + b := 99999 + { + a := 99999 + } + a -= 1 +} +a <=> -1 +", ); } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index d7b28aa..b54e194 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -116,7 +116,7 @@ pub enum Token { #[token("||")] Or, #[token("!")] - Not, + Bang, #[token(",")] Comma, @@ -63,9 +63,10 @@ pub struct VM { pub print_blocks: bool, pub print_ops: bool, + runtime: bool, - extern_functions: Vec<RustFunction>, + extern_functions: Vec<RustFunction>, } #[derive(Eq, PartialEq)] @@ -92,6 +93,7 @@ impl VM { print_blocks: false, print_ops: false, + runtime: false, extern_functions: Vec::new() } @@ -158,10 +160,25 @@ impl VM { self.frame().block.borrow().ops[ip] } + fn print_stacktrace(&self) { + if !self.runtime { return; } + + println!("\n<{}>", "STACK".red()); + for (i, frame) in self.frames.iter().enumerate() { + println!(" {:>3}. {}:{:<4} in {:10}", + i, + frame.block.borrow().file.display(), + frame.block.borrow().line(self.frame().ip), + frame.block.borrow().name.blue()); + } + println!("") + } + /// Stop the program, violently fn crash_and_burn(&self) -> ! { self.print_stack(); println!("\n"); + self.print_stacktrace(); self.frame().block.borrow().debug_print(); println!(" ip: {}, line: {}\n", self.frame().ip.blue(), @@ -171,6 +188,7 @@ impl VM { fn error(&self, kind: ErrorKind, message: Option<String>) -> Error { let frame = self.frames.last().unwrap(); + self.print_stacktrace(); Error { kind, file: frame.block.borrow().file.clone(), @@ -472,6 +490,7 @@ impl VM { self.extern_functions = prog.functions.clone(); self.stack.clear(); self.frames.clear(); + self.runtime = true; self.push(Value::Function(Vec::new(), Rc::clone(&block))); @@ -755,6 +774,7 @@ impl VM { self.blobs = prog.blobs.clone(); self.constants = prog.constants.clone(); self.strings = prog.strings.clone(); + self.runtime = false; self.extern_functions = prog.functions.clone(); for block in prog.blocks.iter() { |
