From 80f06d5293797f2b1a4161d11c08df3d19958cfa Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Sat, 30 Dec 2023 23:46:59 -0600 Subject: [PATCH 1/2] Rename StmtAssign to StmtLet to better reflect what it's doing --- res/grammar.lalrpop | 12 ++++++------ src/syntax/ast.rs | 9 ++++----- src/vm/compiler.rs | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/res/grammar.lalrpop b/res/grammar.lalrpop index 3b7346e..3cd5ac7 100644 --- a/res/grammar.lalrpop +++ b/res/grammar.lalrpop @@ -16,7 +16,7 @@ pub Program: ast::Program = *> => ast::Program { <> }; Decl = { DeclClass, DeclFn, - DeclLetVar, + DeclLet, Stmt, } @@ -24,7 +24,7 @@ DeclClass: ast::Stmt = => ast::Stmt::Class(<>); StmtClass: ast::StmtClass = "class" >)?> "{" - >)*> + >)*> >)*> "}" => ast::StmtClass { <> }; @@ -67,10 +67,10 @@ StmtFn: ast::StmtFn = { } } -DeclLetVar: ast::Stmt = => - ast::Stmt::Assign(<>); +DeclLet: ast::Stmt = => + ast::Stmt::Let(<>); -StmtAssign: ast::StmtAssign = "let" )?> ";" => ast::StmtAssign { +StmtLet: ast::StmtLet = "let" )?> ";" => ast::StmtLet { identifier: ast::Identifier { name, depth: None @@ -111,7 +111,7 @@ StmtClosed: ast::Stmt = { } ForInit: Option = { - > => Some(<>), + > => Some(<>), > => Some(<>), ";" => None, } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index ecd41be..44fbd27 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -20,7 +20,7 @@ pub enum Stmt { If(Box), Print(StmtPrint), Return(StmtReturn), - Assign(StmtAssign), + Let(StmtLet), While(Box), Error, } @@ -36,7 +36,7 @@ impl Debug for Stmt { Self::If(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::Print(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::Return(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), - Self::Assign(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), + Self::Let(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::While(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::Error => write!(f, "Error"), } @@ -53,7 +53,7 @@ pub struct StmtClass { pub name: String, pub super_: Option, pub methods: Vec>, - pub fields: Vec>, + pub fields: Vec>, } /// An expression statement evaluates an expression and discards the result. @@ -94,9 +94,8 @@ pub struct StmtReturn { pub value: Option, } -/// Statement that sets `var.name` to `value` #[derive(Clone, Debug, PartialEq)] -pub struct StmtAssign { +pub struct StmtLet { pub identifier: Identifier, pub value: Option, } diff --git a/src/vm/compiler.rs b/src/vm/compiler.rs index f06b9cc..56d95cb 100644 --- a/src/vm/compiler.rs +++ b/src/vm/compiler.rs @@ -276,7 +276,7 @@ impl Compiler { } self.emit_u8(op::RETURN, span); } - Stmt::Assign(assign) => { + Stmt::Let(assign) => { let name = &assign.identifier.name; if self.is_global() { let name = gc.alloc(name); From fc4fd2ee142e919300c26c0c72dc3a9d0c7e2868 Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Sun, 31 Dec 2023 14:16:40 -0600 Subject: [PATCH 2/2] Add StmtConst --- .../const/collide_with_parameter.locks | 3 +++ res/examples/const/duplicate_local.locks | 4 ++++ res/examples/const/in_middle_of_block.locks | 10 ++++++++++ res/examples/const/in_nested_block.locks | 6 ++++++ res/examples/const/local_from_method.locks | 9 +++++++++ res/examples/const/redeclare_global.locks | 2 ++ res/examples/const/redefine_global.locks | 2 ++ .../scope_reuse_in_different_blocks.locks | 9 +++++++++ res/examples/const/shadow_and_local.locks | 7 +++++++ res/examples/const/shadow_global.locks | 6 ++++++ res/examples/const/shadow_local.locks | 8 ++++++++ res/examples/const/uninitialized.locks | 2 ++ res/examples/const/use_clock_as_var.locks | 1 + res/examples/const/use_false_as_var.locks | 2 ++ .../const/use_global_in_initializer.locks | 3 +++ .../const/use_local_in_initializer.locks | 4 ++++ res/examples/const/use_nil_as_var.locks | 2 ++ res/examples/const/use_this_as_var.locks | 2 ++ .../collide_with_parameter.locks | 0 .../{variable => let}/duplicate_local.locks | 0 .../duplicate_parameter.locks | 0 .../{variable => let}/early_bound.locks | 0 .../in_middle_of_block.locks | 0 .../{variable => let}/in_nested_block.locks | 0 .../{variable => let}/local_from_method.locks | 0 .../{variable => let}/redeclare_global.locks | 0 .../{variable => let}/redefine_global.locks | 0 .../scope_reuse_in_different_blocks.locks | 0 .../{variable => let}/shadow_and_local.locks | 0 .../{variable => let}/shadow_global.locks | 0 .../{variable => let}/shadow_local.locks | 0 .../{variable => let}/undefined_global.locks | 0 .../{variable => let}/undefined_local.locks | 0 .../{variable => let}/uninitialized.locks | 0 .../unreached_undefined.locks | 0 .../{variable => let}/use_clock_as_var.locks | 0 .../{variable => let}/use_false_as_var.locks | 0 .../use_global_in_initializer.locks | 0 .../use_local_in_initializer.locks | 0 .../{variable => let}/use_nil_as_var.locks | 0 .../{variable => let}/use_this_as_var.locks | 0 res/grammar.lalrpop | 13 +++++++++++++ src/syntax/ast.rs | 8 ++++++++ src/syntax/lexer.rs | 3 ++- src/syntax/mod.rs | 8 ++++++-- src/vm/compiler.rs | 19 +++++++++++++++++++ 46 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 res/examples/const/collide_with_parameter.locks create mode 100644 res/examples/const/duplicate_local.locks create mode 100644 res/examples/const/in_middle_of_block.locks create mode 100644 res/examples/const/in_nested_block.locks create mode 100644 res/examples/const/local_from_method.locks create mode 100644 res/examples/const/redeclare_global.locks create mode 100644 res/examples/const/redefine_global.locks create mode 100644 res/examples/const/scope_reuse_in_different_blocks.locks create mode 100644 res/examples/const/shadow_and_local.locks create mode 100644 res/examples/const/shadow_global.locks create mode 100644 res/examples/const/shadow_local.locks create mode 100644 res/examples/const/uninitialized.locks create mode 100644 res/examples/const/use_clock_as_var.locks create mode 100644 res/examples/const/use_false_as_var.locks create mode 100644 res/examples/const/use_global_in_initializer.locks create mode 100644 res/examples/const/use_local_in_initializer.locks create mode 100644 res/examples/const/use_nil_as_var.locks create mode 100644 res/examples/const/use_this_as_var.locks rename res/examples/{variable => let}/collide_with_parameter.locks (100%) rename res/examples/{variable => let}/duplicate_local.locks (100%) rename res/examples/{variable => let}/duplicate_parameter.locks (100%) rename res/examples/{variable => let}/early_bound.locks (100%) rename res/examples/{variable => let}/in_middle_of_block.locks (100%) rename res/examples/{variable => let}/in_nested_block.locks (100%) rename res/examples/{variable => let}/local_from_method.locks (100%) rename res/examples/{variable => let}/redeclare_global.locks (100%) rename res/examples/{variable => let}/redefine_global.locks (100%) rename res/examples/{variable => let}/scope_reuse_in_different_blocks.locks (100%) rename res/examples/{variable => let}/shadow_and_local.locks (100%) rename res/examples/{variable => let}/shadow_global.locks (100%) rename res/examples/{variable => let}/shadow_local.locks (100%) rename res/examples/{variable => let}/undefined_global.locks (100%) rename res/examples/{variable => let}/undefined_local.locks (100%) rename res/examples/{variable => let}/uninitialized.locks (100%) rename res/examples/{variable => let}/unreached_undefined.locks (100%) rename res/examples/{variable => let}/use_clock_as_var.locks (100%) rename res/examples/{variable => let}/use_false_as_var.locks (100%) rename res/examples/{variable => let}/use_global_in_initializer.locks (100%) rename res/examples/{variable => let}/use_local_in_initializer.locks (100%) rename res/examples/{variable => let}/use_nil_as_var.locks (100%) rename res/examples/{variable => let}/use_this_as_var.locks (100%) diff --git a/res/examples/const/collide_with_parameter.locks b/res/examples/const/collide_with_parameter.locks new file mode 100644 index 0000000..4a5800a --- /dev/null +++ b/res/examples/const/collide_with_parameter.locks @@ -0,0 +1,3 @@ +fn foo(a) { + const a = 123; // out: NameError: name "a" is already defined +} diff --git a/res/examples/const/duplicate_local.locks b/res/examples/const/duplicate_local.locks new file mode 100644 index 0000000..b88bc55 --- /dev/null +++ b/res/examples/const/duplicate_local.locks @@ -0,0 +1,4 @@ +{ + const a = "value"; + const a = "other"; // out: NameError: name "a" is already defined +} diff --git a/res/examples/const/in_middle_of_block.locks b/res/examples/const/in_middle_of_block.locks new file mode 100644 index 0000000..0e5f33d --- /dev/null +++ b/res/examples/const/in_middle_of_block.locks @@ -0,0 +1,10 @@ +{ + const a = "a"; + print a; // out: a + const b = a + " b"; + print b; // out: a b + const c = a + " c"; + print c; // out: a c + const d = b + " d"; + print d; // out: a b d +} diff --git a/res/examples/const/in_nested_block.locks b/res/examples/const/in_nested_block.locks new file mode 100644 index 0000000..8808d2c --- /dev/null +++ b/res/examples/const/in_nested_block.locks @@ -0,0 +1,6 @@ +{ + const a = "outer"; + { + print a; // out: outer + } +} diff --git a/res/examples/const/local_from_method.locks b/res/examples/const/local_from_method.locks new file mode 100644 index 0000000..d12d1ec --- /dev/null +++ b/res/examples/const/local_from_method.locks @@ -0,0 +1,9 @@ +const foo = "variable"; + +class Foo { + fn method() { + print foo; + } +} + +Foo().method(); // out: variable diff --git a/res/examples/const/redeclare_global.locks b/res/examples/const/redeclare_global.locks new file mode 100644 index 0000000..b967044 --- /dev/null +++ b/res/examples/const/redeclare_global.locks @@ -0,0 +1,2 @@ +const a = "1"; +const a; // out: NameError: name "a" is already defined diff --git a/res/examples/const/redefine_global.locks b/res/examples/const/redefine_global.locks new file mode 100644 index 0000000..e2929a4 --- /dev/null +++ b/res/examples/const/redefine_global.locks @@ -0,0 +1,2 @@ +const a = "1"; +const a = "2"; // out: NameError: name "a" is already defined diff --git a/res/examples/const/scope_reuse_in_different_blocks.locks b/res/examples/const/scope_reuse_in_different_blocks.locks new file mode 100644 index 0000000..f583b23 --- /dev/null +++ b/res/examples/const/scope_reuse_in_different_blocks.locks @@ -0,0 +1,9 @@ +{ + const a = "first"; + print a; // out: first +} + +{ + const a = "second"; + print a; // out: second +} diff --git a/res/examples/const/shadow_and_local.locks b/res/examples/const/shadow_and_local.locks new file mode 100644 index 0000000..29915ef --- /dev/null +++ b/res/examples/const/shadow_and_local.locks @@ -0,0 +1,7 @@ +{ + const a = "outer"; + { + print a; // out: outer + let a = "inner"; // out: NameError: name "a" is already defined + } +} diff --git a/res/examples/const/shadow_global.locks b/res/examples/const/shadow_global.locks new file mode 100644 index 0000000..a7f22cc --- /dev/null +++ b/res/examples/const/shadow_global.locks @@ -0,0 +1,6 @@ +const a = "global"; +{ + let a = "shadow"; // out: NameError: name "a" is already defined + print a; +} +print a; diff --git a/res/examples/const/shadow_local.locks b/res/examples/const/shadow_local.locks new file mode 100644 index 0000000..ee6f280 --- /dev/null +++ b/res/examples/const/shadow_local.locks @@ -0,0 +1,8 @@ +{ + const a = "local"; + { + let a = "shadow"; // out: NameError: name "a" is already defined + print a; + } + print a; +} diff --git a/res/examples/const/uninitialized.locks b/res/examples/const/uninitialized.locks new file mode 100644 index 0000000..1390f78 --- /dev/null +++ b/res/examples/const/uninitialized.locks @@ -0,0 +1,2 @@ +const a; // out: Error: const must be initialized with a value +print a; diff --git a/res/examples/const/use_clock_as_var.locks b/res/examples/const/use_clock_as_var.locks new file mode 100644 index 0000000..90ed20c --- /dev/null +++ b/res/examples/const/use_clock_as_var.locks @@ -0,0 +1 @@ +const clock = "new clock"; // out: NameError: name "clock" is already defined diff --git a/res/examples/const/use_false_as_var.locks b/res/examples/const/use_false_as_var.locks new file mode 100644 index 0000000..bab0a63 --- /dev/null +++ b/res/examples/const/use_false_as_var.locks @@ -0,0 +1,2 @@ +// out: SyntaxError: unexpected "false" +const false = "value"; diff --git a/res/examples/const/use_global_in_initializer.locks b/res/examples/const/use_global_in_initializer.locks new file mode 100644 index 0000000..4002e40 --- /dev/null +++ b/res/examples/const/use_global_in_initializer.locks @@ -0,0 +1,3 @@ +const a = "value"; +const a = a; // out: NameError: name "a" is already defined +print a; diff --git a/res/examples/const/use_local_in_initializer.locks b/res/examples/const/use_local_in_initializer.locks new file mode 100644 index 0000000..da3dc9b --- /dev/null +++ b/res/examples/const/use_local_in_initializer.locks @@ -0,0 +1,4 @@ +{ + // out: NameError: cannot access variable "a" in its own initializer + const a = a; +} diff --git a/res/examples/const/use_nil_as_var.locks b/res/examples/const/use_nil_as_var.locks new file mode 100644 index 0000000..c5ec8d2 --- /dev/null +++ b/res/examples/const/use_nil_as_var.locks @@ -0,0 +1,2 @@ +// out: SyntaxError: unexpected "nil" +const nil = "value"; diff --git a/res/examples/const/use_this_as_var.locks b/res/examples/const/use_this_as_var.locks new file mode 100644 index 0000000..65e73ef --- /dev/null +++ b/res/examples/const/use_this_as_var.locks @@ -0,0 +1,2 @@ +// out: SyntaxError: unexpected "this" +const this = "value"; diff --git a/res/examples/variable/collide_with_parameter.locks b/res/examples/let/collide_with_parameter.locks similarity index 100% rename from res/examples/variable/collide_with_parameter.locks rename to res/examples/let/collide_with_parameter.locks diff --git a/res/examples/variable/duplicate_local.locks b/res/examples/let/duplicate_local.locks similarity index 100% rename from res/examples/variable/duplicate_local.locks rename to res/examples/let/duplicate_local.locks diff --git a/res/examples/variable/duplicate_parameter.locks b/res/examples/let/duplicate_parameter.locks similarity index 100% rename from res/examples/variable/duplicate_parameter.locks rename to res/examples/let/duplicate_parameter.locks diff --git a/res/examples/variable/early_bound.locks b/res/examples/let/early_bound.locks similarity index 100% rename from res/examples/variable/early_bound.locks rename to res/examples/let/early_bound.locks diff --git a/res/examples/variable/in_middle_of_block.locks b/res/examples/let/in_middle_of_block.locks similarity index 100% rename from res/examples/variable/in_middle_of_block.locks rename to res/examples/let/in_middle_of_block.locks diff --git a/res/examples/variable/in_nested_block.locks b/res/examples/let/in_nested_block.locks similarity index 100% rename from res/examples/variable/in_nested_block.locks rename to res/examples/let/in_nested_block.locks diff --git a/res/examples/variable/local_from_method.locks b/res/examples/let/local_from_method.locks similarity index 100% rename from res/examples/variable/local_from_method.locks rename to res/examples/let/local_from_method.locks diff --git a/res/examples/variable/redeclare_global.locks b/res/examples/let/redeclare_global.locks similarity index 100% rename from res/examples/variable/redeclare_global.locks rename to res/examples/let/redeclare_global.locks diff --git a/res/examples/variable/redefine_global.locks b/res/examples/let/redefine_global.locks similarity index 100% rename from res/examples/variable/redefine_global.locks rename to res/examples/let/redefine_global.locks diff --git a/res/examples/variable/scope_reuse_in_different_blocks.locks b/res/examples/let/scope_reuse_in_different_blocks.locks similarity index 100% rename from res/examples/variable/scope_reuse_in_different_blocks.locks rename to res/examples/let/scope_reuse_in_different_blocks.locks diff --git a/res/examples/variable/shadow_and_local.locks b/res/examples/let/shadow_and_local.locks similarity index 100% rename from res/examples/variable/shadow_and_local.locks rename to res/examples/let/shadow_and_local.locks diff --git a/res/examples/variable/shadow_global.locks b/res/examples/let/shadow_global.locks similarity index 100% rename from res/examples/variable/shadow_global.locks rename to res/examples/let/shadow_global.locks diff --git a/res/examples/variable/shadow_local.locks b/res/examples/let/shadow_local.locks similarity index 100% rename from res/examples/variable/shadow_local.locks rename to res/examples/let/shadow_local.locks diff --git a/res/examples/variable/undefined_global.locks b/res/examples/let/undefined_global.locks similarity index 100% rename from res/examples/variable/undefined_global.locks rename to res/examples/let/undefined_global.locks diff --git a/res/examples/variable/undefined_local.locks b/res/examples/let/undefined_local.locks similarity index 100% rename from res/examples/variable/undefined_local.locks rename to res/examples/let/undefined_local.locks diff --git a/res/examples/variable/uninitialized.locks b/res/examples/let/uninitialized.locks similarity index 100% rename from res/examples/variable/uninitialized.locks rename to res/examples/let/uninitialized.locks diff --git a/res/examples/variable/unreached_undefined.locks b/res/examples/let/unreached_undefined.locks similarity index 100% rename from res/examples/variable/unreached_undefined.locks rename to res/examples/let/unreached_undefined.locks diff --git a/res/examples/variable/use_clock_as_var.locks b/res/examples/let/use_clock_as_var.locks similarity index 100% rename from res/examples/variable/use_clock_as_var.locks rename to res/examples/let/use_clock_as_var.locks diff --git a/res/examples/variable/use_false_as_var.locks b/res/examples/let/use_false_as_var.locks similarity index 100% rename from res/examples/variable/use_false_as_var.locks rename to res/examples/let/use_false_as_var.locks diff --git a/res/examples/variable/use_global_in_initializer.locks b/res/examples/let/use_global_in_initializer.locks similarity index 100% rename from res/examples/variable/use_global_in_initializer.locks rename to res/examples/let/use_global_in_initializer.locks diff --git a/res/examples/variable/use_local_in_initializer.locks b/res/examples/let/use_local_in_initializer.locks similarity index 100% rename from res/examples/variable/use_local_in_initializer.locks rename to res/examples/let/use_local_in_initializer.locks diff --git a/res/examples/variable/use_nil_as_var.locks b/res/examples/let/use_nil_as_var.locks similarity index 100% rename from res/examples/variable/use_nil_as_var.locks rename to res/examples/let/use_nil_as_var.locks diff --git a/res/examples/variable/use_this_as_var.locks b/res/examples/let/use_this_as_var.locks similarity index 100% rename from res/examples/variable/use_this_as_var.locks rename to res/examples/let/use_this_as_var.locks diff --git a/res/grammar.lalrpop b/res/grammar.lalrpop index 3cd5ac7..c33302b 100644 --- a/res/grammar.lalrpop +++ b/res/grammar.lalrpop @@ -17,6 +17,7 @@ Decl = { DeclClass, DeclFn, DeclLet, + DeclConst, Stmt, } @@ -67,6 +68,17 @@ StmtFn: ast::StmtFn = { } } +DeclConst: ast::Stmt = => + ast::Stmt::Const(<>); + +StmtConst: ast::StmtConst = "const" )?> ";" => ast::StmtConst { + identifier: ast::Identifier { + name, + depth: None + }, + value, +}; + DeclLet: ast::Stmt = => ast::Stmt::Let(<>); @@ -365,6 +377,7 @@ extern { "this" => lexer::Token::This, "true" => lexer::Token::True, "let" => lexer::Token::Let, + "const" => lexer::Token::Const, "while" => lexer::Token::While, "extends" => lexer::Token::Extends, } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 44fbd27..ac2f7d8 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -21,6 +21,7 @@ pub enum Stmt { Print(StmtPrint), Return(StmtReturn), Let(StmtLet), + Const(StmtConst), While(Box), Error, } @@ -37,6 +38,7 @@ impl Debug for Stmt { Self::Print(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::Return(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::Let(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), + Self::Const(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::While(arg0) => f.write_fmt(format_args!("{:#?}", arg0)), Self::Error => write!(f, "Error"), } @@ -100,6 +102,12 @@ pub struct StmtLet { pub value: Option, } +#[derive(Clone, Debug, PartialEq)] +pub struct StmtConst { + pub identifier: Identifier, + pub value: Option, +} + #[derive(Clone, Debug, PartialEq)] pub struct StmtWhile { pub cond: ExprS, diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs index e26137c..852b791 100644 --- a/src/syntax/lexer.rs +++ b/src/syntax/lexer.rs @@ -146,8 +146,9 @@ pub enum Token { This, #[token("true")] True, - #[token("let")] Let, + #[token("const")] + Const, #[token("while")] While, #[token("extends")] diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 9aa3120..eefdb3f 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -49,7 +49,7 @@ pub fn parse(source: &str, offset: usize) -> Result> { } ParseError::UnrecognizedToken { token: (start, _, end), expected } => ( Error::SyntaxError(SyntaxError::UnrecognizedToken { - token: source[start..end].to_string(), + token: source[start - offset..end - offset].to_string(), expected, }), start..end, @@ -57,5 +57,9 @@ pub fn parse(source: &str, offset: usize) -> Result> { ParseError::User { error } => error, })); - if errors.is_empty() { Ok(program) } else { Err(errors) } + if errors.is_empty() { + Ok(program) + } else { + Err(errors) + } } diff --git a/src/vm/compiler.rs b/src/vm/compiler.rs index 56d95cb..b685ff5 100644 --- a/src/vm/compiler.rs +++ b/src/vm/compiler.rs @@ -295,6 +295,25 @@ impl Compiler { self.define_local(); } } + Stmt::Const(assign) => { + let name = &assign.identifier.name; + if self.is_global() { + let name = gc.alloc(name); + match &assign.value { + Some(value) => self.compile_expr(value, gc)?, + None => self.emit_u8(op::NIL, span), + } + self.emit_u8(op::DEFINE_GLOBAL, span); + self.emit_constant(name.into(), span)?; + } else { + self.declare_local(name, span)?; + match &assign.value { + Some(value) => self.compile_expr(value, gc)?, + None => self.emit_u8(op::NIL, span), + } + self.define_local(); + } + } Stmt::While(while_) => { // START: let loop_start = self.start_loop();