diff --git a/src/parser.rs b/src/parser.rs index d990281..950648c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -137,6 +137,10 @@ pub enum AstNode { params: Option, block: NodeId, }, + Alias { + new_name: NodeId, + old_name: NodeId, + }, /// Long flag ('--' + one or more letters) FlagLong, @@ -373,9 +377,13 @@ impl Parser { Token::LCurly => self.record_or_closure(), Token::LParen => { self.tokens.advance(); - let output = self.expression(); - self.rparen(); - output + if self.tokens.peek_token() == Token::RParen { + self.error("use null instead of ()") + } else { + let output = self.expression(); + self.rparen(); + output + } } Token::LSquare => self.list_or_table(), Token::Int => self.advance_node(AstNode::Int, span), @@ -771,6 +779,10 @@ impl Parser { let pattern_result = self.simple_expression(BarewordContext::String); + if self.is_comma() { + self.tokens.advance(); + } + match_arms.push((pattern, pattern_result)); } else if self.is_newline() { self.tokens.advance(); @@ -1112,6 +1124,8 @@ impl Parser { code_body.push(self.continue_statement()); } else if self.is_keyword(b"break") { code_body.push(self.break_statement()); + } else if self.is_keyword(b"alias") { + code_body.push(self.alias_statement()); } else { let exp_span_start = self.position(); let expression = self.expression_or_assignment(); @@ -1146,6 +1160,12 @@ impl Parser { let span_start = self.position(); self.keyword(b"while"); + if self.is_operator() { + // TODO: flag parsing + self.error("WIP: Flags on while are not supported yet"); + self.tokens.advance(); + } + let condition = self.expression(); let block = self.block(BlockContext::Curlies); let span_end = self.get_span_end(block); @@ -1223,6 +1243,25 @@ impl Parser { self.create_node(AstNode::Break, span_start, span_end) } + pub fn alias_statement(&mut self) -> NodeId { + let _span = span!(); + let span_start = self.position(); + self.keyword(b"alias"); + let new_name = if self.is_string() { + self.string() + } else { + self.name() + }; + self.equals(); + let old_name = if self.is_string() { + self.string() + } else { + self.name() + }; + let span_end = self.get_span_end(old_name); + self.create_node(AstNode::Alias { new_name, old_name }, span_start, span_end) + } + pub fn is_operator(&mut self) -> bool { let (token, span) = self.tokens.peek(); diff --git a/src/resolver.rs b/src/resolver.rs index 4d32b38..537d786 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -238,6 +238,12 @@ impl<'a> Resolver<'a> { self.resolve_block(block, block_id, Some(def_scope)); } + AstNode::Alias { + new_name, + old_name: _, + } => { + self.define_decl(new_name); + } AstNode::Params(ref params) => { for param in params { if let AstNode::Param { name, .. } = self.compiler.ast_nodes[param.0] { 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 79badab..02149e2 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 @@ -5,21 +5,21 @@ input_file: tests/alias.nu snapshot_kind: text --- ==== COMPILER ==== -0: Name (4 to 7) "foo" -1: Name (9 to 10) "a" -2: Param { name: NodeId(1), ty: None } (9 to 10) -3: Params([NodeId(2)]) (8 to 11) -4: Variable (14 to 16) "$a" -5: Block(BlockId(0)) (12 to 18) -6: Def { name: NodeId(0), params: NodeId(3), return_ty: None, block: NodeId(5) } (0 to 18) -7: Name (20 to 25) "alias" -8: Name (26 to 29) "bar" -9: Garbage (30 to 31) -10: String (32 to 35) "foo" -11: Call { parts: [NodeId(7), NodeId(8), NodeId(9), NodeId(10)] } (26 to 35) -12: Name (37 to 40) "bar" -13: Int (41 to 42) "1" -14: Call { parts: [NodeId(12), NodeId(13)] } (41 to 42) -15: Block(BlockId(1)) (0 to 43) -==== COMPILER ERRORS ==== -Error (NodeId 9): incomplete expression +0: 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) +==== SCOPE ==== +0: Frame Scope, node_id: NodeId(6) + decls: [ fancy alias: NodeId(0) ] +==== TYPES ==== +0: unknown +1: unknown +2: () +3: unknown +4: string +5: stream +6: stream 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 29f0028..bfe066d 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,6 +2,7 @@ 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" 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 4ef7169..00ef4a6 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,6 +2,7 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/closure.nu +snapshot_kind: text --- ==== COMPILER ==== 0: Name (3 to 4) "a" 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 a841564..f192ad4 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,6 +2,7 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/closure2.nu +snapshot_kind: text --- ==== COMPILER ==== 0: Variable (4 to 6) "$a" 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 2a4dccb..c5d3a4c 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,6 +2,7 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/invalid_range.nu +snapshot_kind: text --- ==== COMPILER ==== 0: Int (0 to 1) "1" 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 a439045..f8b4411 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,6 +2,7 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/invalid_record.nu +snapshot_kind: text --- ==== COMPILER ==== 0: String (2 to 3) "a" 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 296fd4a..05cb6ac 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 @@ -12,38 +12,22 @@ snapshot_kind: text 4: Variable (27 to 29) "$x" 5: Int (34 to 35) "1" 6: String (39 to 44) ""one"" -7: Garbage (44 to 45) -8: Let { variable_name: NodeId(3), ty: None, initializer: NodeId(7), is_mutable: false } (11 to 45) -9: Int (48 to 49) "2" -10: Garbage (50 to 52) -11: Variable (63 to 64) "w" -12: Int (67 to 68) "3" -13: Let { variable_name: NodeId(11), ty: None, initializer: NodeId(12), is_mutable: false } (59 to 68) -14: Int (73 to 74) "2" -15: Plus (75 to 76) -16: Variable (77 to 79) "$w" -17: BinaryOp { lhs: NodeId(14), op: NodeId(15), rhs: NodeId(16) } (73 to 79) -18: Block(BlockId(0)) (59 to 82) -19: Closure { params: None, block: NodeId(18) } (53 to 83) -20: Garbage (83 to 84) -21: Int (87 to 88) "3" -22: Garbage (89 to 91) -23: Null (92 to 96) -24: Garbage (96 to 97) -25: Name (100 to 101) "_" -26: Garbage (102 to 104) -27: Garbage (106 to 107) -28: Garbage (107 to 108) -29: Call { parts: [NodeId(25), NodeId(26), NodeId(27)] } (102 to 108) -30: Garbage (109 to 110) -31: Block(BlockId(1)) (0 to 111) +7: Int (48 to 49) "2" +8: Variable (63 to 64) "w" +9: 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" +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) "_" +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) +22: Let { variable_name: NodeId(3), ty: None, initializer: NodeId(21), is_mutable: false } (11 to 110) +23: Block(BlockId(1)) (0 to 111) ==== COMPILER ERRORS ==== -Error (NodeId 7): expected match arm in match -Error (NodeId 10): incomplete expression -Error (NodeId 20): incomplete expression -Error (NodeId 22): incomplete expression -Error (NodeId 24): incomplete expression -Error (NodeId 26): incomplete expression -Error (NodeId 27): incomplete expression -Error (NodeId 28): expected: right paren ')' -Error (NodeId 30): incomplete expression +Error (NodeId 20): use null instead of () 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 1c9b477..3573c8c 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,6 +2,7 @@ source: src/test.rs expression: evaluate_example(path) input_file: tests/mut_.nu +snapshot_kind: text --- ==== COMPILER ==== 0: Variable (4 to 5) "x" diff --git a/src/typechecker.rs b/src/typechecker.rs index 27dd9f9..ce978b6 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -348,6 +348,9 @@ impl<'a> Typechecker<'a> { return_ty, block, } => self.typecheck_def(name, params, return_ty, 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, @@ -609,6 +612,27 @@ impl<'a> Typechecker<'a> { }]; } + fn typecheck_alias(&mut self, new_name: NodeId, old_name: NodeId, node_id: NodeId) { + self.set_node_type_id(node_id, NONE_TYPE); + + // set input/output types for the command + let decl_id_new = self + .compiler + .decl_resolution + .get(&new_name) + .expect("missing declared new name for alias"); + + let decl_id_old = self.compiler.decl_resolution.get(&old_name); + + self.decl_types[decl_id_new.0] = decl_id_old.map_or( + vec![InOutType { + in_type: ANY_TYPE, + out_type: BYTE_STREAM_TYPE, + }], + |decl_id| self.decl_types[decl_id.0].clone(), + ); + } + 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` diff --git a/tests/alias.nu b/tests/alias.nu new file mode 100644 index 0000000..639d233 --- /dev/null +++ b/tests/alias.nu @@ -0,0 +1,3 @@ +alias "fancy alias" = foo + +fancy alias