From 84d3c826b3bb53e8b4903f6fb396132b85f45ae5 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sat, 22 Feb 2025 23:22:40 -0500 Subject: [PATCH 01/12] return_ty -> in_out_types --- src/parser.rs | 6 +++--- src/resolver.rs | 2 +- .../new_nu_parser__test__node_output@calls.nu.snap | 3 +-- .../new_nu_parser__test__node_output@def.nu.snap | 5 ++--- ...parser__test__node_output@def_return_type.nu.snap | 7 +++---- ...u_parser__test__node_output@invalid_types.nu.snap | 7 +++---- src/typechecker.rs | 12 ++++++------ 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index fa2512c..b1ea590 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -130,7 +130,7 @@ pub enum AstNode { Def { name: NodeId, params: NodeId, - return_ty: Option, + in_out_types: Option, block: NodeId, }, Params(Vec), @@ -1051,7 +1051,7 @@ impl Parser { }; let params = self.signature_params(ParamsContext::Squares); - let return_ty = if self.is_colon() { + let in_out_types = if self.is_colon() { Some(self.in_out_types()) } else { None @@ -1064,7 +1064,7 @@ impl Parser { AstNode::Def { name, params, - return_ty, + in_out_types, block, }, span_start, diff --git a/src/resolver.rs b/src/resolver.rs index 537d786..bbdb7ca 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -221,7 +221,7 @@ impl<'a> Resolver<'a> { AstNode::Def { name, params, - return_ty: _, + in_out_types: _, block, } => { // define the command before the block to enable recursive calls diff --git a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap index 01b2108..d62f7f2 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap @@ -2,7 +2,6 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/calls.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (0 to 4) "spam" @@ -32,7 +31,7 @@ snapshot_kind: text 24: Variable (80 to 82) "$c" 25: List([NodeId(22), NodeId(23), NodeId(24)]) (70 to 82) 26: Block(BlockId(0)) (68 to 85) -27: Def { name: NodeId(8), params: NodeId(21), return_ty: None, block: NodeId(26) } (24 to 85) +27: Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) } (24 to 85) 28: Name (86 to 94) "existing" 29: Name (95 to 98) "foo" 30: String (100 to 104) ""ba"" diff --git a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap index fbda6c4..3c6f836 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap @@ -2,7 +2,6 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/def.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (4 to 7) "foo" @@ -28,7 +27,7 @@ snapshot_kind: text 20: Variable (51 to 53) "$z" 21: List([NodeId(18), NodeId(19), NodeId(20)]) (42 to 54) 22: Block(BlockId(0)) (40 to 57) -23: Def { name: NodeId(0), params: NodeId(17), return_ty: None, block: NodeId(22) } (0 to 57) +23: Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) } (0 to 57) 24: Block(BlockId(1)) (0 to 57) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(24) @@ -65,4 +64,4 @@ snapshot_kind: text register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 23): node Def { name: NodeId(0), params: NodeId(17), return_ty: None, block: NodeId(22) } not suported yet +Error (NodeId 23): node Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) } not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap index 7a0e8e9..aec6c67 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap @@ -2,7 +2,6 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/def_return_type.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (4 to 7) "foo" @@ -18,7 +17,7 @@ snapshot_kind: text 10: InOutTypes([NodeId(9)]) (14 to 35) 11: List([]) (37 to 38) 12: Block(BlockId(0)) (35 to 41) -13: Def { name: NodeId(0), params: NodeId(1), return_ty: Some(NodeId(10)), block: NodeId(12) } (0 to 41) +13: Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) } (0 to 41) 14: Name (46 to 49) "bar" 15: Params([]) (50 to 53) 16: Name (58 to 64) "string" @@ -40,7 +39,7 @@ snapshot_kind: text 32: InOutTypes([NodeId(23), NodeId(31)]) (56 to 101) 33: List([]) (103 to 104) 34: Block(BlockId(1)) (101 to 107) -35: Def { name: NodeId(14), params: NodeId(15), return_ty: Some(NodeId(32)), block: NodeId(34) } (42 to 107) +35: Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) } (42 to 107) 36: Block(BlockId(2)) (0 to 108) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(36) @@ -89,4 +88,4 @@ snapshot_kind: text register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(1), return_ty: Some(NodeId(10)), block: NodeId(12) } not suported yet +Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) } not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap index 84a69aa..cc9f157 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap @@ -2,7 +2,6 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/invalid_types.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (4 to 7) "foo" @@ -18,7 +17,7 @@ snapshot_kind: text 10: Params([NodeId(9)]) (8 to 30) 11: Variable (33 to 35) "$x" 12: Block(BlockId(0)) (31 to 37) -13: Def { name: NodeId(0), params: NodeId(10), return_ty: None, block: NodeId(12) } (0 to 37) +13: Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) } (0 to 37) 14: Name (42 to 45) "bar" 15: Name (47 to 48) "y" 16: Name (50 to 54) "list" @@ -28,7 +27,7 @@ snapshot_kind: text 20: Params([NodeId(19)]) (46 to 57) 21: Variable (60 to 62) "$y" 22: Block(BlockId(1)) (58 to 64) -23: Def { name: NodeId(14), params: NodeId(20), return_ty: None, block: NodeId(22) } (38 to 64) +23: Def { name: NodeId(14), params: NodeId(20), in_out_types: None, block: NodeId(22) } (38 to 64) 24: Block(BlockId(2)) (0 to 65) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(24) @@ -70,4 +69,4 @@ Error (NodeId 17): list must have one type parameter register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(10), return_ty: None, block: NodeId(12) } not suported yet +Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) } not suported yet diff --git a/src/typechecker.rs b/src/typechecker.rs index c5d4ae5..92b777a 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -345,9 +345,9 @@ impl<'a> Typechecker<'a> { AstNode::Def { name, params, - return_ty, + in_out_types, block, - } => self.typecheck_def(name, params, return_ty, block, node_id), + } => self.typecheck_def(name, params, in_out_types, block, node_id), AstNode::Alias { new_name, old_name } => { self.typecheck_alias(new_name, old_name, node_id) } @@ -626,11 +626,11 @@ impl<'a> Typechecker<'a> { &mut self, name: NodeId, params: NodeId, - return_ty: Option, + in_out_types: Option, block: NodeId, node_id: NodeId, ) { - let return_ty = return_ty + let in_out_types = in_out_types .map(|ty| { let AstNode::InOutTypes(types) = self.compiler.get_node(ty) else { panic!("internal error: return type is not a return type"); @@ -677,14 +677,14 @@ impl<'a> Typechecker<'a> { .get(&name) .expect("missing declared decl"); - if return_ty.is_empty() { + if in_out_types.is_empty() { self.decl_types[decl_id.0] = vec![InOutType { in_type: ANY_TYPE, out_type: self.type_id_of(block), }]; } else { // TODO check that block output type matches expected type - self.decl_types[decl_id.0] = return_ty; + self.decl_types[decl_id.0] = in_out_types; } } From 596795f19301ef4cb4e1f1f3bc8cc8253618ea86 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sat, 22 Feb 2025 23:39:42 -0500 Subject: [PATCH 02/12] Use term type args rather than params --- src/parser.rs | 19 ++++---- ...nu_parser__test__node_output@calls.nu.snap | 7 ++- ...parser__test__node_output@closure3.nu.snap | 5 +-- ...w_nu_parser__test__node_output@def.nu.snap | 13 +++--- ..._test__node_output@def_return_type.nu.snap | 25 ++++++----- ...r__test__node_output@invalid_types.nu.snap | 17 ++++---- ...er__test__node_output@let_mismatch.nu.snap | 17 ++++---- ..._nu_parser__test__node_output@mut_.nu.snap | 3 +- src/typechecker.rs | 43 +++++++++++-------- 9 files changed, 75 insertions(+), 74 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index fa2512c..bdf6c3e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -61,9 +61,10 @@ pub enum AstNode { Name, Type { name: NodeId, - params: Option, + args: Option, optional: bool, }, + TypeArgs(Vec), Variable, // Booleans @@ -611,14 +612,14 @@ impl Parser { // Explicit closure case if self.is_pipe() { - let args = Some(self.signature_params(ParamsContext::Pipes)); + let params = Some(self.signature_params(ParamsContext::Pipes)); let block = self.block(BlockContext::Closure); self.rcurly(); span_end = self.position(); return self.create_node( AstNode::Closure { - params: args, + params, block, }, span_start, @@ -927,11 +928,11 @@ impl Parser { self.create_node(AstNode::Params(param_list), span_start, span_end) } - pub fn type_params(&mut self) -> NodeId { + pub fn type_args(&mut self) -> NodeId { let _span = span!(); let span_start = self.position(); let span_end; - let param_list = { + let arg_list = { self.less_than(); let mut output = vec![]; @@ -955,17 +956,17 @@ impl Parser { output }; - self.create_node(AstNode::Params(param_list), span_start, span_end) + self.create_node(AstNode::TypeArgs(arg_list), span_start, span_end) } pub fn typename(&mut self) -> NodeId { let _span = span!(); if let (Token::Bareword, span) = self.tokens.peek() { let name = self.name(); - let mut params = None; + let mut args = None; if self.is_less_than() { // We have generics - params = Some(self.type_params()); + args = Some(self.type_args()); } let optional = if self.is_question_mark() { @@ -979,7 +980,7 @@ impl Parser { self.create_node( AstNode::Type { name, - params, + args, optional, }, span.start, diff --git a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap index 01b2108..44cef22 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap @@ -2,7 +2,6 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/calls.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (0 to 4) "spam" @@ -16,15 +15,15 @@ snapshot_kind: text 8: Name (28 to 36) "existing" 9: Name (38 to 39) "a" 10: Name (41 to 47) "string" -11: Type { name: NodeId(10), params: None, optional: false } (41 to 47) +11: Type { name: NodeId(10), args: None, optional: false } (41 to 47) 12: Param { name: NodeId(9), ty: Some(NodeId(11)) } (38 to 47) 13: Name (49 to 50) "b" 14: Name (52 to 58) "string" -15: Type { name: NodeId(14), params: None, optional: false } (52 to 58) +15: Type { name: NodeId(14), args: None, optional: false } (52 to 58) 16: Param { name: NodeId(13), ty: Some(NodeId(15)) } (49 to 58) 17: Name (60 to 61) "c" 18: Name (63 to 66) "int" -19: Type { name: NodeId(18), params: None, optional: false } (63 to 66) +19: Type { name: NodeId(18), args: None, optional: false } (63 to 66) 20: Param { name: NodeId(17), ty: Some(NodeId(19)) } (60 to 66) 21: Params([NodeId(12), NodeId(16), NodeId(20)]) (37 to 67) 22: Variable (72 to 74) "$a" diff --git a/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap b/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap index 20bd057..670e39c 100644 --- a/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap @@ -2,17 +2,16 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/closure3.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Variable (4 to 11) "closure" 1: Name (16 to 17) "a" 2: Name (19 to 22) "int" -3: Type { name: NodeId(2), params: None, optional: false } (19 to 22) +3: Type { name: NodeId(2), args: None, optional: false } (19 to 22) 4: Param { name: NodeId(1), ty: Some(NodeId(3)) } (16 to 22) 5: Name (24 to 25) "b" 6: Name (27 to 30) "int" -7: Type { name: NodeId(6), params: None, optional: false } (27 to 30) +7: Type { name: NodeId(6), args: None, optional: false } (27 to 30) 8: Param { name: NodeId(5), ty: Some(NodeId(7)) } (24 to 30) 9: Params([NodeId(4), NodeId(8)]) (15 to 31) 10: Variable (32 to 34) "$a" diff --git a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap index fbda6c4..9ae1b61 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap @@ -2,7 +2,6 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/def.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (4 to 7) "foo" @@ -10,17 +9,17 @@ snapshot_kind: text 2: Param { name: NodeId(1), ty: None } (9 to 10) 3: Name (11 to 12) "y" 4: Name (14 to 17) "int" -5: Type { name: NodeId(4), params: None, optional: false } (14 to 17) +5: Type { name: NodeId(4), args: None, optional: false } (14 to 17) 6: Param { name: NodeId(3), ty: Some(NodeId(5)) } (11 to 17) 7: Name (19 to 20) "z" 8: Name (22 to 26) "list" 9: Name (27 to 31) "list" 10: Name (32 to 35) "int" -11: Type { name: NodeId(10), params: None, optional: false } (32 to 35) -12: Params([NodeId(11)]) (31 to 36) -13: Type { name: NodeId(9), params: Some(NodeId(12)), optional: false } (27 to 31) -14: Params([NodeId(13)]) (26 to 37) -15: Type { name: NodeId(8), params: Some(NodeId(14)), optional: false } (22 to 26) +11: Type { name: NodeId(10), args: None, optional: false } (32 to 35) +12: TypeArgs([NodeId(11)]) (31 to 36) +13: Type { name: NodeId(9), args: Some(NodeId(12)), optional: false } (27 to 31) +14: TypeArgs([NodeId(13)]) (26 to 37) +15: Type { name: NodeId(8), args: Some(NodeId(14)), optional: false } (22 to 26) 16: Param { name: NodeId(7), ty: Some(NodeId(15)) } (19 to 26) 17: Params([NodeId(2), NodeId(6), NodeId(16)]) (8 to 39) 18: Variable (44 to 46) "$x" diff --git a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap index 7a0e8e9..707220c 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap @@ -2,18 +2,17 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/def_return_type.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (4 to 7) "foo" 1: Params([]) (8 to 11) 2: Name (14 to 21) "nothing" -3: Type { name: NodeId(2), params: None, optional: false } (14 to 21) +3: Type { name: NodeId(2), args: None, optional: false } (14 to 21) 4: Name (25 to 29) "list" 5: Name (30 to 33) "any" -6: Type { name: NodeId(5), params: None, optional: false } (30 to 33) -7: Params([NodeId(6)]) (29 to 34) -8: Type { name: NodeId(4), params: Some(NodeId(7)), optional: false } (25 to 29) +6: Type { name: NodeId(5), args: None, optional: false } (30 to 33) +7: TypeArgs([NodeId(6)]) (29 to 34) +8: Type { name: NodeId(4), args: Some(NodeId(7)), optional: false } (25 to 29) 9: InOutType(NodeId(3), NodeId(8)) (14 to 35) 10: InOutTypes([NodeId(9)]) (14 to 35) 11: List([]) (37 to 38) @@ -22,20 +21,20 @@ snapshot_kind: text 14: Name (46 to 49) "bar" 15: Params([]) (50 to 53) 16: Name (58 to 64) "string" -17: Type { name: NodeId(16), params: None, optional: false } (58 to 64) +17: Type { name: NodeId(16), args: None, optional: false } (58 to 64) 18: Name (68 to 72) "list" 19: Name (73 to 79) "string" -20: Type { name: NodeId(19), params: None, optional: false } (73 to 79) -21: Params([NodeId(20)]) (72 to 80) -22: Type { name: NodeId(18), params: Some(NodeId(21)), optional: false } (68 to 72) +20: Type { name: NodeId(19), args: None, optional: false } (73 to 79) +21: TypeArgs([NodeId(20)]) (72 to 80) +22: Type { name: NodeId(18), args: Some(NodeId(21)), optional: false } (68 to 72) 23: InOutType(NodeId(17), NodeId(22)) (58 to 80) 24: Name (82 to 85) "int" -25: Type { name: NodeId(24), params: None, optional: false } (82 to 85) +25: Type { name: NodeId(24), args: None, optional: false } (82 to 85) 26: Name (89 to 93) "list" 27: Name (94 to 97) "int" -28: Type { name: NodeId(27), params: None, optional: false } (94 to 97) -29: Params([NodeId(28)]) (93 to 98) -30: Type { name: NodeId(26), params: Some(NodeId(29)), optional: false } (89 to 93) +28: Type { name: NodeId(27), args: None, optional: false } (94 to 97) +29: TypeArgs([NodeId(28)]) (93 to 98) +30: Type { name: NodeId(26), args: Some(NodeId(29)), optional: false } (89 to 93) 31: InOutType(NodeId(25), NodeId(30)) (82 to 99) 32: InOutTypes([NodeId(23), NodeId(31)]) (56 to 101) 33: List([]) (103 to 104) diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap index 84a69aa..50d2e5c 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap @@ -2,18 +2,17 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/invalid_types.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (4 to 7) "foo" 1: Name (9 to 10) "x" 2: Name (12 to 16) "list" 3: Name (17 to 20) "int" -4: Type { name: NodeId(3), params: None, optional: false } (17 to 20) +4: Type { name: NodeId(3), args: None, optional: false } (17 to 20) 5: Name (22 to 28) "string" -6: Type { name: NodeId(5), params: None, optional: false } (22 to 28) -7: Params([NodeId(4), NodeId(6)]) (16 to 29) -8: Type { name: NodeId(2), params: Some(NodeId(7)), optional: false } (12 to 16) +6: Type { name: NodeId(5), args: None, optional: false } (22 to 28) +7: TypeArgs([NodeId(4), NodeId(6)]) (16 to 29) +8: Type { name: NodeId(2), args: Some(NodeId(7)), optional: false } (12 to 16) 9: Param { name: NodeId(1), ty: Some(NodeId(8)) } (9 to 16) 10: Params([NodeId(9)]) (8 to 30) 11: Variable (33 to 35) "$x" @@ -22,8 +21,8 @@ snapshot_kind: text 14: Name (42 to 45) "bar" 15: Name (47 to 48) "y" 16: Name (50 to 54) "list" -17: Params([]) (54 to 56) -18: Type { name: NodeId(16), params: Some(NodeId(17)), optional: false } (50 to 54) +17: TypeArgs([]) (54 to 56) +18: Type { name: NodeId(16), args: Some(NodeId(17)), optional: false } (50 to 54) 19: Param { name: NodeId(15), ty: Some(NodeId(18)) } (47 to 54) 20: Params([NodeId(19)]) (46 to 57) 21: Variable (60 to 62) "$y" @@ -64,8 +63,8 @@ snapshot_kind: text 23: () 24: () ==== TYPE ERRORS ==== -Error (NodeId 7): list must have only one type parameter (to allow selection of types, use oneof -- WIP) -Error (NodeId 17): list must have one type parameter +Error (NodeId 7): list must have only one type argument (to allow selection of types, use oneof -- WIP) +Error (NodeId 17): list must have one type argument ==== IR ==== register_count: 0 file_count: 0 diff --git a/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap b/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap index d7abbf5..e851998 100644 --- a/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap @@ -2,33 +2,32 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/let_mismatch.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Variable (4 to 5) "x" 1: Name (7 to 13) "number" -2: Type { name: NodeId(1), params: None, optional: false } (7 to 13) +2: Type { name: NodeId(1), args: None, optional: false } (7 to 13) 3: Int (16 to 18) "10" 4: Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: false } (0 to 18) 5: Variable (32 to 33) "y" 6: Name (35 to 38) "any" -7: Type { name: NodeId(6), params: None, optional: false } (35 to 38) +7: Type { name: NodeId(6), args: None, optional: false } (35 to 38) 8: String (41 to 47) ""spam"" 9: Let { variable_name: NodeId(5), ty: Some(NodeId(7)), initializer: NodeId(8), is_mutable: false } (28 to 47) 10: Variable (60 to 61) "z" 11: Name (63 to 69) "string" -12: Type { name: NodeId(11), params: None, optional: false } (63 to 69) +12: Type { name: NodeId(11), args: None, optional: false } (63 to 69) 13: Int (72 to 75) "123" 14: Let { variable_name: NodeId(10), ty: Some(NodeId(12)), initializer: NodeId(13), is_mutable: false } (56 to 75) 15: Variable (91 to 92) "w" 16: Name (94 to 98) "list" 17: Name (99 to 103) "list" 18: Name (104 to 107) "int" -19: Type { name: NodeId(18), params: None, optional: false } (104 to 107) -20: Params([NodeId(19)]) (103 to 108) -21: Type { name: NodeId(17), params: Some(NodeId(20)), optional: false } (99 to 103) -22: Params([NodeId(21)]) (98 to 109) -23: Type { name: NodeId(16), params: Some(NodeId(22)), optional: false } (94 to 98) +19: Type { name: NodeId(18), args: None, optional: false } (104 to 107) +20: TypeArgs([NodeId(19)]) (103 to 108) +21: Type { name: NodeId(17), args: Some(NodeId(20)), optional: false } (99 to 103) +22: TypeArgs([NodeId(21)]) (98 to 109) +23: Type { name: NodeId(16), args: Some(NodeId(22)), optional: false } (94 to 98) 24: String (116 to 119) "'a'" 25: List([NodeId(24)]) (114 to 120) 26: List([NodeId(25)]) (112 to 122) diff --git a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap index fdda7b3..a366991 100644 --- a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap @@ -2,12 +2,11 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/mut_.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Variable (4 to 5) "x" 1: Name (7 to 10) "int" -2: Type { name: NodeId(1), params: None, optional: false } (7 to 10) +2: Type { name: NodeId(1), args: None, optional: false } (7 to 10) 3: Int (13 to 16) "123" 4: Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: true } (0 to 16) 5: Variable (18 to 20) "$x" diff --git a/src/typechecker.rs b/src/typechecker.rs index c5d4ae5..d2b1562 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -225,12 +225,19 @@ impl<'a> Typechecker<'a> { } AstNode::Type { name, - params, + args, optional, } => { - let ty_id = self.typecheck_type(name, params, optional); + let ty_id = self.typecheck_type(name, args, optional); self.set_node_type_id(node_id, ty_id); } + AstNode::TypeArgs(ref args) => { + for arg in args { + self.typecheck_node(*arg); + } + // Type argument lists are not supposed to be evaluated + self.set_node_type_id(node_id, FORBIDDEN_TYPE); + } AstNode::List(ref items) => { if let Some(first_id) = items.first() { self.typecheck_node(*first_id); @@ -643,7 +650,7 @@ impl<'a> Typechecker<'a> { }; let AstNode::Type { name: in_name, - params: in_params, + args: in_args, optional: in_optional, } = *self.compiler.get_node(*in_ty) else { @@ -651,15 +658,15 @@ impl<'a> Typechecker<'a> { }; let AstNode::Type { name: out_name, - params: out_params, + args: out_args, optional: out_optional, } = *self.compiler.get_node(*out_ty) else { panic!("internal error: type is not a type"); }; InOutType { - in_type: self.typecheck_type(in_name, in_params, in_optional), - out_type: self.typecheck_type(out_name, out_params, out_optional), + in_type: self.typecheck_type(in_name, in_args, in_optional), + out_type: self.typecheck_type(out_name, out_args, out_optional), } }) .collect::>() @@ -767,7 +774,7 @@ impl<'a> Typechecker<'a> { fn typecheck_type( &mut self, name_id: NodeId, - params_id: Option, + args_id: Option, _optional: bool, ) -> TypeId { let name = self.compiler.get_span_contents(name_id); @@ -778,24 +785,24 @@ impl<'a> Typechecker<'a> { // b"binary" => SyntaxShape::Binary, // b"block" => // not possible to pass blocks b"list" => { - if let Some(params_id) = params_id { - self.typecheck_node(params_id); + if let Some(args_id) = args_id { + self.typecheck_node(args_id); - if let AstNode::Params(params) = self.compiler.get_node(params_id) { - if params.len() > 1 { + if let AstNode::TypeArgs(args) = self.compiler.get_node(args_id) { + if args.len() > 1 { let types = - String::from_utf8_lossy(self.compiler.get_span_contents(params_id)); - self.error(format!("list must have only one type parameter (to allow selection of types, use oneof{} -- WIP)", types), params_id); + String::from_utf8_lossy(self.compiler.get_span_contents(args_id)); + self.error(format!("list must have only one type argument (to allow selection of types, use oneof{} -- WIP)", types), args_id); self.push_type(Type::List(UNKNOWN_TYPE)) - } else if params.is_empty() { - self.error("list must have one type parameter", params_id); + } else if args.is_empty() { + self.error("list must have one type argument", args_id); self.push_type(Type::List(UNKNOWN_TYPE)) } else { - let params_ty_id = self.type_id_of(params[0]); - self.push_type(Type::List(params_ty_id)) + let args_ty_id = self.type_id_of(args[0]); + self.push_type(Type::List(args_ty_id)) } } else { - panic!("params are not params"); + panic!("args are not args"); } } else { LIST_ANY_TYPE From 0014d901308c9eb6d81f2bdae869ce64e84f1b91 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:00:53 -0500 Subject: [PATCH 03/12] Format --- src/parser.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index bdf6c3e..57ec49d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -617,14 +617,7 @@ impl Parser { self.rcurly(); span_end = self.position(); - return self.create_node( - AstNode::Closure { - params, - block, - }, - span_start, - span_end, - ); + return self.create_node(AstNode::Closure { params, block }, span_start, span_end); } let rollback_point = self.get_rollback_point(); From bd5372a3176785c98ac157df8bb66471b75625f5 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 4 Mar 2025 14:52:11 -0500 Subject: [PATCH 04/12] Separate Expr and Def types --- src/compiler.rs | 6 +- src/ir_generator.rs | 8 +- src/parser.rs | 232 ++++++++++-------- src/resolver.rs | 87 ++++--- ...nu_parser__test__node_output@alias.nu.snap | 7 +- ...test__node_output@binary_ops_exact.nu.snap | 51 ++-- ...t__node_output@binary_ops_mismatch.nu.snap | 28 +-- ...est__node_output@binary_ops_spaces.nu.snap | 27 +- ...t__node_output@binary_ops_subtypes.nu.snap | 93 ++++--- ...nu_parser__test__node_output@calls.nu.snap | 38 +-- ..._parser__test__node_output@closure.nu.snap | 15 +- ...parser__test__node_output@closure2.nu.snap | 13 +- ...parser__test__node_output@closure3.nu.snap | 22 +- ...w_nu_parser__test__node_output@def.nu.snap | 16 +- ..._test__node_output@def_return_type.nu.snap | 16 +- ...w_nu_parser__test__node_output@for.nu.snap | 51 ++-- ...st__node_output@for_break_continue.nu.snap | 49 ++-- ...w_nu_parser__test__node_output@if_.nu.snap | 35 ++- ...rser__test__node_output@invalid_if.nu.snap | 17 +- ...r__test__node_output@invalid_range.nu.snap | 7 +- ...__test__node_output@invalid_record.nu.snap | 11 +- ...r__test__node_output@invalid_types.nu.snap | 16 +- ..._nu_parser__test__node_output@let_.nu.snap | 9 +- ...er__test__node_output@let_mismatch.nu.snap | 22 +- ..._nu_parser__test__node_output@list.nu.snap | 45 ++-- ...parser__test__node_output@literals.nu.snap | 17 +- ..._nu_parser__test__node_output@loop.nu.snap | 25 +- ...nu_parser__test__node_output@match.nu.snap | 39 ++- ..._nu_parser__test__node_output@math.nu.snap | 9 +- ..._test__node_output@math_precedence.nu.snap | 17 +- ..._nu_parser__test__node_output@mut_.nu.snap | 16 +- src/typechecker.rs | 207 ++++++++-------- 32 files changed, 641 insertions(+), 610 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 98b6c3e..b38797f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,5 +1,5 @@ use crate::errors::SourceError; -use crate::parser::{AstNode, Block, NodeId}; +use crate::parser::{AstNode, Block, Expr, NodeId}; use crate::protocol::Command; use crate::resolver::{DeclId, Frame, NameBindings, ScopeId, VarId, Variable}; use crate::typechecker::{TypeId, Types}; @@ -126,7 +126,9 @@ impl Compiler { if matches!( ast_node, - AstNode::Name | AstNode::Variable | AstNode::Int | AstNode::Float | AstNode::String + AstNode::Name + | AstNode::VarDecl + | AstNode::Expr(Expr::VarRef | Expr::Int | Expr::Float | Expr::String) ) { result.push_str(&format!( " \"{}\"", diff --git a/src/ir_generator.rs b/src/ir_generator.rs index 74a61f4..f2c91cc 100644 --- a/src/ir_generator.rs +++ b/src/ir_generator.rs @@ -1,6 +1,6 @@ use crate::compiler::Compiler; use crate::errors::{Severity, SourceError}; -use crate::parser::{AstNode, NodeId}; +use crate::parser::{AstNode, Expr, NodeId}; use nu_protocol::ast::{Math, Operator}; use nu_protocol::ir::{Instruction, IrBlock, Literal}; use nu_protocol::{RegId, Span}; @@ -97,7 +97,7 @@ impl<'a> IrGenerator<'a> { fn generate_node(&mut self, node_id: NodeId) -> Option { let ast_node = &self.compiler.ast_nodes[node_id.0]; match ast_node { - AstNode::Int => { + AstNode::Expr(Expr::Int) => { let next_reg = self.next_register(); let val = self.compiler.node_as_i64(node_id); self.add_instruction( @@ -109,7 +109,7 @@ impl<'a> IrGenerator<'a> { ); Some(next_reg) } - AstNode::Block(block_id) => { + AstNode::Expr(Expr::Block(block_id)) => { let block = &self.compiler.blocks[block_id.0]; let mut last = None; for id in &block.nodes { @@ -118,7 +118,7 @@ impl<'a> IrGenerator<'a> { } last } - AstNode::BinaryOp { lhs, op, rhs } => { + AstNode::Expr(Expr::BinaryOp { lhs, op, rhs }) => { let l = self.generate_node(*lhs)?; let r = self.generate_node(*rhs)?; let o = self.node_to_operator(*op)?; diff --git a/src/parser.rs b/src/parser.rs index 78626c9..8f98312 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -52,20 +52,19 @@ pub enum BarewordContext { Call, } -// TODO: All nodes with Vec<...> should be moved to their own ID (like BlockId) to allow Copy trait #[derive(Debug, PartialEq, Clone)] -pub enum AstNode { +pub struct Def { + pub name: NodeId, + pub params: NodeId, + pub in_out_types: Option, + pub block: NodeId, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Expr { Int, Float, String, - Name, - Type { - name: NodeId, - args: Option, - optional: bool, - }, - TypeArgs(Vec), - Variable, // Booleans True, @@ -74,6 +73,67 @@ pub enum AstNode { // Empty values Null, + VarRef, + + Closure { + params: Option, + block: NodeId, + }, + + Call { + parts: Vec, + }, + NamedValue { + name: NodeId, + value: NodeId, + }, + BinaryOp { + lhs: NodeId, + op: NodeId, + rhs: NodeId, + }, + Range { + lhs: NodeId, + rhs: NodeId, + }, + List(Vec), + Table { + header: NodeId, + rows: Vec, + }, + Record { + pairs: Vec<(NodeId, NodeId)>, + }, + MemberAccess { + target: NodeId, + field: NodeId, + }, + Block(BlockId), + If { + condition: NodeId, + then_block: NodeId, + else_block: Option, + }, + Match { + target: NodeId, + match_arms: Vec<(NodeId, NodeId)>, + }, +} + +// TODO: All nodes with Vec<...> should be moved to their own ID (like BlockId) to allow Copy trait +#[derive(Debug, PartialEq, Clone)] +pub enum AstNode { + Expr(Expr), + + Name, + Type { + name: NodeId, + args: Option, + optional: bool, + }, + TypeArgs(Vec), + VarDecl, + // Operators Pow, Multiply, @@ -128,12 +188,7 @@ pub enum AstNode { Continue, // Definitions - Def { - name: NodeId, - params: NodeId, - in_out_types: Option, - block: NodeId, - }, + Def(Def), Params(Vec), Param { name: NodeId, @@ -142,10 +197,6 @@ pub enum AstNode { InOutTypes(Vec), /// Input/output type pair for a command InOutType(NodeId, NodeId), - Closure { - params: Option, - block: NodeId, - }, Alias { new_name: NodeId, old_name: NodeId, @@ -159,44 +210,6 @@ pub enum AstNode { FlagShortGroup, // Expressions - Call { - parts: Vec, - }, - NamedValue { - name: NodeId, - value: NodeId, - }, - BinaryOp { - lhs: NodeId, - op: NodeId, - rhs: NodeId, - }, - Range { - lhs: NodeId, - rhs: NodeId, - }, - List(Vec), - Table { - header: NodeId, - rows: Vec, - }, - Record { - pairs: Vec<(NodeId, NodeId)>, - }, - MemberAccess { - target: NodeId, - field: NodeId, - }, - Block(BlockId), - If { - condition: NodeId, - then_block: NodeId, - else_block: Option, - }, - Match { - target: NodeId, - match_arms: Vec<(NodeId, NodeId)>, - }, Statement(NodeId), Garbage, } @@ -293,8 +306,8 @@ impl Parser { let rhs = self.expression(); let span_end = self.get_span_end(rhs); - return self.create_node( - AstNode::BinaryOp { + return self.create_expr( + Expr::BinaryOp { lhs: leftmost, op, rhs, @@ -345,8 +358,8 @@ impl Parser { let lhs = expr_stack.last_mut().map_or(&mut leftmost, |l| &mut l.1); let (span_start, span_end) = self.spanning(*lhs, rhs); - *lhs = self.create_node( - AstNode::BinaryOp { lhs: *lhs, op, rhs }, + *lhs = self.create_expr( + Expr::BinaryOp { lhs: *lhs, op, rhs }, span_start, span_end, ); @@ -365,11 +378,7 @@ impl Parser { let (span_start, span_end) = self.spanning(*lhs, rhs); - *lhs = self.create_node( - AstNode::BinaryOp { lhs: *lhs, op, rhs }, - span_start, - span_end, - ); + *lhs = self.create_expr(Expr::BinaryOp { lhs: *lhs, op, rhs }, span_start, span_end); } leftmost @@ -400,19 +409,19 @@ impl Parser { } } Token::LSquare => self.list_or_table(), - Token::Int => self.advance_node(AstNode::Int, span), - Token::Float => self.advance_node(AstNode::Float, span), - Token::DoubleQuotedString => self.advance_node(AstNode::String, span), - Token::SingleQuotedString => self.advance_node(AstNode::String, span), + Token::Int => self.advance_node(AstNode::Expr(Expr::Int), span), + Token::Float => self.advance_node(AstNode::Expr(Expr::Float), span), + Token::DoubleQuotedString => self.advance_node(AstNode::Expr(Expr::String), span), + Token::SingleQuotedString => self.advance_node(AstNode::Expr(Expr::String), span), Token::Dollar => self.variable(), Token::Bareword => match self.compiler.get_span_contents_manual(span.start, span.end) { - b"true" => self.advance_node(AstNode::True, span), - b"false" => self.advance_node(AstNode::False, span), - b"null" => self.advance_node(AstNode::Null, span), + b"true" => self.advance_node(AstNode::Expr(Expr::True), span), + b"false" => self.advance_node(AstNode::Expr(Expr::False), span), + b"null" => self.advance_node(AstNode::Expr(Expr::Null), span), _ => match bareword_context { BarewordContext::String => { let node_id = self.name(); - self.compiler.ast_nodes[node_id.0] = AstNode::String; + self.compiler.ast_nodes[node_id.0] = AstNode::Expr(Expr::String); node_id } BarewordContext::Call => self.call(), @@ -438,8 +447,7 @@ impl Parser { let rhs = self.simple_expression(BarewordContext::String); let span_end = self.get_span_end(rhs); - expr = - self.create_node(AstNode::Range { lhs: expr, rhs }, span_start, span_end); + expr = self.create_expr(Expr::Range { lhs: expr, rhs }, span_start, span_end); } } else if self.is_dot() { // Member access @@ -460,9 +468,9 @@ impl Parser { let span_end = self.get_span_end(field_or_call); match self.compiler.get_node_mut(field_or_call) { - AstNode::Variable | AstNode::Name => { - expr = self.create_node( - AstNode::MemberAccess { + AstNode::Expr(Expr::VarRef) | AstNode::Name => { + expr = self.create_expr( + Expr::MemberAccess { target: expr, field: field_or_call, }, @@ -485,6 +493,12 @@ impl Parser { self.create_node(node, span.start, span.end) } + pub fn advance_expr(&mut self, node: Expr, span: Span) -> NodeId { + self.tokens.advance(); + self.create_expr(node, span.start, span.end) + } + + /// A variable name (reference, not declaration) pub fn variable(&mut self) -> NodeId { if self.is_dollar() { let span_start = self.position(); @@ -492,7 +506,7 @@ impl Parser { if let (Token::Bareword, name_span) = self.tokens.peek() { self.tokens.advance(); - self.create_node(AstNode::Variable, span_start, name_span.end) + self.create_expr(Expr::VarRef, span_start, name_span.end) } else { self.error("variable name must be a bareword") } @@ -501,6 +515,7 @@ impl Parser { } } + /// A variable name (declaration, not reference) pub fn variable_decl(&mut self) -> NodeId { let _span = span!(); @@ -512,7 +527,7 @@ impl Parser { if let (Token::Bareword, name_span) = self.tokens.peek() { self.tokens.advance(); - self.create_node(AstNode::Variable, span_start, name_span.end) + self.create_node(AstNode::VarDecl, span_start, name_span.end) } else { self.error("variable assignment name must be a bareword") } @@ -543,7 +558,7 @@ impl Parser { let span_end = self.position(); - self.create_node(AstNode::Call { parts }, span_start, span_end) + self.create_expr(Expr::Call { parts }, span_start, span_end) } pub fn list_or_table(&mut self) -> NodeId { @@ -566,7 +581,10 @@ impl Parser { } else if self.is_semicolon() { if items.len() != 1 { self.error("semicolon to create table should immediately follow headers"); - } else if !matches!(self.compiler.get_node(items[0]), AstNode::List(_)) { + } else if !matches!( + self.compiler.get_node(items[0]), + AstNode::Expr(Expr::List(_)) + ) { self.error_on_node("tables require a list for their headers", items[0]) } self.tokens.advance(); @@ -584,8 +602,8 @@ impl Parser { if is_table { let header = items.remove(0); - self.create_node( - AstNode::Table { + self.create_expr( + Expr::Table { header, rows: items, }, @@ -593,7 +611,7 @@ impl Parser { span_end, ) } else { - self.create_node(AstNode::List(items), span_start, span_end) + self.create_expr(Expr::List(items), span_start, span_end) } } @@ -617,7 +635,7 @@ impl Parser { self.rcurly(); span_end = self.position(); - return self.create_node(AstNode::Closure { params, block }, span_start, span_end); + return self.create_expr(Expr::Closure { params, block }, span_start, span_end); } let rollback_point = self.get_rollback_point(); @@ -656,8 +674,8 @@ impl Parser { span_end = self.position(); - self.create_node( - AstNode::Closure { + self.create_expr( + Expr::Closure { params: None, block, }, @@ -665,7 +683,7 @@ impl Parser { span_end, ) } else { - self.create_node(AstNode::Record { pairs: items }, span_start, span_end) + self.create_expr(Expr::Record { pairs: items }, span_start, span_end) } } @@ -722,8 +740,12 @@ impl Parser { pub fn string(&mut self) -> NodeId { match self.tokens.peek() { - (Token::DoubleQuotedString, span) => self.advance_node(AstNode::String, span), - (Token::SingleQuotedString, span) => self.advance_node(AstNode::String, span), + (Token::DoubleQuotedString, span) => { + self.advance_node(AstNode::Expr(Expr::String), span) + } + (Token::SingleQuotedString, span) => { + self.advance_node(AstNode::Expr(Expr::String), span) + } _ => self.error("expected: string"), } } @@ -805,7 +827,7 @@ impl Parser { } } - self.create_node(AstNode::Match { target, match_arms }, span_start, span_end) + self.create_expr(Expr::Match { target, match_arms }, span_start, span_end) } pub fn if_expression(&mut self) -> NodeId { @@ -839,8 +861,8 @@ impl Parser { None }; - self.create_node( - AstNode::If { + self.create_expr( + Expr::If { condition, then_block, else_block, @@ -1039,7 +1061,7 @@ impl Parser { let name = match self.tokens.peek() { (Token::Bareword, span) => self.advance_node(AstNode::Name, span), (Token::DoubleQuotedString | Token::SingleQuotedString, span) => { - self.advance_node(AstNode::String, span) + self.advance_expr(Expr::String, span) } _ => return self.error("expected def name"), }; @@ -1055,12 +1077,12 @@ impl Parser { let span_end = self.get_span_end(block); self.create_node( - AstNode::Def { + AstNode::Def(Def { name, params, in_out_types, block, - }, + }), span_start, span_end, ) @@ -1213,8 +1235,8 @@ impl Parser { self.compiler.blocks.push(Block::new(code_body)); let span_end = self.position(); - self.create_node( - AstNode::Block(BlockId(self.compiler.blocks.len() - 1)), + self.create_expr( + Expr::Block(BlockId(self.compiler.blocks.len() - 1)), span_start, span_end, ) @@ -1531,6 +1553,14 @@ impl Parser { node_id } + pub fn create_expr(&mut self, expr: Expr, span_start: usize, span_end: usize) -> NodeId { + self.compiler.spans.push(Span { + start: span_start, + end: span_end, + }); + self.compiler.push_node(AstNode::Expr(expr)) + } + pub fn create_node(&mut self, ast_node: AstNode, span_start: usize, span_end: usize) -> NodeId { self.compiler.spans.push(Span { start: span_start, diff --git a/src/resolver.rs b/src/resolver.rs index bbdb7ca..f6327cf 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,3 +1,4 @@ +use crate::parser::{Def, Expr}; use crate::protocol::{Command, Declaration}; use crate::{ compiler::Compiler, @@ -199,31 +200,13 @@ impl<'a> Resolver<'a> { pub fn resolve_node(&mut self, node_id: NodeId) { // TODO: Move node_id param to the end, same as in typechecker match self.compiler.ast_nodes[node_id.0] { - AstNode::Variable => self.resolve_variable(node_id), - AstNode::Call { ref parts } => self.resolve_call(node_id, parts), - AstNode::Block(block_id) => self.resolve_block(node_id, block_id, None), - AstNode::Closure { params, block } => { - // making sure the closure parameters and body end up in the same scope frame - let closure_scope = if let Some(params) = params { - self.enter_scope(block); - self.resolve_node(params); - Some(self.exit_scope()) - } else { - None - }; - - let AstNode::Block(block_id) = self.compiler.ast_nodes[block.0] else { - panic!("internal error: closure's body is not a block"); - }; - - self.resolve_block(block, block_id, closure_scope); - } - AstNode::Def { + AstNode::Expr(ref expr) => self.resolve_expr(expr.clone(), node_id), + AstNode::Def(Def { name, params, in_out_types: _, block, - } => { + }) => { // define the command before the block to enable recursive calls self.define_decl(name); @@ -232,7 +215,7 @@ impl<'a> Resolver<'a> { self.resolve_node(params); let def_scope = self.exit_scope(); - let AstNode::Block(block_id) = self.compiler.ast_nodes[block.0] else { + let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { panic!("internal error: command definition's body is not a block"); }; @@ -278,7 +261,7 @@ impl<'a> Resolver<'a> { self.resolve_node(range); - let AstNode::Block(block_id) = self.compiler.ast_nodes[block.0] else { + let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { panic!("internal error: for's body is not a block"); }; @@ -287,36 +270,65 @@ impl<'a> Resolver<'a> { AstNode::Loop { block } => { self.resolve_node(block); } - AstNode::BinaryOp { lhs, op: _, rhs } => { + AstNode::Statement(node) => self.resolve_node(node), + AstNode::Param { .. } => (/* seems unused for now */), + AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ), + // All remaining matches do not contain NodeId => there is nothing to resolve + _ => (), + } + } + + pub fn resolve_expr(&mut self, expr: Expr, node_id: NodeId) { + match expr { + Expr::VarRef => self.resolve_variable(node_id), + Expr::Call { ref parts } => self.resolve_call(node_id, parts), + Expr::Block(block_id) => self.resolve_block(node_id, block_id, None), + Expr::Closure { params, block } => { + // making sure the closure parameters and body end up in the same scope frame + let closure_scope = if let Some(params) = params { + self.enter_scope(block); + self.resolve_node(params); + Some(self.exit_scope()) + } else { + None + }; + + let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { + panic!("internal error: closure's body is not a block"); + }; + + self.resolve_block(block, block_id, closure_scope); + } + Expr::BinaryOp { lhs, op: _, rhs } => { self.resolve_node(lhs); self.resolve_node(rhs); } - AstNode::Range { lhs, rhs } => { + Expr::Range { lhs, rhs } => { self.resolve_node(lhs); self.resolve_node(rhs); } - AstNode::List(ref nodes) => { + Expr::List(ref nodes) => { for node in nodes { self.resolve_node(*node); } } - AstNode::Table { header, ref rows } => { + Expr::Table { header, ref rows } => { self.resolve_node(header); for row in rows { self.resolve_node(*row); } } - AstNode::Record { ref pairs } => { + Expr::Record { ref pairs } => { for (key, val) in pairs { self.resolve_node(*key); self.resolve_node(*val); } } - AstNode::MemberAccess { target, field } => { + Expr::MemberAccess { target, field } => { self.resolve_node(target); self.resolve_node(field); } - AstNode::If { + Expr::If { condition, then_block, else_block, @@ -327,7 +339,7 @@ impl<'a> Resolver<'a> { self.resolve_node(block); } } - AstNode::Match { + Expr::Match { target, ref match_arms, } => { @@ -337,12 +349,13 @@ impl<'a> Resolver<'a> { self.resolve_node(*arm_rhs); } } - AstNode::Statement(node) => self.resolve_node(node), - AstNode::Param { .. } => (/* seems unused for now */), - AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ), - AstNode::NamedValue { .. } => (/* seems unused for now */), - // All remaining matches do not contain NodeId => there is nothing to resolve - _ => (), + Expr::NamedValue { .. } => (/* seems unused for now */), + Expr::Int + | Expr::Float + | Expr::String + | Expr::True + | Expr::False + | Expr::Null => {} } } diff --git a/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap b/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap index c26dc91..5b96ec3 100644 --- a/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap @@ -2,16 +2,15 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/alias.nu -snapshot_kind: text --- ==== COMPILER ==== -0: String (6 to 19) ""fancy alias"" +0: Expr(String) (6 to 19) ""fancy alias"" 1: Name (22 to 25) "foo" 2: Alias { new_name: NodeId(0), old_name: NodeId(1) } (0 to 25) 3: Name (27 to 32) "fancy" 4: Name (33 to 38) "alias" -5: Call { parts: [NodeId(3), NodeId(4)] } (33 to 38) -6: Block(BlockId(0)) (0 to 39) +5: Expr(Call { parts: [NodeId(3), NodeId(4)] }) (33 to 38) +6: Expr(Block(BlockId(0))) (0 to 39) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(6) decls: [ fancy alias: NodeId(0) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@binary_ops_exact.nu.snap b/src/snapshots/new_nu_parser__test__node_output@binary_ops_exact.nu.snap index 9233e08..ce84e25 100644 --- a/src/snapshots/new_nu_parser__test__node_output@binary_ops_exact.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@binary_ops_exact.nu.snap @@ -2,41 +2,40 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/binary_ops_exact.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (0 to 1) "1" +0: Expr(Int) (0 to 1) "1" 1: Equal (2 to 4) -2: Int (5 to 6) "1" -3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 6) -4: True (8 to 12) -5: List([NodeId(4)]) (7 to 12) +2: Expr(Int) (5 to 6) "1" +3: Expr(BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) }) (0 to 6) +4: Expr(True) (8 to 12) +5: Expr(List([NodeId(4)])) (7 to 12) 6: Append (14 to 16) -7: False (17 to 22) -8: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(7) } (7 to 22) -9: Int (23 to 24) "1" +7: Expr(False) (17 to 22) +8: Expr(BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(7) }) (7 to 22) +9: Expr(Int) (23 to 24) "1" 10: Plus (25 to 26) -11: Int (27 to 28) "1" -12: BinaryOp { lhs: NodeId(9), op: NodeId(10), rhs: NodeId(11) } (23 to 28) -13: Float (29 to 32) "1.0" +11: Expr(Int) (27 to 28) "1" +12: Expr(BinaryOp { lhs: NodeId(9), op: NodeId(10), rhs: NodeId(11) }) (23 to 28) +13: Expr(Float) (29 to 32) "1.0" 14: Plus (33 to 34) -15: Float (35 to 38) "1.0" -16: BinaryOp { lhs: NodeId(13), op: NodeId(14), rhs: NodeId(15) } (29 to 38) -17: True (39 to 43) +15: Expr(Float) (35 to 38) "1.0" +16: Expr(BinaryOp { lhs: NodeId(13), op: NodeId(14), rhs: NodeId(15) }) (29 to 38) +17: Expr(True) (39 to 43) 18: And (44 to 47) -19: False (48 to 53) -20: BinaryOp { lhs: NodeId(17), op: NodeId(18), rhs: NodeId(19) } (39 to 53) -21: String (54 to 59) ""foo"" +19: Expr(False) (48 to 53) +20: Expr(BinaryOp { lhs: NodeId(17), op: NodeId(18), rhs: NodeId(19) }) (39 to 53) +21: Expr(String) (54 to 59) ""foo"" 22: RegexMatch (60 to 62) -23: String (63 to 68) "".*o"" -24: BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) } (54 to 68) -25: Int (69 to 70) "1" +23: Expr(String) (63 to 68) "".*o"" +24: Expr(BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) }) (54 to 68) +25: Expr(Int) (69 to 70) "1" 26: In (71 to 73) -27: Int (75 to 76) "1" -28: Int (78 to 79) "2" -29: List([NodeId(27), NodeId(28)]) (74 to 79) -30: BinaryOp { lhs: NodeId(25), op: NodeId(26), rhs: NodeId(29) } (69 to 79) -31: Block(BlockId(0)) (0 to 81) +27: Expr(Int) (75 to 76) "1" +28: Expr(Int) (78 to 79) "2" +29: Expr(List([NodeId(27), NodeId(28)])) (74 to 79) +30: Expr(BinaryOp { lhs: NodeId(25), op: NodeId(26), rhs: NodeId(29) }) (69 to 79) +31: Expr(Block(BlockId(0))) (0 to 81) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(31) (empty) ==== TYPES ==== diff --git a/src/snapshots/new_nu_parser__test__node_output@binary_ops_mismatch.nu.snap b/src/snapshots/new_nu_parser__test__node_output@binary_ops_mismatch.nu.snap index fa39f5a..5b0b48e 100644 --- a/src/snapshots/new_nu_parser__test__node_output@binary_ops_mismatch.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@binary_ops_mismatch.nu.snap @@ -4,23 +4,23 @@ expression: evaluate_example(path) input_file: tests/binary_ops_mismatch.nu --- ==== COMPILER ==== -0: String (0 to 3) ""a"" +0: Expr(String) (0 to 3) ""a"" 1: Plus (4 to 5) -2: Float (6 to 9) "1.0" -3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 9) -4: String (10 to 13) ""a"" +2: Expr(Float) (6 to 9) "1.0" +3: Expr(BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) }) (0 to 9) +4: Expr(String) (10 to 13) ""a"" 5: Append (14 to 16) -6: Float (17 to 20) "1.0" -7: BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) } (10 to 20) -8: True (21 to 25) +6: Expr(Float) (17 to 20) "1.0" +7: Expr(BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) }) (10 to 20) +8: Expr(True) (21 to 25) 9: And (26 to 29) -10: String (30 to 33) ""a"" -11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (21 to 33) -12: True (34 to 38) +10: Expr(String) (30 to 33) ""a"" +11: Expr(BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) }) (21 to 33) +12: Expr(True) (34 to 38) 13: NotRegexMatch (39 to 41) -14: String (42 to 48) ""true"" -15: BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) } (34 to 48) -16: Block(BlockId(0)) (0 to 49) +14: Expr(String) (42 to 48) ""true"" +15: Expr(BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) }) (34 to 48) +16: Expr(Block(BlockId(0))) (0 to 49) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(16) (empty) ==== TYPES ==== @@ -50,4 +50,4 @@ Error (NodeId 13): type mismatch: unsupported string operation between bool and register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 0): node String not suported yet +Error (NodeId 0): node Expr(String) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@binary_ops_spaces.nu.snap b/src/snapshots/new_nu_parser__test__node_output@binary_ops_spaces.nu.snap index bfe066d..ef0c389 100644 --- a/src/snapshots/new_nu_parser__test__node_output@binary_ops_spaces.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@binary_ops_spaces.nu.snap @@ -2,26 +2,25 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/binary_ops_spaces.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (0 to 1) "1" +0: Expr(Int) (0 to 1) "1" 1: Plus (2 to 3) -2: Int (4 to 5) "2" -3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 5) -4: Int (6 to 7) "1" +2: Expr(Int) (4 to 5) "2" +3: Expr(BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) }) (0 to 5) +4: Expr(Int) (6 to 7) "1" 5: Plus (7 to 8) -6: Int (9 to 10) "2" -7: BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) } (6 to 10) -8: Int (11 to 12) "1" +6: Expr(Int) (9 to 10) "2" +7: Expr(BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) }) (6 to 10) +8: Expr(Int) (11 to 12) "1" 9: Plus (13 to 14) -10: Int (14 to 15) "2" -11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (11 to 15) -12: Int (16 to 17) "1" +10: Expr(Int) (14 to 15) "2" +11: Expr(BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) }) (11 to 15) +12: Expr(Int) (16 to 17) "1" 13: Plus (17 to 18) -14: Int (18 to 19) "2" -15: BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) } (16 to 19) -16: Block(BlockId(0)) (0 to 20) +14: Expr(Int) (18 to 19) "2" +15: Expr(BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) }) (16 to 19) +16: Expr(Block(BlockId(0))) (0 to 20) ==== COMPILER ERRORS ==== Error (NodeId 5): missing space before operator Error (NodeId 9): missing space after operator diff --git a/src/snapshots/new_nu_parser__test__node_output@binary_ops_subtypes.nu.snap b/src/snapshots/new_nu_parser__test__node_output@binary_ops_subtypes.nu.snap index 321c8fe..d180e8b 100644 --- a/src/snapshots/new_nu_parser__test__node_output@binary_ops_subtypes.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@binary_ops_subtypes.nu.snap @@ -2,63 +2,62 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/binary_ops_subtypes.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (0 to 1) "1" +0: Expr(Int) (0 to 1) "1" 1: Equal (2 to 4) -2: Float (5 to 8) "1.0" -3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 8) -4: String (9 to 12) ""a"" +2: Expr(Float) (5 to 8) "1.0" +3: Expr(BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) }) (0 to 8) +4: Expr(String) (9 to 12) ""a"" 5: Equal (13 to 15) -6: Float (16 to 19) "1.0" -7: BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) } (9 to 19) -8: Int (20 to 21) "1" +6: Expr(Float) (16 to 19) "1.0" +7: Expr(BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) }) (9 to 19) +8: Expr(Int) (20 to 21) "1" 9: Plus (22 to 23) -10: Float (24 to 27) "1.0" -11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (20 to 27) -12: Int (29 to 30) "1" -13: List([NodeId(12)]) (28 to 30) +10: Expr(Float) (24 to 27) "1.0" +11: Expr(BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) }) (20 to 27) +12: Expr(Int) (29 to 30) "1" +13: Expr(List([NodeId(12)])) (28 to 30) 14: Append (32 to 34) -15: Float (35 to 38) "1.0" -16: BinaryOp { lhs: NodeId(13), op: NodeId(14), rhs: NodeId(15) } (28 to 38) -17: Float (40 to 43) "1.0" -18: Int (44 to 45) "1" -19: List([NodeId(17), NodeId(18)]) (39 to 45) +15: Expr(Float) (35 to 38) "1.0" +16: Expr(BinaryOp { lhs: NodeId(13), op: NodeId(14), rhs: NodeId(15) }) (28 to 38) +17: Expr(Float) (40 to 43) "1.0" +18: Expr(Int) (44 to 45) "1" +19: Expr(List([NodeId(17), NodeId(18)])) (39 to 45) 20: Append (47 to 49) -21: String (50 to 53) ""a"" -22: BinaryOp { lhs: NodeId(19), op: NodeId(20), rhs: NodeId(21) } (39 to 53) -23: Int (56 to 57) "1" -24: List([NodeId(23)]) (55 to 57) -25: Int (60 to 61) "2" -26: List([NodeId(25)]) (59 to 61) -27: List([NodeId(24), NodeId(26)]) (54 to 62) +21: Expr(String) (50 to 53) ""a"" +22: Expr(BinaryOp { lhs: NodeId(19), op: NodeId(20), rhs: NodeId(21) }) (39 to 53) +23: Expr(Int) (56 to 57) "1" +24: Expr(List([NodeId(23)])) (55 to 57) +25: Expr(Int) (60 to 61) "2" +26: Expr(List([NodeId(25)])) (59 to 61) +27: Expr(List([NodeId(24), NodeId(26)])) (54 to 62) 28: Append (64 to 66) -29: Int (69 to 70) "3" -30: List([NodeId(29)]) (68 to 70) -31: List([NodeId(30)]) (67 to 71) -32: BinaryOp { lhs: NodeId(27), op: NodeId(28), rhs: NodeId(31) } (54 to 71) -33: Int (75 to 76) "1" -34: List([NodeId(33)]) (74 to 76) -35: Int (79 to 80) "2" -36: List([NodeId(35)]) (78 to 80) -37: List([NodeId(34), NodeId(36)]) (73 to 81) +29: Expr(Int) (69 to 70) "3" +30: Expr(List([NodeId(29)])) (68 to 70) +31: Expr(List([NodeId(30)])) (67 to 71) +32: Expr(BinaryOp { lhs: NodeId(27), op: NodeId(28), rhs: NodeId(31) }) (54 to 71) +33: Expr(Int) (75 to 76) "1" +34: Expr(List([NodeId(33)])) (74 to 76) +35: Expr(Int) (79 to 80) "2" +36: Expr(List([NodeId(35)])) (78 to 80) +37: Expr(List([NodeId(34), NodeId(36)])) (73 to 81) 38: Append (83 to 85) -39: Float (88 to 91) "3.0" -40: List([NodeId(39)]) (87 to 91) -41: List([NodeId(40)]) (86 to 92) -42: BinaryOp { lhs: NodeId(37), op: NodeId(38), rhs: NodeId(41) } (73 to 92) -43: Int (94 to 95) "1" +39: Expr(Float) (88 to 91) "3.0" +40: Expr(List([NodeId(39)])) (87 to 91) +41: Expr(List([NodeId(40)])) (86 to 92) +42: Expr(BinaryOp { lhs: NodeId(37), op: NodeId(38), rhs: NodeId(41) }) (73 to 92) +43: Expr(Int) (94 to 95) "1" 44: In (96 to 98) -45: Float (100 to 103) "1.0" -46: Int (105 to 106) "1" -47: List([NodeId(45), NodeId(46)]) (99 to 106) -48: BinaryOp { lhs: NodeId(43), op: NodeId(44), rhs: NodeId(47) } (94 to 106) -49: Float (108 to 111) "2.3" +45: Expr(Float) (100 to 103) "1.0" +46: Expr(Int) (105 to 106) "1" +47: Expr(List([NodeId(45), NodeId(46)])) (99 to 106) +48: Expr(BinaryOp { lhs: NodeId(43), op: NodeId(44), rhs: NodeId(47) }) (94 to 106) +49: Expr(Float) (108 to 111) "2.3" 50: Modulo (112 to 115) -51: Int (116 to 117) "1" -52: BinaryOp { lhs: NodeId(49), op: NodeId(50), rhs: NodeId(51) } (108 to 117) -53: Block(BlockId(0)) (0 to 118) +51: Expr(Int) (116 to 117) "1" +52: Expr(BinaryOp { lhs: NodeId(49), op: NodeId(50), rhs: NodeId(51) }) (108 to 117) +53: Expr(Block(BlockId(0))) (0 to 118) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(53) (empty) ==== TYPES ==== @@ -121,4 +120,4 @@ register_count: 1 file_count: 0 0: LoadLiteral { dst: RegId(0), lit: Int(1) } ==== IR ERRORS ==== -Error (NodeId 2): node Float not suported yet +Error (NodeId 2): node Expr(Float) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap index d450b35..7c611f1 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap @@ -6,12 +6,12 @@ input_file: tests/calls.nu ==== COMPILER ==== 0: Name (0 to 4) "spam" 1: Name (5 to 8) "foo" -2: String (9 to 14) ""bar"" -3: Int (16 to 17) "1" +2: Expr(String) (9 to 14) ""bar"" +3: Expr(Int) (16 to 17) "1" 4: Plus (18 to 19) -5: Int (20 to 21) "2" -6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (16 to 21) -7: Call { parts: [NodeId(0), NodeId(1), NodeId(2), NodeId(6)] } (5 to 22) +5: Expr(Int) (20 to 21) "2" +6: Expr(BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) }) (16 to 21) +7: Expr(Call { parts: [NodeId(0), NodeId(1), NodeId(2), NodeId(6)] }) (5 to 22) 8: Name (28 to 36) "existing" 9: Name (38 to 39) "a" 10: Name (41 to 47) "string" @@ -26,24 +26,24 @@ input_file: tests/calls.nu 19: Type { name: NodeId(18), args: None, optional: false } (63 to 66) 20: Param { name: NodeId(17), ty: Some(NodeId(19)) } (60 to 66) 21: Params([NodeId(12), NodeId(16), NodeId(20)]) (37 to 67) -22: Variable (72 to 74) "$a" -23: Variable (76 to 78) "$b" -24: Variable (80 to 82) "$c" -25: List([NodeId(22), NodeId(23), NodeId(24)]) (70 to 82) -26: Block(BlockId(0)) (68 to 85) -27: Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) } (24 to 85) +22: Expr(VarRef) (72 to 74) "$a" +23: Expr(VarRef) (76 to 78) "$b" +24: Expr(VarRef) (80 to 82) "$c" +25: Expr(List([NodeId(22), NodeId(23), NodeId(24)])) (70 to 82) +26: Expr(Block(BlockId(0))) (68 to 85) +27: Def(Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) }) (24 to 85) 28: Name (86 to 94) "existing" 29: Name (95 to 98) "foo" -30: String (100 to 104) ""ba"" +30: Expr(String) (100 to 104) ""ba"" 31: Plus (105 to 106) -32: String (107 to 110) ""r"" -33: BinaryOp { lhs: NodeId(30), op: NodeId(31), rhs: NodeId(32) } (100 to 110) -34: Int (112 to 113) "3" -35: Call { parts: [NodeId(28), NodeId(29), NodeId(33), NodeId(34)] } (95 to 113) +32: Expr(String) (107 to 110) ""r"" +33: Expr(BinaryOp { lhs: NodeId(30), op: NodeId(31), rhs: NodeId(32) }) (100 to 110) +34: Expr(Int) (112 to 113) "3" +35: Expr(Call { parts: [NodeId(28), NodeId(29), NodeId(33), NodeId(34)] }) (95 to 113) 36: Name (115 to 128) "foo/bar/spam " -37: Call { parts: [NodeId(36)] } (127 to 127) -38: Block(BlockId(1)) (0 to 128) +37: Expr(Call { parts: [NodeId(36)] }) (127 to 127) +38: Expr(Block(BlockId(1))) (0 to 128) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(38) decls: [ existing: NodeId(8) ] @@ -93,4 +93,4 @@ input_file: tests/calls.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 7): node Call { parts: [NodeId(0), NodeId(1), NodeId(2), NodeId(6)] } not suported yet +Error (NodeId 7): node Expr(Call { parts: [NodeId(0), NodeId(1), NodeId(2), NodeId(6)] }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@closure.nu.snap b/src/snapshots/new_nu_parser__test__node_output@closure.nu.snap index 00ef4a6..bce2fef 100644 --- a/src/snapshots/new_nu_parser__test__node_output@closure.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@closure.nu.snap @@ -2,7 +2,6 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/closure.nu -snapshot_kind: text --- ==== COMPILER ==== 0: Name (3 to 4) "a" @@ -10,14 +9,14 @@ snapshot_kind: text 2: Name (6 to 7) "b" 3: Param { name: NodeId(2), ty: None } (6 to 7) 4: Params([NodeId(1), NodeId(3)]) (2 to 8) -5: Variable (9 to 11) "$a" +5: Expr(VarRef) (9 to 11) "$a" 6: Plus (12 to 13) -7: Variable (14 to 16) "$b" -8: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(7) } (9 to 16) -9: Block(BlockId(0)) (9 to 16) -10: Closure { params: Some(NodeId(4)), block: NodeId(9) } (0 to 17) -11: Variable (18 to 20) "$a" -12: Block(BlockId(1)) (0 to 42) +7: Expr(VarRef) (14 to 16) "$b" +8: Expr(BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(7) }) (9 to 16) +9: Expr(Block(BlockId(0))) (9 to 16) +10: Expr(Closure { params: Some(NodeId(4)), block: NodeId(9) }) (0 to 17) +11: Expr(VarRef) (18 to 20) "$a" +12: Expr(Block(BlockId(1))) (0 to 42) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(12) (empty) 1: Frame Scope, node_id: NodeId(9) diff --git a/src/snapshots/new_nu_parser__test__node_output@closure2.nu.snap b/src/snapshots/new_nu_parser__test__node_output@closure2.nu.snap index f192ad4..77bdc42 100644 --- a/src/snapshots/new_nu_parser__test__node_output@closure2.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@closure2.nu.snap @@ -2,16 +2,15 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/closure2.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 6) "$a" +0: Expr(VarRef) (4 to 6) "$a" 1: Plus (7 to 8) -2: Variable (9 to 11) "$b" -3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (4 to 11) -4: Block(BlockId(0)) (4 to 12) -5: Closure { params: None, block: NodeId(4) } (0 to 13) -6: Block(BlockId(1)) (0 to 14) +2: Expr(VarRef) (9 to 11) "$b" +3: Expr(BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) }) (4 to 11) +4: Expr(Block(BlockId(0))) (4 to 12) +5: Expr(Closure { params: None, block: NodeId(4) }) (0 to 13) +6: Expr(Block(BlockId(1))) (0 to 14) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(6) (empty) 1: Frame Scope, node_id: NodeId(4) (empty) diff --git a/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap b/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap index 670e39c..c1b3f27 100644 --- a/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap @@ -4,7 +4,7 @@ expression: evaluate_example(path) input_file: tests/closure3.nu --- ==== COMPILER ==== -0: Variable (4 to 11) "closure" +0: VarDecl (4 to 11) "closure" 1: Name (16 to 17) "a" 2: Name (19 to 22) "int" 3: Type { name: NodeId(2), args: None, optional: false } (19 to 22) @@ -14,20 +14,20 @@ input_file: tests/closure3.nu 7: Type { name: NodeId(6), args: None, optional: false } (27 to 30) 8: Param { name: NodeId(5), ty: Some(NodeId(7)) } (24 to 30) 9: Params([NodeId(4), NodeId(8)]) (15 to 31) -10: Variable (32 to 34) "$a" +10: Expr(VarRef) (32 to 34) "$a" 11: Plus (35 to 36) -12: Variable (37 to 39) "$b" +12: Expr(VarRef) (37 to 39) "$b" 13: LessThan (40 to 41) -14: Int (42 to 43) "5" -15: BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) } (32 to 39) -16: BinaryOp { lhs: NodeId(15), op: NodeId(13), rhs: NodeId(14) } (32 to 43) -17: Block(BlockId(0)) (32 to 43) -18: Closure { params: Some(NodeId(9)), block: NodeId(17) } (14 to 44) +14: Expr(Int) (42 to 43) "5" +15: Expr(BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) }) (32 to 39) +16: Expr(BinaryOp { lhs: NodeId(15), op: NodeId(13), rhs: NodeId(14) }) (32 to 43) +17: Expr(Block(BlockId(0))) (32 to 43) +18: Expr(Closure { params: Some(NodeId(9)), block: NodeId(17) }) (14 to 44) 19: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(18), is_mutable: false } (0 to 44) 20: Name (46 to 52) "filter" -21: Variable (53 to 61) "$closure" -22: Call { parts: [NodeId(20), NodeId(21)] } (53 to 61) -23: Block(BlockId(1)) (0 to 62) +21: Expr(VarRef) (53 to 61) "$closure" +22: Expr(Call { parts: [NodeId(20), NodeId(21)] }) (53 to 61) +23: Expr(Block(BlockId(1))) (0 to 62) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(23) variables: [ closure: NodeId(0) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap index 4ac1c3b..d62653f 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap @@ -22,13 +22,13 @@ input_file: tests/def.nu 15: Type { name: NodeId(8), args: Some(NodeId(14)), optional: false } (22 to 26) 16: Param { name: NodeId(7), ty: Some(NodeId(15)) } (19 to 26) 17: Params([NodeId(2), NodeId(6), NodeId(16)]) (8 to 39) -18: Variable (44 to 46) "$x" -19: Variable (47 to 49) "$y" -20: Variable (51 to 53) "$z" -21: List([NodeId(18), NodeId(19), NodeId(20)]) (42 to 54) -22: Block(BlockId(0)) (40 to 57) -23: Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) } (0 to 57) -24: Block(BlockId(1)) (0 to 57) +18: Expr(VarRef) (44 to 46) "$x" +19: Expr(VarRef) (47 to 49) "$y" +20: Expr(VarRef) (51 to 53) "$z" +21: Expr(List([NodeId(18), NodeId(19), NodeId(20)])) (42 to 54) +22: Expr(Block(BlockId(0))) (40 to 57) +23: Def(Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) }) (0 to 57) +24: Expr(Block(BlockId(1))) (0 to 57) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(24) decls: [ foo: NodeId(0) ] @@ -64,4 +64,4 @@ input_file: tests/def.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 23): node Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) } not suported yet +Error (NodeId 23): node Def(Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap index a73afae..98653b6 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap @@ -15,9 +15,9 @@ input_file: tests/def_return_type.nu 8: Type { name: NodeId(4), args: Some(NodeId(7)), optional: false } (25 to 29) 9: InOutType(NodeId(3), NodeId(8)) (14 to 35) 10: InOutTypes([NodeId(9)]) (14 to 35) -11: List([]) (37 to 38) -12: Block(BlockId(0)) (35 to 41) -13: Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) } (0 to 41) +11: Expr(List([])) (37 to 38) +12: Expr(Block(BlockId(0))) (35 to 41) +13: Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) (0 to 41) 14: Name (46 to 49) "bar" 15: Params([]) (50 to 53) 16: Name (58 to 64) "string" @@ -37,10 +37,10 @@ input_file: tests/def_return_type.nu 30: Type { name: NodeId(26), args: Some(NodeId(29)), optional: false } (89 to 93) 31: InOutType(NodeId(25), NodeId(30)) (82 to 99) 32: InOutTypes([NodeId(23), NodeId(31)]) (56 to 101) -33: List([]) (103 to 104) -34: Block(BlockId(1)) (101 to 107) -35: Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) } (42 to 107) -36: Block(BlockId(2)) (0 to 108) +33: Expr(List([])) (103 to 104) +34: Expr(Block(BlockId(1))) (101 to 107) +35: Def(Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) }) (42 to 107) +36: Expr(Block(BlockId(2))) (0 to 108) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(36) decls: [ bar: NodeId(14), foo: NodeId(0) ] @@ -88,4 +88,4 @@ input_file: tests/def_return_type.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) } not suported yet +Error (NodeId 13): node Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@for.nu.snap b/src/snapshots/new_nu_parser__test__node_output@for.nu.snap index 75889ed..1dca6f6 100644 --- a/src/snapshots/new_nu_parser__test__node_output@for.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@for.nu.snap @@ -2,41 +2,40 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/for.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 5) "x" -1: Int (8 to 9) "0" +0: VarDecl (4 to 5) "x" +1: Expr(Int) (8 to 9) "0" 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) -3: Variable (14 to 15) "i" -4: Int (20 to 21) "1" -5: Int (22 to 23) "2" -6: Int (24 to 25) "3" -7: List([NodeId(4), NodeId(5), NodeId(6)]) (19 to 25) -8: Variable (33 to 35) "$x" +3: VarDecl (14 to 15) "i" +4: Expr(Int) (20 to 21) "1" +5: Expr(Int) (22 to 23) "2" +6: Expr(Int) (24 to 25) "3" +7: Expr(List([NodeId(4), NodeId(5), NodeId(6)])) (19 to 25) +8: Expr(VarRef) (33 to 35) "$x" 9: Assignment (36 to 37) -10: Variable (38 to 40) "$x" +10: Expr(VarRef) (38 to 40) "$x" 11: Plus (41 to 42) -12: Variable (43 to 45) "$i" -13: BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) } (38 to 45) -14: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(13) } (33 to 45) -15: Block(BlockId(0)) (27 to 47) +12: Expr(VarRef) (43 to 45) "$i" +13: Expr(BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) }) (38 to 45) +14: Expr(BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(13) }) (33 to 45) +15: Expr(Block(BlockId(0))) (27 to 47) 16: For { variable: NodeId(3), range: NodeId(7), block: NodeId(15) } (10 to 47) -17: Variable (53 to 55) "$i" -18: Int (60 to 61) "4" -19: Int (62 to 63) "5" -20: Int (64 to 65) "6" -21: List([NodeId(18), NodeId(19), NodeId(20)]) (59 to 65) -22: Variable (73 to 75) "$x" +17: VarDecl (53 to 55) "$i" +18: Expr(Int) (60 to 61) "4" +19: Expr(Int) (62 to 63) "5" +20: Expr(Int) (64 to 65) "6" +21: Expr(List([NodeId(18), NodeId(19), NodeId(20)])) (59 to 65) +22: Expr(VarRef) (73 to 75) "$x" 23: Assignment (76 to 77) -24: Variable (78 to 80) "$x" +24: Expr(VarRef) (78 to 80) "$x" 25: Plus (81 to 82) -26: Variable (83 to 85) "$i" -27: BinaryOp { lhs: NodeId(24), op: NodeId(25), rhs: NodeId(26) } (78 to 85) -28: BinaryOp { lhs: NodeId(22), op: NodeId(23), rhs: NodeId(27) } (73 to 85) -29: Block(BlockId(1)) (67 to 87) +26: Expr(VarRef) (83 to 85) "$i" +27: Expr(BinaryOp { lhs: NodeId(24), op: NodeId(25), rhs: NodeId(26) }) (78 to 85) +28: Expr(BinaryOp { lhs: NodeId(22), op: NodeId(23), rhs: NodeId(27) }) (73 to 85) +29: Expr(Block(BlockId(1))) (67 to 87) 30: For { variable: NodeId(17), range: NodeId(21), block: NodeId(29) } (49 to 87) -31: Block(BlockId(2)) (0 to 88) +31: Expr(Block(BlockId(2))) (0 to 88) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(31) variables: [ x: NodeId(0) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap b/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap index 86ad7e5..c83b3a6 100644 --- a/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap @@ -2,41 +2,40 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/for_break_continue.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 5) "x" -1: Int (8 to 9) "0" +0: VarDecl (4 to 5) "x" +1: Expr(Int) (8 to 9) "0" 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) -3: Variable (14 to 15) "i" -4: Int (20 to 21) "1" -5: Int (22 to 23) "2" -6: Int (24 to 25) "3" -7: List([NodeId(4), NodeId(5), NodeId(6)]) (19 to 25) -8: Variable (36 to 38) "$x" +3: VarDecl (14 to 15) "i" +4: Expr(Int) (20 to 21) "1" +5: Expr(Int) (22 to 23) "2" +6: Expr(Int) (24 to 25) "3" +7: Expr(List([NodeId(4), NodeId(5), NodeId(6)])) (19 to 25) +8: Expr(VarRef) (36 to 38) "$x" 9: GreaterThan (39 to 40) -10: Int (41 to 42) "2" -11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (36 to 42) +10: Expr(Int) (41 to 42) "2" +11: Expr(BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) }) (36 to 42) 12: Break (53 to 58) -13: Block(BlockId(0)) (43 to 64) -14: If { condition: NodeId(11), then_block: NodeId(13), else_block: None } (33 to 64) -15: Variable (73 to 75) "$i" +13: Expr(Block(BlockId(0))) (43 to 64) +14: Expr(If { condition: NodeId(11), then_block: NodeId(13), else_block: None }) (33 to 64) +15: Expr(VarRef) (73 to 75) "$i" 16: LessThan (76 to 77) -17: Int (78 to 79) "3" -18: BinaryOp { lhs: NodeId(15), op: NodeId(16), rhs: NodeId(17) } (73 to 79) +17: Expr(Int) (78 to 79) "3" +18: Expr(BinaryOp { lhs: NodeId(15), op: NodeId(16), rhs: NodeId(17) }) (73 to 79) 19: Continue (90 to 98) -20: Block(BlockId(1)) (80 to 104) -21: If { condition: NodeId(18), then_block: NodeId(20), else_block: None } (70 to 104) -22: Variable (110 to 112) "$x" +20: Expr(Block(BlockId(1))) (80 to 104) +21: Expr(If { condition: NodeId(18), then_block: NodeId(20), else_block: None }) (70 to 104) +22: Expr(VarRef) (110 to 112) "$x" 23: Assignment (113 to 114) -24: Variable (115 to 117) "$x" +24: Expr(VarRef) (115 to 117) "$x" 25: Plus (118 to 119) -26: Variable (120 to 122) "$i" -27: BinaryOp { lhs: NodeId(24), op: NodeId(25), rhs: NodeId(26) } (115 to 122) -28: BinaryOp { lhs: NodeId(22), op: NodeId(23), rhs: NodeId(27) } (110 to 122) -29: Block(BlockId(2)) (27 to 124) +26: Expr(VarRef) (120 to 122) "$i" +27: Expr(BinaryOp { lhs: NodeId(24), op: NodeId(25), rhs: NodeId(26) }) (115 to 122) +28: Expr(BinaryOp { lhs: NodeId(22), op: NodeId(23), rhs: NodeId(27) }) (110 to 122) +29: Expr(Block(BlockId(2))) (27 to 124) 30: For { variable: NodeId(3), range: NodeId(7), block: NodeId(29) } (10 to 124) -31: Block(BlockId(3)) (0 to 124) +31: Expr(Block(BlockId(3))) (0 to 124) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(31) variables: [ x: NodeId(0) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@if_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@if_.nu.snap index 6eada57..72e0759 100644 --- a/src/snapshots/new_nu_parser__test__node_output@if_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@if_.nu.snap @@ -2,29 +2,28 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/if_.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 5) "x" -1: Int (8 to 11) "123" +0: VarDecl (4 to 5) "x" +1: Expr(Int) (8 to 11) "123" 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 11) -3: Variable (16 to 18) "$x" +3: Expr(VarRef) (16 to 18) "$x" 4: LessThan (19 to 20) -5: Int (21 to 24) "100" -6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (16 to 24) -7: Int (31 to 32) "5" -8: Block(BlockId(0)) (25 to 35) -9: Variable (43 to 45) "$x" +5: Expr(Int) (21 to 24) "100" +6: Expr(BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) }) (16 to 24) +7: Expr(Int) (31 to 32) "5" +8: Expr(Block(BlockId(0))) (25 to 35) +9: Expr(VarRef) (43 to 45) "$x" 10: GreaterThan (46 to 47) -11: Int (48 to 51) "200" -12: BinaryOp { lhs: NodeId(9), op: NodeId(10), rhs: NodeId(11) } (43 to 51) -13: Int (58 to 59) "6" -14: Block(BlockId(1)) (52 to 62) -15: Int (73 to 74) "7" -16: Block(BlockId(2)) (67 to 76) -17: If { condition: NodeId(12), then_block: NodeId(14), else_block: Some(NodeId(16)) } (40 to 76) -18: If { condition: NodeId(6), then_block: NodeId(8), else_block: Some(NodeId(17)) } (13 to 76) -19: Block(BlockId(3)) (0 to 77) +11: Expr(Int) (48 to 51) "200" +12: Expr(BinaryOp { lhs: NodeId(9), op: NodeId(10), rhs: NodeId(11) }) (43 to 51) +13: Expr(Int) (58 to 59) "6" +14: Expr(Block(BlockId(1))) (52 to 62) +15: Expr(Int) (73 to 74) "7" +16: Expr(Block(BlockId(2))) (67 to 76) +17: Expr(If { condition: NodeId(12), then_block: NodeId(14), else_block: Some(NodeId(16)) }) (40 to 76) +18: Expr(If { condition: NodeId(6), then_block: NodeId(8), else_block: Some(NodeId(17)) }) (13 to 76) +19: Expr(Block(BlockId(3))) (0 to 77) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(19) variables: [ x: NodeId(0) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap index e96d1b5..97e3aa0 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap @@ -2,16 +2,15 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/invalid_if.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (3 to 4) "1" -1: Int (9 to 10) "4" -2: Block(BlockId(0)) (5 to 13) -3: Int (22 to 23) "3" -4: Block(BlockId(1)) (18 to 25) -5: If { condition: NodeId(0), then_block: NodeId(2), else_block: Some(NodeId(4)) } (0 to 25) -6: Block(BlockId(2)) (0 to 26) +0: Expr(Int) (3 to 4) "1" +1: Expr(Int) (9 to 10) "4" +2: Expr(Block(BlockId(0))) (5 to 13) +3: Expr(Int) (22 to 23) "3" +4: Expr(Block(BlockId(1))) (18 to 25) +5: Expr(If { condition: NodeId(0), then_block: NodeId(2), else_block: Some(NodeId(4)) }) (0 to 25) +6: Expr(Block(BlockId(2))) (0 to 26) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(6) (empty) 1: Frame Scope, node_id: NodeId(2) (empty) @@ -30,4 +29,4 @@ Error (NodeId 0): The condition for if branch is not a boolean register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 5): node If { condition: NodeId(0), then_block: NodeId(2), else_block: Some(NodeId(4)) } not suported yet +Error (NodeId 5): node Expr(If { condition: NodeId(0), then_block: NodeId(2), else_block: Some(NodeId(4)) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_range.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_range.nu.snap index c5d3a4c..27190be 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_range.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_range.nu.snap @@ -2,12 +2,11 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/invalid_range.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (0 to 1) "1" +0: Expr(Int) (0 to 1) "1" 1: Garbage (2 to 4) -2: Int (5 to 6) "2" -3: Block(BlockId(0)) (0 to 7) +2: Expr(Int) (5 to 6) "2" +3: Expr(Block(BlockId(0))) (0 to 7) ==== COMPILER ERRORS ==== Error (NodeId 1): incomplete expression diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_record.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_record.nu.snap index f8b4411..29fe7c4 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_record.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_record.nu.snap @@ -2,16 +2,15 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/invalid_record.nu -snapshot_kind: text --- ==== COMPILER ==== -0: String (2 to 3) "a" -1: Int (5 to 6) "1" -2: String (9 to 10) "b" +0: Expr(String) (2 to 3) "a" +1: Expr(Int) (5 to 6) "1" +2: Expr(String) (9 to 10) "b" 3: Garbage (11 to 12) 4: Garbage (13 to 13) -5: Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(4))] } (0 to 0) -6: Block(BlockId(0)) (0 to 13) +5: Expr(Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(4))] }) (0 to 0) +6: Expr(Block(BlockId(0))) (0 to 13) ==== COMPILER ERRORS ==== Error (NodeId 3): expected: colon ':' Error (NodeId 4): incomplete expression diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap index 89de622..261c765 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap @@ -15,9 +15,9 @@ input_file: tests/invalid_types.nu 8: Type { name: NodeId(2), args: Some(NodeId(7)), optional: false } (12 to 16) 9: Param { name: NodeId(1), ty: Some(NodeId(8)) } (9 to 16) 10: Params([NodeId(9)]) (8 to 30) -11: Variable (33 to 35) "$x" -12: Block(BlockId(0)) (31 to 37) -13: Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) } (0 to 37) +11: Expr(VarRef) (33 to 35) "$x" +12: Expr(Block(BlockId(0))) (31 to 37) +13: Def(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) }) (0 to 37) 14: Name (42 to 45) "bar" 15: Name (47 to 48) "y" 16: Name (50 to 54) "list" @@ -25,10 +25,10 @@ input_file: tests/invalid_types.nu 18: Type { name: NodeId(16), args: Some(NodeId(17)), optional: false } (50 to 54) 19: Param { name: NodeId(15), ty: Some(NodeId(18)) } (47 to 54) 20: Params([NodeId(19)]) (46 to 57) -21: Variable (60 to 62) "$y" -22: Block(BlockId(1)) (58 to 64) -23: Def { name: NodeId(14), params: NodeId(20), in_out_types: None, block: NodeId(22) } (38 to 64) -24: Block(BlockId(2)) (0 to 65) +21: Expr(VarRef) (60 to 62) "$y" +22: Expr(Block(BlockId(1))) (58 to 64) +23: Def(Def { name: NodeId(14), params: NodeId(20), in_out_types: None, block: NodeId(22) }) (38 to 64) +24: Expr(Block(BlockId(2))) (0 to 65) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(24) decls: [ bar: NodeId(14), foo: NodeId(0) ] @@ -69,4 +69,4 @@ Error (NodeId 17): list must have one type argument register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) } not suported yet +Error (NodeId 13): node Def(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap index c151562..72a8579 100644 --- a/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@let_.nu.snap @@ -2,14 +2,13 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/let_.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 5) "x" -1: Int (8 to 11) "123" +0: VarDecl (4 to 5) "x" +1: Expr(Int) (8 to 11) "123" 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 11) -3: Variable (13 to 15) "$x" -4: Block(BlockId(0)) (0 to 15) +3: Expr(VarRef) (13 to 15) "$x" +4: Expr(Block(BlockId(0))) (0 to 15) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(4) variables: [ x: NodeId(0) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap b/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap index e851998..2861acf 100644 --- a/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap @@ -4,22 +4,22 @@ expression: evaluate_example(path) input_file: tests/let_mismatch.nu --- ==== COMPILER ==== -0: Variable (4 to 5) "x" +0: VarDecl (4 to 5) "x" 1: Name (7 to 13) "number" 2: Type { name: NodeId(1), args: None, optional: false } (7 to 13) -3: Int (16 to 18) "10" +3: Expr(Int) (16 to 18) "10" 4: Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: false } (0 to 18) -5: Variable (32 to 33) "y" +5: VarDecl (32 to 33) "y" 6: Name (35 to 38) "any" 7: Type { name: NodeId(6), args: None, optional: false } (35 to 38) -8: String (41 to 47) ""spam"" +8: Expr(String) (41 to 47) ""spam"" 9: Let { variable_name: NodeId(5), ty: Some(NodeId(7)), initializer: NodeId(8), is_mutable: false } (28 to 47) -10: Variable (60 to 61) "z" +10: VarDecl (60 to 61) "z" 11: Name (63 to 69) "string" 12: Type { name: NodeId(11), args: None, optional: false } (63 to 69) -13: Int (72 to 75) "123" +13: Expr(Int) (72 to 75) "123" 14: Let { variable_name: NodeId(10), ty: Some(NodeId(12)), initializer: NodeId(13), is_mutable: false } (56 to 75) -15: Variable (91 to 92) "w" +15: VarDecl (91 to 92) "w" 16: Name (94 to 98) "list" 17: Name (99 to 103) "list" 18: Name (104 to 107) "int" @@ -28,11 +28,11 @@ input_file: tests/let_mismatch.nu 21: Type { name: NodeId(17), args: Some(NodeId(20)), optional: false } (99 to 103) 22: TypeArgs([NodeId(21)]) (98 to 109) 23: Type { name: NodeId(16), args: Some(NodeId(22)), optional: false } (94 to 98) -24: String (116 to 119) "'a'" -25: List([NodeId(24)]) (114 to 120) -26: List([NodeId(25)]) (112 to 122) +24: Expr(String) (116 to 119) "'a'" +25: Expr(List([NodeId(24)])) (114 to 120) +26: Expr(List([NodeId(25)])) (112 to 122) 27: Let { variable_name: NodeId(15), ty: Some(NodeId(23)), initializer: NodeId(26), is_mutable: false } (87 to 122) -28: Block(BlockId(0)) (0 to 124) +28: Expr(Block(BlockId(0))) (0 to 124) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(28) variables: [ w: NodeId(15), x: NodeId(0), y: NodeId(5), z: NodeId(10) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@list.nu.snap b/src/snapshots/new_nu_parser__test__node_output@list.nu.snap index fa681ca..7d17fca 100644 --- a/src/snapshots/new_nu_parser__test__node_output@list.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@list.nu.snap @@ -2,30 +2,29 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/list.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (1 to 2) "1" -1: Int (4 to 5) "2" -2: Int (7 to 8) "3" -3: List([NodeId(0), NodeId(1), NodeId(2)]) (0 to 8) -4: Int (11 to 12) "0" -5: Int (13 to 14) "1" -6: Int (15 to 16) "2" -7: List([NodeId(4), NodeId(5), NodeId(6)]) (10 to 16) -8: String (19 to 22) ""0"" -9: String (23 to 26) ""1"" -10: String (27 to 30) ""2"" -11: List([NodeId(8), NodeId(9), NodeId(10)]) (18 to 30) -12: Int (33 to 34) "0" -13: Int (35 to 36) "1" -14: Float (37 to 40) "2.2" -15: List([NodeId(12), NodeId(13), NodeId(14)]) (32 to 40) -16: Int (43 to 44) "0" -17: Int (45 to 46) "1" -18: String (47 to 50) ""2"" -19: List([NodeId(16), NodeId(17), NodeId(18)]) (42 to 50) -20: Block(BlockId(0)) (0 to 52) +0: Expr(Int) (1 to 2) "1" +1: Expr(Int) (4 to 5) "2" +2: Expr(Int) (7 to 8) "3" +3: Expr(List([NodeId(0), NodeId(1), NodeId(2)])) (0 to 8) +4: Expr(Int) (11 to 12) "0" +5: Expr(Int) (13 to 14) "1" +6: Expr(Int) (15 to 16) "2" +7: Expr(List([NodeId(4), NodeId(5), NodeId(6)])) (10 to 16) +8: Expr(String) (19 to 22) ""0"" +9: Expr(String) (23 to 26) ""1"" +10: Expr(String) (27 to 30) ""2"" +11: Expr(List([NodeId(8), NodeId(9), NodeId(10)])) (18 to 30) +12: Expr(Int) (33 to 34) "0" +13: Expr(Int) (35 to 36) "1" +14: Expr(Float) (37 to 40) "2.2" +15: Expr(List([NodeId(12), NodeId(13), NodeId(14)])) (32 to 40) +16: Expr(Int) (43 to 44) "0" +17: Expr(Int) (45 to 46) "1" +18: Expr(String) (47 to 50) ""2"" +19: Expr(List([NodeId(16), NodeId(17), NodeId(18)])) (42 to 50) +20: Expr(Block(BlockId(0))) (0 to 52) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(20) (empty) ==== TYPES ==== @@ -54,4 +53,4 @@ snapshot_kind: text register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 3): node List([NodeId(0), NodeId(1), NodeId(2)]) not suported yet +Error (NodeId 3): node Expr(List([NodeId(0), NodeId(1), NodeId(2)])) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@literals.nu.snap b/src/snapshots/new_nu_parser__test__node_output@literals.nu.snap index db77463..6535d93 100644 --- a/src/snapshots/new_nu_parser__test__node_output@literals.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@literals.nu.snap @@ -2,16 +2,15 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/literals.nu -snapshot_kind: text --- ==== COMPILER ==== -0: True (1 to 5) -1: Null (6 to 10) -2: Int (11 to 12) "1" -3: String (13 to 16) "abc" -4: String (17 to 22) ""foo"" -5: List([NodeId(0), NodeId(1), NodeId(2), NodeId(3), NodeId(4)]) (0 to 22) -6: Block(BlockId(0)) (0 to 24) +0: Expr(True) (1 to 5) +1: Expr(Null) (6 to 10) +2: Expr(Int) (11 to 12) "1" +3: Expr(String) (13 to 16) "abc" +4: Expr(String) (17 to 22) ""foo"" +5: Expr(List([NodeId(0), NodeId(1), NodeId(2), NodeId(3), NodeId(4)])) (0 to 22) +6: Expr(Block(BlockId(0))) (0 to 24) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(6) (empty) ==== TYPES ==== @@ -26,4 +25,4 @@ snapshot_kind: text register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 5): node List([NodeId(0), NodeId(1), NodeId(2), NodeId(3), NodeId(4)]) not suported yet +Error (NodeId 5): node Expr(List([NodeId(0), NodeId(1), NodeId(2), NodeId(3), NodeId(4)])) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap b/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap index 8b4fb48..c4dc302 100644 --- a/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap @@ -2,26 +2,25 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/loop.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 5) "x" -1: Int (8 to 9) "0" +0: VarDecl (4 to 5) "x" +1: Expr(Int) (8 to 9) "0" 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) -3: Variable (24 to 26) "$x" +3: Expr(VarRef) (24 to 26) "$x" 4: GreaterThan (27 to 28) -5: Int (29 to 31) "10" -6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (24 to 31) +5: Expr(Int) (29 to 31) "10" +6: Expr(BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) }) (24 to 31) 7: Break (42 to 47) -8: Block(BlockId(0)) (32 to 53) -9: If { condition: NodeId(6), then_block: NodeId(8), else_block: None } (21 to 53) -10: Variable (59 to 61) "$x" +8: Expr(Block(BlockId(0))) (32 to 53) +9: Expr(If { condition: NodeId(6), then_block: NodeId(8), else_block: None }) (21 to 53) +10: Expr(VarRef) (59 to 61) "$x" 11: AddAssignment (62 to 64) -12: Int (65 to 66) "1" -13: BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) } (59 to 66) -14: Block(BlockId(1)) (15 to 68) +12: Expr(Int) (65 to 66) "1" +13: Expr(BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) }) (59 to 66) +14: Expr(Block(BlockId(1))) (15 to 68) 15: Loop { block: NodeId(14) } (10 to 68) -16: Block(BlockId(2)) (0 to 68) +16: Expr(Block(BlockId(2))) (0 to 68) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(16) variables: [ x: NodeId(0) ] diff --git a/src/snapshots/new_nu_parser__test__node_output@match.nu.snap b/src/snapshots/new_nu_parser__test__node_output@match.nu.snap index 05cb6ac..59c9839 100644 --- a/src/snapshots/new_nu_parser__test__node_output@match.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@match.nu.snap @@ -2,32 +2,31 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/match.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 5) "x" -1: Int (8 to 9) "1" +0: VarDecl (4 to 5) "x" +1: Expr(Int) (8 to 9) "1" 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 9) -3: Variable (15 to 18) "foo" -4: Variable (27 to 29) "$x" -5: Int (34 to 35) "1" -6: String (39 to 44) ""one"" -7: Int (48 to 49) "2" -8: Variable (63 to 64) "w" -9: Int (67 to 68) "3" +3: VarDecl (15 to 18) "foo" +4: Expr(VarRef) (27 to 29) "$x" +5: Expr(Int) (34 to 35) "1" +6: Expr(String) (39 to 44) ""one"" +7: Expr(Int) (48 to 49) "2" +8: VarDecl (63 to 64) "w" +9: Expr(Int) (67 to 68) "3" 10: Let { variable_name: NodeId(8), ty: None, initializer: NodeId(9), is_mutable: false } (59 to 68) -11: Int (73 to 74) "2" +11: Expr(Int) (73 to 74) "2" 12: Plus (75 to 76) -13: Variable (77 to 79) "$w" -14: BinaryOp { lhs: NodeId(11), op: NodeId(12), rhs: NodeId(13) } (73 to 79) -15: Block(BlockId(0)) (59 to 82) -16: Closure { params: None, block: NodeId(15) } (53 to 83) -17: Int (87 to 88) "3" -18: Null (92 to 96) -19: String (100 to 101) "_" +13: Expr(VarRef) (77 to 79) "$w" +14: Expr(BinaryOp { lhs: NodeId(11), op: NodeId(12), rhs: NodeId(13) }) (73 to 79) +15: Expr(Block(BlockId(0))) (59 to 82) +16: Expr(Closure { params: None, block: NodeId(15) }) (53 to 83) +17: Expr(Int) (87 to 88) "3" +18: Expr(Null) (92 to 96) +19: Expr(String) (100 to 101) "_" 20: Garbage (106 to 107) -21: Match { target: NodeId(4), match_arms: [(NodeId(5), NodeId(6)), (NodeId(7), NodeId(16)), (NodeId(17), NodeId(18)), (NodeId(19), NodeId(20))] } (21 to 110) +21: Expr(Match { target: NodeId(4), match_arms: [(NodeId(5), NodeId(6)), (NodeId(7), NodeId(16)), (NodeId(17), NodeId(18)), (NodeId(19), NodeId(20))] }) (21 to 110) 22: Let { variable_name: NodeId(3), ty: None, initializer: NodeId(21), is_mutable: false } (11 to 110) -23: Block(BlockId(1)) (0 to 111) +23: Expr(Block(BlockId(1))) (0 to 111) ==== COMPILER ERRORS ==== Error (NodeId 20): use null instead of () diff --git a/src/snapshots/new_nu_parser__test__node_output@math.nu.snap b/src/snapshots/new_nu_parser__test__node_output@math.nu.snap index dc8f2ae..d64cb40 100644 --- a/src/snapshots/new_nu_parser__test__node_output@math.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@math.nu.snap @@ -2,14 +2,13 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/math.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (0 to 1) "3" +0: Expr(Int) (0 to 1) "3" 1: Plus (2 to 3) -2: Int (4 to 5) "4" -3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 5) -4: Block(BlockId(0)) (0 to 5) +2: Expr(Int) (4 to 5) "4" +3: Expr(BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) }) (0 to 5) +4: Expr(Block(BlockId(0))) (0 to 5) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(4) (empty) ==== TYPES ==== diff --git a/src/snapshots/new_nu_parser__test__node_output@math_precedence.nu.snap b/src/snapshots/new_nu_parser__test__node_output@math_precedence.nu.snap index 01dc14b..a63e639 100644 --- a/src/snapshots/new_nu_parser__test__node_output@math_precedence.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@math_precedence.nu.snap @@ -2,20 +2,19 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/math_precedence.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Int (0 to 1) "1" +0: Expr(Int) (0 to 1) "1" 1: Plus (2 to 3) -2: Int (4 to 5) "2" +2: Expr(Int) (4 to 5) "2" 3: Multiply (6 to 7) -4: Int (8 to 9) "3" +4: Expr(Int) (8 to 9) "3" 5: Plus (10 to 11) -6: Int (12 to 13) "4" -7: BinaryOp { lhs: NodeId(2), op: NodeId(3), rhs: NodeId(4) } (4 to 9) -8: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(7) } (0 to 9) -9: BinaryOp { lhs: NodeId(8), op: NodeId(5), rhs: NodeId(6) } (0 to 13) -10: Block(BlockId(0)) (0 to 13) +6: Expr(Int) (12 to 13) "4" +7: Expr(BinaryOp { lhs: NodeId(2), op: NodeId(3), rhs: NodeId(4) }) (4 to 9) +8: Expr(BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(7) }) (0 to 9) +9: Expr(BinaryOp { lhs: NodeId(8), op: NodeId(5), rhs: NodeId(6) }) (0 to 13) +10: Expr(Block(BlockId(0))) (0 to 13) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(10) (empty) ==== TYPES ==== diff --git a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap index a366991..324fd3d 100644 --- a/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap @@ -4,19 +4,19 @@ expression: evaluate_example(path) input_file: tests/mut_.nu --- ==== COMPILER ==== -0: Variable (4 to 5) "x" +0: VarDecl (4 to 5) "x" 1: Name (7 to 10) "int" 2: Type { name: NodeId(1), args: None, optional: false } (7 to 10) -3: Int (13 to 16) "123" +3: Expr(Int) (13 to 16) "123" 4: Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: true } (0 to 16) -5: Variable (18 to 20) "$x" +5: Expr(VarRef) (18 to 20) "$x" 6: Assignment (21 to 22) -7: Int (23 to 24) "3" +7: Expr(Int) (23 to 24) "3" 8: Plus (25 to 26) -9: Int (27 to 30) "456" -10: BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) } (23 to 30) -11: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(10) } (18 to 30) -12: Block(BlockId(0)) (0 to 30) +9: Expr(Int) (27 to 30) "456" +10: Expr(BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) }) (23 to 30) +11: Expr(BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(10) }) (18 to 30) +12: Expr(Block(BlockId(0))) (0 to 30) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(12) variables: [ x: NodeId(0) ] diff --git a/src/typechecker.rs b/src/typechecker.rs index 9cb4794..07442e5 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,6 +1,6 @@ use crate::compiler::Compiler; use crate::errors::{Severity, SourceError}; -use crate::parser::{AstNode, NodeId}; +use crate::parser::{AstNode, Def, Expr, NodeId}; use std::cmp::Ordering; use std::collections::HashSet; @@ -186,21 +186,7 @@ impl<'a> Typechecker<'a> { fn typecheck_node(&mut self, node_id: NodeId) { match self.compiler.ast_nodes[node_id.0] { - AstNode::Null => { - self.set_node_type_id(node_id, NOTHING_TYPE); - } - AstNode::Int => { - self.set_node_type_id(node_id, INT_TYPE); - } - AstNode::Float => { - self.set_node_type_id(node_id, FLOAT_TYPE); - } - AstNode::True | AstNode::False => { - self.set_node_type_id(node_id, BOOL_TYPE); - } - AstNode::String => { - self.set_node_type_id(node_id, STRING_TYPE); - } + AstNode::Expr(ref expr) => self.typecheck_expr(expr.clone(), node_id), AstNode::Params(ref params) => { for param in params { self.typecheck_node(*param); @@ -238,7 +224,91 @@ impl<'a> Typechecker<'a> { // Type argument lists are not supposed to be evaluated self.set_node_type_id(node_id, FORBIDDEN_TYPE); } - AstNode::List(ref items) => { + AstNode::Let { + variable_name, + ty, + initializer, + is_mutable: _, + } => self.typecheck_let(variable_name, ty, initializer, node_id), + AstNode::Def(ref def) => self.typecheck_def(def.clone(), node_id), + AstNode::Alias { new_name, old_name } => { + self.typecheck_alias(new_name, old_name, node_id) + } + AstNode::For { + variable, + range, + block, + } => { + // We don't need to typecheck variable after this + self.typecheck_node(range); + + let var_id = self + .compiler + .var_resolution + .get(&variable) + .expect("missing resolved variable"); + if let Type::List(type_id) = self.type_of(range) { + self.variable_types[var_id.0] = type_id; + self.set_node_type_id(variable, type_id); + } else { + self.variable_types[var_id.0] = ANY_TYPE; + self.set_node_type_id(variable, ERROR_TYPE); + self.error("For loop range is not a list", range); + } + + self.typecheck_node(block); + if self.type_id_of(block) != NONE_TYPE { + self.error("Blocks in looping constructs cannot return values", block); + } + + if self.type_id_of(node_id) != ERROR_TYPE { + self.set_node_type_id(node_id, NONE_TYPE); + } + } + AstNode::While { condition, block } => { + self.typecheck_node(block); + if self.type_id_of(block) != NONE_TYPE { + self.error("Blocks in looping constructs cannot return values", block); + } + + self.typecheck_node(condition); + + // the condition should always evaluate to a boolean + if self.type_of(condition) != Type::Bool { + self.error("The condition for while loop is not a boolean", condition); + self.set_node_type_id(node_id, ERROR_TYPE); + } else { + self.set_node_type_id(node_id, self.type_id_of(block)); + } + } + _ => self.error( + format!( + "unsupported ast node '{:?}' in typechecker", + self.compiler.ast_nodes[node_id.0] + ), + node_id, + ), + } + } + + fn typecheck_expr(&mut self, expr: Expr, node_id: NodeId) { + match expr { + Expr::Null => { + self.set_node_type_id(node_id, NOTHING_TYPE); + } + Expr::Int => { + self.set_node_type_id(node_id, INT_TYPE); + } + Expr::Float => { + self.set_node_type_id(node_id, FLOAT_TYPE); + } + Expr::True | Expr::False => { + self.set_node_type_id(node_id, BOOL_TYPE); + } + Expr::String => { + self.set_node_type_id(node_id, STRING_TYPE); + } + Expr::List(ref items) => { if let Some(first_id) = items.first() { self.typecheck_node(*first_id); let first_type = self.type_of(*first_id); @@ -270,7 +340,7 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, LIST_ANY_TYPE); } } - AstNode::Block(block_id) => { + Expr::Block(block_id) => { let block = &self.compiler.blocks[block_id.0]; for inner_node_id in &block.nodes { @@ -286,7 +356,7 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, block_type); } - AstNode::Closure { params, block } => { + Expr::Closure { params, block } => { // TODO: input/output types if let Some(params_node_id) = params { self.typecheck_node(params_node_id); @@ -295,14 +365,8 @@ impl<'a> Typechecker<'a> { self.typecheck_node(block); self.set_node_type_id(node_id, CLOSURE_TYPE); } - AstNode::BinaryOp { lhs, op, rhs } => self.typecheck_binary_op(lhs, op, rhs, node_id), - AstNode::Let { - variable_name, - ty, - initializer, - is_mutable: _, - } => self.typecheck_let(variable_name, ty, initializer, node_id), - AstNode::Variable => { + Expr::BinaryOp { lhs, op, rhs } => self.typecheck_binary_op(lhs, op, rhs, node_id), + Expr::VarRef => { let var_id = self .compiler .var_resolution @@ -311,7 +375,7 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, self.variable_types[var_id.0]); } - AstNode::If { + Expr::If { condition, then_block, else_block, @@ -349,64 +413,8 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, *types.iter().next().expect("Can't be empty")); } } - AstNode::Def { - name, - params, - in_out_types, - block, - } => self.typecheck_def(name, params, in_out_types, block, node_id), - AstNode::Alias { new_name, old_name } => { - self.typecheck_alias(new_name, old_name, node_id) - } - AstNode::Call { ref parts } => self.typecheck_call(parts, node_id), - AstNode::For { - variable, - range, - block, - } => { - // We don't need to typecheck variable after this - self.typecheck_node(range); - - let var_id = self - .compiler - .var_resolution - .get(&variable) - .expect("missing resolved variable"); - if let Type::List(type_id) = self.type_of(range) { - self.variable_types[var_id.0] = type_id; - self.set_node_type_id(variable, type_id); - } else { - self.variable_types[var_id.0] = ANY_TYPE; - self.set_node_type_id(variable, ERROR_TYPE); - self.error("For loop range is not a list", range); - } - - self.typecheck_node(block); - if self.type_id_of(block) != NONE_TYPE { - self.error("Blocks in looping constructs cannot return values", block); - } - - if self.type_id_of(node_id) != ERROR_TYPE { - self.set_node_type_id(node_id, NONE_TYPE); - } - } - AstNode::While { condition, block } => { - self.typecheck_node(block); - if self.type_id_of(block) != NONE_TYPE { - self.error("Blocks in looping constructs cannot return values", block); - } - - self.typecheck_node(condition); - - // the condition should always evaluate to a boolean - if self.type_of(condition) != Type::Bool { - self.error("The condition for while loop is not a boolean", condition); - self.set_node_type_id(node_id, ERROR_TYPE); - } else { - self.set_node_type_id(node_id, self.type_id_of(block)); - } - } - AstNode::Match { + Expr::Call { ref parts } => self.typecheck_call(parts, node_id), + Expr::Match { ref target, ref match_arms, } => { @@ -434,13 +442,11 @@ impl<'a> Typechecker<'a> { } } } - _ => self.error( - format!( - "unsupported ast node '{:?}' in typechecker", - self.compiler.ast_nodes[node_id.0] - ), - node_id, - ), + Expr::NamedValue { .. } => todo!(), + Expr::Range { .. } => todo!(), + Expr::Table { .. } => todo!(), + Expr::Record { .. } => todo!(), + Expr::MemberAccess { .. } => todo!(), } } @@ -629,14 +635,13 @@ impl<'a> Typechecker<'a> { } } - fn typecheck_def( - &mut self, - name: NodeId, - params: NodeId, - in_out_types: Option, - block: NodeId, - node_id: NodeId, - ) { + fn typecheck_def(&mut self, def: Def, node_id: NodeId) { + let Def { + name, + params, + in_out_types, + block, + } = def; let in_out_types = in_out_types .map(|ty| { let AstNode::InOutTypes(types) = self.compiler.get_node(ty) else { From 712d3834a83ed0abf9edb1d662c26764398aba62 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 4 Mar 2025 15:08:46 -0500 Subject: [PATCH 05/12] Separate Stmt type --- src/parser.rs | 113 +++++++------- src/resolver.rs | 138 +++++++++--------- ...nu_parser__test__node_output@alias.nu.snap | 4 +- ...nu_parser__test__node_output@calls.nu.snap | 2 +- ...parser__test__node_output@closure3.nu.snap | 4 +- ...w_nu_parser__test__node_output@def.nu.snap | 4 +- ..._test__node_output@def_return_type.nu.snap | 6 +- ...w_nu_parser__test__node_output@for.nu.snap | 8 +- src/typechecker.rs | 125 ++++++++-------- 9 files changed, 211 insertions(+), 193 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 8f98312..9b3b4c3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -120,10 +120,45 @@ pub enum Expr { }, } +#[derive(Debug, PartialEq, Clone)] +pub enum Stmt { + // Definitions + Def(Def), + Alias { + new_name: NodeId, + old_name: NodeId, + }, + Let { + variable_name: NodeId, + ty: Option, + initializer: NodeId, + is_mutable: bool, + }, + + While { + condition: NodeId, + block: NodeId, + }, + For { + variable: NodeId, + range: NodeId, + block: NodeId, + }, + Loop { + block: NodeId, + }, + Return(Option), + Break, + Continue, + + Expr(NodeId), +} + // TODO: All nodes with Vec<...> should be moved to their own ID (like BlockId) to allow Copy trait #[derive(Debug, PartialEq, Clone)] pub enum AstNode { Expr(Expr), + Stmt(Stmt), Name, Type { @@ -164,31 +199,6 @@ pub enum AstNode { DivideAssignment, AppendAssignment, - // Statements - Let { - variable_name: NodeId, - ty: Option, - initializer: NodeId, - is_mutable: bool, - }, - While { - condition: NodeId, - block: NodeId, - }, - For { - variable: NodeId, - range: NodeId, - block: NodeId, - }, - Loop { - block: NodeId, - }, - Return(Option), - Break, - Continue, - - // Definitions - Def(Def), Params(Vec), Param { name: NodeId, @@ -197,10 +207,6 @@ pub enum AstNode { InOutTypes(Vec), /// Input/output type pair for a command InOutType(NodeId, NodeId), - Alias { - new_name: NodeId, - old_name: NodeId, - }, /// Long flag ('--' + one or more letters) FlagLong, @@ -209,8 +215,6 @@ pub enum AstNode { /// Group of short flags ('-' + more than 1 letters) FlagShortGroup, - // Expressions - Statement(NodeId), Garbage, } @@ -493,11 +497,6 @@ impl Parser { self.create_node(node, span.start, span.end) } - pub fn advance_expr(&mut self, node: Expr, span: Span) -> NodeId { - self.tokens.advance(); - self.create_expr(node, span.start, span.end) - } - /// A variable name (reference, not declaration) pub fn variable(&mut self) -> NodeId { if self.is_dollar() { @@ -1061,7 +1060,7 @@ impl Parser { let name = match self.tokens.peek() { (Token::Bareword, span) => self.advance_node(AstNode::Name, span), (Token::DoubleQuotedString | Token::SingleQuotedString, span) => { - self.advance_expr(Expr::String, span) + self.advance_node(AstNode::Expr(Expr::String), span) } _ => return self.error("expected def name"), }; @@ -1076,8 +1075,8 @@ impl Parser { let span_end = self.get_span_end(block); - self.create_node( - AstNode::Def(Def { + self.create_stmt( + Stmt::Def(Def { name, params, in_out_types, @@ -1113,8 +1112,8 @@ impl Parser { let span_end = self.get_span_end(initializer); - self.create_node( - AstNode::Let { + self.create_stmt( + Stmt::Let { variable_name, ty, initializer, @@ -1150,8 +1149,8 @@ impl Parser { let span_end = self.get_span_end(initializer); - self.create_node( - AstNode::Let { + self.create_stmt( + Stmt::Let { variable_name, ty, initializer, @@ -1221,8 +1220,8 @@ impl Parser { if self.is_semicolon() { // This is a statement, not an expression self.tokens.advance(); - code_body.push(self.create_node( - AstNode::Statement(expression), + code_body.push(self.create_stmt( + Stmt::Expr(expression), exp_span_start, exp_span_end, )) @@ -1257,7 +1256,7 @@ impl Parser { let block = self.block(BlockContext::Curlies); let span_end = self.get_span_end(block); - self.create_node(AstNode::While { condition, block }, span_start, span_end) + self.create_stmt(Stmt::While { condition, block }, span_start, span_end) } pub fn for_statement(&mut self) -> NodeId { @@ -1272,8 +1271,8 @@ impl Parser { let block = self.block(BlockContext::Curlies); let span_end = self.get_span_end(block); - self.create_node( - AstNode::For { + self.create_stmt( + Stmt::For { variable, range, block, @@ -1290,7 +1289,7 @@ impl Parser { let block = self.block(BlockContext::Curlies); let span_end = self.get_span_end(block); - self.create_node(AstNode::Loop { block }, span_start, span_end) + self.create_stmt(Stmt::Loop { block }, span_start, span_end) } pub fn return_statement(&mut self) -> NodeId { @@ -1309,7 +1308,7 @@ impl Parser { None }; - self.create_node(AstNode::Return(ret_val), span_start, span_end) + self.create_stmt(Stmt::Return(ret_val), span_start, span_end) } pub fn continue_statement(&mut self) -> NodeId { @@ -1318,7 +1317,7 @@ impl Parser { self.keyword(b"continue"); let span_end = span_start + b"continue".len(); - self.create_node(AstNode::Continue, span_start, span_end) + self.create_stmt(Stmt::Continue, span_start, span_end) } pub fn break_statement(&mut self) -> NodeId { @@ -1327,7 +1326,7 @@ impl Parser { self.keyword(b"break"); let span_end = span_start + b"break".len(); - self.create_node(AstNode::Break, span_start, span_end) + self.create_stmt(Stmt::Break, span_start, span_end) } pub fn alias_statement(&mut self) -> NodeId { @@ -1346,7 +1345,7 @@ impl Parser { self.name() }; let span_end = self.get_span_end(old_name); - self.create_node(AstNode::Alias { new_name, old_name }, span_start, span_end) + self.create_stmt(Stmt::Alias { new_name, old_name }, span_start, span_end) } pub fn is_operator(&mut self) -> bool { @@ -1561,6 +1560,14 @@ impl Parser { self.compiler.push_node(AstNode::Expr(expr)) } + pub fn create_stmt(&mut self, stmt: Stmt, span_start: usize, span_end: usize) -> NodeId { + self.compiler.spans.push(Span { + start: span_start, + end: span_end, + }); + self.compiler.push_node(AstNode::Stmt(stmt)) + } + pub fn create_node(&mut self, ast_node: AstNode, span_start: usize, span_end: usize) -> NodeId { self.compiler.spans.push(Span { start: span_start, diff --git a/src/resolver.rs b/src/resolver.rs index f6327cf..b38eac3 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,4 +1,4 @@ -use crate::parser::{Def, Expr}; +use crate::parser::{Def, Expr, Stmt}; use crate::protocol::{Command, Declaration}; use crate::{ compiler::Compiler, @@ -201,32 +201,7 @@ impl<'a> Resolver<'a> { // TODO: Move node_id param to the end, same as in typechecker match self.compiler.ast_nodes[node_id.0] { AstNode::Expr(ref expr) => self.resolve_expr(expr.clone(), node_id), - AstNode::Def(Def { - name, - params, - in_out_types: _, - block, - }) => { - // define the command before the block to enable recursive calls - self.define_decl(name); - - // making sure the def parameters and body end up in the same scope frame - self.enter_scope(block); - self.resolve_node(params); - let def_scope = self.exit_scope(); - - let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { - panic!("internal error: command definition's body is not a block"); - }; - - self.resolve_block(block, block_id, Some(def_scope)); - } - AstNode::Alias { - new_name, - old_name: _, - } => { - self.define_decl(new_name); - } + AstNode::Stmt(ref stmt) => self.resolve_stmt(stmt.clone()), AstNode::Params(ref params) => { for param in params { if let AstNode::Param { name, .. } = self.compiler.ast_nodes[param.0] { @@ -236,41 +211,6 @@ impl<'a> Resolver<'a> { } } } - AstNode::Let { - variable_name, - ty: _, - initializer, - is_mutable, - } => { - self.resolve_node(initializer); - self.define_variable(variable_name, is_mutable) - } - AstNode::While { condition, block } => { - self.resolve_node(condition); - self.resolve_node(block); - } - AstNode::For { - variable, - range, - block, - } => { - // making sure the for loop variable and body end up in the same scope frame - self.enter_scope(block); - self.define_variable(variable, false); - let for_body_scope = self.exit_scope(); - - self.resolve_node(range); - - let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { - panic!("internal error: for's body is not a block"); - }; - - self.resolve_block(block, block_id, Some(for_body_scope)); - } - AstNode::Loop { block } => { - self.resolve_node(block); - } - AstNode::Statement(node) => self.resolve_node(node), AstNode::Param { .. } => (/* seems unused for now */), AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ), // All remaining matches do not contain NodeId => there is nothing to resolve @@ -350,12 +290,74 @@ impl<'a> Resolver<'a> { } } Expr::NamedValue { .. } => (/* seems unused for now */), - Expr::Int - | Expr::Float - | Expr::String - | Expr::True - | Expr::False - | Expr::Null => {} + Expr::Int | Expr::Float | Expr::String | Expr::True | Expr::False | Expr::Null => {} + } + } + + pub fn resolve_stmt(&mut self, stmt: Stmt) { + match stmt { + Stmt::Def(Def { + name, + params, + in_out_types: _, + block, + }) => { + // define the command before the block to enable recursive calls + self.define_decl(name); + + // making sure the def parameters and body end up in the same scope frame + self.enter_scope(block); + self.resolve_node(params); + let def_scope = self.exit_scope(); + + let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { + panic!("internal error: command definition's body is not a block"); + }; + + self.resolve_block(block, block_id, Some(def_scope)); + } + Stmt::Alias { + new_name, + old_name: _, + } => { + self.define_decl(new_name); + } + Stmt::Let { + variable_name, + ty: _, + initializer, + is_mutable, + } => { + self.resolve_node(initializer); + self.define_variable(variable_name, is_mutable) + } + Stmt::While { condition, block } => { + self.resolve_node(condition); + self.resolve_node(block); + } + Stmt::For { + variable, + range, + block, + } => { + // making sure the for loop variable and body end up in the same scope frame + self.enter_scope(block); + self.define_variable(variable, false); + let for_body_scope = self.exit_scope(); + + self.resolve_node(range); + + let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { + panic!("internal error: for's body is not a block"); + }; + + self.resolve_block(block, block_id, Some(for_body_scope)); + } + Stmt::Loop { block } => { + self.resolve_node(block); + } + Stmt::Expr(node) => self.resolve_node(node), + Stmt::Return(_) | Stmt::Break | Stmt::Continue => {} } } diff --git a/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap b/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap index 5b96ec3..462e92b 100644 --- a/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@alias.nu.snap @@ -6,7 +6,7 @@ input_file: tests/alias.nu ==== COMPILER ==== 0: Expr(String) (6 to 19) ""fancy alias"" 1: Name (22 to 25) "foo" -2: Alias { new_name: NodeId(0), old_name: NodeId(1) } (0 to 25) +2: Stmt(Alias { new_name: NodeId(0), old_name: NodeId(1) }) (0 to 25) 3: Name (27 to 32) "fancy" 4: Name (33 to 38) "alias" 5: Expr(Call { parts: [NodeId(3), NodeId(4)] }) (33 to 38) @@ -26,4 +26,4 @@ input_file: tests/alias.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 2): node Alias { new_name: NodeId(0), old_name: NodeId(1) } not suported yet +Error (NodeId 2): node Stmt(Alias { new_name: NodeId(0), old_name: NodeId(1) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap index 7c611f1..f533b20 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap @@ -31,7 +31,7 @@ input_file: tests/calls.nu 24: Expr(VarRef) (80 to 82) "$c" 25: Expr(List([NodeId(22), NodeId(23), NodeId(24)])) (70 to 82) 26: Expr(Block(BlockId(0))) (68 to 85) -27: Def(Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) }) (24 to 85) +27: Stmt(Def(Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) })) (24 to 85) 28: Name (86 to 94) "existing" 29: Name (95 to 98) "foo" 30: Expr(String) (100 to 104) ""ba"" diff --git a/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap b/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap index c1b3f27..eae0cd4 100644 --- a/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap @@ -23,7 +23,7 @@ input_file: tests/closure3.nu 16: Expr(BinaryOp { lhs: NodeId(15), op: NodeId(13), rhs: NodeId(14) }) (32 to 43) 17: Expr(Block(BlockId(0))) (32 to 43) 18: Expr(Closure { params: Some(NodeId(9)), block: NodeId(17) }) (14 to 44) -19: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(18), is_mutable: false } (0 to 44) +19: Stmt(Let { variable_name: NodeId(0), ty: None, initializer: NodeId(18), is_mutable: false }) (0 to 44) 20: Name (46 to 52) "filter" 21: Expr(VarRef) (53 to 61) "$closure" 22: Expr(Call { parts: [NodeId(20), NodeId(21)] }) (53 to 61) @@ -62,4 +62,4 @@ input_file: tests/closure3.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 19): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(18), is_mutable: false } not suported yet +Error (NodeId 19): node Stmt(Let { variable_name: NodeId(0), ty: None, initializer: NodeId(18), is_mutable: false }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap index d62653f..f29c989 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap @@ -27,7 +27,7 @@ input_file: tests/def.nu 20: Expr(VarRef) (51 to 53) "$z" 21: Expr(List([NodeId(18), NodeId(19), NodeId(20)])) (42 to 54) 22: Expr(Block(BlockId(0))) (40 to 57) -23: Def(Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) }) (0 to 57) +23: Stmt(Def(Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) })) (0 to 57) 24: Expr(Block(BlockId(1))) (0 to 57) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(24) @@ -64,4 +64,4 @@ input_file: tests/def.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 23): node Def(Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) }) not suported yet +Error (NodeId 23): node Stmt(Def(Def { name: NodeId(0), params: NodeId(17), in_out_types: None, block: NodeId(22) })) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap index 98653b6..a49fc9f 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap @@ -17,7 +17,7 @@ input_file: tests/def_return_type.nu 10: InOutTypes([NodeId(9)]) (14 to 35) 11: Expr(List([])) (37 to 38) 12: Expr(Block(BlockId(0))) (35 to 41) -13: Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) (0 to 41) +13: Stmt(Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) })) (0 to 41) 14: Name (46 to 49) "bar" 15: Params([]) (50 to 53) 16: Name (58 to 64) "string" @@ -39,7 +39,7 @@ input_file: tests/def_return_type.nu 32: InOutTypes([NodeId(23), NodeId(31)]) (56 to 101) 33: Expr(List([])) (103 to 104) 34: Expr(Block(BlockId(1))) (101 to 107) -35: Def(Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) }) (42 to 107) +35: Stmt(Def(Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) })) (42 to 107) 36: Expr(Block(BlockId(2))) (0 to 108) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(36) @@ -88,4 +88,4 @@ input_file: tests/def_return_type.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) not suported yet +Error (NodeId 13): node Stmt(Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) })) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@for.nu.snap b/src/snapshots/new_nu_parser__test__node_output@for.nu.snap index 1dca6f6..04cd2c7 100644 --- a/src/snapshots/new_nu_parser__test__node_output@for.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@for.nu.snap @@ -6,7 +6,7 @@ input_file: tests/for.nu ==== COMPILER ==== 0: VarDecl (4 to 5) "x" 1: Expr(Int) (8 to 9) "0" -2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) +2: Stmt(Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true }) (0 to 9) 3: VarDecl (14 to 15) "i" 4: Expr(Int) (20 to 21) "1" 5: Expr(Int) (22 to 23) "2" @@ -20,7 +20,7 @@ input_file: tests/for.nu 13: Expr(BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) }) (38 to 45) 14: Expr(BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(13) }) (33 to 45) 15: Expr(Block(BlockId(0))) (27 to 47) -16: For { variable: NodeId(3), range: NodeId(7), block: NodeId(15) } (10 to 47) +16: Stmt(For { variable: NodeId(3), range: NodeId(7), block: NodeId(15) }) (10 to 47) 17: VarDecl (53 to 55) "$i" 18: Expr(Int) (60 to 61) "4" 19: Expr(Int) (62 to 63) "5" @@ -34,7 +34,7 @@ input_file: tests/for.nu 27: Expr(BinaryOp { lhs: NodeId(24), op: NodeId(25), rhs: NodeId(26) }) (78 to 85) 28: Expr(BinaryOp { lhs: NodeId(22), op: NodeId(23), rhs: NodeId(27) }) (73 to 85) 29: Expr(Block(BlockId(1))) (67 to 87) -30: For { variable: NodeId(17), range: NodeId(21), block: NodeId(29) } (49 to 87) +30: Stmt(For { variable: NodeId(17), range: NodeId(21), block: NodeId(29) }) (49 to 87) 31: Expr(Block(BlockId(2))) (0 to 88) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(31) @@ -80,4 +80,4 @@ input_file: tests/for.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } not suported yet +Error (NodeId 2): node Stmt(Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true }) not suported yet diff --git a/src/typechecker.rs b/src/typechecker.rs index 07442e5..4dc01ff 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,6 +1,6 @@ use crate::compiler::Compiler; use crate::errors::{Severity, SourceError}; -use crate::parser::{AstNode, Def, Expr, NodeId}; +use crate::parser::{AstNode, Def, Expr, NodeId, Stmt}; use std::cmp::Ordering; use std::collections::HashSet; @@ -187,6 +187,7 @@ impl<'a> Typechecker<'a> { fn typecheck_node(&mut self, node_id: NodeId) { match self.compiler.ast_nodes[node_id.0] { AstNode::Expr(ref expr) => self.typecheck_expr(expr.clone(), node_id), + AstNode::Stmt(ref stmt) => self.typecheck_stmt(stmt.clone(), node_id), AstNode::Params(ref params) => { for param in params { self.typecheck_node(*param); @@ -224,63 +225,6 @@ impl<'a> Typechecker<'a> { // Type argument lists are not supposed to be evaluated self.set_node_type_id(node_id, FORBIDDEN_TYPE); } - AstNode::Let { - variable_name, - ty, - initializer, - is_mutable: _, - } => self.typecheck_let(variable_name, ty, initializer, node_id), - AstNode::Def(ref def) => self.typecheck_def(def.clone(), node_id), - AstNode::Alias { new_name, old_name } => { - self.typecheck_alias(new_name, old_name, node_id) - } - AstNode::For { - variable, - range, - block, - } => { - // We don't need to typecheck variable after this - self.typecheck_node(range); - - let var_id = self - .compiler - .var_resolution - .get(&variable) - .expect("missing resolved variable"); - if let Type::List(type_id) = self.type_of(range) { - self.variable_types[var_id.0] = type_id; - self.set_node_type_id(variable, type_id); - } else { - self.variable_types[var_id.0] = ANY_TYPE; - self.set_node_type_id(variable, ERROR_TYPE); - self.error("For loop range is not a list", range); - } - - self.typecheck_node(block); - if self.type_id_of(block) != NONE_TYPE { - self.error("Blocks in looping constructs cannot return values", block); - } - - if self.type_id_of(node_id) != ERROR_TYPE { - self.set_node_type_id(node_id, NONE_TYPE); - } - } - AstNode::While { condition, block } => { - self.typecheck_node(block); - if self.type_id_of(block) != NONE_TYPE { - self.error("Blocks in looping constructs cannot return values", block); - } - - self.typecheck_node(condition); - - // the condition should always evaluate to a boolean - if self.type_of(condition) != Type::Bool { - self.error("The condition for while loop is not a boolean", condition); - self.set_node_type_id(node_id, ERROR_TYPE); - } else { - self.set_node_type_id(node_id, self.type_id_of(block)); - } - } _ => self.error( format!( "unsupported ast node '{:?}' in typechecker", @@ -450,6 +394,71 @@ impl<'a> Typechecker<'a> { } } + fn typecheck_stmt(&mut self, stmt: Stmt, node_id: NodeId) { + match stmt { + Stmt::Let { + variable_name, + ty, + initializer, + is_mutable: _, + } => self.typecheck_let(variable_name, ty, initializer, node_id), + Stmt::Def(ref def) => self.typecheck_def(def.clone(), node_id), + Stmt::Alias { new_name, old_name } => self.typecheck_alias(new_name, old_name, node_id), + Stmt::For { + variable, + range, + block, + } => { + // We don't need to typecheck variable after this + self.typecheck_node(range); + + let var_id = self + .compiler + .var_resolution + .get(&variable) + .expect("missing resolved variable"); + if let Type::List(type_id) = self.type_of(range) { + self.variable_types[var_id.0] = type_id; + self.set_node_type_id(variable, type_id); + } else { + self.variable_types[var_id.0] = ANY_TYPE; + self.set_node_type_id(variable, ERROR_TYPE); + self.error("For loop range is not a list", range); + } + + self.typecheck_node(block); + if self.type_id_of(block) != NONE_TYPE { + self.error("Blocks in looping constructs cannot return values", block); + } + + if self.type_id_of(node_id) != ERROR_TYPE { + self.set_node_type_id(node_id, NONE_TYPE); + } + } + Stmt::While { condition, block } => { + self.typecheck_node(block); + if self.type_id_of(block) != NONE_TYPE { + self.error("Blocks in looping constructs cannot return values", block); + } + + self.typecheck_node(condition); + + // the condition should always evaluate to a boolean + if self.type_of(condition) != Type::Bool { + self.error("The condition for while loop is not a boolean", condition); + self.set_node_type_id(node_id, ERROR_TYPE); + } else { + self.set_node_type_id(node_id, self.type_id_of(block)); + } + } + Stmt::Expr(node) => self.typecheck_node(node), + Stmt::Loop { .. } => todo!(), + Stmt::Return(_) => todo!(), + Stmt::Break => todo!(), + Stmt::Continue => todo!(), + } + } + fn typecheck_match( &mut self, target: &NodeId, From 108d45fc2513db88cebc3e92fd5574be89afa33d Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 9 Mar 2025 14:45:45 -0400 Subject: [PATCH 06/12] Don't panic on unsupported expressions --- src/parser.rs | 12 ++------- ...nu_parser__test__node_output@table.nu.snap | 27 +++++++++---------- ...u_parser__test__node_output@table2.nu.snap | 27 +++++++++---------- ...__test__node_output@variable_names.nu.snap | 9 +++---- ...nu_parser__test__node_output@while.nu.snap | 27 +++++++++---------- src/typechecker.rs | 11 +++++--- 6 files changed, 52 insertions(+), 61 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index a4de954..696c10a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1590,19 +1590,11 @@ impl Parser { } pub fn create_expr(&mut self, expr: Expr, span_start: usize, span_end: usize) -> NodeId { - self.compiler.spans.push(Span { - start: span_start, - end: span_end, - }); - self.compiler.push_node(AstNode::Expr(expr)) + self.create_node(AstNode::Expr(expr), span_start, span_end) } pub fn create_stmt(&mut self, stmt: Stmt, span_start: usize, span_end: usize) -> NodeId { - self.compiler.spans.push(Span { - start: span_start, - end: span_end, - }); - self.compiler.push_node(AstNode::Stmt(stmt)) + self.create_node(AstNode::Stmt(stmt), span_start, span_end) } pub fn create_node(&mut self, ast_node: AstNode, span_start: usize, span_end: usize) -> NodeId { diff --git a/src/snapshots/new_nu_parser__test__node_output@table.nu.snap b/src/snapshots/new_nu_parser__test__node_output@table.nu.snap index 9cc85a8..46ec8b8 100644 --- a/src/snapshots/new_nu_parser__test__node_output@table.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@table.nu.snap @@ -2,20 +2,19 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/table.nu -snapshot_kind: text --- ==== COMPILER ==== -0: String (7 to 10) ""a"" -1: String (12 to 15) ""b"" -2: List([NodeId(0), NodeId(1)]) (6 to 15) -3: Int (24 to 25) "1" -4: Int (27 to 28) "2" -5: List([NodeId(3), NodeId(4)]) (23 to 28) -6: Int (35 to 36) "3" -7: Int (38 to 39) "4" -8: List([NodeId(6), NodeId(7)]) (34 to 39) -9: Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } (0 to 41) -10: Block(BlockId(0)) (0 to 42) +0: Expr(String) (7 to 10) ""a"" +1: Expr(String) (12 to 15) ""b"" +2: Expr(List([NodeId(0), NodeId(1)])) (6 to 15) +3: Expr(Int) (24 to 25) "1" +4: Expr(Int) (27 to 28) "2" +5: Expr(List([NodeId(3), NodeId(4)])) (23 to 28) +6: Expr(Int) (35 to 36) "3" +7: Expr(Int) (38 to 39) "4" +8: Expr(List([NodeId(6), NodeId(7)])) (34 to 39) +9: Expr(Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }) (0 to 41) +10: Expr(Block(BlockId(0))) (0 to 42) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(10) (empty) ==== TYPES ==== @@ -31,9 +30,9 @@ snapshot_kind: text 9: unknown 10: unknown ==== TYPE ERRORS ==== -Error (NodeId 9): unsupported ast node 'Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }' in typechecker +Error (NodeId 9): unsupported ast node 'Expr(Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] })' in typechecker ==== IR ==== register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 9): node Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } not suported yet +Error (NodeId 9): node Expr(Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@table2.nu.snap b/src/snapshots/new_nu_parser__test__node_output@table2.nu.snap index 46ed72c..bbd39b5 100644 --- a/src/snapshots/new_nu_parser__test__node_output@table2.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@table2.nu.snap @@ -2,20 +2,19 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/table2.nu -snapshot_kind: text --- ==== COMPILER ==== -0: String (7 to 8) "a" -1: String (10 to 11) "b" -2: List([NodeId(0), NodeId(1)]) (6 to 11) -3: Int (20 to 21) "1" -4: Int (23 to 24) "2" -5: List([NodeId(3), NodeId(4)]) (19 to 24) -6: Int (31 to 32) "3" -7: Int (34 to 35) "4" -8: List([NodeId(6), NodeId(7)]) (30 to 35) -9: Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } (0 to 37) -10: Block(BlockId(0)) (0 to 38) +0: Expr(String) (7 to 8) "a" +1: Expr(String) (10 to 11) "b" +2: Expr(List([NodeId(0), NodeId(1)])) (6 to 11) +3: Expr(Int) (20 to 21) "1" +4: Expr(Int) (23 to 24) "2" +5: Expr(List([NodeId(3), NodeId(4)])) (19 to 24) +6: Expr(Int) (31 to 32) "3" +7: Expr(Int) (34 to 35) "4" +8: Expr(List([NodeId(6), NodeId(7)])) (30 to 35) +9: Expr(Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }) (0 to 37) +10: Expr(Block(BlockId(0))) (0 to 38) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(10) (empty) ==== TYPES ==== @@ -31,9 +30,9 @@ snapshot_kind: text 9: unknown 10: unknown ==== TYPE ERRORS ==== -Error (NodeId 9): unsupported ast node 'Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }' in typechecker +Error (NodeId 9): unsupported ast node 'Expr(Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] })' in typechecker ==== IR ==== register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 9): node Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } not suported yet +Error (NodeId 9): node Expr(Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@variable_names.nu.snap b/src/snapshots/new_nu_parser__test__node_output@variable_names.nu.snap index ecd843c..0225df8 100644 --- a/src/snapshots/new_nu_parser__test__node_output@variable_names.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@variable_names.nu.snap @@ -2,13 +2,12 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/variable_names.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (0 to 4) "$abc" -1: Variable (5 to 7) "$_" -2: Variable (8 to 12) "$a_c" +0: Expr(VarRef) (0 to 4) "$abc" +1: Expr(VarRef) (5 to 7) "$_" +2: Expr(VarRef) (8 to 12) "$a_c" 3: Garbage (14 to 17) -4: Block(BlockId(0)) (0 to 18) +4: Expr(Block(BlockId(0))) (0 to 18) ==== COMPILER ERRORS ==== Error (NodeId 3): variable name must be a bareword diff --git a/src/snapshots/new_nu_parser__test__node_output@while.nu.snap b/src/snapshots/new_nu_parser__test__node_output@while.nu.snap index f3cc1b8..7bd834c 100644 --- a/src/snapshots/new_nu_parser__test__node_output@while.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@while.nu.snap @@ -2,23 +2,22 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/while.nu -snapshot_kind: text --- ==== COMPILER ==== -0: Variable (4 to 5) "x" -1: Int (8 to 9) "0" -2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) -3: Int (16 to 17) "1" +0: VarDecl (4 to 5) "x" +1: Expr(Int) (8 to 9) "0" +2: Stmt(Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true }) (0 to 9) +3: Expr(Int) (16 to 17) "1" 4: LessThan (18 to 19) -5: Int (20 to 21) "2" -6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (16 to 21) -7: Variable (26 to 28) "$x" +5: Expr(Int) (20 to 21) "2" +6: Expr(BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) }) (16 to 21) +7: Expr(VarRef) (26 to 28) "$x" 8: AddAssignment (29 to 31) -9: Int (32 to 33) "1" -10: BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) } (26 to 33) -11: Block(BlockId(0)) (22 to 35) -12: While { condition: NodeId(6), block: NodeId(11) } (10 to 35) -13: Block(BlockId(1)) (0 to 36) +9: Expr(Int) (32 to 33) "1" +10: Expr(BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) }) (26 to 33) +11: Expr(Block(BlockId(0))) (22 to 35) +12: Stmt(While { condition: NodeId(6), block: NodeId(11) }) (10 to 35) +13: Expr(Block(BlockId(1))) (0 to 36) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(13) variables: [ x: NodeId(0) ] @@ -42,4 +41,4 @@ snapshot_kind: text register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } not suported yet +Error (NodeId 2): node Stmt(Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true }) not suported yet diff --git a/src/typechecker.rs b/src/typechecker.rs index 2b304dc..f577004 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -403,10 +403,13 @@ impl<'a> Typechecker<'a> { } } } - Expr::NamedValue { .. } => todo!(), - Expr::Range { .. } => todo!(), - Expr::Table { .. } => todo!(), - Expr::MemberAccess { .. } => todo!(), + _ => self.error( + format!( + "unsupported ast node '{:?}' in typechecker", + self.compiler.ast_nodes[node_id.0] + ), + node_id, + ), } } From 7108425a4bc07bfccbfd6c2b67b2583eaebe439e Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 9 Mar 2025 14:49:17 -0400 Subject: [PATCH 07/12] Don't make separate Def struct --- src/parser.rs | 19 +++++++-------- src/resolver.rs | 6 ++--- ...nu_parser__test__node_output@calls.nu.snap | 2 +- ...w_nu_parser__test__node_output@def.nu.snap | 4 ++-- ..._test__node_output@def_return_type.nu.snap | 6 ++--- ...r__test__node_output@invalid_types.nu.snap | 6 ++--- src/typechecker.rs | 24 ++++++++++++------- 7 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 696c10a..23db711 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -54,14 +54,6 @@ pub enum BarewordContext { Call, } -#[derive(Debug, PartialEq, Clone)] -pub struct Def { - pub name: NodeId, - pub params: NodeId, - pub in_out_types: Option, - pub block: NodeId, -} - #[derive(Debug, PartialEq, Clone)] pub enum TypeAst { Ref { @@ -139,7 +131,12 @@ pub enum Expr { #[derive(Debug, PartialEq, Clone)] pub enum Stmt { // Definitions - Def(Def), + Def { + name: NodeId, + params: NodeId, + in_out_types: Option, + block: NodeId, + }, Alias { new_name: NodeId, old_name: NodeId, @@ -1113,12 +1110,12 @@ impl Parser { let span_end = self.get_span_end(block); self.create_stmt( - Stmt::Def(Def { + Stmt::Def { name, params, in_out_types, block, - }), + }, span_start, span_end, ) diff --git a/src/resolver.rs b/src/resolver.rs index b38eac3..9be507a 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,4 +1,4 @@ -use crate::parser::{Def, Expr, Stmt}; +use crate::parser::{Expr, Stmt}; use crate::protocol::{Command, Declaration}; use crate::{ compiler::Compiler, @@ -296,12 +296,12 @@ impl<'a> Resolver<'a> { pub fn resolve_stmt(&mut self, stmt: Stmt) { match stmt { - Stmt::Def(Def { + Stmt::Def { name, params, in_out_types: _, block, - }) => { + } => { // define the command before the block to enable recursive calls self.define_decl(name); diff --git a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap index b28034d..8cbe397 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap @@ -31,7 +31,7 @@ input_file: tests/calls.nu 24: Expr(VarRef) (80 to 82) "$c" 25: Expr(List([NodeId(22), NodeId(23), NodeId(24)])) (70 to 82) 26: Expr(Block(BlockId(0))) (68 to 85) -27: Stmt(Def(Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) })) (24 to 85) +27: Stmt(Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) }) (24 to 85) 28: Name (86 to 94) "existing" 29: Name (95 to 98) "foo" 30: Expr(String) (100 to 104) ""ba"" diff --git a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap index a3ce9d7..4ca156a 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap @@ -39,7 +39,7 @@ input_file: tests/def.nu 32: Expr(VarRef) (77 to 79) "$z" 33: Expr(List([NodeId(29), NodeId(30), NodeId(31), NodeId(32)])) (64 to 80) 34: Expr(Block(BlockId(0))) (62 to 83) -35: Stmt(Def(Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) })) (0 to 83) +35: Stmt(Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) }) (0 to 83) 36: Expr(Block(BlockId(1))) (0 to 83) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(36) @@ -88,4 +88,4 @@ input_file: tests/def.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 35): node Stmt(Def(Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) })) not suported yet +Error (NodeId 35): node Stmt(Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap index 181243d..c808256 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap @@ -17,7 +17,7 @@ input_file: tests/def_return_type.nu 10: InOutTypes([NodeId(9)]) (14 to 35) 11: Expr(List([])) (37 to 38) 12: Expr(Block(BlockId(0))) (35 to 41) -13: Stmt(Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) })) (0 to 41) +13: Stmt(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) (0 to 41) 14: Name (46 to 49) "bar" 15: Params([]) (50 to 53) 16: Name (58 to 64) "string" @@ -39,7 +39,7 @@ input_file: tests/def_return_type.nu 32: InOutTypes([NodeId(23), NodeId(31)]) (56 to 101) 33: Expr(List([])) (103 to 104) 34: Expr(Block(BlockId(1))) (101 to 107) -35: Stmt(Def(Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) })) (42 to 107) +35: Stmt(Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) }) (42 to 107) 36: Expr(Block(BlockId(2))) (0 to 108) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(36) @@ -88,4 +88,4 @@ input_file: tests/def_return_type.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Stmt(Def(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) })) not suported yet +Error (NodeId 13): node Stmt(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap index ca78288..dc17c89 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap @@ -17,7 +17,7 @@ input_file: tests/invalid_types.nu 10: Params([NodeId(9)]) (8 to 30) 11: Expr(VarRef) (33 to 35) "$x" 12: Expr(Block(BlockId(0))) (31 to 37) -13: Stmt(Def(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) })) (0 to 37) +13: Stmt(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) }) (0 to 37) 14: Name (42 to 45) "bar" 15: Name (47 to 48) "y" 16: Name (50 to 54) "list" @@ -27,7 +27,7 @@ input_file: tests/invalid_types.nu 20: Params([NodeId(19)]) (46 to 57) 21: Expr(VarRef) (60 to 62) "$y" 22: Expr(Block(BlockId(1))) (58 to 64) -23: Stmt(Def(Def { name: NodeId(14), params: NodeId(20), in_out_types: None, block: NodeId(22) })) (38 to 64) +23: Stmt(Def { name: NodeId(14), params: NodeId(20), in_out_types: None, block: NodeId(22) }) (38 to 64) 24: Expr(Block(BlockId(2))) (0 to 65) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(24) @@ -69,4 +69,4 @@ Error (NodeId 17): list must have one type argument register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Stmt(Def(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) })) not suported yet +Error (NodeId 13): node Stmt(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) }) not suported yet diff --git a/src/typechecker.rs b/src/typechecker.rs index f577004..6d42468 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,6 +1,6 @@ use crate::compiler::Compiler; use crate::errors::{Severity, SourceError}; -use crate::parser::{AstNode, Def, Expr, NodeId, Stmt, TypeAst}; +use crate::parser::{AstNode, Expr, NodeId, Stmt, TypeAst}; use std::cmp::Ordering; use std::collections::HashSet; @@ -421,7 +421,12 @@ impl<'a> Typechecker<'a> { initializer, is_mutable: _, } => self.typecheck_let(variable_name, ty, initializer, node_id), - Stmt::Def(ref def) => self.typecheck_def(def.clone(), node_id), + Stmt::Def { + name, + params, + in_out_types, + block, + } => self.typecheck_def(name, params, in_out_types, block, node_id), Stmt::Alias { new_name, old_name } => self.typecheck_alias(new_name, old_name, node_id), Stmt::For { variable, @@ -666,13 +671,14 @@ impl<'a> Typechecker<'a> { } } - fn typecheck_def(&mut self, def: Def, node_id: NodeId) { - let Def { - name, - params, - in_out_types, - block, - } = def; + fn typecheck_def( + &mut self, + name: NodeId, + params: NodeId, + in_out_types: Option, + block: NodeId, + node_id: NodeId, + ) { let in_out_types = in_out_types .map(|ty| { let AstNode::InOutTypes(types) = self.compiler.get_node(ty) else { From 3fccf202d1fe444bb9d61c6515b4bd30b87766ae Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 9 Mar 2025 14:56:58 -0400 Subject: [PATCH 08/12] Remove AstNode::Type from typecheck_node --- src/typechecker.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/typechecker.rs b/src/typechecker.rs index 6d42468..b3e3211 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -205,7 +205,7 @@ impl<'a> Typechecker<'a> { } AstNode::Param { name, ty } => { if let Some(ty) = ty { - self.typecheck_node(ty); + self.typecheck_type(ty); let var_id = self .compiler @@ -218,12 +218,9 @@ impl<'a> Typechecker<'a> { self.set_node_type_id(node_id, ANY_TYPE); } } - AstNode::Type(_) => { - self.typecheck_type(node_id); - } AstNode::TypeArgs(ref args) => { for arg in args { - self.typecheck_node(*arg); + self.typecheck_type(*arg); } // Type argument lists are not supposed to be evaluated self.set_node_type_id(node_id, FORBIDDEN_TYPE); @@ -773,7 +770,7 @@ impl<'a> Typechecker<'a> { self.typecheck_node(initializer); if let Some(ty) = ty { - self.typecheck_node(ty); + self.typecheck_type(ty); if !self.is_type_compatible(self.type_of(ty), self.type_of(initializer)) { self.error("initializer does not match declared type", initializer) @@ -882,10 +879,7 @@ impl<'a> Typechecker<'a> { panic!("internal error: record field isn't Param"); }; let ty_id = match ty { - Some(ty) => { - self.typecheck_node(*ty); - self.type_id_of(*ty) - } + Some(ty) => self.typecheck_type(*ty), None => ANY_TYPE, }; (*name, ty_id) From 8e6be34c9975e4cafdb5cc804ec7012efe4f431a Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 11 Mar 2025 00:16:42 -0400 Subject: [PATCH 09/12] Simple type inference --- src/compiler.rs | 6 +- src/resolver.rs | 18 +- ...nu_parser__test__node_output@calls.nu.snap | 2 +- ...r__test__node_output@calls_invalid.nu.snap | 52 +++ ...st__node_output@for_break_continue.nu.snap | 8 +- ...rser__test__node_output@invalid_if.nu.snap | 6 +- ...er__test__node_output@let_mismatch.nu.snap | 6 +- ..._nu_parser__test__node_output@loop.nu.snap | 2 +- src/typechecker.rs | 425 +++++++++++------- tests/calls_invalid.nu | 3 + 10 files changed, 351 insertions(+), 177 deletions(-) create mode 100644 src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap create mode 100644 tests/calls_invalid.nu diff --git a/src/compiler.rs b/src/compiler.rs index b38797f..74b6aea 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -57,8 +57,10 @@ pub struct Compiler { pub variables: Vec, /// Mapping of variable's name node -> Variable pub var_resolution: HashMap, - /// Declarations (commands, aliases, externs), indexed by VarId + /// Declarations (commands, aliases, externs), indexed by DeclId pub decls: Vec>, + /// Declaration NodeIds, indexed by DeclId + pub decl_nodes: Vec, /// Mapping of decl's name node -> Command pub decl_resolution: HashMap, @@ -95,6 +97,7 @@ impl Compiler { variables: vec![], var_resolution: HashMap::new(), decls: vec![], + decl_nodes: vec![], decl_resolution: HashMap::new(), // variables: vec![], @@ -158,6 +161,7 @@ impl Compiler { self.variables.extend(name_bindings.variables); self.var_resolution.extend(name_bindings.var_resolution); self.decls.extend(name_bindings.decls); + self.decl_nodes.extend(name_bindings.decl_nodes); self.decl_resolution.extend(name_bindings.decl_resolution); self.errors.extend(name_bindings.errors); } diff --git a/src/resolver.rs b/src/resolver.rs index 9be507a..e859491 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -58,6 +58,7 @@ pub struct NameBindings { pub variables: Vec, pub var_resolution: HashMap, pub decls: Vec>, + pub decl_nodes: Vec, pub decl_resolution: HashMap, pub errors: Vec, } @@ -70,6 +71,7 @@ impl NameBindings { variables: vec![], var_resolution: HashMap::new(), decls: vec![], + decl_nodes: vec![], decl_resolution: HashMap::new(), errors: vec![], } @@ -96,6 +98,8 @@ pub struct Resolver<'a> { pub var_resolution: HashMap, /// Declarations (commands, aliases, etc.), indexed by DeclId pub decls: Vec>, + /// Declaration NodeIds, indexed by DeclId + pub decl_nodes: Vec, /// Mapping of decl's name node -> Command pub decl_resolution: HashMap, /// Errors encountered during name binding @@ -111,6 +115,7 @@ impl<'a> Resolver<'a> { variables: vec![], var_resolution: HashMap::new(), decls: vec![], + decl_nodes: vec![], decl_resolution: HashMap::new(), errors: vec![], } @@ -123,6 +128,7 @@ impl<'a> Resolver<'a> { variables: self.variables, var_resolution: self.var_resolution, decls: self.decls, + decl_nodes: self.decl_nodes, decl_resolution: self.decl_resolution, errors: self.errors, } @@ -201,7 +207,7 @@ impl<'a> Resolver<'a> { // TODO: Move node_id param to the end, same as in typechecker match self.compiler.ast_nodes[node_id.0] { AstNode::Expr(ref expr) => self.resolve_expr(expr.clone(), node_id), - AstNode::Stmt(ref stmt) => self.resolve_stmt(stmt.clone()), + AstNode::Stmt(ref stmt) => self.resolve_stmt(stmt.clone(), node_id), AstNode::Params(ref params) => { for param in params { if let AstNode::Param { name, .. } = self.compiler.ast_nodes[param.0] { @@ -294,7 +300,7 @@ impl<'a> Resolver<'a> { } } - pub fn resolve_stmt(&mut self, stmt: Stmt) { + pub fn resolve_stmt(&mut self, stmt: Stmt, node_id: NodeId) { match stmt { Stmt::Def { name, @@ -303,7 +309,7 @@ impl<'a> Resolver<'a> { block, } => { // define the command before the block to enable recursive calls - self.define_decl(name); + self.define_decl(name, node_id); // making sure the def parameters and body end up in the same scope frame self.enter_scope(block); @@ -320,7 +326,7 @@ impl<'a> Resolver<'a> { new_name, old_name: _, } => { - self.define_decl(new_name); + self.define_decl(new_name, node_id); } Stmt::Let { variable_name, @@ -491,7 +497,7 @@ impl<'a> Resolver<'a> { self.var_resolution.insert(var_name_id, var_id); } - pub fn define_decl(&mut self, decl_name_id: NodeId) { + pub fn define_decl(&mut self, decl_name_id: NodeId, decl_node_id: NodeId) { // TODO: Deduplicate code with define_variable() let decl_name = self.compiler.get_span_contents(decl_name_id); let decl_name = trim_decl_name(decl_name).to_vec(); @@ -509,6 +515,8 @@ impl<'a> Resolver<'a> { self.decls.push(Box::new(decl)); let decl_id = DeclId(self.decls.len() - 1); + self.decl_nodes.push(decl_node_id); + // let the definition of a decl also count as its use self.decl_resolution.insert(decl_name_id, decl_id); } diff --git a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap index 8cbe397..fb26ac3 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap @@ -85,7 +85,7 @@ input_file: tests/calls.nu 32: string 33: string 34: int -35: any +35: list 36: unknown 37: stream 38: stream diff --git a/src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap new file mode 100644 index 0000000..f8a9e8b --- /dev/null +++ b/src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap @@ -0,0 +1,52 @@ +--- +source: src/test.rs +expression: evaluate_example(path) +input_file: tests/calls_invalid.nu +--- +==== COMPILER ==== +0: Name (4 to 7) "foo" +1: Name (10 to 11) "a" +2: Name (13 to 16) "int" +3: Type(Ref { name: NodeId(2), args: None, optional: false }) (13 to 16) +4: Param { name: NodeId(1), ty: Some(NodeId(3)) } (10 to 16) +5: Params([NodeId(4)]) (8 to 18) +6: Expr(Block(BlockId(0))) (19 to 21) +7: Stmt(Def { name: NodeId(0), params: NodeId(5), in_out_types: None, block: NodeId(6) }) (0 to 21) +8: Name (22 to 25) "foo" +9: Expr(Int) (26 to 27) "1" +10: Expr(Int) (28 to 29) "2" +11: Expr(Call { parts: [NodeId(8), NodeId(9), NodeId(10)] }) (26 to 29) +12: Name (30 to 33) "foo" +13: Expr(String) (34 to 42) ""string"" +14: Expr(Call { parts: [NodeId(12), NodeId(13)] }) (34 to 42) +15: Expr(Block(BlockId(1))) (0 to 43) +==== SCOPE ==== +0: Frame Scope, node_id: NodeId(15) + decls: [ foo: NodeId(0) ] +1: Frame Scope, node_id: NodeId(6) + variables: [ a: NodeId(1) ] +==== TYPES ==== +0: unknown +1: unknown +2: unknown +3: int +4: int +5: forbidden +6: () +7: () +8: unknown +9: int +10: unknown +11: () +12: unknown +13: string +14: () +15: () +==== TYPE ERRORS ==== +Error (NodeId 11): Expected 1 argument(s), got 2 +Error (NodeId 13): Expected int, got string +==== IR ==== +register_count: 0 +file_count: 0 +==== IR ERRORS ==== +Error (NodeId 7): node Stmt(Def { name: NodeId(0), params: NodeId(5), in_out_types: None, block: NodeId(6) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap b/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap index 853854d..bc56be7 100644 --- a/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap @@ -57,15 +57,15 @@ input_file: tests/for_break_continue.nu 10: int 11: bool 12: unknown -13: unknown -14: oneof<(), unknown> +13: () +14: () 15: int 16: forbidden 17: int 18: bool 19: unknown -20: unknown -21: oneof<(), unknown> +20: () +21: () 22: int 23: forbidden 24: int diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap index 97e3aa0..60dd98d 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap @@ -21,10 +21,10 @@ input_file: tests/invalid_if.nu 2: int 3: int 4: int -5: error -6: error +5: int +6: int ==== TYPE ERRORS ==== -Error (NodeId 0): The condition for if branch is not a boolean +Error (NodeId 0): Expected bool, got int ==== IR ==== register_count: 0 file_count: 0 diff --git a/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap b/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap index 6874a1d..4b9ae43 100644 --- a/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap @@ -91,9 +91,9 @@ input_file: tests/let_mismatch.nu 39: () 40: () ==== TYPE ERRORS ==== -Error (NodeId 13): initializer does not match declared type -Error (NodeId 26): initializer does not match declared type -Error (NodeId 38): initializer does not match declared type +Error (NodeId 13): Expected string, got int +Error (NodeId 26): Expected list>, got list> +Error (NodeId 38): Expected record, got record ==== IR ==== register_count: 0 file_count: 0 diff --git a/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap b/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap index 851cb2b..f53af03 100644 --- a/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@loop.nu.snap @@ -43,7 +43,7 @@ input_file: tests/loop.nu 13: unknown 14: unknown 15: unknown -16: unknown +16: () ==== TYPE ERRORS ==== Error (NodeId 15): unsupported ast node 'Stmt(Loop { block: NodeId(14) })' in typechecker ==== IR ==== diff --git a/src/typechecker.rs b/src/typechecker.rs index b3e3211..0892027 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,6 +1,6 @@ use crate::compiler::Compiler; use crate::errors::{Severity, SourceError}; -use crate::parser::{AstNode, Expr, NodeId, Stmt, TypeAst}; +use crate::parser::{AstNode, BlockId, Expr, NodeId, Stmt, TypeAst}; use std::cmp::Ordering; use std::collections::HashSet; @@ -31,6 +31,7 @@ pub enum Type { /// output anything and thus don't have any type. None, Any, + Top, Number, Nothing, Int, @@ -73,6 +74,7 @@ pub const CLOSURE_TYPE: TypeId = TypeId(11); pub const LIST_ANY_TYPE: TypeId = TypeId(12); pub const BYTE_STREAM_TYPE: TypeId = TypeId(13); pub const ERROR_TYPE: TypeId = TypeId(14); +pub const TOP_TYPE: TypeId = TypeId(15); pub struct Typechecker<'a> { /// Immutable reference to a compiler after the name binding pass @@ -117,18 +119,13 @@ impl<'a> Typechecker<'a> { Type::List(ANY_TYPE), Type::Stream(BINARY_TYPE), Type::Error, + Type::Top, ], node_types: vec![UNKNOWN_TYPE; compiler.ast_nodes.len()], record_types: Vec::new(), oneof_types: Vec::new(), variable_types: vec![UNKNOWN_TYPE; compiler.variables.len()], - decl_types: vec![ - vec![InOutType { - in_type: ANY_TYPE, - out_type: ANY_TYPE, - }]; - compiler.decls.len() - ], + decl_types: vec![vec![]; compiler.decls.len()], errors: vec![], } } @@ -194,7 +191,9 @@ impl<'a> Typechecker<'a> { fn typecheck_node(&mut self, node_id: NodeId) { match self.compiler.ast_nodes[node_id.0] { - AstNode::Expr(ref expr) => self.typecheck_expr(expr.clone(), node_id), + AstNode::Expr(ref expr) => { + self.typecheck_expr(expr.clone(), node_id, UNKNOWN_TYPE); + } AstNode::Stmt(ref stmt) => self.typecheck_stmt(stmt.clone(), node_id), AstNode::Params(ref params) => { for param in params { @@ -235,23 +234,63 @@ impl<'a> Typechecker<'a> { } } - fn typecheck_expr(&mut self, expr: Expr, node_id: NodeId) { - match expr { - Expr::Null => { - self.set_node_type_id(node_id, NOTHING_TYPE); - } - Expr::Int => { - self.set_node_type_id(node_id, INT_TYPE); - } - Expr::Float => { - self.set_node_type_id(node_id, FLOAT_TYPE); - } - Expr::True | Expr::False => { - self.set_node_type_id(node_id, BOOL_TYPE); + fn typecheck_block(&mut self, block_id: BlockId, expected: TypeId) -> TypeId { + let block = &self.compiler.blocks[block_id.0]; + // Block type is the type of the last statement, since blocks + // by themselves aren't supposed to be typed + let mut block_type = NONE_TYPE; + + for inner_node_id in &block.nodes { + if let AstNode::Stmt(Stmt::Def { + name, + params, + in_out_types, + .. + }) = self.compiler.get_node(*inner_node_id) + { + self.add_def_signature(*name, *params, *in_out_types) } - Expr::String => { - self.set_node_type_id(node_id, STRING_TYPE); + } + + for (i, inner_node_id) in block.nodes.iter().enumerate() { + match self.compiler.get_node(*inner_node_id) { + AstNode::Stmt(Stmt::Expr(expr_id)) => { + if i == block.nodes.len() - 1 { + block_type = self.typecheck_expr_id(*expr_id, expected); + } else { + self.typecheck_expr_id(*expr_id, TOP_TYPE); + } + } + AstNode::Expr(expr) => { + if i == block.nodes.len() - 1 { + block_type = self.typecheck_expr(expr.clone(), *inner_node_id, expected); + } else { + self.typecheck_expr(expr.clone(), *inner_node_id, TOP_TYPE); + } + } + AstNode::Stmt(stmt) => self.typecheck_stmt(stmt.clone(), *inner_node_id), + x => panic!("Internal error: block did not contain statement: {:?}", x), } + } + + block_type + } + + fn typecheck_expr_id(&mut self, expr_id: NodeId, expected: TypeId) -> TypeId { + match self.compiler.get_node(expr_id) { + AstNode::Expr(ref expr) => self.typecheck_expr(expr.clone(), expr_id, expected), + _ => panic!("Internal error: Node {} is not an expression", expr_id.0), + } + } + + /// Infer a type for an expression and update self.types + fn typecheck_expr(&mut self, expr: Expr, node_id: NodeId, expected: TypeId) -> TypeId { + let type_id = match expr { + Expr::Null => NOTHING_TYPE, + Expr::Int => INT_TYPE, + Expr::Float => FLOAT_TYPE, + Expr::True | Expr::False => BOOL_TYPE, + Expr::String => STRING_TYPE, Expr::List(ref items) => { if let Some(first_id) = items.first() { self.typecheck_node(*first_id); @@ -274,16 +313,17 @@ impl<'a> Typechecker<'a> { } if all_same { - self.set_node_type(node_id, Type::List(self.type_id_of(*first_id))); + self.push_type(Type::List(self.type_id_of(*first_id))) } else if all_numbers { - self.set_node_type(node_id, Type::List(NUMBER_TYPE)); + self.push_type(Type::List(NUMBER_TYPE)) } else { - self.set_node_type_id(node_id, LIST_ANY_TYPE); + LIST_ANY_TYPE } } else { - self.set_node_type_id(node_id, LIST_ANY_TYPE); + LIST_ANY_TYPE } } + Expr::Block(block_id) => self.typecheck_block(block_id, expected), Expr::Record { ref pairs } => { let mut field_types = pairs .iter() @@ -295,24 +335,7 @@ impl<'a> Typechecker<'a> { field_types.sort_by_cached_key(|(name, _)| self.compiler.get_span_contents(*name)); self.record_types.push(field_types); - let ty_id = self.push_type(Type::Record(RecordTypeId(self.record_types.len() - 1))); - self.set_node_type_id(node_id, ty_id); - } - Expr::Block(block_id) => { - let block = &self.compiler.blocks[block_id.0]; - - for inner_node_id in &block.nodes { - self.typecheck_node(*inner_node_id); - } - - // Block type is the type of the last statement, since blocks - // by themselves aren't supposed to be typed - let block_type = block - .nodes - .last() - .map_or(NONE_TYPE, |node_id| self.type_id_of(*node_id)); - - self.set_node_type_id(node_id, block_type); + self.push_type(Type::Record(RecordTypeId(self.record_types.len() - 1))) } Expr::Closure { params, block } => { // TODO: input/output types @@ -321,9 +344,9 @@ impl<'a> Typechecker<'a> { } self.typecheck_node(block); - self.set_node_type_id(node_id, CLOSURE_TYPE); + CLOSURE_TYPE } - Expr::BinaryOp { lhs, op, rhs } => self.typecheck_binary_op(lhs, op, rhs, node_id), + Expr::BinaryOp { lhs, op, rhs } => self.typecheck_binary_op(lhs, op, rhs), Expr::VarRef => { let var_id = self .compiler @@ -331,44 +354,25 @@ impl<'a> Typechecker<'a> { .get(&node_id) .expect("missing resolved variable"); - self.set_node_type_id(node_id, self.variable_types[var_id.0]); + self.variable_types[var_id.0] } Expr::If { condition, then_block, else_block, } => { - self.typecheck_node(condition); - self.typecheck_node(then_block); + self.typecheck_expr_id(condition, BOOL_TYPE); - let then_type_id = self.type_id_of(then_block); - let mut else_type = None; + let then_type_id = self.typecheck_expr_id(then_block, expected); if let Some(else_blk) = else_block { - self.typecheck_node(else_blk); - else_type = Some(self.type_of(else_blk)); - } - - let mut types = HashSet::new(); - self.add_resolved_types(&mut types, &then_type_id); - - if let Some(Type::OneOf(id)) = else_type { - types.extend(self.oneof_types[id.0].iter()); - } else if else_type.is_none() { - types.insert(NONE_TYPE); + let else_type_id = self.typecheck_expr_id(else_blk, expected); + let mut types = HashSet::new(); + types.insert(then_type_id); + types.insert(else_type_id); + self.create_oneof(types) } else { - types.insert(self.type_id_of(else_block.expect("Already checked"))); - } - - // the condition should always evaluate to a boolean - if self.type_of(condition) != Type::Bool { - self.error("The condition for if branch is not a boolean", condition); - self.set_node_type_id(node_id, ERROR_TYPE); - } else if types.len() > 1 { - self.oneof_types.push(types); - self.set_node_type(node_id, Type::OneOf(OneOfId(self.oneof_types.len() - 1))); - } else { - self.set_node_type_id(node_id, *types.iter().next().expect("Can't be empty")); + then_type_id } } Expr::Call { ref parts } => self.typecheck_call(parts, node_id), @@ -378,36 +382,39 @@ impl<'a> Typechecker<'a> { } => { // Check all the output types of match let output_types = self.typecheck_match(target, match_arms); - match output_types.len().cmp(&1) { - Ordering::Greater => { - self.oneof_types.push(output_types); - self.set_node_type( - node_id, - Type::OneOf(OneOfId(self.oneof_types.len() - 1)), - ); - } - Ordering::Equal => { - self.set_node_type_id( - node_id, - *output_types - .iter() - .next() - .expect("Will contain one element"), - ); - } - Ordering::Less => { - self.set_node_type_id(node_id, NOTHING_TYPE); - } + if output_types.is_empty() { + NOTHING_TYPE + } else { + self.create_oneof(output_types) } } - _ => self.error( + _ => { + self.error( + format!( + "unsupported ast node '{:?}' in typechecker", + self.compiler.ast_nodes[node_id.0] + ), + node_id, + ); + UNKNOWN_TYPE + } + }; + + let got = self.types[type_id.0]; + let exp = self.types[expected.0]; + if !self.is_subtype(got, exp) { + self.error( format!( - "unsupported ast node '{:?}' in typechecker", - self.compiler.ast_nodes[node_id.0] + "Expected {}, got {}", + self.type_to_string(expected), + self.type_to_string(type_id) ), node_id, - ), + ); } + + self.set_node_type_id(node_id, type_id); + type_id } fn typecheck_stmt(&mut self, stmt: Stmt, node_id: NodeId) { @@ -418,12 +425,7 @@ impl<'a> Typechecker<'a> { initializer, is_mutable: _, } => self.typecheck_let(variable_name, ty, initializer, node_id), - Stmt::Def { - name, - params, - in_out_types, - block, - } => self.typecheck_def(name, params, in_out_types, block, node_id), + Stmt::Def { name, block, .. } => self.typecheck_def(name, block, node_id), Stmt::Alias { new_name, old_name } => self.typecheck_alias(new_name, old_name, node_id), Stmt::For { variable, @@ -540,7 +542,7 @@ impl<'a> Typechecker<'a> { output_types } - fn typecheck_binary_op(&mut self, lhs: NodeId, op: NodeId, rhs: NodeId, node_id: NodeId) { + fn typecheck_binary_op(&mut self, lhs: NodeId, op: NodeId, rhs: NodeId) -> TypeId { self.typecheck_node(lhs); self.typecheck_node(rhs); self.set_node_type_id(op, FORBIDDEN_TYPE); @@ -662,20 +664,14 @@ impl<'a> Typechecker<'a> { }; if let Some(ty) = out_type { - self.set_node_type(node_id, ty); + self.push_type(ty) } else { - self.set_node_type_id(node_id, ERROR_TYPE); + ERROR_TYPE } } - fn typecheck_def( - &mut self, - name: NodeId, - params: NodeId, - in_out_types: Option, - block: NodeId, - node_id: NodeId, - ) { + fn add_def_signature(&mut self, name: NodeId, params: NodeId, in_out_types: Option) { + self.typecheck_node(params); let in_out_types = in_out_types .map(|ty| { let AstNode::InOutTypes(types) = self.compiler.get_node(ty) else { @@ -696,8 +692,19 @@ impl<'a> Typechecker<'a> { }) .unwrap_or_default(); - self.typecheck_node(params); - self.typecheck_node(block); + // set input/output types for the command + let decl_id = self + .compiler + .decl_resolution + .get(&name) + .expect("missing declared decl"); + + if !in_out_types.is_empty() { + self.decl_types[decl_id.0] = in_out_types; + } + } + + fn typecheck_def(&mut self, name: NodeId, block: NodeId, node_id: NodeId) { self.set_node_type_id(node_id, NONE_TYPE); // set input/output types for the command @@ -707,14 +714,18 @@ impl<'a> Typechecker<'a> { .get(&name) .expect("missing declared decl"); + let in_out_types = &self.decl_types[decl_id.0]; if in_out_types.is_empty() { + let inferred = self.typecheck_expr_id(block, ANY_TYPE); self.decl_types[decl_id.0] = vec![InOutType { in_type: ANY_TYPE, - out_type: self.type_id_of(block), + out_type: inferred, }]; } else { - // TODO check that block output type matches expected type - self.decl_types[decl_id.0] = in_out_types; + // TODO this doesn't match each input type to its corresponding output type, + // just uses a union of all the output types as the expected type + let expected = self.create_oneof(in_out_types.iter().map(|io| io.out_type).collect()); + self.typecheck_expr_id(block, expected); } } @@ -739,24 +750,57 @@ impl<'a> Typechecker<'a> { ); } - fn typecheck_call(&mut self, parts: &[NodeId], node_id: NodeId) { - let num_name_parts = if let Some(decl_id) = self.compiler.decl_resolution.get(&node_id) { - // TODO: The type should be `oneof` - self.set_node_type_id(node_id, ANY_TYPE); + fn typecheck_call(&mut self, parts: &[NodeId], node_id: NodeId) -> TypeId { + if let Some(decl_id) = self.compiler.decl_resolution.get(&node_id) { + let num_name_parts = self.compiler.decls[decl_id.0].name().split(' ').count(); + let decl_node_id = self.compiler.decl_nodes[decl_id.0]; + let AstNode::Stmt(Stmt::Def { params, .. }) = self.compiler.get_node(decl_node_id) + else { + panic!("Internal error: Expected def") + }; + let AstNode::Params(params) = self.compiler.get_node(*params) else { + panic!("Internal error: Expected def") + }; + let num_args = parts.len() - num_name_parts; + if params.len() != num_args { + self.error( + format!("Expected {} argument(s), got {}", params.len(), num_args), + node_id, + ); + } - self.compiler.decls[decl_id.0].name().split(' ').count() + for (param, arg) in params.iter().zip(&parts[num_name_parts..]) { + let expected = self.type_id_of(*param); + if matches!(self.compiler.ast_nodes[arg.0], AstNode::Name) { + self.set_node_type_id(*arg, STRING_TYPE); + if !self.is_subtype(Type::String, self.types[expected.0]) { + self.error( + format!("Expected {}, got string", self.type_to_string(expected)), + *arg, + ); + } + } else { + self.typecheck_expr_id(*arg, expected); + } + } + + // TODO base this on pipeline input type + self.create_oneof( + self.decl_types[decl_id.0] + .iter() + .map(|io| io.out_type) + .collect(), + ) } else { // external call - self.node_types[node_id.0] = BYTE_STREAM_TYPE; - 1 - }; - - for part in &parts[num_name_parts..] { - if matches!(self.compiler.ast_nodes[part.0], AstNode::Name) { - self.set_node_type_id(*part, STRING_TYPE); - } else { - self.typecheck_node(*part); + for part in &parts[1..] { + if matches!(self.compiler.ast_nodes[part.0], AstNode::Name) { + self.set_node_type_id(*part, STRING_TYPE); + } else { + self.typecheck_node(*part); + } } + BYTE_STREAM_TYPE } } @@ -767,15 +811,13 @@ impl<'a> Typechecker<'a> { initializer: NodeId, node_id: NodeId, ) { - self.typecheck_node(initializer); - - if let Some(ty) = ty { - self.typecheck_type(ty); - - if !self.is_type_compatible(self.type_of(ty), self.type_of(initializer)) { - self.error("initializer does not match declared type", initializer) - } - } + let expected = if let Some(node_id) = ty { + self.typecheck_type(node_id) + } else { + ANY_TYPE + }; + let init_ty_id = self.typecheck_expr_id(initializer, expected); + let type_id = if ty.is_some() { expected } else { init_ty_id }; let var_id = self .compiler @@ -783,12 +825,6 @@ impl<'a> Typechecker<'a> { .get(&variable_name) .expect("missing declared variable"); - let type_id = if let Some(ty) = ty { - self.type_id_of(ty) - } else { - self.type_id_of(initializer) - }; - self.variable_types[var_id.0] = type_id; self.set_node_type_id(variable_name, type_id); self.set_node_type_id(node_id, NONE_TYPE); @@ -798,7 +834,7 @@ impl<'a> Typechecker<'a> { let AstNode::Type(ty) = self.compiler.get_node(node_id) else { panic!("internal error: Expected type"); }; - let res = match ty { + let type_id = match ty { TypeAst::Ref { name: name_id, args: args_id, @@ -892,8 +928,9 @@ impl<'a> Typechecker<'a> { self.push_type(Type::Record(RecordTypeId(self.record_types.len() - 1))) } }; - self.set_node_type_id(node_id, res); - res + + self.set_node_type_id(node_id, type_id); + type_id } /// Add a new type and return its ID. To save space, common types are not pushed and their ID is @@ -919,11 +956,6 @@ impl<'a> Typechecker<'a> { } } - fn set_node_type(&mut self, node_id: NodeId, ty: Type) { - let type_id = self.push_type(ty); - self.node_types[node_id.0] = type_id; - } - fn set_node_type_id(&mut self, node_id: NodeId, type_id: TypeId) { self.node_types[node_id.0] = type_id; } @@ -982,6 +1014,22 @@ impl<'a> Typechecker<'a> { } } + /// Check if `sub` is a subtype of `supe` + fn is_subtype(&self, sub: Type, supe: Type) -> bool { + match (sub, supe) { + (_, Type::Top | Type::Any | Type::Unknown) => true, + (Type::Any | Type::Unknown, _) => true, + (Type::Int | Type::Float | Type::Number, Type::Number) => true, + (Type::List(inner_sub), Type::List(inner_supe)) => { + self.is_subtype(self.types[inner_sub.0], self.types[inner_supe.0]) + } + (_, Type::OneOf(oneof_id)) => self.oneof_types[oneof_id.0] + .iter() + .any(|ty| self.is_subtype(sub, self.types[ty.0])), + (_, _) => sub == supe, + } + } + fn type_to_string(&self, type_id: TypeId) -> String { let ty = &self.types[type_id.0]; @@ -990,6 +1038,7 @@ impl<'a> Typechecker<'a> { Type::Forbidden => "forbidden".to_string(), Type::None => "()".to_string(), Type::Any => "any".to_string(), + Type::Top => "top".to_string(), Type::Number => "number".to_string(), Type::Nothing => "nothing".to_string(), Type::Int => "int".to_string(), @@ -1112,6 +1161,64 @@ impl<'a> Typechecker<'a> { types.insert(*ty); } } + + /// Use this to create any union types, to ensure that the created union type + /// is as simple as possible + fn create_oneof(&mut self, types: HashSet) -> TypeId { + if types.is_empty() { + // TODO return bottom type instead? + return ANY_TYPE; + } + + let mut basic = HashSet::::new(); + let mut list = HashSet::new(); + + let mut flattened = HashSet::new(); + for ty_id in types { + match self.types[ty_id.0] { + Type::OneOf(oneof_id) => { + flattened.extend(&self.oneof_types[oneof_id.0]); + } + _ => { + flattened.insert(ty_id); + } + } + } + + for ty_id in flattened { + match self.types[ty_id.0] { + Type::List(inner) => { + list.insert(inner); + } + ty => { + if !basic.contains(&ty_id) + && basic.iter().all(|b| !self.is_subtype(ty, self.types[b.0])) + { + basic.insert(ty_id); + } + } + } + } + + if list.is_empty() { + if basic.len() == 1 { + *basic.iter().next().unwrap() + } else { + self.oneof_types.push(basic); + self.push_type(Type::OneOf(OneOfId(self.oneof_types.len() - 1))) + } + } else { + let list_inner = self.create_oneof(list); + let list_ty = self.push_type(Type::List(list_inner)); + if basic.is_empty() { + list_ty + } else { + basic.insert(list_ty); + self.oneof_types.push(basic); + self.push_type(Type::OneOf(OneOfId(self.oneof_types.len() - 1))) + } + } + } } /// Check whether two types can perform common numeric operations diff --git a/tests/calls_invalid.nu b/tests/calls_invalid.nu new file mode 100644 index 0000000..f435080 --- /dev/null +++ b/tests/calls_invalid.nu @@ -0,0 +1,3 @@ +def foo [ a: int ] {} +foo 1 2 +foo "string" From 255499c8d1802d0a08211a6f22e94503a7122182 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 11 Mar 2025 00:41:44 -0400 Subject: [PATCH 10/12] Fix create_oneof on lists --- src/typechecker.rs | 53 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/typechecker.rs b/src/typechecker.rs index 0892027..4247760 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1170,8 +1170,7 @@ impl<'a> Typechecker<'a> { return ANY_TYPE; } - let mut basic = HashSet::::new(); - let mut list = HashSet::new(); + let mut res = HashSet::::new(); let mut flattened = HashSet::new(); for ty_id in types { @@ -1186,37 +1185,37 @@ impl<'a> Typechecker<'a> { } for ty_id in flattened { - match self.types[ty_id.0] { - Type::List(inner) => { - list.insert(inner); + if res.contains(&ty_id) { + continue; + } + + let ty = self.types[ty_id.0]; + let mut add = true; + let mut remove = HashSet::new(); + for other_id in res.iter() { + let other = self.types[other_id.0]; + if self.is_subtype(ty, other) { + add = false; + break; } - ty => { - if !basic.contains(&ty_id) - && basic.iter().all(|b| !self.is_subtype(ty, self.types[b.0])) - { - basic.insert(ty_id); - } + if self.is_subtype(other, ty) { + remove.insert(*other_id); } } - } - if list.is_empty() { - if basic.len() == 1 { - *basic.iter().next().unwrap() - } else { - self.oneof_types.push(basic); - self.push_type(Type::OneOf(OneOfId(self.oneof_types.len() - 1))) + if add { + res.insert(ty_id); + for other in remove { + res.remove(&other); + } } + } + + if res.len() == 1 { + *res.iter().next().unwrap() } else { - let list_inner = self.create_oneof(list); - let list_ty = self.push_type(Type::List(list_inner)); - if basic.is_empty() { - list_ty - } else { - basic.insert(list_ty); - self.oneof_types.push(basic); - self.push_type(Type::OneOf(OneOfId(self.oneof_types.len() - 1))) - } + self.oneof_types.push(res); + self.push_type(Type::OneOf(OneOfId(self.oneof_types.len() - 1))) } } } From dafaa6de8cc2095b4641278f68036c572ee27f67 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 11 Mar 2025 02:51:41 -0400 Subject: [PATCH 11/12] Parse type params for defs --- src/compiler.rs | 12 +- src/parser.rs | 36 ++++ src/resolver.rs | 155 +++++++++++++++++- ...nu_parser__test__node_output@calls.nu.snap | 2 +- ...r__test__node_output@calls_invalid.nu.snap | 4 +- ...w_nu_parser__test__node_output@def.nu.snap | 4 +- ...er__test__node_output@def_generics.nu.snap | 72 ++++++++ ..._test__node_output@def_return_type.nu.snap | 6 +- ...r__test__node_output@invalid_types.nu.snap | 6 +- src/typechecker.rs | 18 +- tests/def_generics.nu | 4 + 11 files changed, 299 insertions(+), 20 deletions(-) create mode 100644 src/snapshots/new_nu_parser__test__node_output@def_generics.nu.snap create mode 100644 tests/def_generics.nu diff --git a/src/compiler.rs b/src/compiler.rs index 74b6aea..16d5311 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,7 +1,9 @@ use crate::errors::SourceError; use crate::parser::{AstNode, Block, Expr, NodeId}; use crate::protocol::Command; -use crate::resolver::{DeclId, Frame, NameBindings, ScopeId, VarId, Variable}; +use crate::resolver::{ + DeclId, Frame, NameBindings, ScopeId, TypeDecl, TypeDeclId, VarId, Variable, +}; use crate::typechecker::{TypeId, Types}; use std::collections::HashMap; @@ -57,6 +59,10 @@ pub struct Compiler { pub variables: Vec, /// Mapping of variable's name node -> Variable pub var_resolution: HashMap, + /// Type declarations, indexed by TypeDeclId + pub type_decls: Vec, + /// Mapping of type decl's name node -> TypeDecl + pub type_resolution: HashMap, /// Declarations (commands, aliases, externs), indexed by DeclId pub decls: Vec>, /// Declaration NodeIds, indexed by DeclId @@ -96,6 +102,8 @@ impl Compiler { scope_stack: vec![], variables: vec![], var_resolution: HashMap::new(), + type_decls: vec![], + type_resolution: HashMap::new(), decls: vec![], decl_nodes: vec![], decl_resolution: HashMap::new(), @@ -160,6 +168,8 @@ impl Compiler { self.scope_stack.extend(name_bindings.scope_stack); self.variables.extend(name_bindings.variables); self.var_resolution.extend(name_bindings.var_resolution); + self.type_decls.extend(name_bindings.type_decls); + self.type_resolution.extend(name_bindings.type_resolution); self.decls.extend(name_bindings.decls); self.decl_nodes.extend(name_bindings.decl_nodes); self.decl_resolution.extend(name_bindings.decl_resolution); diff --git a/src/parser.rs b/src/parser.rs index 23db711..d9bed64 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -133,6 +133,7 @@ pub enum Stmt { // Definitions Def { name: NodeId, + type_params: Option, params: NodeId, in_out_types: Option, block: NodeId, @@ -175,6 +176,8 @@ pub enum AstNode { Type(TypeAst), Name, + /// For now, each type parameter is a Name + TypeParams(Vec), TypeArgs(Vec), VarDecl, @@ -958,6 +961,32 @@ impl Parser { self.create_node(AstNode::Params(param_list), span_start, span_end) } + pub fn type_params(&mut self) -> NodeId { + let _span = span!(); + let span_start = self.position(); + self.less_than(); + + let mut param_list = vec![]; + + while self.has_tokens() { + if self.is_greater_than() { + break; + } + + if self.is_comma() { + self.tokens.advance(); + continue; + } + + param_list.push(self.name()); + } + + let span_end = self.position() + 1; + self.greater_than(); + + self.create_node(AstNode::TypeParams(param_list), span_start, span_end) + } + pub fn type_args(&mut self) -> NodeId { let _span = span!(); let span_start = self.position(); @@ -1099,6 +1128,12 @@ impl Parser { _ => return self.error("expected def name"), }; + let type_params = if self.is_less_than() { + Some(self.type_params()) + } else { + None + }; + let params = self.signature_params(ParamsContext::Squares); let in_out_types = if self.is_colon() { Some(self.in_out_types()) @@ -1112,6 +1147,7 @@ impl Parser { self.create_stmt( Stmt::Def { name, + type_params, params, in_out_types, block, diff --git a/src/resolver.rs b/src/resolver.rs index e859491..3dc4d93 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,4 +1,4 @@ -use crate::parser::{Expr, Stmt}; +use crate::parser::{Expr, Stmt, TypeAst}; use crate::protocol::{Command, Declaration}; use crate::{ compiler::Compiler, @@ -24,6 +24,7 @@ pub enum FrameType { pub struct Frame { pub frame_type: FrameType, pub variables: HashMap, NodeId>, + pub type_decls: HashMap, NodeId>, pub decls: HashMap, NodeId>, /// Node that defined the scope frame (e.g., a block or overlay) pub node_id: NodeId, @@ -34,6 +35,7 @@ impl Frame { Frame { frame_type: scope_type, variables: HashMap::new(), + type_decls: HashMap::new(), decls: HashMap::new(), node_id, } @@ -48,6 +50,15 @@ pub struct Variable { #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct VarId(pub usize); +#[derive(Debug, Clone)] +pub enum TypeDecl { + /// A type parameter. Holds the parameter name node + Param(NodeId), +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct TypeDeclId(pub usize); + #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct DeclId(pub usize); @@ -57,6 +68,8 @@ pub struct NameBindings { pub scope_stack: Vec, pub variables: Vec, pub var_resolution: HashMap, + pub type_decls: Vec, + pub type_resolution: HashMap, pub decls: Vec>, pub decl_nodes: Vec, pub decl_resolution: HashMap, @@ -70,6 +83,8 @@ impl NameBindings { scope_stack: vec![], variables: vec![], var_resolution: HashMap::new(), + type_decls: vec![], + type_resolution: HashMap::new(), decls: vec![], decl_nodes: vec![], decl_resolution: HashMap::new(), @@ -96,6 +111,10 @@ pub struct Resolver<'a> { pub variables: Vec, /// Mapping of variable's name node -> Variable pub var_resolution: HashMap, + /// Type declarations, indexed by TypeDeclId + pub type_decls: Vec, + /// Mapping of type decl's name node -> TypeDecl + pub type_resolution: HashMap, /// Declarations (commands, aliases, etc.), indexed by DeclId pub decls: Vec>, /// Declaration NodeIds, indexed by DeclId @@ -114,6 +133,8 @@ impl<'a> Resolver<'a> { scope_stack: vec![], variables: vec![], var_resolution: HashMap::new(), + type_decls: vec![], + type_resolution: HashMap::new(), decls: vec![], decl_nodes: vec![], decl_resolution: HashMap::new(), @@ -127,6 +148,8 @@ impl<'a> Resolver<'a> { scope_stack: self.scope_stack, variables: self.variables, var_resolution: self.var_resolution, + type_decls: self.type_decls, + type_resolution: self.type_resolution, decls: self.decls, decl_nodes: self.decl_nodes, decl_resolution: self.decl_resolution, @@ -156,13 +179,19 @@ impl<'a> Resolver<'a> { .map(|(name, id)| format!("{0}: {id:?}", String::from_utf8_lossy(name))) .collect(); + let mut types: Vec = scope + .type_decls + .iter() + .map(|(name, id)| format!("{0}: {id:?}", String::from_utf8_lossy(name))) + .collect(); + let mut decls: Vec = scope .decls .iter() .map(|(name, id)| format!("{0}: {id:?}", String::from_utf8_lossy(name))) .collect(); - if vars.is_empty() && decls.is_empty() { + if vars.is_empty() && types.is_empty() && decls.is_empty() { result.push_str(" (empty)\n"); continue; } @@ -175,6 +204,12 @@ impl<'a> Resolver<'a> { result.push_str(&line_var); } + if !types.is_empty() { + types.sort(); + let line_type = format!(" type decls: [ {0} ]\n", types.join(", ")); + result.push_str(&line_type); + } + if !decls.is_empty() { decls.sort(); let line_decl = format!(" decls: [ {0} ]\n", decls.join(", ")); @@ -210,15 +245,49 @@ impl<'a> Resolver<'a> { AstNode::Stmt(ref stmt) => self.resolve_stmt(stmt.clone(), node_id), AstNode::Params(ref params) => { for param in params { - if let AstNode::Param { name, .. } = self.compiler.ast_nodes[param.0] { + if let AstNode::Param { name, ty } = self.compiler.ast_nodes[param.0] { self.define_variable(name, false); + if let Some(ty) = ty { + self.resolve_node(ty); + } } else { panic!("param is not a param"); } } } - AstNode::Param { .. } => (/* seems unused for now */), - AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ), + AstNode::Type(ref ty) => match ty { + TypeAst::Ref { name, args, .. } => { + self.resolve_type(*name); + if let Some(args) = args { + self.resolve_node(*args); + } + } + TypeAst::Record { fields, .. } => { + let AstNode::Params(fields) = self.compiler.get_node(*fields) else { + panic!("Internal error: expected params for record field types"); + }; + for field in fields { + if let AstNode::Param { ty: Some(ty), .. } = self.compiler.get_node(*field) + { + self.resolve_node(*ty); + } + } + } + }, + AstNode::TypeArgs(ref args) => { + for arg in args { + self.resolve_node(*arg); + } + } + AstNode::InOutTypes(ref in_out_types) => { + for in_out_ty in in_out_types { + self.resolve_node(*in_out_ty); + } + } + AstNode::InOutType(in_ty, out_ty) => { + self.resolve_node(in_ty); + self.resolve_node(out_ty); + } // All remaining matches do not contain NodeId => there is nothing to resolve _ => (), } @@ -304,8 +373,9 @@ impl<'a> Resolver<'a> { match stmt { Stmt::Def { name, + type_params, params, - in_out_types: _, + in_out_types, block, } => { // define the command before the block to enable recursive calls @@ -313,7 +383,21 @@ impl<'a> Resolver<'a> { // making sure the def parameters and body end up in the same scope frame self.enter_scope(block); + + if let Some(type_params) = type_params { + let AstNode::TypeParams(type_params) = self.compiler.get_node(type_params) + else { + panic!("Internal error: expected type params") + }; + for type_param_id in type_params { + self.define_type_decl(*type_param_id, TypeDecl::Param(*type_param_id)); + } + } + self.resolve_node(params); + if let Some(in_out_types) = in_out_types { + self.resolve_node(in_out_types); + } let def_scope = self.exit_scope(); let AstNode::Expr(Expr::Block(block_id)) = self.compiler.ast_nodes[block.0] else { @@ -330,11 +414,14 @@ impl<'a> Resolver<'a> { } Stmt::Let { variable_name, - ty: _, + ty, initializer, is_mutable, } => { self.resolve_node(initializer); + if let Some(ty) = ty { + self.resolve_node(ty); + } self.define_variable(variable_name, is_mutable) } Stmt::While { condition, block } => { @@ -386,6 +473,31 @@ impl<'a> Resolver<'a> { } } + pub fn resolve_type(&mut self, unbound_node_id: NodeId) { + let type_name = self.compiler.get_span_contents(unbound_node_id); + + match type_name { + b"any" | b"list" | b"bool" | b"closure" | b"float" | b"int" | b"nothing" + | b"number" | b"string" => return, + _ => {} + } + + if let Some(node_id) = self.find_type(type_name) { + let type_id = self + .type_resolution + .get(&node_id) + .expect("internal error: missing resolved variable"); + + self.type_resolution.insert(unbound_node_id, *type_id); + } else { + self.errors.push(SourceError { + message: format!("type `{}` not found", String::from_utf8_lossy(type_name)), + node_id: unbound_node_id, + severity: Severity::Error, + }) + } + } + pub fn resolve_call(&mut self, unbound_node_id: NodeId, parts: &[NodeId]) { // Find out the potentially longest command name let max_name_parts = parts @@ -497,6 +609,25 @@ impl<'a> Resolver<'a> { self.var_resolution.insert(var_name_id, var_id); } + pub fn define_type_decl(&mut self, type_name_id: NodeId, type_decl: TypeDecl) { + let type_name = self.compiler.get_span_contents(type_name_id).to_vec(); + + let current_scope_id = self + .scope_stack + .last() + .expect("internal error: missing scope frame id"); + + self.scope[current_scope_id.0] + .type_decls + .insert(type_name, type_name_id); + + self.type_decls.push(type_decl); + let type_id = TypeDeclId(self.type_decls.len() - 1); + + // let the definition of a type also count as its use + self.type_resolution.insert(type_name_id, type_id); + } + pub fn define_decl(&mut self, decl_name_id: NodeId, decl_node_id: NodeId) { // TODO: Deduplicate code with define_variable() let decl_name = self.compiler.get_span_contents(decl_name_id); @@ -531,6 +662,16 @@ impl<'a> Resolver<'a> { None } + pub fn find_type(&self, type_name: &[u8]) -> Option { + for scope_id in self.scope_stack.iter().rev() { + if let Some(id) = self.scope[scope_id.0].type_decls.get(type_name) { + return Some(*id); + } + } + + None + } + pub fn find_decl(&self, var_name: &[u8]) -> Option { // TODO: Deduplicate code with find_variable() for scope_id in self.scope_stack.iter().rev() { diff --git a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap index fb26ac3..1508dd0 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls.nu.snap @@ -31,7 +31,7 @@ input_file: tests/calls.nu 24: Expr(VarRef) (80 to 82) "$c" 25: Expr(List([NodeId(22), NodeId(23), NodeId(24)])) (70 to 82) 26: Expr(Block(BlockId(0))) (68 to 85) -27: Stmt(Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) }) (24 to 85) +27: Stmt(Def { name: NodeId(8), type_params: None, params: NodeId(21), in_out_types: None, block: NodeId(26) }) (24 to 85) 28: Name (86 to 94) "existing" 29: Name (95 to 98) "foo" 30: Expr(String) (100 to 104) ""ba"" diff --git a/src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap b/src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap index f8a9e8b..a685338 100644 --- a/src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@calls_invalid.nu.snap @@ -11,7 +11,7 @@ input_file: tests/calls_invalid.nu 4: Param { name: NodeId(1), ty: Some(NodeId(3)) } (10 to 16) 5: Params([NodeId(4)]) (8 to 18) 6: Expr(Block(BlockId(0))) (19 to 21) -7: Stmt(Def { name: NodeId(0), params: NodeId(5), in_out_types: None, block: NodeId(6) }) (0 to 21) +7: Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(5), in_out_types: None, block: NodeId(6) }) (0 to 21) 8: Name (22 to 25) "foo" 9: Expr(Int) (26 to 27) "1" 10: Expr(Int) (28 to 29) "2" @@ -49,4 +49,4 @@ Error (NodeId 13): Expected int, got string register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 7): node Stmt(Def { name: NodeId(0), params: NodeId(5), in_out_types: None, block: NodeId(6) }) not suported yet +Error (NodeId 7): node Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(5), in_out_types: None, block: NodeId(6) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap index 4ca156a..de1f4f9 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def.nu.snap @@ -39,7 +39,7 @@ input_file: tests/def.nu 32: Expr(VarRef) (77 to 79) "$z" 33: Expr(List([NodeId(29), NodeId(30), NodeId(31), NodeId(32)])) (64 to 80) 34: Expr(Block(BlockId(0))) (62 to 83) -35: Stmt(Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) }) (0 to 83) +35: Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(28), in_out_types: None, block: NodeId(34) }) (0 to 83) 36: Expr(Block(BlockId(1))) (0 to 83) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(36) @@ -88,4 +88,4 @@ input_file: tests/def.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 35): node Stmt(Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) }) not suported yet +Error (NodeId 35): node Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(28), in_out_types: None, block: NodeId(34) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def_generics.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def_generics.nu.snap new file mode 100644 index 0000000..e1812e3 --- /dev/null +++ b/src/snapshots/new_nu_parser__test__node_output@def_generics.nu.snap @@ -0,0 +1,72 @@ +--- +source: src/test.rs +expression: evaluate_example(path) +input_file: tests/def_generics.nu +--- +==== COMPILER ==== +0: Name (4 to 5) "f" +1: Name (6 to 7) "T" +2: TypeParams([NodeId(1)]) (5 to 8) +3: Name (11 to 12) "x" +4: Name (14 to 15) "T" +5: Type(Ref { name: NodeId(4), args: None, optional: false }) (14 to 15) +6: Param { name: NodeId(3), ty: Some(NodeId(5)) } (11 to 15) +7: Params([NodeId(6)]) (9 to 17) +8: Name (20 to 27) "nothing" +9: Type(Ref { name: NodeId(8), args: None, optional: false }) (20 to 27) +10: Name (31 to 35) "list" +11: Name (36 to 37) "T" +12: Type(Ref { name: NodeId(11), args: None, optional: false }) (36 to 37) +13: TypeArgs([NodeId(12)]) (35 to 38) +14: Type(Ref { name: NodeId(10), args: Some(NodeId(13)), optional: false }) (31 to 35) +15: InOutType(NodeId(9), NodeId(14)) (20 to 39) +16: InOutTypes([NodeId(15)]) (20 to 39) +17: VarDecl (47 to 48) "z" +18: Name (50 to 51) "T" +19: Type(Ref { name: NodeId(18), args: None, optional: false }) (50 to 51) +20: Expr(VarRef) (54 to 56) "$x" +21: Stmt(Let { variable_name: NodeId(17), ty: Some(NodeId(19)), initializer: NodeId(20), is_mutable: false }) (43 to 56) +22: Expr(VarRef) (60 to 62) "$z" +23: Expr(List([NodeId(22)])) (59 to 62) +24: Expr(Block(BlockId(0))) (39 to 65) +25: Stmt(Def { name: NodeId(0), type_params: Some(NodeId(2)), params: NodeId(7), in_out_types: Some(NodeId(16)), block: NodeId(24) }) (0 to 65) +26: Expr(Block(BlockId(1))) (0 to 66) +==== SCOPE ==== +0: Frame Scope, node_id: NodeId(26) + decls: [ f: NodeId(0) ] +1: Frame Scope, node_id: NodeId(24) + variables: [ x: NodeId(3), z: NodeId(17) ] + type decls: [ T: NodeId(1) ] +==== TYPES ==== +0: unknown +1: unknown +2: unknown +3: unknown +4: unknown +5: T +6: T +7: forbidden +8: unknown +9: nothing +10: unknown +11: unknown +12: T +13: forbidden +14: list +15: unknown +16: unknown +17: T +18: unknown +19: T +20: T +21: () +22: T +23: list +24: list +25: () +26: () +==== IR ==== +register_count: 0 +file_count: 0 +==== IR ERRORS ==== +Error (NodeId 25): node Stmt(Def { name: NodeId(0), type_params: Some(NodeId(2)), params: NodeId(7), in_out_types: Some(NodeId(16)), block: NodeId(24) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap index c808256..9fee6a5 100644 --- a/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap @@ -17,7 +17,7 @@ input_file: tests/def_return_type.nu 10: InOutTypes([NodeId(9)]) (14 to 35) 11: Expr(List([])) (37 to 38) 12: Expr(Block(BlockId(0))) (35 to 41) -13: Stmt(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) (0 to 41) +13: Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) (0 to 41) 14: Name (46 to 49) "bar" 15: Params([]) (50 to 53) 16: Name (58 to 64) "string" @@ -39,7 +39,7 @@ input_file: tests/def_return_type.nu 32: InOutTypes([NodeId(23), NodeId(31)]) (56 to 101) 33: Expr(List([])) (103 to 104) 34: Expr(Block(BlockId(1))) (101 to 107) -35: Stmt(Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) }) (42 to 107) +35: Stmt(Def { name: NodeId(14), type_params: None, params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) }) (42 to 107) 36: Expr(Block(BlockId(2))) (0 to 108) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(36) @@ -88,4 +88,4 @@ input_file: tests/def_return_type.nu register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Stmt(Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) not suported yet +Error (NodeId 13): node Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) }) not suported yet diff --git a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap index dc17c89..9d1cee3 100644 --- a/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap @@ -17,7 +17,7 @@ input_file: tests/invalid_types.nu 10: Params([NodeId(9)]) (8 to 30) 11: Expr(VarRef) (33 to 35) "$x" 12: Expr(Block(BlockId(0))) (31 to 37) -13: Stmt(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) }) (0 to 37) +13: Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(10), in_out_types: None, block: NodeId(12) }) (0 to 37) 14: Name (42 to 45) "bar" 15: Name (47 to 48) "y" 16: Name (50 to 54) "list" @@ -27,7 +27,7 @@ input_file: tests/invalid_types.nu 20: Params([NodeId(19)]) (46 to 57) 21: Expr(VarRef) (60 to 62) "$y" 22: Expr(Block(BlockId(1))) (58 to 64) -23: Stmt(Def { name: NodeId(14), params: NodeId(20), in_out_types: None, block: NodeId(22) }) (38 to 64) +23: Stmt(Def { name: NodeId(14), type_params: None, params: NodeId(20), in_out_types: None, block: NodeId(22) }) (38 to 64) 24: Expr(Block(BlockId(2))) (0 to 65) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(24) @@ -69,4 +69,4 @@ Error (NodeId 17): list must have one type argument register_count: 0 file_count: 0 ==== IR ERRORS ==== -Error (NodeId 13): node Stmt(Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) }) not suported yet +Error (NodeId 13): node Stmt(Def { name: NodeId(0), type_params: None, params: NodeId(10), in_out_types: None, block: NodeId(12) }) not suported yet diff --git a/src/typechecker.rs b/src/typechecker.rs index 4247760..0964a21 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,6 +1,7 @@ use crate::compiler::Compiler; use crate::errors::{Severity, SourceError}; use crate::parser::{AstNode, BlockId, Expr, NodeId, Stmt, TypeAst}; +use crate::resolver::{TypeDecl, TypeDeclId}; use std::cmp::Ordering; use std::collections::HashSet; @@ -44,6 +45,7 @@ pub enum Type { Stream(TypeId), Record(RecordTypeId), OneOf(OneOfId), + Ref(TypeDeclId), Error, } @@ -76,12 +78,16 @@ pub const BYTE_STREAM_TYPE: TypeId = TypeId(13); pub const ERROR_TYPE: TypeId = TypeId(14); pub const TOP_TYPE: TypeId = TypeId(15); +pub struct ExVarId(pub usize); + pub struct Typechecker<'a> { /// Immutable reference to a compiler after the name binding pass compiler: &'a Compiler, /// Types referenced by TypeId types: Vec, + /// Existential type variables referenced by ExVarId + ex_vars: Vec<()>, /// Types of nodes. Each type in this vector matches a node in compiler.ast_nodes at the same position. pub node_types: Vec, @@ -121,6 +127,7 @@ impl<'a> Typechecker<'a> { Type::Error, Type::Top, ], + ex_vars: Vec::new(), node_types: vec![UNKNOWN_TYPE; compiler.ast_nodes.len()], record_types: Vec::new(), oneof_types: Vec::new(), @@ -899,8 +906,12 @@ impl<'a> Typechecker<'a> { // if bytes.contains(&b'@') { // // type with completion // } else { - UNKNOWN_TYPE // } + if let Some(ty_id) = self.compiler.type_resolution.get(name_id) { + self.push_type(Type::Ref(*ty_id)) + } else { + UNKNOWN_TYPE + } } } } @@ -1086,6 +1097,11 @@ impl<'a> Typechecker<'a> { fmt.push('>'); fmt } + Type::Ref(id) => match self.compiler.type_decls[id.0] { + TypeDecl::Param(name) => { + String::from_utf8_lossy(self.compiler.get_span_contents(name)).to_string() + } + }, Type::Error => "error".to_string(), } } diff --git a/tests/def_generics.nu b/tests/def_generics.nu new file mode 100644 index 0000000..1e9b850 --- /dev/null +++ b/tests/def_generics.nu @@ -0,0 +1,4 @@ +def f [ x: T ] : nothing -> list { + let z: T = $x + [$z] +} From df30d49e97b4978857022429b13d72906486d736 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 7 Apr 2025 02:05:39 -0400 Subject: [PATCH 12/12] Start on constraints or something idk --- src/typechecker.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/typechecker.rs b/src/typechecker.rs index 0964a21..38f8e3d 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -46,6 +46,7 @@ pub enum Type { Record(RecordTypeId), OneOf(OneOfId), Ref(TypeDeclId), + ExVar(ExVarId), Error, } @@ -78,6 +79,7 @@ pub const BYTE_STREAM_TYPE: TypeId = TypeId(13); pub const ERROR_TYPE: TypeId = TypeId(14); pub const TOP_TYPE: TypeId = TypeId(15); +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ExVarId(pub usize); pub struct Typechecker<'a> { @@ -87,7 +89,7 @@ pub struct Typechecker<'a> { /// Types referenced by TypeId types: Vec, /// Existential type variables referenced by ExVarId - ex_vars: Vec<()>, + ex_vars: Vec>, /// Types of nodes. Each type in this vector matches a node in compiler.ast_nodes at the same position. pub node_types: Vec, @@ -1025,6 +1027,35 @@ impl<'a> Typechecker<'a> { } } + /// Try constraining `sub` to be a subtype of `supe`. Returns whether it was successful + fn constrain_subtype(&mut self, sub: TypeId, supe: TypeId) -> bool { + match (self.types[sub.0], self.types[supe.0]) { + (Type::ExVar(a), Type::ExVar(b)) => a == b, + (Type::ExVar(a), b) => todo!(), + (a, Type::ExVar(b)) => todo!(), + // todo handle lists, unions, and records containing existential variables within + (a, b) => self.is_subtype(a, b), + } + } + + /// Constrain an existential variable to be a subtype of `supe` + fn constrain_ex_sub(&mut self, ex: ExVarId, supe: TypeId) { + match self.types[supe.0] { + Type::ExVar(other) => { + if ex.0 < other.0 { + self.ex_vars[other.0] = ex; + } else { + todo!("add constraint that ex = other"); + } + } + } + } + + /// Constrain an existential variable to be a supertype of `sub` + fn constrain_ex_supe(&mut self, ex: ExVarId, sub: TypeId) { + todo!() + } + /// Check if `sub` is a subtype of `supe` fn is_subtype(&self, sub: Type, supe: Type) -> bool { match (sub, supe) {