diff --git a/contributing/grammar.md b/contributing/grammar.md new file mode 100644 index 0000000..240f4ef --- /dev/null +++ b/contributing/grammar.md @@ -0,0 +1,368 @@ +# Nushell Grammar in EBNF format + +This is a descriptive, context-free grammar for the Nushell surface syntax targeted +by this repository. It is intentionally written in the reader-friendly EBNF style +used in [Crafting Interpreters](https://craftinginterpreters.com/parsing-expressions.html), rather than in executable PEG syntax. + +Note that the rule name in this grammar does not map directly to [parser.rs](../src/parser.rs), still you can use it as a grammar reference. + +## Notation + +- `a → b` means “`a` is composed of `b`”. +- `a | b` means “choose one of `a` or `b`”. +- `x*`, `x+`, and `x?` mean zero-or-more, one-or-more, and optional `x`. +- Parentheses group alternatives. +- Quoted text is literal source text; uppercase names are lexer tokens. +- This grammar describes syntax only unless a rule is marked **parse-time semantic + requirement** below. + +## Program structure + +```text +program → shebang? terminator* statement_sequence? terminator* EOF ; +statement_sequence + → statement (terminator+ statement)* ; +terminator → NEWLINE | ";" ; + +statement → declaration + | loop_statement + | flow_statement + | assignment + | pipeline ; + +pipeline → pipe_element (pipe pipe_element)* ; +pipe_element → expression_command redirection* ; +pipe → "|" | "e>|" | "err>|" | "out+err>|" | "err+out>|" ; +redirection → file_redirection expression ; +file_redirection + → ">" | "o>" | ">>" | "o>>" + | "e>" | "err>" | "e>>" | "err>>" + | "out+err>" | "err+out>" | "o+e>" | "e+o>" + | "out+err>>" | "err+out>>" | "o+e>>" | "e+o>>" ; +``` + +## Declarations, modules, and overlays + +```text +declaration → let_decl | mut_decl | const_decl | def_decl | extern_decl + | alias_decl | module_decl | use_decl | source_decl + | export_decl | export_env_decl | hide_decl | overlay_decl + | plugin_use_decl ; + +let_decl → "let" binding "=" pipeline ; +mut_decl → "mut" binding "=" pipeline ; +const_decl → "const" binding "=" expression ; +binding → variable_decl type_annotation? ; +variable_decl → "$"? IDENTIFIER ; + +def_decl → "def" def_option* command_name type_params? signature + io_signature? block ; +def_option → "--env" | "--wrapped" ; +extern_decl → "extern" command_name signature ; +alias_decl → "alias" command_name "=" pipeline ; + +module_decl → "module" module_name block | "module" module_path ; +use_decl → "use" import_pattern ; +source_decl → ("source" | "source-env") expression ; +export_decl → "export" exportable_declaration ; +exportable_declaration + → def_decl | extern_decl | alias_decl | const_decl | module_decl + | use_decl ; +export_env_decl → "export-env" block ; +hide_decl → "hide" import_pattern ; + +plugin_use_decl → "plugin" "use" expression ; +overlay_decl → "overlay" overlay_action ; +overlay_action → "use" "--prefix"* import_pattern ("as" command_name)? + | "hide" overlay_hide_option* command_name? overlay_hide_option* + | "new" command_name + | "list" ; +overlay_hide_option + → "--keep-custom" | "--keep-env" list ; +``` + +## Statements and command calls + +```text +assignment → cell_ref assign_op pipeline ; +assign_op → "=" | "+=" | "++=" | "-=" | "*=" | "/=" ; + +loop_statement → for_statement | while_statement | loop_forever ; +for_statement → "for" variable_decl "in" expression block ; +while_statement → "while" expression block ; +loop_forever → "loop" block ; + +flow_statement → "return" expression? | "break" | "continue" ; + +expression_command + → environment_assignment* command ; +environment_assignment + → ENV_NAME "=" (string | variable | bare_word) ; +command → external_call | internal_call | expression ; +external_call → "^" external_name external_argument* ; +external_name → command_name | variable | string ; +external_argument + → spread | expression | bare_word | string ; +internal_call → call_name argument* ; +argument → flag | spread | expression | bare_word ; +flag → flag_long (("=" expression) | expression)? + | short_flag (("=" expression) | expression)? ; +flag_long → "--" flag_name; +short_flag → "-" SHORT_FLAGS ; +spread → "..." expression ; + +command_name → string | call_name | IDENTIFIER ; +flag_name → IDENTIFIER_ALLOW_DASH ; +call_name + → IDENTIFIER_ALLOW_DASH IDENTIFIER_ALLOW_DASH+ ; +``` + +**Command-name semantic requirement:** command heads are scope-sensitive. The +parser/resolver must select the longest name that is a command known in the current +parse-time scope, then parse the remaining words as arguments. The EBNF rule above +only admits the possible word sequence; it does not perform name resolution. + +## Expressions and precedence + +Each rule accepts its own precedence level and every tighter level below it. This +makes expressions such as `1 + 2 * 3` unambiguous without precedence metadata. +Binary repetition in the following rules is left-associative unless stated otherwise. + +```text +expression → range ; +range → logical_or (range_op logical_or? (range_op logical_or?)?)? + | range_op logical_or? ; +range_op → ".." | "..<" ; + +logical_or → logical_xor ("or" logical_xor)* ; +logical_xor → logical_and ("xor" logical_and)* ; +logical_and → bit_or ("and" bit_or)* ; +bit_or → bit_xor ("bit-or" bit_xor)* ; +bit_xor → bit_and ("bit-xor" bit_and)* ; +bit_and → comparison ("bit-and" comparison)* ; +comparison → shift (compare_op shift)* ; +compare_op → "==" | "!=" | "<" | "<=" | ">" | ">=" + | "=~" | "!~" | "in" | "not-in" | "has" | "not-has" + | "like" | "not-like" | "starts-with" | "not-starts-with" + | "ends-with" | "not-ends-with" | "++" ; +shift → addition (("bit-shl" | "bit-shr") addition)* ; +addition → multiply (("+" | "-") multiply)* ; +multiply → power (("*" | "/" | "//" | "mod") power)* ; +power → unary ("**" power)? ; // right-associative +unary → ("not" | "+" | "-") unary | postfix ; +postfix → primary cell_path? ; + +primary → if_expression | try_expression | match_expression + | literal | variable | cell_path_literal | table | list | closure + | record | block | subexpression ; +subexpression → "(" statement_sequence? ")" ; +block → "{" statement_sequence? "}" ; +``` + +## Values, collections, and paths + +```text +literal → FILESIZE | DURATION | DATE | BINARY | interpolated_string + | raw_string | string | FLOAT | INT | BOOL | "null" ; +string → single_quoted_string | double_quoted_string | backtick_string ; +variable → special_variable | "$" IDENTIFIER ; +special_variable + → "$env" | "$in" | "$it" | "$nu" + | "$NU_LIB_DIRS" | "$NU_PLUGIN_DIRS" ; + +cell_ref → variable cell_path? | cell_path_literal ; +cell_path_literal + → "$" cell_path ; +cell_path → cell_member+ ; +cell_member → "." path_member "?"? ; +path_member → string | INT | IDENTIFIER ; + +list → "[" list_item (separator? list_item)* separator? "]" + | "[" "]" ; +list_item → spread | expression | bare_word ; + +table → "[" table_header ";" table_row* "]" ; +table_header → "[" table_header_name (separator? table_header_name)* separator? "]" + | "[" "]" ; +table_header_name + → string | IDENTIFIER ; +table_row → "[" table_row_value (separator? table_row_value)* separator? "]" + | "[" "]" ; +table_row_value → expression | bare_word ; + +record → "{" record_item (separator? record_item)* separator? "}" + | "{" "}" ; +record_item → record_key ":" expression ; +record_key → string | IDENTIFIER ; + +closure → "{" "|" closure_parameter ("," closure_parameter)* ","? "|" + statement_sequence? "}" + | "{" "|" "|" statement_sequence? "}" + | "{" statement_sequence? "}" ; +closure_parameter + → IDENTIFIER type_annotation? ; +separator → "," | NEWLINE | ";" ; +``` + +## Control expressions and patterns + +```text +if_expression → "if" expression block else_clause? ; +else_clause → "else" if_expression | "else" match_expression | "else" block ; +try_expression → "try" block catch_clause? finally_clause? ; +catch_clause → "catch" (closure | block) ; +finally_clause → "finally" block ; + +match_expression + → "match" expression "{" match_arm (separator match_arm)* + separator? "}" ; +match_arm → pattern guard? "=>" (expression | block) ; +guard → "if" expression ; +pattern → single_pattern ("|" single_pattern)* ; +single_pattern → "_" | literal | variable | list_pattern | record_pattern ; +list_pattern → "[" pattern ("," pattern)* ","? "]" ; +record_pattern → "{" record_pattern_item ("," record_pattern_item)* ","? "}" ; +record_pattern_item + → record_key ":" pattern ; +``` + +## Signatures, types, imports + +```text +signature → "[" signature_parameter (separator? signature_parameter)* + separator? "]" ; +signature_parameter + → rest_parameter | flag_parameter | positional_parameter ; +positional_parameter + → IDENTIFIER "?"? type_annotation? default_value? ; +rest_parameter → "..." IDENTIFIER type_annotation? ; +flag_parameter → flag_long ("(" short_flag ")")? type_annotation? default_value? ; +default_value → "=" expression ; +io_signature → ":" "[" in_out_type (separator? in_out_type)* separator? "]" + | ":" in_out_type ; +in_out_type → type "->" type ; +type_annotation → ":" type ("@" command_name)? ; +type → "record" "<" record_type_field ("," record_type_field)* ","? ">" "?"? + | IDENTIFIER type_arguments? "?"? ; +type_arguments → "<" type ("," type)* ","? ">" ; +record_type_field + → record_key type_annotation? ; +type_params → "<" IDENTIFIER ("," IDENTIFIER)* ","? ">" ; + +import_pattern → module_ref import_members? ; +module_ref → module_path | module_name ; +module_path → PATH | string ; +module_name → string | command_name ; +import_members → "*" | command_name + | "[" import_member (separator? import_member)* separator? "]" + | "[" "]" ; +import_member → "*" | string | command_name ; +``` + +## Lexer contracts + +The lexer owns token boundaries, comments, escape decoding, and raw-string delimiter +matching. These are deliberately not expressed as ordinary context-free productions. + +```text +line_comment → "#" characters_until_newline ; +shebang → "#!" characters_until_newline NEWLINE ; +skip → (space | tab | line_comment)* ; + +single_quoted_string + → "'" any_character_until("'") "'" ; +double_quoted_string + → '"' (escape | character_except_quote_backslash_or_newline)* '"' ; +backtick_string → "`" any_character_until("`") "`" ; +interpolated_string + → '$"' interpolation_part* '"' + | "$'" interpolation_part* "'" ; +``` + +### Raw-string lexer algorithm + +Nushell raw strings begin with one or more `#` characters and use the *same count* +in their closing delimiter: + +```text +raw_string → "r" raw_delimiter raw_content raw_closing_delimiter ; +raw_delimiter → "#"+ "'" ; +``` + +The equality between the number of opening and closing `#` characters is not a +regular EBNF/PEG relationship. Implement it in the lexer: + +1. After reading `r`, count consecutive `#` characters as `hash_count`. Require + `hash_count >= 1` and then require an opening `'`. +2. Scan raw content without processing escapes or interpolation. +3. At each `'`, check whether it is immediately followed by `hash_count` `#` + characters. +4. If so, consume that quote and those hashes and emit one `RAW_STRING` token. + Otherwise, keep scanning; that quote belongs to the content. +5. If EOF is reached first, emit an unterminated-raw-string error whose span starts + at the opening `r`. + +Examples: + +```text +r#'text with 'quotes''# // one-hash delimiter +r###'r##'nested-looking text'##'### // three-hash delimiter +r##'a '# is content; '## closes'## // only quote + two hashes closes +``` + +A PEG may retain a placeholder such as `RAW_STRING ← lexer_raw_string`, but it should +not use independent `'#'+` expressions for the opener and closer because that admits +mismatched delimiters. + +## Parse-time semantic requirements (not PEG) + +Nushell parses a complete source unit before ordinary evaluation. Some declarations +and commands therefore have syntax *and* parse-time effects. The PEG/EBNF should +accept their structure; a resolver/semantic pass must enforce the following rules in +source order. + +### Parse-time declarations and scope + +- `const name = expression` requires a constant-evaluable expression. Store the + resulting value in the parse-time constant scope. +- `def`, `extern`, `alias`, `module`, and their `export` variants register names in + the parse-time declaration/module scope when their declaration is processed. +- Parse-time scope determines multiword command resolution and which imported or + overlaid names are available to subsequent source. + +### Parse-time loading and mutation + +- `source`, `source-env`, `use`, `overlay use`, and `plugin use` require arguments + that the parser can resolve without ordinary runtime evaluation. Where Nushell + permits computed inputs, they must be constant-evaluable. +- Resolve the module/script/plugin during parsing, parse its contents or signatures, + and merge the resulting declarations into the appropriate parse-time scope. +- `hide` and `overlay hide` operate on names/overlays that are known in that scope. +- Context checks are semantic: for example, `export` forms are valid only where the + module/export context allows them. + +### Diagnostics + +The resolver should diagnose a non-constant parser-keyword argument at its argument +span, an unresolved path/name at the corresponding operand, and a source-order +failure with a note that Nushell resolves parser keywords before normal evaluation. + +```nu +# Valid: `root` is known while this source unit is parsed. +const root = $nu.default-config-dir | path join "modules" +use $"($root)/tools.nu" * + +# Invalid: ordinary `let` values do not exist until evaluation. +let root = $nu.default-config-dir +use $"($root)/tools.nu" * +``` + +This separation is required by Nushell's parse-then-evaluate execution model and its +restricted parse-time constant evaluation. + +## Deliberate omissions + +This document does not enumerate every built-in command. Built-ins and subcommands +are accepted by `internal_call`/`command_name`; their command-specific signatures and +semantics belong in the command registry, not the grammar. + diff --git a/src/parser.rs b/src/parser.rs index 33e61a9..71046e3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -304,9 +304,16 @@ pub enum AstNode { params: NodeId, }, Params(ParamsId), - Param { + FlagParam { + long: NodeId, + short: Option, + ty: Option, + default: Option, + }, + PosParam { name: NodeId, ty: Option, + default: Option, }, InOutTypes(InOutTypesId), /// Input/output type pair for a command @@ -321,11 +328,13 @@ pub enum AstNode { }, /// Long flag ('--' + one or more letters) - FlagLong, + FlagLong(NodeId), /// Short flag ('-' + single letter) - FlagShort, + FlagShort(NodeId), /// Group of short flags ('-' + more than 1 letters) - FlagShortGroup, + FlagShortGroup(NodeId), + /// Spread + Spread(NodeId), // Expressions Call(CallId), @@ -622,7 +631,7 @@ impl Parser { self.compiler.ast_nodes[node_id.0] = AstNode::String; node_id } - BarewordContext::Call => self.call(), + BarewordContext::Call => self.internal_call(), }, }, _ => self.error("incomplete expression"), @@ -725,27 +734,23 @@ impl Parser { } } - pub fn call(&mut self) -> NodeId { - let _span = span!(); - let mut parts = vec![self.call_name()]; - let mut is_head = true; - let span_start = self.position(); - - while self.has_tokens() { - if self.is_newline() { - break; - } + fn call_name(&mut self) -> Vec { + let mut parts = vec![self.identifier_allow_dash()]; - if self.is_name() && is_head { - parts.push(self.name()); - continue; - } + while self.has_tokens() && self.is_name() && !self.is_newline() { + parts.push(self.identifier_allow_dash()); + } + parts + } - // TODO: Add flags + pub fn internal_call(&mut self) -> NodeId { + let _span = span!(); + let span_start = self.position(); + let mut parts = self.call_name(); - is_head = false; - let arg_id = self.simple_expression(BarewordContext::String); - parts.push(arg_id); + // Arguments. + while self.has_tokens() && !self.is_newline() { + parts.push(self.argument()); } let span_end = self.position(); @@ -758,6 +763,61 @@ impl Parser { ) } + fn argument(&mut self) -> NodeId { + match self.tokens.peek_token() { + Token::DotDotDot => self.spread_expression(), + Token::DashDash => self.flag_long(), + Token::Dash => self.flag_short(), + _ => self.simple_expression(BarewordContext::String), + } + } + + fn spread_expression(&mut self) -> NodeId { + let span_start = self.position(); + self.tokens.advance(); + let expression = self.simple_expression(BarewordContext::String); + let span_end = self.compiler.get_span(expression).end; + self.create_node(AstNode::Spread(expression), span_start, span_end) + } + + fn flag_long(&mut self) -> NodeId { + let span_start = self.position(); + if !self.is_dashdash() { + return self.error("Expect dashdash(--)"); + } + self.tokens.advance(); + let flag_name = self.flag_name(); + let span_end = self.compiler.get_span(flag_name).end; + let result = self.create_node(AstNode::FlagLong(flag_name), span_start, span_end); + + // may skip additional `=` + if self.is_equals() { + self.tokens.advance(); + } + result + } + + fn flag_short(&mut self) -> NodeId { + let span_start = self.position(); + if !self.is_dash() { + return self.error("Expect dash(-)"); + } + self.tokens.advance(); + let flag_name = self.name(); + let span_end = self.compiler.get_span(flag_name).end; + let result = if self.compiler.get_span_contents(flag_name).len() > 1 { + self.create_node(AstNode::FlagShortGroup(flag_name), span_start, span_end) + } else { + self.create_node(AstNode::FlagShort(flag_name), span_start, span_end) + }; + + // may skip additional `=` + if self.is_equals() { + self.tokens.advance(); + } + result + } + pub fn list_or_table(&mut self) -> NodeId { let _span = span!(); let span_start = self.position(); @@ -955,27 +1015,24 @@ impl Parser { } } - pub fn call_name(&mut self) -> NodeId { - let (mut token, mut span) = self.tokens.peek(); - - loop { - if [Token::Eof, Token::Newline].contains(&token) { - break; - } + fn flag_name(&mut self) -> NodeId { + self.identifier_allow_dash() + } + fn identifier_allow_dash(&mut self) -> NodeId { + let span = self.tokens.peek_span(); + let (span_start, mut span_end) = (span.start, span.end); + while self.has_tokens() && (self.is_name() || self.is_dash()) { + span_end = self.tokens.peek_span().end; self.tokens.advance(); - let (next_token, next_span) = self.tokens.peek(); - - if next_span.start > span.end { - // horizontal whitespace + let next_span = self.tokens.peek_span(); + if next_span.start > span_end { + // horizontal whitespace. break; } - - token = next_token; - span.end = next_span.end; } - self.create_node(AstNode::Name, span.start, span.end) + self.create_node(AstNode::Name, span_start, span_end) } pub fn has_tokens(&mut self) -> bool { @@ -1160,7 +1217,21 @@ impl Parser { continue; } - let name = self.name(); + let is_flag_param = self.is_dashdash(); + let (name, short_name) = + if is_flag_param && matches!(params_context, ParamsContext::Squares) { + let result = self.flag_long(); + if self.is_lparen() { + self.tokens.advance(); + let short = self.flag_short(); + self.rparen(); + (result, Some(short)) + } else { + (result, None) + } + } else { + (self.name(), None) + }; let ty = if self.is_colon() { // We have a type @@ -1171,15 +1242,42 @@ impl Parser { None }; - let name_span = self.compiler.spans[name.0]; - let param_span_end = if let Some(ty_id) = ty { - self.compiler.spans[ty_id.0].end + let default_val = if self.is_equals() { + // We have a default value. + self.equals(); + Some(self.simple_expression(BarewordContext::String)) } else { - name_span.end + None }; - let param = - self.create_node(AstNode::Param { name, ty }, name_span.start, param_span_end); + let name_span = self.compiler.spans[name.0]; + let param_span_end = default_val.map_or_else( + || ty.map_or(name_span.end, |ty_node| self.get_span_end(ty_node)), + |default_val| self.get_span_end(default_val), + ); + + let param = if is_flag_param { + self.create_node( + AstNode::FlagParam { + long: name, + short: short_name, + ty, + default: default_val, + }, + name_span.start, + param_span_end, + ) + } else { + self.create_node( + AstNode::PosParam { + name, + ty, + default: default_val, + }, + name_span.start, + param_span_end, + ) + }; // output.push(self.name()); output.push(param); @@ -1412,7 +1510,7 @@ impl Parser { } let name = match self.tokens.peek() { - (Token::Bareword, span) => self.advance_node(AstNode::Name, span), + (Token::Bareword, _) => self.identifier_allow_dash(), (Token::DoubleQuotedString | Token::SingleQuotedString, span) => { self.advance_node(AstNode::String, span) } @@ -1778,6 +1876,14 @@ impl Parser { self.tokens.peek_token() == Token::LCurly } + pub fn is_dash(&self) -> bool { + self.tokens.peek_token() == Token::Dash + } + + pub fn is_dashdash(&self) -> bool { + self.tokens.peek_token() == Token::DashDash + } + pub fn is_rcurly(&mut self) -> bool { self.tokens.peek_token() == Token::RCurly } diff --git a/src/resolver.rs b/src/resolver.rs index a2d8b0f..57ef9c5 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -296,12 +296,25 @@ impl<'a> Resolver<'a> { AstNode::Params(_) => { let params = self.compiler.get_params(node_id); for param in ¶ms.nodes { - let AstNode::Param { name, ty } = self.compiler.ast_nodes[param.0] else { - panic!("param is not a param"); - }; - self.define_variable(name, false); - if let Some(ty) = ty { - self.resolve_node(ty); + // TODO: handle default, and maybe FlagParam. + match self.compiler.ast_nodes[param.0] { + AstNode::PosParam { + name, + ty, + default: _, + } + | AstNode::FlagParam { + long: name, + short: _, + ty, + default: _, + } => { + self.define_variable(name, false); + if let Some(ty) = ty { + self.resolve_node(ty); + } + } + _ => panic!("param is not a param"), } } } @@ -397,7 +410,8 @@ impl<'a> Resolver<'a> { AstNode::RecordType { fields, .. } => { let fields = self.compiler.get_params(fields); for field in &fields.nodes { - if let AstNode::Param { ty: Some(ty), .. } = self.compiler.get_node(*field) { + // TODO: handle default. + if let AstNode::PosParam { ty: Some(ty), .. } = self.compiler.get_node(*field) { self.resolve_node(*ty); } } @@ -417,7 +431,7 @@ impl<'a> Resolver<'a> { self.resolve_node(out_ty); } AstNode::Pipeline(pipeline_id) => self.resolve_pipeline(pipeline_id), - AstNode::Param { .. } => (/* seems unused for now */), + AstNode::PosParam { .. } => (/* seems unused for now */), AstNode::NamedValue { .. } => (/* seems unused for now */), // All remaining matches do not contain NodeId => there is nothing to resolve _ => (), 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 1c80976..7e8b393 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 @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/alias.nu --- @@ -10,7 +9,7 @@ input_file: tests/alias.nu 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(CallId(0)) (33 to 38) +5: Call(CallId(0)) (27 to 38) 6: Block(BlockId(0)) (0 to 39) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(6) @@ -28,4 +27,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 2): node 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 4f033f2..48e7a5a 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 @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/calls.nu --- @@ -12,20 +11,20 @@ input_file: tests/calls.nu 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(CallId(0)) (5 to 22) +7: Call(CallId(0)) (0 to 22) 8: Name (28 to 36) "existing" 9: Name (38 to 39) "a" 10: Name (41 to 47) "string" 11: Type { name: NodeId(10), args: None, optional: false } (41 to 47) -12: Param { name: NodeId(9), ty: Some(NodeId(11)) } (38 to 47) +12: PosParam { name: NodeId(9), ty: Some(NodeId(11)), default: None } (38 to 47) 13: Name (49 to 50) "b" 14: Name (52 to 58) "string" 15: Type { name: NodeId(14), args: None, optional: false } (52 to 58) -16: Param { name: NodeId(13), ty: Some(NodeId(15)) } (49 to 58) +16: PosParam { name: NodeId(13), ty: Some(NodeId(15)), default: None } (49 to 58) 17: Name (60 to 61) "c" 18: Name (63 to 66) "int" 19: Type { name: NodeId(18), args: None, optional: false } (63 to 66) -20: Param { name: NodeId(17), ty: Some(NodeId(19)) } (60 to 66) +20: PosParam { name: NodeId(17), ty: Some(NodeId(19)), default: None } (60 to 66) 21: Params(ParamsId(0)) (37 to 67) 22: Variable (72 to 74) "$a" 23: Variable (76 to 78) "$b" @@ -40,13 +39,10 @@ input_file: tests/calls.nu 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(CallId(1)) (95 to 113) -36: Name (115 to 128) "foo/bar/spam -" -37: Call(CallId(2)) (127 to 127) -38: Block(BlockId(1)) (0 to 128) +35: Call(CallId(1)) (86 to 113) +36: Block(BlockId(1)) (0 to 115) ==== SCOPE ==== -0: Frame Scope, node_id: NodeId(38) +0: Frame Scope, node_id: NodeId(36) decls: [ existing: NodeId(8) ] 1: Frame Scope, node_id: NodeId(26) variables: [ a: NodeId(9), b: NodeId(13), c: NodeId(17) ] @@ -87,12 +83,9 @@ input_file: tests/calls.nu 33: string 34: int 35: list -36: unknown -37: stream -38: stream +36: list ==== IR ==== register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 7): node Call(CallId(0)) not suported yet - 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 d8c8ccc..653d079 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 @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/calls_invalid.nu --- @@ -9,17 +8,17 @@ input_file: tests/calls_invalid.nu 1: Name (10 to 11) "a" 2: Name (13 to 16) "int" 3: Type { name: NodeId(2), args: None, optional: false } (13 to 16) -4: Param { name: NodeId(1), ty: Some(NodeId(3)) } (10 to 16) +4: PosParam { name: NodeId(1), ty: Some(NodeId(3)), default: None } (10 to 16) 5: Params(ParamsId(0)) (8 to 18) 6: Block(BlockId(0)) (19 to 21) 7: Def { name: NodeId(0), type_params: None, params: NodeId(5), in_out_types: None, block: NodeId(6), env: false, wrapped: false } (0 to 21) 8: Name (22 to 25) "foo" 9: Int (26 to 27) "1" 10: Int (28 to 29) "2" -11: Call(CallId(0)) (26 to 29) +11: Call(CallId(0)) (22 to 29) 12: Name (30 to 33) "foo" 13: String (34 to 42) ""string"" -14: Call(CallId(1)) (34 to 42) +14: Call(CallId(1)) (30 to 42) 15: Block(BlockId(1)) (0 to 43) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(15) @@ -51,4 +50,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 7): node Def { name: NodeId(0), type_params: None, params: NodeId(5), in_out_types: None, block: NodeId(6), env: false, wrapped: false } 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 b2c9fd6..3af804e 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 @@ -1,14 +1,13 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/closure.nu --- ==== COMPILER ==== 0: Name (3 to 4) "a" -1: Param { name: NodeId(0), ty: None } (3 to 4) +1: PosParam { name: NodeId(0), ty: None, default: None } (3 to 4) 2: Name (6 to 7) "b" -3: Param { name: NodeId(2), ty: None } (6 to 7) +3: PosParam { name: NodeId(2), ty: None, default: None } (6 to 7) 4: Params(ParamsId(0)) (2 to 8) 5: Variable (9 to 11) "$a" 6: Plus (12 to 13) @@ -24,4 +23,3 @@ input_file: tests/closure.nu variables: [ a: NodeId(0), b: NodeId(2) ] ==== SCOPE ERRORS ==== Error (NodeId 11): variable `a` not found - 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 3448451..dfaafc0 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 @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/closure3.nu --- @@ -9,11 +8,11 @@ input_file: tests/closure3.nu 1: Name (16 to 17) "a" 2: Name (19 to 22) "int" 3: Type { name: NodeId(2), args: None, optional: false } (19 to 22) -4: Param { name: NodeId(1), ty: Some(NodeId(3)) } (16 to 22) +4: PosParam { name: NodeId(1), ty: Some(NodeId(3)), default: None } (16 to 22) 5: Name (24 to 25) "b" 6: Name (27 to 30) "int" 7: Type { name: NodeId(6), args: None, optional: false } (27 to 30) -8: Param { name: NodeId(5), ty: Some(NodeId(7)) } (24 to 30) +8: PosParam { name: NodeId(5), ty: Some(NodeId(7)), default: None } (24 to 30) 9: Params(ParamsId(0)) (15 to 31) 10: Variable (32 to 34) "$a" 11: Plus (35 to 36) @@ -27,7 +26,7 @@ input_file: tests/closure3.nu 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(CallId(0)) (53 to 61) +22: Call(CallId(0)) (46 to 61) 23: Block(BlockId(1)) (0 to 62) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(23) @@ -64,4 +63,3 @@ 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 - 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 8e2b2fe..55106c7 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 @@ -1,17 +1,16 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/def.nu --- ==== COMPILER ==== 0: Name (4 to 7) "foo" 1: Name (9 to 10) "w" -2: Param { name: NodeId(1), ty: None } (9 to 10) +2: PosParam { name: NodeId(1), ty: None, default: None } (9 to 10) 3: Name (11 to 12) "x" 4: Name (14 to 17) "int" 5: Type { name: NodeId(4), args: None, optional: false } (14 to 17) -6: Param { name: NodeId(3), ty: Some(NodeId(5)) } (11 to 17) +6: PosParam { name: NodeId(3), ty: Some(NodeId(5)), default: None } (11 to 17) 7: Name (19 to 20) "y" 8: Name (22 to 26) "list" 9: Name (27 to 31) "list" @@ -21,18 +20,18 @@ input_file: tests/def.nu 13: Type { name: NodeId(9), args: Some(NodeId(12)), optional: false } (27 to 31) 14: TypeArgs(TypeArgsId(1)) (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) +16: PosParam { name: NodeId(7), ty: Some(NodeId(15)), default: None } (19 to 26) 17: Name (39 to 40) "z" 18: Name (42 to 48) "record" 19: Name (49 to 50) "a" -20: Param { name: NodeId(19), ty: None } (49 to 50) +20: PosParam { name: NodeId(19), ty: None, default: None } (49 to 50) 21: Name (52 to 53) "b" 22: Name (55 to 58) "int" 23: Type { name: NodeId(22), args: None, optional: false } (55 to 58) -24: Param { name: NodeId(21), ty: Some(NodeId(23)) } (52 to 58) +24: PosParam { name: NodeId(21), ty: Some(NodeId(23)), default: None } (52 to 58) 25: Params(ParamsId(0)) (48 to 59) 26: RecordType { fields: NodeId(25), optional: false } (42 to 60) -27: Param { name: NodeId(17), ty: Some(NodeId(26)) } (39 to 60) +27: PosParam { name: NodeId(17), ty: Some(NodeId(26)), default: None } (39 to 60) 28: Params(ParamsId(1)) (8 to 61) 29: Variable (66 to 68) "$w" 30: Variable (69 to 71) "$x" @@ -41,53 +40,40 @@ input_file: tests/def.nu 33: List(ListId(0)) (64 to 80) 34: Block(BlockId(0)) (62 to 83) 35: Def { name: NodeId(0), type_params: None, params: NodeId(28), in_out_types: None, block: NodeId(34), env: false, wrapped: false } (0 to 83) -36: Block(BlockId(1)) (0 to 83) +36: Name (103 to 116) "foo-with-flag" +37: Name (120 to 123) "bar" +38: FlagLong(NodeId(37)) (118 to 123) +39: Name (125 to 128) "int" +40: Type { name: NodeId(39), args: None, optional: false } (125 to 128) +41: Int (131 to 132) "3" +42: FlagParam { long: NodeId(38), short: None, ty: Some(NodeId(40)), default: Some(NodeId(41)) } (118 to 132) +43: Name (136 to 139) "baz" +44: FlagLong(NodeId(43)) (134 to 139) +45: Name (141 to 142) "b" +46: FlagShort(NodeId(45)) (140 to 142) +47: FlagParam { long: NodeId(44), short: Some(NodeId(46)), ty: None, default: None } (134 to 139) +48: Name (145 to 146) "x" +49: PosParam { name: NodeId(48), ty: None, default: None } (145 to 146) +50: Name (148 to 149) "y" +51: Name (151 to 154) "int" +52: Type { name: NodeId(51), args: None, optional: false } (151 to 154) +53: PosParam { name: NodeId(50), ty: Some(NodeId(52)), default: None } (148 to 154) +54: Params(ParamsId(2)) (117 to 155) +55: Variable (159 to 163) "$bar" +56: Variable (165 to 169) "$baz" +57: Variable (171 to 173) "$x" +58: Variable (175 to 177) "$y" +59: List(ListId(1)) (158 to 177) +60: Block(BlockId(1)) (156 to 180) +61: Def { name: NodeId(36), type_params: None, params: NodeId(54), in_out_types: None, block: NodeId(60), env: false, wrapped: false } (99 to 180) +62: Block(BlockId(2)) (0 to 181) ==== SCOPE ==== -0: Frame Scope, node_id: NodeId(36) - decls: [ foo: NodeId(0) ] +0: Frame Scope, node_id: NodeId(62) + decls: [ foo-with-flag: NodeId(36), foo: NodeId(0) ] 1: Frame Scope, node_id: NodeId(34) variables: [ w: NodeId(1), x: NodeId(3), y: NodeId(7), z: NodeId(17) ] -==== TYPES ==== -0: unknown -1: unknown -2: any -3: unknown -4: unknown -5: int -6: int -7: unknown -8: unknown -9: unknown -10: unknown -11: int -12: forbidden -13: list -14: forbidden -15: list> -16: list> -17: unknown -18: unknown -19: unknown -20: unknown -21: unknown -22: unknown -23: int -24: unknown -25: unknown -26: record -27: record -28: forbidden -29: unknown -30: int -31: list> -32: record -33: list -34: list -35: () -36: () -==== IR ==== -register_count: 0 -file_count: 0 -==== IR ERRORS ==== -Error (NodeId 35): node Def { name: NodeId(0), type_params: None, params: NodeId(28), in_out_types: None, block: NodeId(34), env: false, wrapped: false } not suported yet - +2: Frame Scope, node_id: NodeId(60) + variables: [ --bar: NodeId(38), --baz: NodeId(44), x: NodeId(48), y: NodeId(50) ] +==== SCOPE ERRORS ==== +Error (NodeId 55): variable `bar` not found +Error (NodeId 56): variable `baz` not found diff --git a/src/snapshots/new_nu_parser__test__node_output@extern.nu.snap b/src/snapshots/new_nu_parser__test__node_output@extern.nu.snap index dbf6a8c..2e8add0 100644 --- a/src/snapshots/new_nu_parser__test__node_output@extern.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@extern.nu.snap @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/extern.nu --- @@ -9,7 +8,7 @@ input_file: tests/extern.nu 1: Name (13 to 17) "text" 2: Name (19 to 25) "string" 3: Type { name: NodeId(2), args: None, optional: false } (19 to 25) -4: Param { name: NodeId(1), ty: Some(NodeId(3)) } (13 to 25) +4: PosParam { name: NodeId(1), ty: Some(NodeId(3)), default: None } (13 to 25) 5: Params(ParamsId(0)) (12 to 26) 6: Extern { name: NodeId(0), params: NodeId(5) } (0 to 26) 7: Block(BlockId(0)) (0 to 27) @@ -31,4 +30,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 6): node Extern { name: NodeId(0), params: NodeId(5) } not suported yet - diff --git a/src/snapshots/new_nu_parser__test__node_output@infer_complex.nu.snap b/src/snapshots/new_nu_parser__test__node_output@infer_complex.nu.snap index 05107a4..1c76b0f 100644 --- a/src/snapshots/new_nu_parser__test__node_output@infer_complex.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@infer_complex.nu.snap @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/infer_complex.nu --- @@ -14,27 +13,27 @@ input_file: tests/infer_complex.nu 6: Name (24 to 25) "a" 7: Name (27 to 28) "A" 8: Type { name: NodeId(7), args: None, optional: false } (27 to 28) -9: Param { name: NodeId(6), ty: Some(NodeId(8)) } (24 to 28) +9: PosParam { name: NodeId(6), ty: Some(NodeId(8)), default: None } (24 to 28) 10: Name (30 to 31) "b" 11: Name (33 to 34) "B" 12: Type { name: NodeId(11), args: None, optional: false } (33 to 34) -13: Param { name: NodeId(10), ty: Some(NodeId(12)) } (30 to 34) +13: PosParam { name: NodeId(10), ty: Some(NodeId(12)), default: None } (30 to 34) 14: Params(ParamsId(1)) (23 to 35) 15: RecordType { fields: NodeId(14), optional: false } (17 to 35) -16: Param { name: NodeId(4), ty: Some(NodeId(15)) } (14 to 35) +16: PosParam { name: NodeId(4), ty: Some(NodeId(15)), default: None } (14 to 35) 17: Name (37 to 38) "y" 18: Name (40 to 46) "record" 19: Name (47 to 48) "a" 20: Name (50 to 51) "A" 21: Type { name: NodeId(20), args: None, optional: false } (50 to 51) -22: Param { name: NodeId(19), ty: Some(NodeId(21)) } (47 to 51) +22: PosParam { name: NodeId(19), ty: Some(NodeId(21)), default: None } (47 to 51) 23: Name (53 to 54) "b" 24: Name (56 to 57) "B" 25: Type { name: NodeId(24), args: None, optional: false } (56 to 57) -26: Param { name: NodeId(23), ty: Some(NodeId(25)) } (53 to 57) +26: PosParam { name: NodeId(23), ty: Some(NodeId(25)), default: None } (53 to 57) 27: Params(ParamsId(2)) (46 to 58) 28: RecordType { fields: NodeId(27), optional: false } (40 to 59) -29: Param { name: NodeId(17), ty: Some(NodeId(28)) } (37 to 59) +29: PosParam { name: NodeId(17), ty: Some(NodeId(28)), default: None } (37 to 59) 30: Params(ParamsId(3)) (12 to 60) 31: Name (63 to 70) "nothing" 32: Type { name: NodeId(31), args: None, optional: false } (63 to 70) @@ -42,11 +41,11 @@ input_file: tests/infer_complex.nu 34: Name (81 to 82) "a" 35: Name (84 to 85) "A" 36: Type { name: NodeId(35), args: None, optional: false } (84 to 85) -37: Param { name: NodeId(34), ty: Some(NodeId(36)) } (81 to 85) +37: PosParam { name: NodeId(34), ty: Some(NodeId(36)), default: None } (81 to 85) 38: Name (87 to 88) "b" 39: Name (90 to 91) "B" 40: Type { name: NodeId(39), args: None, optional: false } (90 to 91) -41: Param { name: NodeId(38), ty: Some(NodeId(40)) } (87 to 91) +41: PosParam { name: NodeId(38), ty: Some(NodeId(40)), default: None } (87 to 91) 42: Params(ParamsId(4)) (80 to 92) 43: RecordType { fields: NodeId(42), optional: false } (74 to 93) 44: InOutType(NodeId(32), NodeId(43)) (63 to 93) @@ -60,7 +59,7 @@ input_file: tests/infer_complex.nu 52: Name (122 to 123) "x" 53: Name (125 to 128) "int" 54: Type { name: NodeId(53), args: None, optional: false } (125 to 128) -55: Param { name: NodeId(52), ty: Some(NodeId(54)) } (122 to 128) +55: PosParam { name: NodeId(52), ty: Some(NodeId(54)), default: None } (122 to 128) 56: Params(ParamsId(6)) (120 to 130) 57: Name (133 to 140) "nothing" 58: Type { name: NodeId(57), args: None, optional: false } (133 to 140) @@ -73,14 +72,14 @@ input_file: tests/infer_complex.nu 65: Variable (154 to 155) "m" 66: Name (158 to 168) "mysterious" 67: Int (169 to 170) "0" -68: Call(CallId(0)) (169 to 170) +68: Call(CallId(0)) (158 to 170) 69: Let { variable_name: NodeId(65), ty: None, initializer: NodeId(68), is_mutable: false } (150 to 170) 70: Variable (175 to 176) "a" 71: Name (178 to 184) "record" 72: Name (185 to 186) "a" 73: Name (188 to 194) "number" 74: Type { name: NodeId(73), args: None, optional: false } (188 to 194) -75: Param { name: NodeId(72), ty: Some(NodeId(74)) } (185 to 194) +75: PosParam { name: NodeId(72), ty: Some(NodeId(74)), default: None } (185 to 194) 76: Params(ParamsId(7)) (184 to 195) 77: RecordType { fields: NodeId(76), optional: false } (178 to 196) 78: Name (198 to 199) "f" @@ -94,7 +93,7 @@ input_file: tests/infer_complex.nu 86: String (229 to 230) "b" 87: String (232 to 237) ""foo"" 88: Record(RecordId(1)) (218 to 239) -89: Call(CallId(1)) (200 to 239) +89: Call(CallId(1)) (198 to 239) 90: Let { variable_name: NodeId(70), ty: Some(NodeId(77)), initializer: NodeId(89), is_mutable: false } (171 to 239) 91: Block(BlockId(2)) (0 to 240) ==== SCOPE ==== @@ -205,4 +204,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 48): node Def { name: NodeId(0), type_params: Some(NodeId(3)), params: NodeId(30), in_out_types: Some(NodeId(45)), block: NodeId(47), env: false, wrapped: false } not suported yet - diff --git a/src/snapshots/new_nu_parser__test__node_output@infer_generics.nu.snap b/src/snapshots/new_nu_parser__test__node_output@infer_generics.nu.snap index 94a697c..70be49e 100644 --- a/src/snapshots/new_nu_parser__test__node_output@infer_generics.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@infer_generics.nu.snap @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/infer_generics.nu --- @@ -11,7 +10,7 @@ input_file: tests/infer_generics.nu 3: Name (11 to 12) "x" 4: Name (14 to 15) "T" 5: Type { name: NodeId(4), args: None, optional: false } (14 to 15) -6: Param { name: NodeId(3), ty: Some(NodeId(5)) } (11 to 15) +6: PosParam { name: NodeId(3), ty: Some(NodeId(5)), default: None } (11 to 15) 7: Params(ParamsId(1)) (9 to 17) 8: Name (20 to 27) "nothing" 9: Type { name: NodeId(8), args: None, optional: false } (20 to 27) @@ -33,7 +32,7 @@ input_file: tests/infer_generics.nu 25: Def { name: NodeId(0), type_params: Some(NodeId(2)), params: NodeId(7), in_out_types: Some(NodeId(16)), block: NodeId(24), env: false, wrapped: false } (0 to 65) 26: Name (67 to 68) "f" 27: Int (69 to 70) "1" -28: Call(CallId(0)) (69 to 70) +28: Call(CallId(0)) (67 to 70) 29: Block(BlockId(1)) (0 to 71) ==== SCOPE ==== 0: Frame Scope, node_id: NodeId(29) @@ -77,4 +76,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 25): node Def { name: NodeId(0), type_params: Some(NodeId(2)), params: NodeId(7), in_out_types: Some(NodeId(16)), block: NodeId(24), env: false, wrapped: false } not suported yet - diff --git a/src/snapshots/new_nu_parser__test__node_output@infer_plus.nu.snap b/src/snapshots/new_nu_parser__test__node_output@infer_plus.nu.snap index e65e5c4..571b189 100644 --- a/src/snapshots/new_nu_parser__test__node_output@infer_plus.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@infer_plus.nu.snap @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/infer_plus.nu --- @@ -11,7 +10,7 @@ input_file: tests/infer_plus.nu 3: Name (20 to 21) "x" 4: Name (23 to 26) "int" 5: Type { name: NodeId(4), args: None, optional: false } (23 to 26) -6: Param { name: NodeId(3), ty: Some(NodeId(5)) } (20 to 26) +6: PosParam { name: NodeId(3), ty: Some(NodeId(5)), default: None } (20 to 26) 7: Params(ParamsId(1)) (18 to 28) 8: Name (31 to 38) "nothing" 9: Type { name: NodeId(8), args: None, optional: false } (31 to 38) @@ -24,7 +23,7 @@ input_file: tests/infer_plus.nu 16: Variable (52 to 53) "m" 17: Name (56 to 66) "mysterious" 18: Int (67 to 68) "0" -19: Call(CallId(0)) (67 to 68) +19: Call(CallId(0)) (56 to 68) 20: Let { variable_name: NodeId(16), ty: None, initializer: NodeId(19), is_mutable: false } (48 to 68) 21: Variable (70 to 72) "$m" 22: Plus (73 to 74) @@ -80,4 +79,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 15): node Def { name: NodeId(0), type_params: Some(NodeId(2)), params: NodeId(7), in_out_types: Some(NodeId(13)), block: NodeId(14), env: false, wrapped: false } 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 cc70d8e..749dfe6 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 @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/invalid_types.nu --- @@ -14,7 +13,7 @@ input_file: tests/invalid_types.nu 6: Type { name: NodeId(5), args: None, optional: false } (22 to 28) 7: TypeArgs(TypeArgsId(0)) (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) +9: PosParam { name: NodeId(1), ty: Some(NodeId(8)), default: None } (9 to 16) 10: Params(ParamsId(0)) (8 to 30) 11: Variable (33 to 35) "$x" 12: Block(BlockId(0)) (31 to 37) @@ -24,7 +23,7 @@ input_file: tests/invalid_types.nu 16: Name (50 to 54) "list" 17: TypeArgs(TypeArgsId(1)) (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) +19: PosParam { name: NodeId(15), ty: Some(NodeId(18)), default: None } (47 to 54) 20: Params(ParamsId(1)) (46 to 57) 21: Variable (60 to 62) "$y" 22: Block(BlockId(1)) (58 to 64) @@ -71,4 +70,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 13): node Def { name: NodeId(0), type_params: None, params: NodeId(10), in_out_types: None, block: NodeId(12), env: false, wrapped: false } not suported yet - 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 7cba2b9..7de8e98 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 @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/let_mismatch.nu --- @@ -38,7 +37,7 @@ input_file: tests/let_mismatch.nu 30: Name (138 to 139) "a" 31: Name (141 to 144) "int" 32: Type { name: NodeId(31), args: None, optional: false } (141 to 144) -33: Param { name: NodeId(30), ty: Some(NodeId(32)) } (138 to 144) +33: PosParam { name: NodeId(30), ty: Some(NodeId(32)), default: None } (138 to 144) 34: Params(ParamsId(0)) (137 to 145) 35: RecordType { fields: NodeId(34), optional: false } (131 to 146) 36: String (149 to 150) "a" @@ -102,4 +101,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 4): node Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: false } not suported yet - diff --git a/src/snapshots/new_nu_parser__test__node_output@reparse.nu.snap b/src/snapshots/new_nu_parser__test__node_output@reparse.nu.snap index ec30d51..308e7ee 100644 --- a/src/snapshots/new_nu_parser__test__node_output@reparse.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@reparse.nu.snap @@ -1,13 +1,12 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/reparse.nu --- ==== COMPILER ==== 0: Variable (4 to 5) "x" 1: Name (10 to 11) "a" -2: Param { name: NodeId(1), ty: None } (10 to 11) +2: PosParam { name: NodeId(1), ty: None, default: None } (10 to 11) 3: Params(ParamsId(0)) (9 to 12) 4: Variable (13 to 15) "$a" 5: Block(BlockId(0)) (13 to 16) @@ -44,4 +43,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 7): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(6), is_mutable: false } not suported yet - diff --git a/src/snapshots/new_nu_parser__test__node_output@try.nu.snap b/src/snapshots/new_nu_parser__test__node_output@try.nu.snap index 6bc6308..ef7db69 100644 --- a/src/snapshots/new_nu_parser__test__node_output@try.nu.snap +++ b/src/snapshots/new_nu_parser__test__node_output@try.nu.snap @@ -1,6 +1,5 @@ --- source: src/test.rs -assertion_line: 77 expression: evaluate_example(path) input_file: tests/try.nu --- @@ -18,7 +17,7 @@ input_file: tests/try.nu 10: Block(BlockId(1)) (23 to 37) 11: Name (49 to 54) "print" 12: String (55 to 59) ""aa"" -13: Call(CallId(0)) (55 to 59) +13: Call(CallId(0)) (49 to 59) 14: Block(BlockId(2)) (43 to 61) 15: Try { try_block: NodeId(10), catch_block: Some(NodeId(14)), finally_block: None } (19 to 61) 16: Int (73 to 74) "1" @@ -28,7 +27,7 @@ input_file: tests/try.nu 20: Block(BlockId(3)) (67 to 81) 21: Name (95 to 100) "print" 22: String (101 to 105) ""bb"" -23: Call(CallId(1)) (101 to 105) +23: Call(CallId(1)) (95 to 105) 24: Block(BlockId(4)) (89 to 107) 25: Try { try_block: NodeId(20), catch_block: None, finally_block: Some(NodeId(24)) } (63 to 107) 26: Int (119 to 120) "1" @@ -40,7 +39,7 @@ input_file: tests/try.nu 32: Block(BlockId(6)) (133 to 144) 33: Name (158 to 163) "print" 34: String (164 to 168) ""bb"" -35: Call(CallId(2)) (164 to 168) +35: Call(CallId(2)) (158 to 168) 36: Block(BlockId(7)) (152 to 170) 37: Try { try_block: NodeId(30), catch_block: Some(NodeId(32)), finally_block: Some(NodeId(36)) } (109 to 170) 38: Block(BlockId(8)) (0 to 172) @@ -96,4 +95,3 @@ register_count: 0 file_count: 0 ==== IR ERRORS ==== Error (NodeId 5): node Try { try_block: NodeId(4), catch_block: None, finally_block: None } not suported yet - diff --git a/src/typechecker.rs b/src/typechecker.rs index 43aa544..f304978 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -255,7 +255,12 @@ impl<'a> Typechecker<'a> { // Params are not supposed to be evaluated self.set_node_type_id(node_id, FORBIDDEN_TYPE); } - AstNode::Param { name, ty } => { + // TODO: handle default. + AstNode::PosParam { + name, + ty, + default: _, + } => { if let Some(ty) = ty { let ty_id = self.typecheck_type(ty); @@ -977,7 +982,13 @@ impl<'a> Typechecker<'a> { .nodes .iter() .map(|field| { - let AstNode::Param { name, ty } = self.compiler.get_node(*field) else { + // TODO: handle default. + let AstNode::PosParam { + name, + ty, + default: _, + } = self.compiler.get_node(*field) + else { panic!("internal error: record field isn't Param"); }; let ty_id = match ty { diff --git a/tests/calls.nu b/tests/calls.nu index 568b735..d216eff 100644 --- a/tests/calls.nu +++ b/tests/calls.nu @@ -3,4 +3,3 @@ spam foo "bar" (1 + 2) def existing [a: string, b: string, c: int] { [ $a, $b, $c] } existing foo ("ba" + "r") 3 -foo/bar/spam diff --git a/tests/def.nu b/tests/def.nu index 6d72230..3e7d9a8 100644 --- a/tests/def.nu +++ b/tests/def.nu @@ -1 +1,4 @@ -def foo [w x: int, y: list>, z: record ] { [ $w $x, $y, $z ] } \ No newline at end of file +def foo [w x: int, y: list>, z: record ] { [ $w $x, $y, $z ] } + +# define flag +def foo-with-flag [--bar: int = 3, --baz(-b), x, y: int] { [$bar, $baz, $x, $y] }