├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── benchmarks.rs └── nu │ ├── combined.nu │ ├── combined10.nu │ ├── combined100.nu │ ├── combined1000.nu │ ├── def.nu │ ├── if.nu │ └── int100.nu ├── flake.lock ├── flake.nix ├── rust-toolchain.toml ├── src ├── compiler.rs ├── errors.rs ├── ir_generator.rs ├── lexer.rs ├── lib.rs ├── main.rs ├── parser.rs ├── protocol │ ├── command.rs │ └── mod.rs ├── resolver.rs ├── snapshots │ ├── new_nu_parser__test__lexer.snap │ ├── new_nu_parser__test__lexer@bareword.nu.snap │ ├── new_nu_parser__test__lexer@comment.nu.snap │ ├── new_nu_parser__test__lexer@datetime.nu.snap │ ├── new_nu_parser__test__lexer@dq_string_interp.nu.snap │ ├── new_nu_parser__test__lexer@float.nu.snap │ ├── new_nu_parser__test__lexer@int.nu.snap │ ├── new_nu_parser__test__lexer@raw_string.nu.snap │ ├── new_nu_parser__test__lexer@sq_string_interp.nu.snap │ ├── new_nu_parser__test__node_output@alias.nu.snap │ ├── new_nu_parser__test__node_output@binary_ops_exact.nu.snap │ ├── new_nu_parser__test__node_output@binary_ops_mismatch.nu.snap │ ├── new_nu_parser__test__node_output@binary_ops_spaces.nu.snap │ ├── new_nu_parser__test__node_output@binary_ops_subtypes.nu.snap │ ├── new_nu_parser__test__node_output@calls.nu.snap │ ├── new_nu_parser__test__node_output@closure.nu.snap │ ├── new_nu_parser__test__node_output@closure2.nu.snap │ ├── new_nu_parser__test__node_output@closure3.nu.snap │ ├── new_nu_parser__test__node_output@def.nu.snap │ ├── new_nu_parser__test__node_output@def_return_type.nu.snap │ ├── new_nu_parser__test__node_output@for.nu.snap │ ├── new_nu_parser__test__node_output@for_break_continue.nu.snap │ ├── new_nu_parser__test__node_output@if_.nu.snap │ ├── new_nu_parser__test__node_output@invalid_if.nu.snap │ ├── new_nu_parser__test__node_output@invalid_range.nu.snap │ ├── new_nu_parser__test__node_output@invalid_record.nu.snap │ ├── new_nu_parser__test__node_output@invalid_types.nu.snap │ ├── new_nu_parser__test__node_output@let_.nu.snap │ ├── new_nu_parser__test__node_output@let_mismatch.nu.snap │ ├── new_nu_parser__test__node_output@list.nu.snap │ ├── new_nu_parser__test__node_output@literals.nu.snap │ ├── new_nu_parser__test__node_output@loop.nu.snap │ ├── new_nu_parser__test__node_output@match.nu.snap │ ├── new_nu_parser__test__node_output@math.nu.snap │ ├── new_nu_parser__test__node_output@math_precedence.nu.snap │ ├── new_nu_parser__test__node_output@mut_.nu.snap │ ├── new_nu_parser__test__node_output@pipeline.nu.snap │ ├── new_nu_parser__test__node_output@record.nu.snap │ ├── new_nu_parser__test__node_output@record2.nu.snap │ ├── new_nu_parser__test__node_output@record3.nu.snap │ ├── new_nu_parser__test__node_output@reparse.nu.snap │ ├── new_nu_parser__test__node_output@string.nu.snap │ ├── new_nu_parser__test__node_output@string_operation.nu.snap │ ├── new_nu_parser__test__node_output@table.nu.snap │ ├── new_nu_parser__test__node_output@table2.nu.snap │ ├── new_nu_parser__test__node_output@variable_names.nu.snap │ └── new_nu_parser__test__node_output@while.nu.snap ├── test.rs └── typechecker.rs ├── tests ├── alias.nu ├── binary_ops_exact.nu ├── binary_ops_mismatch.nu ├── binary_ops_spaces.nu ├── binary_ops_subtypes.nu ├── calls.nu ├── closure.nu ├── closure2.nu ├── closure3.nu ├── def.nu ├── def_return_type.nu ├── for.nu ├── for_break_continue.nu ├── if_.nu ├── invalid_if.nu ├── invalid_range.nu ├── invalid_record.nu ├── invalid_types.nu ├── let_.nu ├── let_mismatch.nu ├── lex │ ├── bareword.nu │ ├── comment.nu │ ├── datetime.nu │ ├── dq_string_interp.nu │ ├── float.nu │ ├── int.nu │ ├── raw_string.nu │ └── sq_string_interp.nu ├── list.nu ├── literals.nu ├── loop.nu ├── match.nu ├── math.nu ├── math_precedence.nu ├── mut_.nu ├── pipeline.nu ├── record.nu ├── record2.nu ├── record3.nu ├── reparse.nu ├── string.nu ├── string_operation.nu ├── table.nu ├── table2.nu ├── variable_names.nu └── while.nu └── toolkit.nu /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | push: 4 | branches: 5 | - main 6 | 7 | name: continuous-integration 8 | 9 | env: 10 | NUSHELL_CARGO_PROFILE: ci 11 | NU_LOG_LEVEL: DEBUG 12 | CLIPPY_OPTIONS: "-D warnings -D clippy::unwrap_used" 13 | 14 | jobs: 15 | fmt-clippy: 16 | strategy: 17 | fail-fast: true 18 | matrix: 19 | # Pinning to Ubuntu 24.04 because building on newer Ubuntu versions causes linux-gnu 20 | # builds to link against a too-new-for-many-Linux-installs glibc version. Consider 21 | # revisiting this when 24.04 is closer to EOL 22 | platform: [macos-latest, ubuntu-24.04] 23 | feature: [default] 24 | include: 25 | - feature: default 26 | flags: "" 27 | 28 | runs-on: ${{ matrix.platform }} 29 | 30 | steps: 31 | - uses: actions/checkout@v4 32 | 33 | - name: Setup Rust toolchain and cache 34 | uses: actions-rust-lang/setup-rust-toolchain@v1.5.0 35 | with: 36 | rustflags: "" 37 | 38 | - name: cargo fmt 39 | run: cargo fmt --all -- --check 40 | 41 | - name: Clippy 42 | run: cargo clippy --workspace ${{ matrix.flags }} --exclude nu_plugin_* -- $CLIPPY_OPTIONS 43 | 44 | # In tests we don't have to deny unwrap 45 | - name: Clippy of tests 46 | run: cargo clippy --tests --workspace ${{ matrix.flags }} --exclude nu_plugin_* -- -D warnings 47 | 48 | tests: 49 | strategy: 50 | fail-fast: true 51 | matrix: 52 | platform: [macos-latest, ubuntu-24.04] 53 | feature: [default] 54 | include: 55 | - feature: default 56 | flags: "" 57 | 58 | runs-on: ${{ matrix.platform }} 59 | 60 | steps: 61 | - uses: actions/checkout@v4 62 | 63 | - name: Setup Rust toolchain and cache 64 | uses: actions-rust-lang/setup-rust-toolchain@v1.5.0 65 | with: 66 | rustflags: "" 67 | 68 | - name: Tests 69 | run: cargo test ${{ matrix.flags }} 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | **/*.rs.bk 4 | 5 | .vscode/* 6 | .DS_Store 7 | 8 | # Allow everyone to customize .envrc 9 | /.envrc 10 | # Output / cache from direnv 11 | /.direnv 12 | # Output from nix 13 | /result 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "new-nu-parser" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tracy-client = { version = "0.17.3", default-features = false } # for tracy v0.11.1 10 | logos = "0.15" 11 | nu-protocol = "0.101" 12 | 13 | [profile.profiling] 14 | inherits = "release" 15 | debug = true 16 | 17 | [features] 18 | # By default, profiling is disabled. Enable it by the "profile" feature 19 | tracy = [ 20 | "tracy-client/enable", 21 | "tracy-client/flush-on-exit", 22 | "tracy-client/sampling", 23 | "tracy-client/code-transfer", 24 | ] 25 | 26 | [lib] 27 | name = "new_nu_parser" 28 | path = "src/lib.rs" 29 | 30 | [dev-dependencies] 31 | insta = { version = "1.33.0", features = ["glob"] } 32 | tango-bench = "0.6" 33 | nu-parser = "0.101" 34 | nu-cmd-lang = "0.101" 35 | 36 | [[bench]] 37 | name = "benchmarks" 38 | harness = false 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 - 2023 The Nushell Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # New Nushell Parser 2 | 3 | Experimental new parser for Nushell with the aim to eventually replace the old parser (including related data structures, such as EngineState). 4 | 5 | To test it, run the parser on some file, e.g., `cargo run -- spam.nu`. 6 | 7 | ## Goals 8 | 9 | The goals of the new parser are: 10 | 11 | 1. Readability, maintainability (making it easier for contributors to grasp and improve) 12 | 2. Performance (we need faster parsing such that, for example, parsing standard library on Nushell startup should be instantaneous) 13 | 3. Correctness (thanks to 1., it should be easier to iron out quirks of the old parser) 14 | 15 | ## Structure 16 | 17 | Unlike the old parser, the new parser processes the source file in three stages: 18 | 19 | 1. **Parsing**: Splitting the source file into tokens (lexing) and arranging them into AST nodes. 20 | 2. **Resolving**: Binding symbol names to definitions such as commands or variables and making sure these names are visible in the scopes they are supposed to. 21 | 3. **Typechecking**: Resolving / inferring types of values and making sure they match their expected types. 22 | 23 | (TBD) **Codegen**: Emitting Nushell's IR 24 | 25 | This stage-based approach clearly separates what is being done and makes it easier to contribute. 26 | For example, one can implement a syntax parsing for X inside the parsing stage without needing to worry about type checking just yet. 27 | That can be added as an improvement. 28 | In the old parser, all these stages were intertwined 29 | 30 | The Compiler data structure holds the AST, similar to EngineState in the current Nushell (the Compiler might as well morph into EngineState over time). 31 | Unlike the old parser where AST was arranged in a tree structure, the new AST is just a flat vector of nodes, indexed by their position (NodeId) in the vector. 32 | This new flat structure should bring better performance and makes the AST more inspectable. 33 | 34 | Some other notable differences vs. the old parser: 35 | 36 | - Syntax of keywords like `def` is directly embedded into the language instead of being a weird hybrid of a regular command with parser side effects. 37 | 38 | ## Plans 39 | 40 | The parser is mostly incomplete, whole swaths of features are missing. 41 | Once we are able to emit some Nushell IR, we should start moving the parser to the main Nushell codebase behind a feature / CLI flag and work on integration with the rest of the engine etc. 42 | (To avoid engine-q situation as much as possible, for those who remember :-).) 43 | 44 | ## Tests 45 | 46 | With a great success we've used [`cargo insta`](https://github.com/mitsuhiko/insta). 47 | It takes a bit used to working with the snapshots, but they are really useful for reviewing the impact of your changes. 48 | 49 | ## Benchmarks 50 | 51 | We use [tango](https://github.com/bazhenov/tango) for running the benchmarks. 52 | This allows us to easily compare between different Git revisions. 53 | 54 | To run the benchmarks (using `cargo-export`): 55 | 56 | 1. `cargo export target/benchmarks/compiler -- bench` -- builds the benchmark binary in `target/benchmarks/parser` 57 | 2. `target/benchmarks/compiler/benchmarks solo -s 100 --warmup true` -- run the benchmarks (`--help` to see the available CLI parameters) 58 | 59 | To compare against another revision, build it in another directory, then run `/benchmarks compare ...options`. 60 | See [this helper](https://github.com/nushell/nushell/blob/bdbcf829673c0a51805499832c20fab8a010733d/toolkit.nu#L498) in the Nushell repository for a more streamlined experience (the `benchmark-log` was written before the `solo` run option was available). 61 | 62 | ## Contributing 63 | 64 | You can just start hacking, the code should be more approachable than the old parser, and the stage-based structure makes it possible to split adding a feature into separate steps. 65 | It's better to start small and keep the PRs focused. 66 | 67 | Apart from coding the parser directly, we could use more tests, and organize the current ones better. 68 | Perhaps the test output can be printed better? 69 | Also, old parser's tests can be brought over and in general, harmonizing the tests with the current Nushell's codebase would be a wise idea. 70 | The CI can be expanded to include Windows. 71 | Benchmarks are missing (and should be added since our goal is performance!). 72 | In other words, even if you don't want to code the parser directly, there are plenty other ways to contribute! 73 | -------------------------------------------------------------------------------- /benches/benchmarks.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | use new_nu_parser::lexer::{lex, Tokens}; 4 | use nu_cmd_lang::{ 5 | Break, Collect, Def, Echo, ExportCommand, ExportDef, For, If, Let, Module, Mut, Use, 6 | }; 7 | use nu_protocol::engine::{EngineState, StateWorkingSet}; 8 | use nu_protocol::report_parse_error; 9 | use tango_bench::{benchmark_fn, tango_benchmarks, tango_main, Benchmark, IntoBenchmarks}; 10 | 11 | use new_nu_parser::compiler::Compiler; 12 | use new_nu_parser::parser::Parser; 13 | use new_nu_parser::resolver::Resolver; 14 | use new_nu_parser::typechecker::Typechecker; 15 | 16 | /// Files in benches/nu/ we want to benchmark (without .nu suffix) 17 | const BENCHMARKS: &[&str] = &[ 18 | "def", 19 | "if", 20 | "combined", 21 | "combined10", 22 | "combined100", 23 | "combined1000", 24 | "int100", 25 | ]; 26 | 27 | enum Stage { 28 | Lex, 29 | Parse, 30 | Resolve, 31 | Typecheck, 32 | ResolveMerge, 33 | TypecheckMerge, 34 | Compile, 35 | Nu, 36 | } 37 | 38 | /// Stages of compilation we want to profile 39 | const STAGES: &[Stage] = &[ 40 | Stage::Lex, 41 | Stage::Parse, 42 | Stage::Resolve, 43 | Stage::Typecheck, 44 | Stage::ResolveMerge, 45 | Stage::TypecheckMerge, 46 | Stage::Compile, 47 | Stage::Nu, 48 | ]; 49 | 50 | /// Set up compiler with selected stages pre-run 51 | fn setup_compiler( 52 | fname: &str, 53 | do_parse: bool, 54 | do_resolve: bool, 55 | do_typecheck: bool, 56 | ) -> Result<(Compiler, usize), String> { 57 | let mut compiler = Compiler::new(); 58 | let span_offset = compiler.span_offset(); 59 | 60 | let contents = std::fs::read(fname).map_err(|_| format!("Cannot find file {fname}"))?; 61 | compiler.add_file(&fname, &contents); 62 | 63 | let (tokens, err) = lex(&contents, span_offset); 64 | if let Err(e) = err { 65 | tokens.eprint(&compiler.source); 66 | eprintln!("Lexing error. Error: {:?}", e); 67 | exit(1); 68 | } 69 | 70 | if do_parse { 71 | let parser = Parser::new(compiler, tokens); 72 | compiler = parser.parse(); 73 | 74 | if !compiler.errors.is_empty() { 75 | return Err(format!("Error parsing file {fname}")); 76 | } 77 | } 78 | 79 | if do_resolve { 80 | let mut resolver = Resolver::new(&compiler); 81 | resolver.resolve(); 82 | 83 | if !compiler.errors.is_empty() { 84 | return Err(format!("Error resolving file {fname}")); 85 | } 86 | 87 | compiler.merge_name_bindings(resolver.to_name_bindings()); 88 | } 89 | 90 | if do_typecheck { 91 | let mut typechecker = Typechecker::new(&compiler); 92 | typechecker.typecheck(); 93 | 94 | if !compiler.errors.is_empty() { 95 | return Err(format!("Error typechecking file {fname}")); 96 | } 97 | 98 | compiler.merge_types(typechecker.to_types()); 99 | } 100 | 101 | Ok((compiler, span_offset)) 102 | } 103 | 104 | /// Parse only 105 | pub fn parse(mut compiler: Compiler, tokens: Tokens) { 106 | let parser = Parser::new(compiler, tokens); 107 | compiler = parser.parse(); 108 | 109 | if !compiler.errors.is_empty() { 110 | eprintln!("Error resolving def."); 111 | exit(1); 112 | } 113 | } 114 | 115 | /// Resolve only 116 | pub fn resolve(mut compiler: Compiler, do_merge: bool) { 117 | let mut resolver = Resolver::new(&compiler); 118 | resolver.resolve(); 119 | 120 | if !compiler.errors.is_empty() { 121 | eprintln!("Error resolving def."); 122 | exit(1); 123 | } 124 | 125 | if do_merge { 126 | compiler.merge_name_bindings(resolver.to_name_bindings()); 127 | } 128 | } 129 | 130 | /// Typecheck only 131 | pub fn typecheck(mut compiler: Compiler, do_merge: bool) { 132 | let mut typechecker = Typechecker::new(&compiler); 133 | typechecker.typecheck(); 134 | 135 | if !compiler.errors.is_empty() { 136 | eprintln!("Error typechecking def."); 137 | exit(1); 138 | } 139 | 140 | if do_merge { 141 | compiler.merge_types(typechecker.to_types()); 142 | } 143 | } 144 | 145 | /// Run all compiler stages 146 | pub fn compile(mut compiler: Compiler, span_offset: usize) { 147 | let (tokens, err) = lex(&compiler.source, span_offset); 148 | if let Err(e) = err { 149 | tokens.eprint(&compiler.source); 150 | eprintln!("Lexing error. Error: {:?}", e); 151 | exit(1); 152 | } 153 | 154 | let parser = Parser::new(compiler, tokens); 155 | compiler = parser.parse(); 156 | 157 | if !compiler.errors.is_empty() { 158 | eprintln!("Error resolving def."); 159 | exit(1); 160 | } 161 | 162 | let mut resolver = Resolver::new(&compiler); 163 | resolver.resolve(); 164 | 165 | if !compiler.errors.is_empty() { 166 | eprintln!("Error resolving def."); 167 | exit(1); 168 | } 169 | 170 | compiler.merge_name_bindings(resolver.to_name_bindings()); 171 | 172 | let mut typechecker = Typechecker::new(&compiler); 173 | typechecker.typecheck(); 174 | 175 | if !compiler.errors.is_empty() { 176 | eprintln!("Error typechecking def."); 177 | exit(1); 178 | } 179 | 180 | compiler.merge_types(typechecker.to_types()); 181 | } 182 | 183 | fn make_engine_state() -> Box { 184 | let mut engine_state = Box::new(EngineState::new()); 185 | 186 | let delta = { 187 | let mut working_set = StateWorkingSet::new(&engine_state); 188 | working_set.add_decl(Box::new(Break)); 189 | working_set.add_decl(Box::new(Collect)); 190 | working_set.add_decl(Box::new(Def)); 191 | working_set.add_decl(Box::new(Echo)); 192 | working_set.add_decl(Box::new(ExportCommand)); 193 | working_set.add_decl(Box::new(ExportDef)); 194 | working_set.add_decl(Box::new(For)); 195 | working_set.add_decl(Box::new(If)); 196 | working_set.add_decl(Box::new(Let)); 197 | working_set.add_decl(Box::new(Module)); 198 | working_set.add_decl(Box::new(Mut)); 199 | working_set.add_decl(Box::new(Use)); 200 | 201 | working_set.render() 202 | }; 203 | 204 | engine_state 205 | .merge_delta(delta) 206 | .expect("Error merging delta"); 207 | engine_state 208 | } 209 | 210 | fn parse_nu_old(engine_state: &EngineState, contents: &[u8]) { 211 | let mut working_set = StateWorkingSet::new(engine_state); 212 | let _ = nu_parser::parse(&mut working_set, None, contents, false); 213 | 214 | // if any errors, report them and panic 215 | for error in working_set.parse_errors.iter() { 216 | report_parse_error(&working_set, error); 217 | } 218 | assert!(working_set.parse_errors.is_empty()); 219 | } 220 | 221 | fn compiler_benchmarks() -> impl IntoBenchmarks { 222 | let mut benchmarks: Vec = vec![]; 223 | 224 | for bench_name in BENCHMARKS { 225 | for stage in STAGES { 226 | let bench_file = format!("benches/nu/{bench_name}.nu"); 227 | let bench_contents = 228 | std::fs::read(&bench_file).expect(&format!("Cannot find file {bench_file}")); 229 | 230 | let bench = match stage { 231 | Stage::Lex => { 232 | let name = format!("{bench_name}_lex"); 233 | benchmark_fn(name, move |b| { 234 | let contents = bench_contents.clone(); 235 | b.iter(move || { 236 | let (tokens, err) = lex(&contents, 0); 237 | if let Err(e) = err { 238 | tokens.eprint(&contents); 239 | eprintln!("Lexing error. Error: {:?}", e); 240 | exit(1); 241 | } 242 | }) 243 | }) 244 | } 245 | Stage::Parse => { 246 | let name = format!("{bench_name}_parse"); 247 | benchmark_fn(name, move |b| { 248 | let (compiler_def_init, span_offset) = 249 | setup_compiler(&bench_file, false, false, false) 250 | .expect("Error setting up compiler"); 251 | let contents = bench_contents.clone(); 252 | let (tokens, err) = lex(&contents, span_offset); 253 | if let Err(e) = err { 254 | tokens.eprint(&contents); 255 | eprintln!("Lexing error. Error: {:?}", e); 256 | exit(1); 257 | } 258 | b.iter(move || parse(compiler_def_init.clone(), tokens.clone())) 259 | }) 260 | } 261 | Stage::Resolve => { 262 | let name = format!("{bench_name}_resolve"); 263 | benchmark_fn(name, move |b| { 264 | let (compiler_def_parsed, _) = 265 | setup_compiler(&bench_file.clone(), true, false, false) 266 | .expect("Error setting up compiler"); 267 | b.iter(move || resolve(compiler_def_parsed.clone(), false)) 268 | }) 269 | } 270 | Stage::ResolveMerge => { 271 | let name = format!("{bench_name}_resolve_merge"); 272 | benchmark_fn(name, move |b| { 273 | let (compiler_def_parsed, _) = 274 | setup_compiler(&bench_file.clone(), true, false, false) 275 | .expect("Error setting up compiler"); 276 | b.iter(move || resolve(compiler_def_parsed.clone(), true)) 277 | }) 278 | } 279 | Stage::Typecheck => { 280 | let name = format!("{bench_name}_typecheck"); 281 | benchmark_fn(name, move |b| { 282 | let (compiler_def_parsed, _) = 283 | setup_compiler(&bench_file.clone(), true, true, false) 284 | .expect("Error setting up compiler"); 285 | b.iter(move || typecheck(compiler_def_parsed.clone(), false)) 286 | }) 287 | } 288 | Stage::TypecheckMerge => { 289 | let name = format!("{bench_name}_typecheck_merge"); 290 | benchmark_fn(name, move |b| { 291 | let (compiler_def_parsed, _) = 292 | setup_compiler(&bench_file.clone(), true, true, false) 293 | .expect("Error setting up compiler"); 294 | b.iter(move || typecheck(compiler_def_parsed.clone(), true)) 295 | }) 296 | } 297 | Stage::Compile => { 298 | let name = format!("{bench_name}_compile"); 299 | benchmark_fn(name, move |b| { 300 | let (compiler_def_init, span_offset) = 301 | setup_compiler(&bench_file.clone(), false, false, false) 302 | .expect("Error setting up compiler"); 303 | b.iter(move || compile(compiler_def_init.clone(), span_offset)) 304 | }) 305 | } 306 | Stage::Nu => { 307 | let name = format!("{bench_name}_nu_old"); 308 | benchmark_fn(name, move |b| { 309 | let engine_state = make_engine_state(); 310 | let contents = bench_contents.clone(); 311 | b.iter(move || parse_nu_old(&engine_state, &contents)) 312 | }) 313 | } 314 | }; 315 | 316 | benchmarks.push(bench); 317 | } 318 | } 319 | 320 | benchmarks.push(benchmark_fn(format!("nu_old_empty"), move |b| { 321 | let engine_state = make_engine_state(); 322 | b.iter(move || parse_nu_old(&engine_state, &[])) 323 | })); 324 | 325 | benchmarks 326 | } 327 | 328 | tango_benchmarks!(compiler_benchmarks()); 329 | tango_main!(); 330 | -------------------------------------------------------------------------------- /benches/nu/combined.nu: -------------------------------------------------------------------------------- 1 | def foo [ x: bool, y: int, z: list> ] { 2 | def bar [ y: int ] { 3 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 4 | } 5 | 6 | def baz [ y: int ] { 7 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 8 | } 9 | 10 | let res = if $x { 11 | bar $y 12 | } else { 13 | baz $y 14 | } 15 | 16 | mut out = [ $y ] 17 | 18 | for a in $z { 19 | for b in $a { 20 | $out = $out ++ $b 21 | } 22 | } 23 | 24 | $out = $out ++ $res 25 | $out 26 | } 27 | -------------------------------------------------------------------------------- /benches/nu/combined10.nu: -------------------------------------------------------------------------------- 1 | def foo1 [ x: bool, y: int, z: list> ] { 2 | def bar [ y: int ] { 3 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 4 | } 5 | 6 | def baz [ y: int ] { 7 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 8 | } 9 | 10 | let res = if $x { 11 | bar $y 12 | } else { 13 | baz $y 14 | } 15 | 16 | mut out = [ $y ] 17 | 18 | for a in $z { 19 | for b in $a { 20 | $out = $out ++ $b 21 | } 22 | } 23 | 24 | $out = $out ++ $res 25 | $out 26 | } 27 | 28 | def foo2 [ x: bool, y: int, z: list> ] { 29 | def bar [ y: int ] { 30 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 31 | } 32 | 33 | def baz [ y: int ] { 34 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 35 | } 36 | 37 | let res = if $x { 38 | bar $y 39 | } else { 40 | baz $y 41 | } 42 | 43 | mut out = [ $y ] 44 | 45 | for a in $z { 46 | for b in $a { 47 | $out = $out ++ $b 48 | } 49 | } 50 | 51 | $out = $out ++ $res 52 | $out 53 | } 54 | 55 | def foo3 [ x: bool, y: int, z: list> ] { 56 | def bar [ y: int ] { 57 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 58 | } 59 | 60 | def baz [ y: int ] { 61 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 62 | } 63 | 64 | let res = if $x { 65 | bar $y 66 | } else { 67 | baz $y 68 | } 69 | 70 | mut out = [ $y ] 71 | 72 | for a in $z { 73 | for b in $a { 74 | $out = $out ++ $b 75 | } 76 | } 77 | 78 | $out = $out ++ $res 79 | $out 80 | } 81 | 82 | def foo4 [ x: bool, y: int, z: list> ] { 83 | def bar [ y: int ] { 84 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 85 | } 86 | 87 | def baz [ y: int ] { 88 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 89 | } 90 | 91 | let res = if $x { 92 | bar $y 93 | } else { 94 | baz $y 95 | } 96 | 97 | mut out = [ $y ] 98 | 99 | for a in $z { 100 | for b in $a { 101 | $out = $out ++ $b 102 | } 103 | } 104 | 105 | $out = $out ++ $res 106 | $out 107 | } 108 | 109 | def foo5 [ x: bool, y: int, z: list> ] { 110 | def bar [ y: int ] { 111 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 112 | } 113 | 114 | def baz [ y: int ] { 115 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 116 | } 117 | 118 | let res = if $x { 119 | bar $y 120 | } else { 121 | baz $y 122 | } 123 | 124 | mut out = [ $y ] 125 | 126 | for a in $z { 127 | for b in $a { 128 | $out = $out ++ $b 129 | } 130 | } 131 | 132 | $out = $out ++ $res 133 | $out 134 | } 135 | 136 | def foo6 [ x: bool, y: int, z: list> ] { 137 | def bar [ y: int ] { 138 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 139 | } 140 | 141 | def baz [ y: int ] { 142 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 143 | } 144 | 145 | let res = if $x { 146 | bar $y 147 | } else { 148 | baz $y 149 | } 150 | 151 | mut out = [ $y ] 152 | 153 | for a in $z { 154 | for b in $a { 155 | $out = $out ++ $b 156 | } 157 | } 158 | 159 | $out = $out ++ $res 160 | $out 161 | } 162 | 163 | def foo7 [ x: bool, y: int, z: list> ] { 164 | def bar [ y: int ] { 165 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 166 | } 167 | 168 | def baz [ y: int ] { 169 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 170 | } 171 | 172 | let res = if $x { 173 | bar $y 174 | } else { 175 | baz $y 176 | } 177 | 178 | mut out = [ $y ] 179 | 180 | for a in $z { 181 | for b in $a { 182 | $out = $out ++ $b 183 | } 184 | } 185 | 186 | $out = $out ++ $res 187 | $out 188 | } 189 | 190 | def foo8 [ x: bool, y: int, z: list> ] { 191 | def bar [ y: int ] { 192 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 193 | } 194 | 195 | def baz [ y: int ] { 196 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 197 | } 198 | 199 | let res = if $x { 200 | bar $y 201 | } else { 202 | baz $y 203 | } 204 | 205 | mut out = [ $y ] 206 | 207 | for a in $z { 208 | for b in $a { 209 | $out = $out ++ $b 210 | } 211 | } 212 | 213 | $out = $out ++ $res 214 | $out 215 | } 216 | 217 | def foo9 [ x: bool, y: int, z: list> ] { 218 | def bar [ y: int ] { 219 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 220 | } 221 | 222 | def baz [ y: int ] { 223 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 224 | } 225 | 226 | let res = if $x { 227 | bar $y 228 | } else { 229 | baz $y 230 | } 231 | 232 | mut out = [ $y ] 233 | 234 | for a in $z { 235 | for b in $a { 236 | $out = $out ++ $b 237 | } 238 | } 239 | 240 | $out = $out ++ $res 241 | $out 242 | } 243 | 244 | def foo10 [ x: bool, y: int, z: list> ] { 245 | def bar [ y: int ] { 246 | $y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10)) 247 | } 248 | 249 | def baz [ y: int ] { 250 | $y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20)) 251 | } 252 | 253 | let res = if $x { 254 | bar $y 255 | } else { 256 | baz $y 257 | } 258 | 259 | mut out = [ $y ] 260 | 261 | for a in $z { 262 | for b in $a { 263 | $out = $out ++ $b 264 | } 265 | } 266 | 267 | $out = $out ++ $res 268 | $out 269 | } 270 | -------------------------------------------------------------------------------- /benches/nu/def.nu: -------------------------------------------------------------------------------- 1 | def foo [x y: int, z: list> ] { [ $x $y, $z ] } 2 | -------------------------------------------------------------------------------- /benches/nu/if.nu: -------------------------------------------------------------------------------- 1 | let x = 123 2 | 3 | if $x < 100 { 4 | 5 5 | } else if $x > 200 { 6 | 6 7 | } else { 8 | 7 9 | } 10 | -------------------------------------------------------------------------------- /benches/nu/int100.nu: -------------------------------------------------------------------------------- 1 | 1000 2 | 0x2000 3 | 0o3000 4 | 0b1000 5 | 1000 6 | 0x2000 7 | 0o3000 8 | 0b1000 9 | 1000 10 | 0x2000 11 | 0o3000 12 | 0b1000 13 | 1000 14 | 0x2000 15 | 0o3000 16 | 0b1000 17 | 1000 18 | 0x2000 19 | 0o3000 20 | 0b1000 21 | 1000 22 | 0x2000 23 | 0o3000 24 | 0b1000 25 | 1000 26 | 0x2000 27 | 0o3000 28 | 0b1000 29 | 1000 30 | 0x2000 31 | 0o3000 32 | 0b1000 33 | 1000 34 | 0x2000 35 | 0o3000 36 | 0b1000 37 | 1000 38 | 0x2000 39 | 0o3000 40 | 0b1000 41 | 1000 42 | 0x2000 43 | 0o3000 44 | 0b1000 45 | 1000 46 | 0x2000 47 | 0o3000 48 | 0b1000 49 | 1000 50 | 0x2000 51 | 0o3000 52 | 0b1000 53 | 1000 54 | 0x2000 55 | 0o3000 56 | 0b1000 57 | 1000 58 | 0x2000 59 | 0o3000 60 | 0b1000 61 | 1000 62 | 0x2000 63 | 0o3000 64 | 0b1000 65 | 1000 66 | 0x2000 67 | 0o3000 68 | 0b1000 69 | 1000 70 | 0x2000 71 | 0o3000 72 | 0b1000 73 | 1000 74 | 0x2000 75 | 0o3000 76 | 0b1000 77 | 1000 78 | 0x2000 79 | 0o3000 80 | 0b1000 81 | 1000 82 | 0x2000 83 | 0o3000 84 | 0b1000 85 | 1000 86 | 0x2000 87 | 0o3000 88 | 0b1000 89 | 1000 90 | 0x2000 91 | 0o3000 92 | 0b1000 93 | 1000 94 | 0x2000 95 | 0o3000 96 | 0b1000 97 | 1000 98 | 0x2000 99 | 0o3000 100 | 0b1000 101 | 1000 102 | 0x2000 103 | 0o3000 104 | 0b1000 105 | 1000 106 | 0x2000 107 | 0o3000 108 | 0b1000 109 | 1000 110 | 0x2000 111 | 0o3000 112 | 0b1000 113 | 1000 114 | 0x2000 115 | 0o3000 116 | 0b1000 117 | 1000 118 | 0x2000 119 | 0o3000 120 | 0b1000 121 | 1000 122 | 0x2000 123 | 0o3000 124 | 0b1000 125 | 1000 126 | 0x2000 127 | 0o3000 128 | 0b1000 129 | 1000 130 | 0x2000 131 | 0o3000 132 | 0b1000 133 | 1000 134 | 0x2000 135 | 0o3000 136 | 0b1000 137 | 1000 138 | 0x2000 139 | 0o3000 140 | 0b1000 141 | 1000 142 | 0x2000 143 | 0o3000 144 | 0b1000 145 | 1000 146 | 0x2000 147 | 0o3000 148 | 0b1000 149 | 1000 150 | 0x2000 151 | 0o3000 152 | 0b1000 153 | 1000 154 | 0x2000 155 | 0o3000 156 | 0b1000 157 | 1000 158 | 0x2000 159 | 0o3000 160 | 0b1000 161 | 1000 162 | 0x2000 163 | 0o3000 164 | 0b1000 165 | 1000 166 | 0x2000 167 | 0o3000 168 | 0b1000 169 | 1000 170 | 0x2000 171 | 0o3000 172 | 0b1000 173 | 1000 174 | 0x2000 175 | 0o3000 176 | 0b1000 177 | 1000 178 | 0x2000 179 | 0o3000 180 | 0b1000 181 | 1000 182 | 0x2000 183 | 0o3000 184 | 0b1000 185 | 1000 186 | 0x2000 187 | 0o3000 188 | 0b1000 189 | 1000 190 | 0x2000 191 | 0o3000 192 | 0b1000 193 | 1000 194 | 0x2000 195 | 0o3000 196 | 0b1000 197 | 1000 198 | 0x2000 199 | 0o3000 200 | 0b1000 201 | 1000 202 | 0x2000 203 | 0o3000 204 | 0b1000 205 | 1000 206 | 0x2000 207 | 0o3000 208 | 0b1000 209 | 1000 210 | 0x2000 211 | 0o3000 212 | 0b1000 213 | 1000 214 | 0x2000 215 | 0o3000 216 | 0b1000 217 | 1000 218 | 0x2000 219 | 0o3000 220 | 0b1000 221 | 1000 222 | 0x2000 223 | 0o3000 224 | 0b1000 225 | 1000 226 | 0x2000 227 | 0o3000 228 | 0b1000 229 | 1000 230 | 0x2000 231 | 0o3000 232 | 0b1000 233 | 1000 234 | 0x2000 235 | 0o3000 236 | 0b1000 237 | 1000 238 | 0x2000 239 | 0o3000 240 | 0b1000 241 | 1000 242 | 0x2000 243 | 0o3000 244 | 0b1000 245 | 1000 246 | 0x2000 247 | 0o3000 248 | 0b1000 249 | 1000 250 | 0x2000 251 | 0o3000 252 | 0b1000 253 | 1000 254 | 0x2000 255 | 0o3000 256 | 0b1000 257 | 1000 258 | 0x2000 259 | 0o3000 260 | 0b1000 261 | 1000 262 | 0x2000 263 | 0o3000 264 | 0b1000 265 | 1000 266 | 0x2000 267 | 0o3000 268 | 0b1000 269 | 1000 270 | 0x2000 271 | 0o3000 272 | 0b1000 273 | 1000 274 | 0x2000 275 | 0o3000 276 | 0b1000 277 | 1000 278 | 0x2000 279 | 0o3000 280 | 0b1000 281 | 1000 282 | 0x2000 283 | 0o3000 284 | 0b1000 285 | 1000 286 | 0x2000 287 | 0o3000 288 | 0b1000 289 | 1000 290 | 0x2000 291 | 0o3000 292 | 0b1000 293 | 1000 294 | 0x2000 295 | 0o3000 296 | 0b1000 297 | 1000 298 | 0x2000 299 | 0o3000 300 | 0b1000 301 | 1000 302 | 0x2000 303 | 0o3000 304 | 0b1000 305 | 1000 306 | 0x2000 307 | 0o3000 308 | 0b1000 309 | 1000 310 | 0x2000 311 | 0o3000 312 | 0b1000 313 | 1000 314 | 0x2000 315 | 0o3000 316 | 0b1000 317 | 1000 318 | 0x2000 319 | 0o3000 320 | 0b1000 321 | 1000 322 | 0x2000 323 | 0o3000 324 | 0b1000 325 | 1000 326 | 0x2000 327 | 0o3000 328 | 0b1000 329 | 1000 330 | 0x2000 331 | 0o3000 332 | 0b1000 333 | 1000 334 | 0x2000 335 | 0o3000 336 | 0b1000 337 | 1000 338 | 0x2000 339 | 0o3000 340 | 0b1000 341 | 1000 342 | 0x2000 343 | 0o3000 344 | 0b1000 345 | 1000 346 | 0x2000 347 | 0o3000 348 | 0b1000 349 | 1000 350 | 0x2000 351 | 0o3000 352 | 0b1000 353 | 1000 354 | 0x2000 355 | 0o3000 356 | 0b1000 357 | 1000 358 | 0x2000 359 | 0o3000 360 | 0b1000 361 | 1000 362 | 0x2000 363 | 0o3000 364 | 0b1000 365 | 1000 366 | 0x2000 367 | 0o3000 368 | 0b1000 369 | 1000 370 | 0x2000 371 | 0o3000 372 | 0b1000 373 | 1000 374 | 0x2000 375 | 0o3000 376 | 0b1000 377 | 1000 378 | 0x2000 379 | 0o3000 380 | 0b1000 381 | 1000 382 | 0x2000 383 | 0o3000 384 | 0b1000 385 | 1000 386 | 0x2000 387 | 0o3000 388 | 0b1000 389 | 1000 390 | 0x2000 391 | 0o3000 392 | 0b1000 393 | 1000 394 | 0x2000 395 | 0o3000 396 | 0b1000 397 | 1000 398 | 0x2000 399 | 0o3000 400 | 0b1000 401 | 1000 402 | 0x2000 403 | 0o3000 404 | 0b1000 405 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1736166416, 24 | "narHash": "sha256-U47xeACNBpkSO6IcCm0XvahsVXpJXzjPIQG7TZlOToU=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "b30f97d8c32d804d2d832ee837d0f1ca0695faa5", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixpkgs-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs", 41 | "rust-overlay": "rust-overlay" 42 | } 43 | }, 44 | "rust-overlay": { 45 | "inputs": { 46 | "nixpkgs": [ 47 | "nixpkgs" 48 | ] 49 | }, 50 | "locked": { 51 | "lastModified": 1736216977, 52 | "narHash": "sha256-EMueGrzBpryM8mgOyoyJ7DdNRRk09ug1ggcLLp0WrCQ=", 53 | "owner": "oxalica", 54 | "repo": "rust-overlay", 55 | "rev": "bbe7e4e7a70d235db4bbdcabbf8a2f6671881dd7", 56 | "type": "github" 57 | }, 58 | "original": { 59 | "owner": "oxalica", 60 | "repo": "rust-overlay", 61 | "type": "github" 62 | } 63 | }, 64 | "systems": { 65 | "locked": { 66 | "lastModified": 1681028828, 67 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 68 | "owner": "nix-systems", 69 | "repo": "default", 70 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 71 | "type": "github" 72 | }, 73 | "original": { 74 | "owner": "nix-systems", 75 | "repo": "default", 76 | "type": "github" 77 | } 78 | } 79 | }, 80 | "root": "root", 81 | "version": 7 82 | } 83 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | # This is a Nix Flake that defines development environment for this project. 3 | # 4 | # Using Nix is NOT required to develop this project. 5 | # 6 | # Nix provides a declariative way to define reproducible development environment 7 | # and aims to remove the issue of "works on my machine". 8 | # 9 | # If you use Nix, you can install and activate all dependencies required to 10 | # develop on this project with: 11 | # 12 | # $ nix develop 13 | # 14 | # If in addition you have `direnv` installed, you can create `.envrc` file with 15 | # `use flake` content, so that development environment activates automatically 16 | # when the project root directory is entered. 17 | # 18 | # More information: 19 | # - Nix: https://nixos.org 20 | # - Nix flakes: https://wiki.nixos.org/wiki/Flakes 21 | # - Direnv: https://direnv.net 22 | description = "The New Nushell Parser"; 23 | 24 | inputs = { 25 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 26 | flake-utils.url = "github:numtide/flake-utils"; 27 | rust-overlay = { 28 | url = "github:oxalica/rust-overlay"; 29 | inputs.nixpkgs.follows = "nixpkgs"; 30 | }; 31 | }; 32 | 33 | outputs = 34 | { 35 | nixpkgs, 36 | rust-overlay, 37 | flake-utils, 38 | ... 39 | }: 40 | flake-utils.lib.eachDefaultSystem ( 41 | system: 42 | let 43 | overlays = [ (import rust-overlay) ]; 44 | pkgs = import nixpkgs { inherit system overlays; }; 45 | rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; 46 | in 47 | { 48 | devShells.default = pkgs.mkShell { 49 | nativeBuildInputs = [ 50 | (rustToolchain.override { 51 | extensions = [ 52 | "rust-src" 53 | "rust-analyzer" 54 | ]; 55 | }) 56 | ]; 57 | }; 58 | } 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | # The toolchain should follow the version in nushell/nushell repository. 2 | [toolchain] 3 | # The default profile includes rustc, rust-std, cargo, rust-docs, rustfmt and clippy. 4 | # https://rust-lang.github.io/rustup/concepts/profiles.html 5 | profile = "default" 6 | # When updating, it might be necessary to run `nix flake update`. 7 | channel = "1.81.0" 8 | -------------------------------------------------------------------------------- /src/compiler.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::SourceError; 2 | use crate::parser::{AstNode, Block, NodeId, Pipeline}; 3 | use crate::protocol::Command; 4 | use crate::resolver::{DeclId, Frame, NameBindings, ScopeId, VarId, Variable}; 5 | use crate::typechecker::{TypeId, Types}; 6 | use std::collections::HashMap; 7 | 8 | pub struct RollbackPoint { 9 | idx_span_start: usize, 10 | idx_nodes: usize, 11 | idx_errors: usize, 12 | idx_blocks: usize, 13 | token_pos: usize, 14 | } 15 | 16 | #[derive(Debug, Clone, Copy, PartialEq)] 17 | pub struct Span { 18 | pub start: usize, 19 | pub end: usize, 20 | } 21 | 22 | impl Span { 23 | pub fn new(start: usize, end: usize) -> Self { 24 | Self { start, end } 25 | } 26 | } 27 | 28 | #[derive(Debug, PartialEq)] 29 | pub struct Spanned { 30 | pub item: T, 31 | pub span: Span, 32 | } 33 | 34 | impl Spanned { 35 | pub fn new(item: T, span: Span) -> Self { 36 | Spanned { item, span } 37 | } 38 | } 39 | 40 | #[derive(Clone)] 41 | pub struct Compiler { 42 | // Core information, indexed by NodeId: 43 | pub spans: Vec, 44 | pub ast_nodes: Vec, 45 | pub node_types: Vec, 46 | // node_lifetimes: Vec, 47 | pub blocks: Vec, // Blocks, indexed by BlockId 48 | pub pipelines: Vec, // Pipelines, indexed by PipelineId 49 | pub source: Vec, 50 | pub file_offsets: Vec<(String, usize, usize)>, // fname, start, end 51 | 52 | // name bindings: 53 | /// All scope frames ever entered, indexed by ScopeId 54 | pub scope: Vec, 55 | /// Stack of currently entered scope frames 56 | pub scope_stack: Vec, 57 | /// Variables, indexed by VarId 58 | pub variables: Vec, 59 | /// Mapping of variable's name node -> Variable 60 | pub var_resolution: HashMap, 61 | /// Declarations (commands, aliases, externs), indexed by VarId 62 | pub decls: Vec>, 63 | /// Mapping of decl's name node -> Command 64 | pub decl_resolution: HashMap, 65 | 66 | // Definitions: 67 | // indexed by FunId 68 | // pub functions: Vec, 69 | // indexed by TypeId 70 | // types: Vec, 71 | 72 | // Use/def 73 | // pub call_resolution: HashMap, 74 | // pub type_resolution: HashMap, 75 | pub errors: Vec, 76 | } 77 | 78 | impl Default for Compiler { 79 | fn default() -> Self { 80 | Self::new() 81 | } 82 | } 83 | 84 | impl Compiler { 85 | pub fn new() -> Self { 86 | Self { 87 | spans: vec![], 88 | ast_nodes: vec![], 89 | node_types: vec![], 90 | blocks: vec![], 91 | pipelines: vec![], 92 | source: vec![], 93 | file_offsets: vec![], 94 | 95 | scope: vec![], 96 | scope_stack: vec![], 97 | variables: vec![], 98 | var_resolution: HashMap::new(), 99 | decls: vec![], 100 | decl_resolution: HashMap::new(), 101 | 102 | // variables: vec![], 103 | // functions: vec![], 104 | // types: vec![], 105 | 106 | // call_resolution: HashMap::new(), 107 | // var_resolution: HashMap::new(), 108 | // type_resolution: HashMap::new(), 109 | errors: vec![], 110 | } 111 | } 112 | 113 | pub fn print(&self) { 114 | let output = self.display_state(); 115 | print!("{output}"); 116 | } 117 | 118 | #[allow(clippy::format_collect)] 119 | pub fn display_state(&self) -> String { 120 | // TODO: This should say PARSER, not COMPILER 121 | let mut result = "==== COMPILER ====\n".to_string(); 122 | 123 | for (idx, ast_node) in self.ast_nodes.iter().enumerate() { 124 | result.push_str(&format!( 125 | "{}: {:?} ({} to {})", 126 | idx, ast_node, self.spans[idx].start, self.spans[idx].end 127 | )); 128 | 129 | if matches!( 130 | ast_node, 131 | AstNode::Name | AstNode::Variable | AstNode::Int | AstNode::Float | AstNode::String 132 | ) { 133 | result.push_str(&format!( 134 | " \"{}\"", 135 | String::from_utf8_lossy(self.get_span_contents(NodeId(idx))) 136 | )); 137 | } 138 | 139 | result.push('\n'); 140 | } 141 | 142 | if !self.errors.is_empty() { 143 | result.push_str("==== COMPILER ERRORS ====\n"); 144 | for error in &self.errors { 145 | result.push_str(&format!( 146 | "{:?} (NodeId {}): {}\n", 147 | error.severity, error.node_id.0, error.message 148 | )); 149 | } 150 | } 151 | 152 | result 153 | } 154 | 155 | pub fn merge_name_bindings(&mut self, name_bindings: NameBindings) { 156 | self.scope.extend(name_bindings.scope); 157 | self.scope_stack.extend(name_bindings.scope_stack); 158 | self.variables.extend(name_bindings.variables); 159 | self.var_resolution.extend(name_bindings.var_resolution); 160 | self.decls.extend(name_bindings.decls); 161 | self.decl_resolution.extend(name_bindings.decl_resolution); 162 | self.errors.extend(name_bindings.errors); 163 | } 164 | 165 | pub fn merge_types(&mut self, types: Types) { 166 | self.node_types.extend(types.node_types); 167 | self.errors.extend(types.errors); 168 | } 169 | 170 | pub fn add_file(&mut self, fname: &str, contents: &[u8]) { 171 | let span_offset = self.source.len(); 172 | 173 | self.file_offsets 174 | .push((fname.to_string(), span_offset, span_offset + contents.len())); 175 | 176 | self.source.extend_from_slice(contents); 177 | } 178 | 179 | pub fn span_offset(&self) -> usize { 180 | self.source.len() 181 | } 182 | 183 | pub fn get_node(&self, node_id: NodeId) -> &AstNode { 184 | &self.ast_nodes[node_id.0] 185 | } 186 | 187 | pub fn get_node_mut(&mut self, node_id: NodeId) -> &mut AstNode { 188 | &mut self.ast_nodes[node_id.0] 189 | } 190 | 191 | pub fn push_node(&mut self, ast_node: AstNode) -> NodeId { 192 | self.ast_nodes.push(ast_node); 193 | 194 | NodeId(self.ast_nodes.len() - 1) 195 | } 196 | 197 | pub fn get_rollback_point(&self, token_pos: usize) -> RollbackPoint { 198 | RollbackPoint { 199 | idx_span_start: self.spans.len(), 200 | idx_nodes: self.ast_nodes.len(), 201 | idx_errors: self.errors.len(), 202 | idx_blocks: self.blocks.len(), 203 | token_pos, 204 | } 205 | } 206 | 207 | pub fn apply_compiler_rollback(&mut self, rbp: RollbackPoint) -> usize { 208 | self.blocks.truncate(rbp.idx_blocks); 209 | self.ast_nodes.truncate(rbp.idx_nodes); 210 | self.errors.truncate(rbp.idx_errors); 211 | self.spans.truncate(rbp.idx_span_start); 212 | 213 | rbp.token_pos 214 | } 215 | 216 | /// Get span of node 217 | pub fn get_span(&self, node_id: NodeId) -> Span { 218 | *self 219 | .spans 220 | .get(node_id.0) 221 | .expect("internal error: missing span of node") 222 | } 223 | 224 | /// Get the source contents of a span of a node 225 | pub fn get_span_contents(&self, node_id: NodeId) -> &[u8] { 226 | let span = self.get_span(node_id); 227 | self.source 228 | .get(span.start..span.end) 229 | .expect("internal error: missing source of span") 230 | } 231 | 232 | /// Get the source contents of a span 233 | pub fn get_span_contents_manual(&self, span_start: usize, span_end: usize) -> &[u8] { 234 | self.source 235 | .get(span_start..span_end) 236 | .expect("internal error: missing source of span") 237 | } 238 | 239 | /// Get the source contents of a node 240 | pub fn node_as_str(&self, node_id: NodeId) -> &str { 241 | std::str::from_utf8(self.get_span_contents(node_id)) 242 | .expect("internal error: expected utf8 string") 243 | } 244 | 245 | /// Get the source contents of a node as i64 246 | pub fn node_as_i64(&self, node_id: NodeId) -> i64 { 247 | self.node_as_str(node_id) 248 | .parse::() 249 | .expect("internal error: expected i64") 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::NodeId; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub enum Severity { 5 | Error, 6 | Note, 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct SourceError { 11 | pub message: String, 12 | pub node_id: NodeId, 13 | pub severity: Severity, 14 | } 15 | -------------------------------------------------------------------------------- /src/ir_generator.rs: -------------------------------------------------------------------------------- 1 | use crate::compiler::Compiler; 2 | use crate::errors::{Severity, SourceError}; 3 | use crate::parser::{AstNode, NodeId}; 4 | use nu_protocol::ast::{Math, Operator}; 5 | use nu_protocol::ir::{Instruction, IrBlock, Literal}; 6 | use nu_protocol::{RegId, Span}; 7 | 8 | /// Generates IR (Intermediate Representation) from nu AST. 9 | pub struct IrGenerator<'a> { 10 | // Immutable reference to a compiler after the typechecker pass 11 | compiler: &'a Compiler, 12 | errors: Vec, 13 | block: IrBlock, 14 | } 15 | 16 | impl<'a> IrGenerator<'a> { 17 | pub fn new(compiler: &'a Compiler) -> Self { 18 | Self { 19 | compiler, 20 | errors: Default::default(), 21 | block: IrBlock { 22 | instructions: Default::default(), 23 | spans: Default::default(), 24 | data: Default::default(), 25 | ast: Default::default(), 26 | comments: Default::default(), 27 | register_count: 0, 28 | file_count: 0, 29 | }, 30 | } 31 | } 32 | 33 | /// Generates the IR from the given state of the compiler. 34 | /// After this is called, use `block` and `errors` to get the result. 35 | pub fn generate(&mut self) { 36 | if self.compiler.ast_nodes.is_empty() { 37 | return; 38 | } 39 | let node_id = NodeId(self.compiler.ast_nodes.len() - 1); 40 | let Some(reg) = self.generate_node(node_id) else { 41 | return; 42 | }; 43 | self.add_instruction(node_id, Instruction::Return { src: reg }); 44 | } 45 | 46 | /// Returns generated IR block. 47 | /// 48 | /// Call `generate` before using this method and ensure there are no errors. 49 | pub fn block(self) -> IrBlock { 50 | self.block 51 | } 52 | 53 | /// Returns errors encountered during IR generation step. 54 | /// 55 | /// Call `generate` before using this method. 56 | pub fn errors(&self) -> &Vec { 57 | &self.errors 58 | } 59 | 60 | /// Prints the internal state to standard output. 61 | pub fn print(&self) { 62 | let output = self.display_state(); 63 | print!("{output}"); 64 | } 65 | 66 | /// Displays the state of the IR generator. 67 | /// The output can be used for human debugging and for snapshot tests. 68 | pub fn display_state(&self) -> String { 69 | let mut result = String::new(); 70 | result.push_str("==== IR ====\n"); 71 | result.push_str(&format!("register_count: {}\n", self.block.register_count)); 72 | result.push_str(&format!("file_count: {}\n", self.block.file_count)); 73 | 74 | for (idx, instruction) in self.block.instructions.iter().enumerate() { 75 | result.push_str(&format!("{}: {:?}\n", idx, instruction)); 76 | } 77 | 78 | if !self.errors.is_empty() { 79 | result.push_str("==== IR ERRORS ====\n"); 80 | for error in &self.errors { 81 | result.push_str(&format!( 82 | "{:?} (NodeId {}): {}\n", 83 | error.severity, error.node_id.0, error.message 84 | )); 85 | } 86 | } 87 | result 88 | } 89 | 90 | // Returns unused register. 91 | fn next_register(&mut self) -> RegId { 92 | let r = RegId::new(self.block.register_count); 93 | self.block.register_count += 1; 94 | r 95 | } 96 | 97 | fn generate_node(&mut self, node_id: NodeId) -> Option { 98 | let ast_node = &self.compiler.ast_nodes[node_id.0]; 99 | match ast_node { 100 | AstNode::Int => { 101 | let next_reg = self.next_register(); 102 | let val = self.compiler.node_as_i64(node_id); 103 | self.add_instruction( 104 | node_id, 105 | Instruction::LoadLiteral { 106 | dst: next_reg, 107 | lit: Literal::Int(val), 108 | }, 109 | ); 110 | Some(next_reg) 111 | } 112 | AstNode::Block(block_id) => { 113 | let block = &self.compiler.blocks[block_id.0]; 114 | let mut last = None; 115 | for id in &block.nodes { 116 | last = self.generate_node(*id); 117 | last?; 118 | } 119 | last 120 | } 121 | AstNode::BinaryOp { lhs, op, rhs } => { 122 | let l = self.generate_node(*lhs)?; 123 | let r = self.generate_node(*rhs)?; 124 | let o = self.node_to_operator(*op)?; 125 | self.add_instruction( 126 | node_id, 127 | Instruction::BinaryOp { 128 | lhs_dst: l, 129 | op: o, 130 | rhs: r, 131 | }, 132 | ); 133 | Some(l) 134 | } 135 | _ => { 136 | self.error(format!("node {:?} not suported yet", ast_node), node_id); 137 | None 138 | } 139 | } 140 | } 141 | 142 | fn add_instruction(&mut self, node_id: NodeId, instruction: Instruction) { 143 | let span = self.compiler.get_span(node_id); 144 | self.block.spans.push(Span { 145 | start: span.start, 146 | end: span.end, 147 | }); 148 | self.block.ast.push(None); 149 | self.block.instructions.push(instruction); 150 | } 151 | 152 | fn node_to_operator(&mut self, node_id: NodeId) -> Option { 153 | match self.compiler.get_node(node_id) { 154 | AstNode::Plus => Some(Operator::Math(Math::Plus)), 155 | AstNode::Multiply => Some(Operator::Math(Math::Multiply)), 156 | node => { 157 | self.error(format!("unrecognized operator {:?}", node), node_id); 158 | None 159 | } 160 | } 161 | } 162 | 163 | fn error(&mut self, message: impl Into, node_id: NodeId) { 164 | self.errors.push(SourceError { 165 | message: message.into(), 166 | node_id, 167 | severity: Severity::Error, 168 | }); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::compiler::{Span, Spanned}; 2 | use logos::{Lexer, Logos}; 3 | 4 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 5 | pub enum LexError { 6 | #[default] 7 | Generic, 8 | UnmatchedStrInterpLParen, 9 | UnmatchedStrInterpRParen, 10 | UnmatchedRawStringRSharp, 11 | } 12 | 13 | /// Average number of bytes per token used for estimating the tokens buffer size. 14 | /// 15 | /// Estimated with this snippet: 16 | /// let res = ls tests/**/*.nu | get name | each {|name| 17 | /// let nbytes = open --raw $name | into binary | length 18 | /// let ntokens = cargo run -- $name | lines | where $it starts-with 'Token3' | length 19 | /// { 20 | /// file: $name 21 | /// nbytes: $nbytes 22 | /// ntokens: $ntokens 23 | /// bytes_per_token: ($nbytes / $ntokens) 24 | /// } 25 | /// } 26 | /// 27 | /// TODO: Use larger and more representative codebase to estimate this 28 | const AVG_BYTES_PER_TOKEN: usize = 2; 29 | 30 | /// Lexed tokens 31 | /// 32 | /// Tokens and spans are stored in separate vectors indexed by a position index (starting at 0). 33 | #[derive(Clone)] 34 | pub struct Tokens { 35 | pos: usize, 36 | tokens: Vec, 37 | spans: Vec, 38 | } 39 | 40 | impl Tokens { 41 | /// Create a new Tokens with allocated storage for the tokens and spans 42 | pub fn new(source: &[u8]) -> Self { 43 | let estimated_num_tokens = source.len() / AVG_BYTES_PER_TOKEN; 44 | Tokens { 45 | pos: 0, 46 | tokens: Vec::with_capacity(estimated_num_tokens), 47 | spans: Vec::with_capacity(estimated_num_tokens), 48 | } 49 | } 50 | 51 | // Position-related methods 52 | 53 | /// Advance position to point at the next token 54 | /// 55 | /// Note that this can potentially point beyond the tokens if called enough times. The parser 56 | /// should correctly check for EOF and terminate without advancing further. 57 | pub fn advance(&mut self) { 58 | debug_assert!(self.pos < self.tokens.len()); 59 | self.pos += 1; 60 | } 61 | 62 | /// Return current position 63 | pub fn pos(&self) -> usize { 64 | self.pos 65 | } 66 | 67 | /// Set current position 68 | pub fn set_pos(&mut self, pos: usize) { 69 | self.pos = pos; 70 | } 71 | 72 | // Adding and fetching tokens 73 | 74 | /// Push a spanned token to the internal storage 75 | pub fn push(&mut self, token: Token, span: Span) { 76 | self.tokens.push(token); 77 | self.spans.push(span); 78 | } 79 | 80 | /// Check the token at the current position 81 | pub fn peek(&self) -> (Token, Span) { 82 | (self.peek_token(), self.peek_span()) 83 | } 84 | 85 | /// Same as peek() but return only the token 86 | pub fn peek_token(&self) -> Token { 87 | self.tokens[self.pos] 88 | } 89 | 90 | /// Same as peek() but return only the span 91 | pub fn peek_span(&self) -> Span { 92 | self.spans[self.pos] 93 | } 94 | 95 | // Printing 96 | 97 | /// Format the tokens into a human-readable output for debugging 98 | pub fn display(&self, source: &[u8]) -> String { 99 | let mut result = String::new(); 100 | 101 | result.push_str("==== TOKENS ====\n"); 102 | 103 | for (i, (token, span)) in self.tokens.iter().zip(self.spans.iter()).enumerate() { 104 | result.push_str(&format!( 105 | "Token3 {i:4}: {:25} span: {:4} .. {:4} '{}'\n", 106 | format!("{:?}", token), 107 | span.start, 108 | span.end, 109 | String::from_utf8_lossy( 110 | source 111 | .get(span.start..span.end) 112 | .expect("missing source of token span") 113 | ) 114 | .replace("\r", "\\r") 115 | .replace("\n", "\\n") 116 | .replace("\t", "\\t") 117 | )); 118 | } 119 | 120 | result 121 | } 122 | 123 | /// Print the output of display() to standard output 124 | pub fn print(&self, source: &[u8]) { 125 | let output = self.display(source); 126 | print!("{output}"); 127 | } 128 | 129 | /// Print the output of display() to standard error 130 | pub fn eprint(&self, source: &[u8]) { 131 | let output = self.display(source); 132 | eprint!("{output}"); 133 | } 134 | } 135 | 136 | // TODO: Deduplicate code between lex_internal_dq_string_interp() and lex_internal_sq_string_interp() 137 | /// Lex the contents of a double-quoted string interpolation 138 | fn lex_internal_dq_string_interp( 139 | contents: &[u8], 140 | span_offset: usize, 141 | tokens: &mut Tokens, 142 | ) -> Result<(), Spanned> { 143 | let lexer = DqStrInterpToken::lexer(contents).spanned(); 144 | 145 | for (res, span) in lexer { 146 | let new_span = Span::new(span.start + span_offset, span.end + span_offset); 147 | match res { 148 | Ok(DqStrInterpToken::Start) => { 149 | tokens.push(Token::DqStringInterpStart, new_span); 150 | } 151 | Ok(DqStrInterpToken::StringChunk) => { 152 | tokens.push(Token::StrInterpChunk, new_span); 153 | } 154 | Ok(DqStrInterpToken::Subexpression) => { 155 | tokens.push( 156 | Token::StrInterpLParen, 157 | Span::new(new_span.start, new_span.start + 1), 158 | ); 159 | 160 | lex_internal( 161 | &contents[span.start + 1..span.end - 1], 162 | span_offset + span.start + 1, 163 | tokens, 164 | )?; 165 | 166 | tokens.push( 167 | Token::StrInterpRParen, 168 | Span::new(new_span.end - 1, new_span.end), 169 | ); 170 | } 171 | Ok(DqStrInterpToken::End) => { 172 | tokens.push(Token::StrInterpEnd, new_span); 173 | return Ok(()); 174 | } 175 | Err(e) => { 176 | return Err(Spanned::new(e, new_span)); 177 | } 178 | } 179 | } 180 | 181 | Ok(()) 182 | } 183 | 184 | // TODO: Deduplicate code between lex_internal_dq_string_interp() and lex_internal_sq_string_interp() 185 | /// Lex the contents of a single-quoted string interpolation 186 | fn lex_internal_sq_string_interp( 187 | contents: &[u8], 188 | span_offset: usize, 189 | tokens: &mut Tokens, 190 | ) -> Result<(), Spanned> { 191 | let lexer = SqStrInterpToken::lexer(contents).spanned(); 192 | 193 | for (res, span) in lexer { 194 | let new_span = Span::new(span.start + span_offset, span.end + span_offset); 195 | match res { 196 | Ok(SqStrInterpToken::Start) => { 197 | tokens.push(Token::SqStringInterpStart, new_span); 198 | } 199 | Ok(SqStrInterpToken::StringChunk) => { 200 | tokens.push(Token::StrInterpChunk, new_span); 201 | } 202 | Ok(SqStrInterpToken::Subexpression) => { 203 | tokens.push( 204 | Token::StrInterpLParen, 205 | Span::new(new_span.start, new_span.start + 1), 206 | ); 207 | 208 | lex_internal( 209 | &contents[span.start + 1..span.end - 1], 210 | span_offset + span.start + 1, 211 | tokens, 212 | )?; 213 | 214 | tokens.push( 215 | Token::StrInterpRParen, 216 | Span::new(new_span.end - 1, new_span.end), 217 | ); 218 | } 219 | Ok(SqStrInterpToken::End) => { 220 | tokens.push(Token::StrInterpEnd, new_span); 221 | return Ok(()); 222 | } 223 | Err(e) => { 224 | return Err(Spanned::new(e, new_span)); 225 | } 226 | } 227 | } 228 | 229 | Ok(()) 230 | } 231 | 232 | fn lex_internal( 233 | contents: &[u8], 234 | span_offset: usize, 235 | tokens: &mut Tokens, 236 | ) -> Result<(), Spanned> { 237 | let lexer = Token::lexer(contents).spanned(); 238 | 239 | for (res, span) in lexer { 240 | let new_span = Span::new(span.start + span_offset, span.end + span_offset); 241 | match res { 242 | Ok(Token::DqStrInterp) => lex_internal_dq_string_interp( 243 | &contents[span.start..span.end], 244 | span_offset + span.start, 245 | tokens, 246 | )?, 247 | Ok(Token::SqStrInterp) => lex_internal_sq_string_interp( 248 | &contents[span.start..span.end], 249 | span_offset + span.start, 250 | tokens, 251 | )?, 252 | Ok(token) => tokens.push(token, new_span), 253 | Err(e) => { 254 | return Err(Spanned::new(e, new_span)); 255 | } 256 | } 257 | } 258 | 259 | Ok(()) 260 | } 261 | 262 | /// Lex the source contents and return allocated Tokens. 263 | /// 264 | /// In the case of error, you can look up the last stored token to get a clue what went wrong. The 265 | /// last stored token is always End Of File (EOF), so there will always be at least one token. 266 | pub fn lex(contents: &[u8], span_offset: usize) -> (Tokens, Result<(), Spanned>) { 267 | // TODO: We might require the contents to always end with a newline, in which case return an error 268 | let mut tokens = Tokens::new(contents); 269 | let res = lex_internal(contents, span_offset, &mut tokens); 270 | 271 | tokens.push( 272 | Token::Eof, 273 | Span::new(contents.len() + span_offset, contents.len() + span_offset), 274 | ); 275 | 276 | if let Err(e) = res { 277 | return (tokens, Err(e)); 278 | } 279 | 280 | (tokens, Ok(())) 281 | } 282 | 283 | fn match_rawstring(remainder: &[u8], lexer: &mut Lexer) -> Result<(), LexError> { 284 | let prefix = lexer.slice(); 285 | let prefix_sharp_length = prefix[1..prefix.len() - 1].len(); // without first `r` and last `'` 286 | let mut pos = 0; 287 | 288 | while pos < remainder.len() { 289 | if remainder[pos] == b'\'' { 290 | // might be ending of raw string like '##, move forward and check. 291 | pos += 1; 292 | let mut postfix_sharp_length = 0; 293 | while pos < remainder.len() && remainder[pos] == b'#' { 294 | pos += 1; 295 | postfix_sharp_length += 1; 296 | if postfix_sharp_length == prefix_sharp_length { 297 | // found a matched raw string. 298 | lexer.bump(pos); 299 | return Ok(()); 300 | } 301 | } 302 | } else { 303 | pos += 1; 304 | } 305 | } 306 | Err(LexError::UnmatchedRawStringRSharp) 307 | } 308 | 309 | #[derive(Logos, Debug, Clone, Copy, PartialEq)] 310 | #[logos(skip r"[ \t]+")] 311 | #[logos(source = [u8], error = LexError)] 312 | pub enum Token { 313 | #[regex("(0[xob])?[0-9][0-9_]*", priority = 10)] 314 | Int, 315 | #[regex(r"([0-9][0-9_]*)*\.([0-9][0-9_]*)*([eE][+-]?[0-9_]+)?")] 316 | Float, 317 | #[regex("\n|\r\n|\x0C")] 318 | Newline, 319 | #[regex(r#""([^"\\]|\\["\\bnfrt])*""#)] 320 | DoubleQuotedString, 321 | #[regex(r#"'[^']*'"#)] 322 | SingleQuotedString, 323 | #[regex(r#"`[^`]*`"#)] 324 | BacktickBareword, 325 | #[regex("r#+'", |lex| match_rawstring(lex.remainder(), lex))] 326 | RawString, 327 | // #[regex(r#"[ \t]+"#)] 328 | // HorizontalWhitespace, 329 | #[regex(r#"[0-9]{4}-[0-9]{2}-[0-9]{2}(T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?)?(Z|[\+-][0-9]{2}:[0-9]{2})?"#)] 330 | Datetime, 331 | #[regex(r#"#[^\n]*"#, priority = 20)] 332 | Comment, 333 | // lower priority to avoid clashing with Int 334 | #[regex(r#"(_|[^\s[:punct:]])(#|_|[^\s[:punct:]])*"#, priority = 2)] 335 | Bareword, 336 | #[token("...")] 337 | DotDotDot, 338 | #[token("..")] 339 | DotDot, 340 | #[token(".", priority = 10)] // higher priority to avoid "." being tokenized as Float 341 | Dot, 342 | #[token("(")] 343 | LParen, 344 | #[token(")")] 345 | RParen, 346 | #[token("[")] 347 | LSquare, 348 | #[token("]")] 349 | RSquare, 350 | #[token("{")] 351 | LCurly, 352 | #[token("}")] 353 | RCurly, 354 | #[token("<=")] 355 | LessThanEqual, 356 | #[token("<")] 357 | LessThan, 358 | #[token(">=")] 359 | GreaterThanEqual, 360 | #[token(">")] 361 | GreaterThan, 362 | #[token("++=")] 363 | PlusPlusEquals, 364 | #[token("++")] 365 | PlusPlus, 366 | #[token("+=")] 367 | PlusEquals, 368 | #[token("+")] 369 | Plus, 370 | #[token("->")] 371 | ThinArrow, 372 | #[token("=>")] 373 | ThickArrow, 374 | #[token("-=")] 375 | DashEquals, 376 | #[token("-")] 377 | Dash, 378 | #[token("**")] 379 | AsteriskAsterisk, 380 | #[token("*=")] 381 | AsteriskEquals, 382 | #[token("*")] 383 | Asterisk, 384 | #[token("//")] 385 | ForwardSlashForwardSlash, 386 | #[token("/=")] 387 | ForwardSlashEquals, 388 | #[token("/")] 389 | ForwardSlash, 390 | #[token("==")] 391 | EqualsEquals, 392 | #[token("=~")] 393 | EqualsTilde, 394 | #[token("=")] 395 | Equals, 396 | #[token("::")] 397 | ColonColon, 398 | #[token(":")] 399 | Colon, 400 | #[token("$")] 401 | Dollar, 402 | #[token(";")] 403 | Semicolon, 404 | #[token("!=")] 405 | ExclamationEquals, 406 | #[token("!~")] 407 | ExclamationTilde, 408 | #[token("!")] 409 | Exclamation, 410 | #[token("&&")] 411 | AmpersandAmpersand, 412 | #[token("&")] 413 | Ampersand, 414 | #[token(",")] 415 | Comma, 416 | #[token("?")] 417 | QuestionMark, 418 | #[token("^")] 419 | Caret, 420 | #[token("@")] 421 | At, 422 | #[token("||")] 423 | PipePipe, 424 | #[token("|")] 425 | Pipe, 426 | #[token("o>")] 427 | OutGreaterThan, 428 | #[token("o>>")] 429 | OutGreaterGreaterThan, 430 | #[token("e>")] 431 | ErrGreaterThan, 432 | #[token("e>>")] 433 | ErrGreaterGreaterThan, 434 | #[token("o+e>")] 435 | OutErrGreaterThan, 436 | #[token("o+e>>")] 437 | OutErrGreaterGreaterThan, 438 | #[token("e>|")] 439 | ErrGreaterThanPipe, 440 | #[token("o+e>|")] 441 | OutErrGreaterThanPipe, 442 | /// Double quoted string interpolation $"..." 443 | /// 444 | /// The token is passed to a separate lexer and is not actually present in the result. 445 | /// Unescaped double quotes are not permitted, for example, $"foo("bar")" is not allowed. 446 | #[regex(r#"\$"([^"]|\\")*""#)] 447 | DqStrInterp, 448 | /// Single-quoted string interpolation $'...' 449 | /// 450 | /// The token is passed to a separate lexer and is not actually present in the result. 451 | #[regex(r#"\$'[^']*'"#)] 452 | SqStrInterp, 453 | /// Start of double-quoted string interpoloation $" (returned from separate lexing) 454 | DqStringInterpStart, 455 | /// Start of single-quoted string interpoloation $' (returned from separate lexing) 456 | SqStringInterpStart, 457 | /// Non-interpolated string chunk within any string interpolation (returned from separate lexing) 458 | /// 459 | /// For example, "foo" within $"foo(1)" 460 | StrInterpChunk, 461 | /// Left parenthesis inside any string interpolation (returned from separate lexing) 462 | StrInterpLParen, 463 | /// Right parenthesis inside any string interpolation (returned from separate lexing) 464 | StrInterpRParen, 465 | /// End of any string interpolation (returned from separate lexing) 466 | StrInterpEnd, 467 | /// End of file, doesn't match any syntax, but lexed tokens always end with it 468 | Eof, 469 | } 470 | 471 | fn match_subexpression<'a, T: Logos<'a>>( 472 | remainder: &[u8], 473 | lexer: &mut Lexer<'a, T>, 474 | ) -> Result<(), LexError> { 475 | let mut depth = 1; 476 | let mut pos = 0; 477 | 478 | while pos < remainder.len() { 479 | match remainder[pos] { 480 | b'(' => depth += 1, 481 | b')' => depth -= 1, 482 | _ => (), 483 | } 484 | 485 | if depth == 0 { 486 | break; 487 | } 488 | 489 | if depth < 0 { 490 | // unmatched ) 491 | return Err(LexError::UnmatchedStrInterpRParen); 492 | } 493 | 494 | pos += 1; 495 | } 496 | 497 | if depth > 0 { 498 | // unmatched ( 499 | return Err(LexError::UnmatchedStrInterpLParen); 500 | } 501 | 502 | lexer.bump(pos + 1); 503 | Ok(()) 504 | } 505 | 506 | /// Tokens representing double-quoted string interpolation 507 | #[derive(Logos, Debug, Clone, Copy, PartialEq)] 508 | #[logos(source = [u8], error = LexError)] 509 | enum DqStrInterpToken { 510 | #[token(r#"$""#)] 511 | Start, 512 | #[regex(r#"([^"\\\(]|\\["\\bnfrt\(])+"#)] 513 | StringChunk, 514 | #[token("(", |lex| match_subexpression(lex.remainder(), lex))] 515 | Subexpression, 516 | #[token(r#"""#)] 517 | End, 518 | } 519 | 520 | /// Tokens representing single-quoted string interpolation 521 | #[derive(Logos, Debug, Clone, Copy, PartialEq)] 522 | #[logos(source = [u8], error=LexError)] 523 | enum SqStrInterpToken { 524 | #[token(r#"$'"#)] 525 | Start, 526 | #[regex(r#"[^'\(]+"#)] 527 | StringChunk, 528 | #[token("(", |lex| match_subexpression(lex.remainder(), lex))] 529 | Subexpression, 530 | #[token(r#"'"#)] 531 | End, 532 | } 533 | 534 | #[cfg(test)] 535 | mod test { 536 | /// Lexer tests useful for smaller sources, errors and corner cases 537 | use crate::compiler::{Span, Spanned}; 538 | use crate::lexer::{lex, Token}; 539 | 540 | use super::LexError; 541 | 542 | fn test_lex( 543 | src: &[u8], 544 | expected_tokens: &[(Token, Span)], 545 | expected_result: Result<(), Spanned>, 546 | ) { 547 | let (mut actual_tokens, actual_result) = lex(src, 0); 548 | 549 | assert_eq!(expected_result, actual_result, "Lexing result mismatch"); 550 | 551 | for (i, expected) in expected_tokens.iter().enumerate() { 552 | let actual = actual_tokens.peek(); 553 | assert_eq!(expected, &actual, "Mismatch in token {}", i); 554 | actual_tokens.advance(); 555 | } 556 | } 557 | 558 | fn span(start: usize, end: usize) -> Span { 559 | Span { start, end } 560 | } 561 | 562 | #[test] 563 | fn lex_last_eof() { 564 | test_lex(b"", &[(Token::Eof, span(0, 0))], Ok(())); 565 | } 566 | 567 | #[test] 568 | fn lex_unmatched_string() { 569 | // TODO: Make unmatched delimiters nicer 570 | test_lex( 571 | b"'unmatched string", 572 | &[(Token::Eof, span(17, 17))], 573 | Err(Spanned::new(LexError::Generic, Span::new(0, 17))), 574 | ); 575 | } 576 | 577 | #[test] 578 | fn lex_string_interp_errors() { 579 | test_lex( 580 | br#"$"foo("baz")bar""#, 581 | &[ 582 | (Token::DqStringInterpStart, span(0, 2)), 583 | (Token::StrInterpChunk, span(2, 5)), 584 | (Token::Eof, span(16, 16)), 585 | ], 586 | Err(Spanned::new( 587 | LexError::UnmatchedStrInterpLParen, 588 | Span::new(5, 6), 589 | )), 590 | ); 591 | 592 | test_lex( 593 | br#"$'foo('baz')bar'"#, 594 | &[ 595 | (Token::SqStringInterpStart, span(0, 2)), 596 | (Token::StrInterpChunk, span(2, 5)), 597 | (Token::Eof, span(16, 16)), 598 | ], 599 | Err(Spanned::new( 600 | LexError::UnmatchedStrInterpLParen, 601 | Span::new(5, 6), 602 | )), 603 | ); 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod compiler; 2 | pub mod errors; 3 | pub mod ir_generator; 4 | pub mod lexer; 5 | pub mod parser; 6 | pub mod protocol; 7 | pub mod resolver; 8 | #[cfg(test)] 9 | mod test; 10 | pub mod typechecker; 11 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | use new_nu_parser::compiler::Compiler; 4 | use new_nu_parser::ir_generator::IrGenerator; 5 | use new_nu_parser::lexer::lex; 6 | use new_nu_parser::parser::Parser; 7 | use new_nu_parser::resolver::Resolver; 8 | use new_nu_parser::typechecker::Typechecker; 9 | 10 | fn main() { 11 | let mut compiler = Compiler::new(); 12 | let mut do_print = true; 13 | 14 | for arg in std::env::args().skip(1) { 15 | if arg == "--no-print" { 16 | do_print = false; 17 | } 18 | } 19 | 20 | for fname in std::env::args().skip(1) { 21 | if fname == "--no-print" { 22 | continue; 23 | } 24 | 25 | let contents = std::fs::read(&fname); 26 | 27 | let Ok(contents) = contents else { 28 | eprintln!("can't find {}", fname); 29 | exit(1); 30 | }; 31 | 32 | let span_offset = compiler.span_offset(); 33 | compiler.add_file(&fname, &contents); 34 | 35 | let (tokens, err) = lex(&contents, span_offset); 36 | if let Err(e) = err { 37 | tokens.print(&compiler.source); 38 | eprintln!( 39 | "Lexing error. Error: {:?}, '{}'", 40 | e, 41 | String::from_utf8_lossy( 42 | compiler.get_span_contents_manual(e.span.start, e.span.end) 43 | ) 44 | ); 45 | exit(1); 46 | } 47 | 48 | if do_print { 49 | tokens.print(&compiler.source); 50 | } 51 | 52 | let parser = Parser::new(compiler, tokens); 53 | 54 | compiler = parser.parse(); 55 | 56 | if do_print { 57 | compiler.print(); 58 | } 59 | 60 | if !compiler.errors.is_empty() { 61 | exit(1); 62 | } 63 | 64 | let mut resolver = Resolver::new(&compiler); 65 | resolver.resolve(); 66 | 67 | if do_print { 68 | resolver.print(); 69 | } 70 | 71 | compiler.merge_name_bindings(resolver.to_name_bindings()); 72 | 73 | if !compiler.errors.is_empty() { 74 | exit(1); 75 | } 76 | 77 | let mut typechecker = Typechecker::new(&compiler); 78 | typechecker.typecheck(); 79 | 80 | if do_print { 81 | typechecker.print(); 82 | } 83 | 84 | let mut ir_generator = IrGenerator::new(&compiler); 85 | ir_generator.generate(); 86 | if do_print { 87 | ir_generator.print(); 88 | } 89 | 90 | compiler.merge_types(typechecker.to_types()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/protocol/command.rs: -------------------------------------------------------------------------------- 1 | pub trait Command: CommandClone { 2 | fn name(&self) -> &str; 3 | } 4 | 5 | #[derive(Clone)] 6 | pub struct Declaration { 7 | name: String, 8 | } 9 | 10 | impl Declaration { 11 | pub fn new(name: String) -> Self { 12 | Self { name } 13 | } 14 | } 15 | 16 | impl Command for Declaration { 17 | fn name(&self) -> &str { 18 | &self.name 19 | } 20 | } 21 | 22 | // Cloning implementation taken from Nushell 23 | pub trait CommandClone { 24 | fn clone_box(&self) -> Box; 25 | } 26 | 27 | impl CommandClone for T 28 | where 29 | T: 'static + Command + Clone, 30 | { 31 | fn clone_box(&self) -> Box { 32 | Box::new(self.clone()) 33 | } 34 | } 35 | 36 | impl Clone for Box { 37 | fn clone(&self) -> Box { 38 | self.clone_box() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | //! Some placeholder functionality from nu-protocol 2 | 3 | mod command; 4 | 5 | pub use command::*; 6 | -------------------------------------------------------------------------------- /src/resolver.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::{Command, Declaration}; 2 | use crate::{ 3 | compiler::Compiler, 4 | errors::{Severity, SourceError}, 5 | parser::{AstNode, BlockId, NodeId, PipelineId}, 6 | }; 7 | use std::collections::HashMap; 8 | 9 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] 10 | pub struct ScopeId(pub usize); 11 | 12 | #[derive(Debug, PartialEq, Clone, Copy)] 13 | pub enum FrameType { 14 | /// Default scope frame marking the scope of a block/closure 15 | Scope, 16 | /// Immutable frame brought in by an overlay 17 | Overlay, 18 | /// Mutable frame inserted after activating an overlay to prevent mutating the overlay frame 19 | Light, 20 | } 21 | 22 | #[derive(Debug, Clone)] 23 | pub struct Frame { 24 | pub frame_type: FrameType, 25 | pub variables: HashMap, NodeId>, 26 | pub decls: HashMap, NodeId>, 27 | /// Node that defined the scope frame (e.g., a block or overlay) 28 | pub node_id: NodeId, 29 | } 30 | 31 | impl Frame { 32 | pub fn new(scope_type: FrameType, node_id: NodeId) -> Self { 33 | Frame { 34 | frame_type: scope_type, 35 | variables: HashMap::new(), 36 | decls: HashMap::new(), 37 | node_id, 38 | } 39 | } 40 | } 41 | 42 | #[derive(Debug, Clone)] 43 | pub struct Variable { 44 | pub is_mutable: bool, 45 | } 46 | 47 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] 48 | pub struct VarId(pub usize); 49 | 50 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] 51 | pub struct DeclId(pub usize); 52 | 53 | /// Fields extracted from Resolver 54 | pub struct NameBindings { 55 | pub scope: Vec, 56 | pub scope_stack: Vec, 57 | pub variables: Vec, 58 | pub var_resolution: HashMap, 59 | pub decls: Vec>, 60 | pub decl_resolution: HashMap, 61 | pub errors: Vec, 62 | } 63 | 64 | impl NameBindings { 65 | pub fn new() -> Self { 66 | Self { 67 | scope: vec![], 68 | scope_stack: vec![], 69 | variables: vec![], 70 | var_resolution: HashMap::new(), 71 | decls: vec![], 72 | decl_resolution: HashMap::new(), 73 | errors: vec![], 74 | } 75 | } 76 | } 77 | 78 | impl Default for NameBindings { 79 | fn default() -> Self { 80 | Self::new() 81 | } 82 | } 83 | 84 | pub struct Resolver<'a> { 85 | // Immutable reference to a compiler after the first parsing pass 86 | compiler: &'a Compiler, 87 | 88 | /// All scope frames ever entered, indexed by ScopeId 89 | pub scope: Vec, 90 | /// Stack of currently entered scope frames 91 | pub scope_stack: Vec, 92 | /// Variables, indexed by VarId 93 | pub variables: Vec, 94 | /// Mapping of variable's name node -> Variable 95 | pub var_resolution: HashMap, 96 | /// Declarations (commands, aliases, etc.), indexed by DeclId 97 | pub decls: Vec>, 98 | /// Mapping of decl's name node -> Command 99 | pub decl_resolution: HashMap, 100 | /// Errors encountered during name binding 101 | pub errors: Vec, 102 | } 103 | 104 | impl<'a> Resolver<'a> { 105 | pub fn new(compiler: &'a Compiler) -> Self { 106 | Self { 107 | compiler, 108 | scope: vec![], 109 | scope_stack: vec![], 110 | variables: vec![], 111 | var_resolution: HashMap::new(), 112 | decls: vec![], 113 | decl_resolution: HashMap::new(), 114 | errors: vec![], 115 | } 116 | } 117 | 118 | pub fn to_name_bindings(self) -> NameBindings { 119 | NameBindings { 120 | scope: self.scope, 121 | scope_stack: self.scope_stack, 122 | variables: self.variables, 123 | var_resolution: self.var_resolution, 124 | decls: self.decls, 125 | decl_resolution: self.decl_resolution, 126 | errors: self.errors, 127 | } 128 | } 129 | 130 | pub fn print(&self) { 131 | let output = self.display_state(); 132 | print!("{output}"); 133 | } 134 | 135 | #[allow(clippy::format_collect)] 136 | pub fn display_state(&self) -> String { 137 | let mut result = String::new(); 138 | 139 | result.push_str("==== SCOPE ====\n"); 140 | for (i, scope) in self.scope.iter().enumerate() { 141 | result.push_str(&format!( 142 | "{i}: Frame {0:?}, node_id: {1:?}", 143 | scope.frame_type, scope.node_id 144 | )); 145 | 146 | let mut vars: Vec = scope 147 | .variables 148 | .iter() 149 | .map(|(name, id)| format!("{0}: {id:?}", String::from_utf8_lossy(name))) 150 | .collect(); 151 | 152 | let mut decls: Vec = scope 153 | .decls 154 | .iter() 155 | .map(|(name, id)| format!("{0}: {id:?}", String::from_utf8_lossy(name))) 156 | .collect(); 157 | 158 | if vars.is_empty() && decls.is_empty() { 159 | result.push_str(" (empty)\n"); 160 | continue; 161 | } 162 | 163 | result.push('\n'); 164 | 165 | if !vars.is_empty() { 166 | vars.sort(); 167 | let line_var = format!(" variables: [ {0} ]\n", vars.join(", ")); 168 | result.push_str(&line_var); 169 | } 170 | 171 | if !decls.is_empty() { 172 | decls.sort(); 173 | let line_decl = format!(" decls: [ {0} ]\n", decls.join(", ")); 174 | result.push_str(&line_decl); 175 | } 176 | } 177 | 178 | if !self.errors.is_empty() { 179 | result.push_str("==== SCOPE ERRORS ====\n"); 180 | for error in &self.errors { 181 | result.push_str(&format!( 182 | "{:?} (NodeId {}): {}\n", 183 | error.severity, error.node_id.0, error.message 184 | )); 185 | } 186 | } 187 | 188 | result 189 | } 190 | 191 | pub fn resolve(&mut self) { 192 | if !self.compiler.ast_nodes.is_empty() { 193 | let last = self.compiler.ast_nodes.len() - 1; 194 | let last_node_id = NodeId(last); 195 | self.resolve_node(last_node_id) 196 | } 197 | } 198 | 199 | pub fn resolve_node(&mut self, node_id: NodeId) { 200 | // TODO: Move node_id param to the end, same as in typechecker 201 | match self.compiler.ast_nodes[node_id.0] { 202 | AstNode::Variable => self.resolve_variable(node_id), 203 | AstNode::Call { ref parts } => self.resolve_call(node_id, parts), 204 | AstNode::Block(block_id) => self.resolve_block(node_id, block_id, None), 205 | AstNode::Closure { params, block } => { 206 | // making sure the closure parameters and body end up in the same scope frame 207 | let closure_scope = if let Some(params) = params { 208 | self.enter_scope(block); 209 | self.resolve_node(params); 210 | Some(self.exit_scope()) 211 | } else { 212 | None 213 | }; 214 | 215 | let AstNode::Block(block_id) = self.compiler.ast_nodes[block.0] else { 216 | panic!("internal error: closure's body is not a block"); 217 | }; 218 | 219 | self.resolve_block(block, block_id, closure_scope); 220 | } 221 | AstNode::Def { 222 | name, 223 | params, 224 | in_out_types: _, 225 | block, 226 | } => { 227 | // define the command before the block to enable recursive calls 228 | self.define_decl(name); 229 | 230 | // making sure the def parameters and body end up in the same scope frame 231 | self.enter_scope(block); 232 | self.resolve_node(params); 233 | let def_scope = self.exit_scope(); 234 | 235 | let AstNode::Block(block_id) = self.compiler.ast_nodes[block.0] else { 236 | panic!("internal error: command definition's body is not a block"); 237 | }; 238 | 239 | self.resolve_block(block, block_id, Some(def_scope)); 240 | } 241 | AstNode::Alias { 242 | new_name, 243 | old_name: _, 244 | } => { 245 | self.define_decl(new_name); 246 | } 247 | AstNode::Params(ref params) => { 248 | for param in params { 249 | if let AstNode::Param { name, .. } = self.compiler.ast_nodes[param.0] { 250 | self.define_variable(name, false); 251 | } else { 252 | panic!("param is not a param"); 253 | } 254 | } 255 | } 256 | AstNode::Let { 257 | variable_name, 258 | ty: _, 259 | initializer, 260 | is_mutable, 261 | } => { 262 | self.resolve_node(initializer); 263 | self.define_variable(variable_name, is_mutable) 264 | } 265 | AstNode::While { condition, block } => { 266 | self.resolve_node(condition); 267 | self.resolve_node(block); 268 | } 269 | AstNode::For { 270 | variable, 271 | range, 272 | block, 273 | } => { 274 | // making sure the for loop variable and body end up in the same scope frame 275 | self.enter_scope(block); 276 | self.define_variable(variable, false); 277 | let for_body_scope = self.exit_scope(); 278 | 279 | self.resolve_node(range); 280 | 281 | let AstNode::Block(block_id) = self.compiler.ast_nodes[block.0] else { 282 | panic!("internal error: for's body is not a block"); 283 | }; 284 | 285 | self.resolve_block(block, block_id, Some(for_body_scope)); 286 | } 287 | AstNode::Loop { block } => { 288 | self.resolve_node(block); 289 | } 290 | AstNode::BinaryOp { lhs, op: _, rhs } => { 291 | self.resolve_node(lhs); 292 | self.resolve_node(rhs); 293 | } 294 | AstNode::Range { lhs, rhs } => { 295 | self.resolve_node(lhs); 296 | self.resolve_node(rhs); 297 | } 298 | AstNode::List(ref nodes) => { 299 | for node in nodes { 300 | self.resolve_node(*node); 301 | } 302 | } 303 | AstNode::Table { header, ref rows } => { 304 | self.resolve_node(header); 305 | for row in rows { 306 | self.resolve_node(*row); 307 | } 308 | } 309 | AstNode::Record { ref pairs } => { 310 | for (key, val) in pairs { 311 | self.resolve_node(*key); 312 | self.resolve_node(*val); 313 | } 314 | } 315 | AstNode::MemberAccess { target, field } => { 316 | self.resolve_node(target); 317 | self.resolve_node(field); 318 | } 319 | AstNode::If { 320 | condition, 321 | then_block, 322 | else_block, 323 | } => { 324 | self.resolve_node(condition); 325 | self.resolve_node(then_block); 326 | if let Some(block) = else_block { 327 | self.resolve_node(block); 328 | } 329 | } 330 | AstNode::Match { 331 | target, 332 | ref match_arms, 333 | } => { 334 | self.resolve_node(target); 335 | for (arm_lhs, arm_rhs) in match_arms { 336 | self.resolve_node(*arm_lhs); 337 | self.resolve_node(*arm_rhs); 338 | } 339 | } 340 | AstNode::Statement(node) => self.resolve_node(node), 341 | AstNode::Pipeline(pipeline_id) => self.resolve_pipeline(pipeline_id), 342 | AstNode::Param { .. } => (/* seems unused for now */), 343 | AstNode::Type { .. } => ( /* probably doesn't make sense to resolve? */ ), 344 | AstNode::NamedValue { .. } => (/* seems unused for now */), 345 | // All remaining matches do not contain NodeId => there is nothing to resolve 346 | _ => (), 347 | } 348 | } 349 | 350 | pub fn resolve_pipeline(&mut self, pipeline_id: PipelineId) { 351 | let pipeline = &self.compiler.pipelines[pipeline_id.0]; 352 | 353 | for exp in pipeline.get_expressions() { 354 | self.resolve_node(*exp) 355 | } 356 | } 357 | 358 | pub fn resolve_variable(&mut self, unbound_node_id: NodeId) { 359 | let var_name = trim_var_name(self.compiler.get_span_contents(unbound_node_id)); 360 | 361 | if let Some(node_id) = self.find_variable(var_name) { 362 | let var_id = self 363 | .var_resolution 364 | .get(&node_id) 365 | .expect("internal error: missing resolved variable"); 366 | 367 | self.var_resolution.insert(unbound_node_id, *var_id); 368 | } else { 369 | self.errors.push(SourceError { 370 | message: format!("variable `{}` not found", String::from_utf8_lossy(var_name)), 371 | node_id: unbound_node_id, 372 | severity: Severity::Error, 373 | }) 374 | } 375 | } 376 | 377 | pub fn resolve_call(&mut self, unbound_node_id: NodeId, parts: &[NodeId]) { 378 | // Find out the potentially longest command name 379 | let max_name_parts = parts 380 | .iter() 381 | .position(|part| matches!(self.compiler.ast_nodes[part.0], AstNode::Name)) 382 | .expect("call does not have any name") 383 | + 1; 384 | 385 | // Try to find the longest matching subcommand 386 | let first_start = self.compiler.spans[parts[0].0].start; 387 | 388 | for n in (0..max_name_parts).rev() { 389 | let last_end = self.compiler.spans[parts[n].0].end; 390 | let name = self 391 | .compiler 392 | .get_span_contents_manual(first_start, last_end); 393 | 394 | if let Some(node_id) = self.find_decl(name) { 395 | let decl_id = self 396 | .decl_resolution 397 | .get(&node_id) 398 | .expect("internal error: missing resolved decl"); 399 | 400 | self.decl_resolution.insert(unbound_node_id, *decl_id); 401 | break; 402 | } 403 | } 404 | 405 | // TODO? If the call does not correspond to any existing decl, it is an external call 406 | 407 | // Resolve args 408 | for part in &parts[max_name_parts..] { 409 | self.resolve_node(*part); 410 | } 411 | } 412 | 413 | pub fn resolve_block( 414 | &mut self, 415 | node_id: NodeId, 416 | block_id: BlockId, 417 | reused_scope: Option, 418 | ) { 419 | let block = self 420 | .compiler 421 | .blocks 422 | .get(block_id.0) 423 | .expect("internal error: missing block"); 424 | 425 | if let Some(scope_id) = reused_scope { 426 | self.enter_existing_scope(scope_id); 427 | } else { 428 | self.enter_scope(node_id); 429 | } 430 | 431 | for inner_node_id in &block.nodes { 432 | self.resolve_node(*inner_node_id); 433 | } 434 | self.exit_scope(); 435 | } 436 | 437 | /// Enter an existing scope frame, e.g., a block or a closure 438 | pub fn enter_existing_scope(&mut self, scope_id: ScopeId) { 439 | self.scope_stack.push(scope_id); 440 | } 441 | 442 | /// Enter a new scope frame, e.g., a block or a closure 443 | pub fn enter_scope(&mut self, node_id: NodeId) { 444 | self.scope.push(Frame::new(FrameType::Scope, node_id)); 445 | self.scope_stack.push(ScopeId(self.scope.len() - 1)); 446 | } 447 | 448 | /// Exit a scope frame, e.g., when reaching the end of a block or a closure 449 | /// 450 | /// When exiting a scope frame, all overlays (and corresponding light frames) are removed along 451 | /// with the removed frame. 452 | pub fn exit_scope(&mut self) -> ScopeId { 453 | match self 454 | .scope_stack 455 | .iter() 456 | .rposition(|scope_id| self.scope[scope_id.0].frame_type == FrameType::Scope) 457 | { 458 | None => panic!("internal error: no scope frame to exit"), 459 | Some(pos) => { 460 | let scope_id = self.scope_stack[pos]; 461 | self.scope_stack.truncate(pos); 462 | scope_id 463 | } 464 | } 465 | } 466 | 467 | pub fn define_variable(&mut self, var_name_id: NodeId, is_mutable: bool) { 468 | let var_name = self.compiler.get_span_contents(var_name_id); 469 | let var_name = trim_var_name(var_name).to_vec(); 470 | 471 | let current_scope_id = self 472 | .scope_stack 473 | .last() 474 | .expect("internal error: missing scope frame id"); 475 | 476 | self.scope[current_scope_id.0] 477 | .variables 478 | .insert(var_name, var_name_id); 479 | 480 | let var = Variable { is_mutable }; 481 | self.variables.push(var); 482 | let var_id = VarId(self.variables.len() - 1); 483 | 484 | // let the definition of a variable also count as its use 485 | self.var_resolution.insert(var_name_id, var_id); 486 | } 487 | 488 | pub fn define_decl(&mut self, decl_name_id: NodeId) { 489 | // TODO: Deduplicate code with define_variable() 490 | let decl_name = self.compiler.get_span_contents(decl_name_id); 491 | let decl_name = trim_decl_name(decl_name).to_vec(); 492 | let decl = Declaration::new(String::from_utf8_lossy(&decl_name).to_string()); 493 | 494 | let current_scope_id = self 495 | .scope_stack 496 | .last() 497 | .expect("internal error: missing scope frame id"); 498 | 499 | self.scope[current_scope_id.0] 500 | .decls 501 | .insert(decl_name, decl_name_id); 502 | 503 | self.decls.push(Box::new(decl)); 504 | let decl_id = DeclId(self.decls.len() - 1); 505 | 506 | // let the definition of a decl also count as its use 507 | self.decl_resolution.insert(decl_name_id, decl_id); 508 | } 509 | 510 | pub fn find_variable(&self, var_name: &[u8]) -> Option { 511 | for scope_id in self.scope_stack.iter().rev() { 512 | if let Some(id) = self.scope[scope_id.0].variables.get(var_name) { 513 | return Some(*id); 514 | } 515 | } 516 | 517 | None 518 | } 519 | 520 | pub fn find_decl(&self, var_name: &[u8]) -> Option { 521 | // TODO: Deduplicate code with find_variable() 522 | for scope_id in self.scope_stack.iter().rev() { 523 | if let Some(id) = self.scope[scope_id.0].decls.get(var_name) { 524 | return Some(*id); 525 | } 526 | } 527 | 528 | None 529 | } 530 | } 531 | 532 | fn trim_var_name(name: &[u8]) -> &[u8] { 533 | if name.starts_with(b"$") && name.len() > 1 { 534 | &name[1..] 535 | } else { 536 | name 537 | } 538 | } 539 | fn trim_decl_name(name: &[u8]) -> &[u8] { 540 | if (name.starts_with(b"'") && name.ends_with(b"'")) 541 | || (name.starts_with(b"\"") && name.ends_with(b"\"")) 542 | || (name.starts_with(b"`") && name.ends_with(b"`")) 543 | { 544 | &name[1..name.len() - 1] 545 | } else { 546 | name 547 | } 548 | } 549 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/int.nu 5 | snapshot_kind: text 6 | --- 7 | Token 0: Number span: 0 .. 1 '0' 8 | Token 1: Newline span: 1 .. 2 '\n' 9 | Token 2: Number span: 2 .. 4 '00' 10 | Token 3: Newline span: 4 .. 5 '\n' 11 | Token 4: Number span: 5 .. 10 '0x123' 12 | Token 5: Newline span: 10 .. 11 '\n' 13 | Token 6: Number span: 11 .. 16 '0b101' 14 | Token 7: Newline span: 16 .. 17 '\n' 15 | Token 8: Name span: 17 .. 19 '_0' 16 | Token 9: Newline span: 19 .. 20 '\n' 17 | Token 10: Number span: 20 .. 21 '0' 18 | Token 11: Name span: 21 .. 22 '_' 19 | Token 12: Newline span: 22 .. 23 '\n' 20 | Token 13: Name span: 23 .. 24 '_' 21 | Token 14: Newline span: 24 .. 25 '\n' 22 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@bareword.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/bareword.nu 5 | snapshot_kind: text 6 | --- 7 | ==== TOKENS ==== 8 | Token3 0: Bareword span: 0 .. 1 '_' 9 | Token3 1: Newline span: 1 .. 2 '\n' 10 | Token3 2: Bareword span: 2 .. 4 '_0' 11 | Token3 3: Newline span: 4 .. 5 '\n' 12 | Token3 4: Bareword span: 5 .. 8 'foo' 13 | Token3 5: Bareword span: 9 .. 12 'bar' 14 | Token3 6: Newline span: 12 .. 13 '\n' 15 | Token3 7: Bareword span: 13 .. 16 'foo' 16 | Token3 8: Dot span: 16 .. 17 '.' 17 | Token3 9: Bareword span: 17 .. 20 'bar' 18 | Token3 10: Newline span: 20 .. 21 '\n' 19 | Token3 11: Eof span: 21 .. 21 '' 20 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@comment.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/comment.nu 5 | snapshot_kind: text 6 | --- 7 | ==== TOKENS ==== 8 | Token3 0: Comment span: 0 .. 17 '#!/usr/bin/env nu' 9 | Token3 1: Newline span: 17 .. 18 '\n' 10 | Token3 2: Comment span: 18 .. 27 '# comment' 11 | Token3 3: Newline span: 27 .. 28 '\n' 12 | Token3 4: Comment span: 28 .. 36 '#comment' 13 | Token3 5: Newline span: 36 .. 37 '\n' 14 | Token3 6: Comment span: 37 .. 38 '#' 15 | Token3 7: Newline span: 38 .. 39 '\n' 16 | Token3 8: Bareword span: 39 .. 46 'command' 17 | Token3 9: Comment span: 47 .. 56 '# comment' 18 | Token3 10: Newline span: 56 .. 57 '\n' 19 | Token3 11: Bareword span: 57 .. 64 'command' 20 | Token3 12: Comment span: 65 .. 73 '#comment' 21 | Token3 13: Newline span: 73 .. 74 '\n' 22 | Token3 14: Bareword span: 74 .. 86 'command#call' 23 | Token3 15: Newline span: 86 .. 87 '\n' 24 | Token3 16: Bareword span: 87 .. 95 'command#' 25 | Token3 17: Bareword span: 96 .. 100 'call' 26 | Token3 18: Newline span: 100 .. 101 '\n' 27 | Token3 19: Bareword span: 101 .. 108 'command' 28 | Token3 20: Comment span: 109 .. 117 '#comment' 29 | Token3 21: Newline span: 117 .. 118 '\n' 30 | Token3 22: Eof span: 118 .. 118 '' 31 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@datetime.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/datetime.nu 5 | snapshot_kind: text 6 | --- 7 | ==== TOKENS ==== 8 | Token3 0: Datetime span: 0 .. 10 '2020-12-20' 9 | Token3 1: Newline span: 10 .. 11 '\n' 10 | Token3 2: Datetime span: 11 .. 30 '2020-12-20T12:23:34' 11 | Token3 3: Newline span: 30 .. 31 '\n' 12 | Token3 4: Datetime span: 31 .. 54 '2020-12-20T12:23:34.456' 13 | Token3 5: Newline span: 54 .. 55 '\n' 14 | Token3 6: Datetime span: 55 .. 79 '2020-12-20T12:23:34.456Z' 15 | Token3 7: Newline span: 79 .. 80 '\n' 16 | Token3 8: Datetime span: 80 .. 109 '2020-12-20T12:23:34.456+02:00' 17 | Token3 9: Newline span: 109 .. 110 '\n' 18 | Token3 10: Datetime span: 110 .. 139 '2020-12-20T12:23:34.456-02:00' 19 | Token3 11: Newline span: 139 .. 140 '\n' 20 | Token3 12: Datetime span: 140 .. 165 '2020-12-20T12:23:34+02:00' 21 | Token3 13: Newline span: 165 .. 166 '\n' 22 | Token3 14: Eof span: 166 .. 166 '' 23 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@dq_string_interp.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/dq_string_interp.nu 5 | --- 6 | ==== TOKENS ==== 7 | Token3 0: DqStringInterpStart span: 0 .. 2 '$"' 8 | Token3 1: StrInterpEnd span: 2 .. 3 '"' 9 | Token3 2: Newline span: 3 .. 4 '\n' 10 | Token3 3: DqStringInterpStart span: 4 .. 6 '$"' 11 | Token3 4: StrInterpChunk span: 6 .. 9 'foo' 12 | Token3 5: StrInterpEnd span: 9 .. 10 '"' 13 | Token3 6: Newline span: 10 .. 11 '\n' 14 | Token3 7: DqStringInterpStart span: 11 .. 13 '$"' 15 | Token3 8: StrInterpChunk span: 13 .. 18 'foo\(' 16 | Token3 9: StrInterpEnd span: 18 .. 19 '"' 17 | Token3 10: Newline span: 19 .. 20 '\n' 18 | Token3 11: DqStringInterpStart span: 20 .. 22 '$"' 19 | Token3 12: StrInterpChunk span: 22 .. 28 'foo\()' 20 | Token3 13: StrInterpEnd span: 28 .. 29 '"' 21 | Token3 14: Newline span: 29 .. 30 '\n' 22 | Token3 15: DqStringInterpStart span: 30 .. 32 '$"' 23 | Token3 16: StrInterpChunk span: 32 .. 36 'foo)' 24 | Token3 17: StrInterpEnd span: 36 .. 37 '"' 25 | Token3 18: Newline span: 37 .. 38 '\n' 26 | Token3 19: DqStringInterpStart span: 38 .. 40 '$"' 27 | Token3 20: StrInterpLParen span: 40 .. 41 '(' 28 | Token3 21: Int span: 41 .. 42 '1' 29 | Token3 22: StrInterpRParen span: 42 .. 43 ')' 30 | Token3 23: StrInterpChunk span: 43 .. 46 'bar' 31 | Token3 24: StrInterpEnd span: 46 .. 47 '"' 32 | Token3 25: Newline span: 47 .. 48 '\n' 33 | Token3 26: DqStringInterpStart span: 48 .. 50 '$"' 34 | Token3 27: StrInterpChunk span: 50 .. 53 'foo' 35 | Token3 28: StrInterpLParen span: 53 .. 54 '(' 36 | Token3 29: Int span: 54 .. 55 '1' 37 | Token3 30: StrInterpRParen span: 55 .. 56 ')' 38 | Token3 31: StrInterpChunk span: 56 .. 59 'bar' 39 | Token3 32: StrInterpEnd span: 59 .. 60 '"' 40 | Token3 33: Newline span: 60 .. 61 '\n' 41 | Token3 34: DqStringInterpStart span: 61 .. 63 '$"' 42 | Token3 35: StrInterpChunk span: 63 .. 66 'foo' 43 | Token3 36: StrInterpLParen span: 66 .. 67 '(' 44 | Token3 37: LParen span: 67 .. 68 '(' 45 | Token3 38: Int span: 68 .. 69 '1' 46 | Token3 39: RParen span: 69 .. 70 ')' 47 | Token3 40: StrInterpRParen span: 70 .. 71 ')' 48 | Token3 41: StrInterpChunk span: 71 .. 74 'bar' 49 | Token3 42: StrInterpEnd span: 74 .. 75 '"' 50 | Token3 43: Newline span: 75 .. 76 '\n' 51 | Token3 44: DqStringInterpStart span: 76 .. 78 '$"' 52 | Token3 45: StrInterpChunk span: 78 .. 81 'foo' 53 | Token3 46: StrInterpLParen span: 81 .. 82 '(' 54 | Token3 47: Int span: 82 .. 83 '1' 55 | Token3 48: Plus span: 84 .. 85 '+' 56 | Token3 49: LParen span: 86 .. 87 '(' 57 | Token3 50: Int span: 87 .. 88 '2' 58 | Token3 51: Plus span: 89 .. 90 '+' 59 | Token3 52: Int span: 91 .. 92 '3' 60 | Token3 53: RParen span: 92 .. 93 ')' 61 | Token3 54: StrInterpRParen span: 93 .. 94 ')' 62 | Token3 55: StrInterpChunk span: 94 .. 97 'bar' 63 | Token3 56: StrInterpEnd span: 97 .. 98 '"' 64 | Token3 57: Newline span: 98 .. 99 '\n' 65 | Token3 58: DqStringInterpStart span: 99 .. 101 '$"' 66 | Token3 59: StrInterpChunk span: 101 .. 104 'foo' 67 | Token3 60: StrInterpLParen span: 104 .. 105 '(' 68 | Token3 61: SingleQuotedString span: 105 .. 110 ''baz'' 69 | Token3 62: StrInterpRParen span: 110 .. 111 ')' 70 | Token3 63: StrInterpChunk span: 111 .. 114 'bar' 71 | Token3 64: StrInterpEnd span: 114 .. 115 '"' 72 | Token3 65: Newline span: 115 .. 116 '\n' 73 | Token3 66: DqStringInterpStart span: 116 .. 118 '$"' 74 | Token3 67: StrInterpChunk span: 118 .. 121 'foo' 75 | Token3 68: StrInterpLParen span: 121 .. 122 '(' 76 | Token3 69: StrInterpRParen span: 122 .. 123 ')' 77 | Token3 70: StrInterpChunk span: 123 .. 126 'bar' 78 | Token3 71: StrInterpEnd span: 126 .. 127 '"' 79 | Token3 72: Newline span: 127 .. 128 '\n' 80 | Token3 73: DqStringInterpStart span: 128 .. 130 '$"' 81 | Token3 74: StrInterpChunk span: 130 .. 133 'foo' 82 | Token3 75: StrInterpLParen span: 133 .. 134 '(' 83 | Token3 76: Dollar span: 134 .. 135 '$' 84 | Token3 77: Bareword span: 135 .. 138 'baz' 85 | Token3 78: StrInterpRParen span: 138 .. 139 ')' 86 | Token3 79: StrInterpChunk span: 139 .. 142 'bar' 87 | Token3 80: StrInterpEnd span: 142 .. 143 '"' 88 | Token3 81: Newline span: 143 .. 144 '\n' 89 | Token3 82: DqStringInterpStart span: 144 .. 146 '$"' 90 | Token3 83: StrInterpChunk span: 146 .. 155 'escapes\"' 91 | Token3 84: StrInterpEnd span: 155 .. 156 '"' 92 | Token3 85: Newline span: 156 .. 157 '\n' 93 | Token3 86: DqStringInterpStart span: 157 .. 159 '$"' 94 | Token3 87: StrInterpChunk span: 159 .. 168 'esc\"apes' 95 | Token3 88: StrInterpEnd span: 168 .. 169 '"' 96 | Token3 89: Newline span: 169 .. 170 '\n' 97 | Token3 90: DqStringInterpStart span: 170 .. 172 '$"' 98 | Token3 91: StrInterpChunk span: 172 .. 175 'foo' 99 | Token3 92: StrInterpLParen span: 175 .. 176 '(' 100 | Token3 93: SqStringInterpStart span: 176 .. 178 '$'' 101 | Token3 94: StrInterpLParen span: 178 .. 179 '(' 102 | Token3 95: Int span: 179 .. 180 '1' 103 | Token3 96: Plus span: 181 .. 182 '+' 104 | Token3 97: LParen span: 183 .. 184 '(' 105 | Token3 98: Int span: 184 .. 185 '2' 106 | Token3 99: Plus span: 186 .. 187 '+' 107 | Token3 100: Int span: 188 .. 189 '3' 108 | Token3 101: RParen span: 189 .. 190 ')' 109 | Token3 102: StrInterpRParen span: 190 .. 191 ')' 110 | Token3 103: StrInterpEnd span: 191 .. 192 ''' 111 | Token3 104: StrInterpRParen span: 192 .. 193 ')' 112 | Token3 105: StrInterpChunk span: 193 .. 196 'bar' 113 | Token3 106: StrInterpEnd span: 196 .. 197 '"' 114 | Token3 107: Newline span: 197 .. 198 '\n' 115 | Token3 108: Eof span: 198 .. 198 '' 116 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@float.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/float.nu 5 | snapshot_kind: text 6 | --- 7 | ==== TOKENS ==== 8 | Token3 0: Float span: 0 .. 3 '1.0' 9 | Token3 1: Newline span: 3 .. 4 '\n' 10 | Token3 2: Float span: 4 .. 9 '01.10' 11 | Token3 3: Newline span: 9 .. 10 '\n' 12 | Token3 4: Float span: 10 .. 15 '1.0e1' 13 | Token3 5: Newline span: 15 .. 16 '\n' 14 | Token3 6: Float span: 16 .. 22 '1.0e01' 15 | Token3 7: Newline span: 22 .. 23 '\n' 16 | Token3 8: Float span: 23 .. 25 '.2' 17 | Token3 9: Newline span: 25 .. 26 '\n' 18 | Token3 10: Float span: 26 .. 28 '2.' 19 | Token3 11: Newline span: 28 .. 29 '\n' 20 | Token3 12: Float span: 29 .. 33 '.3e3' 21 | Token3 13: Newline span: 33 .. 34 '\n' 22 | Token3 14: Float span: 34 .. 38 '3.e3' 23 | Token3 15: Newline span: 38 .. 39 '\n' 24 | Token3 16: Float span: 39 .. 45 '.1e-10' 25 | Token3 17: Newline span: 45 .. 46 '\n' 26 | Token3 18: Float span: 46 .. 52 '.2e+20' 27 | Token3 19: Newline span: 52 .. 53 '\n' 28 | Token3 20: Float span: 53 .. 62 '45_67.8_9' 29 | Token3 21: Newline span: 62 .. 63 '\n' 30 | Token3 22: Float span: 63 .. 69 '45_.8_' 31 | Token3 23: Newline span: 69 .. 70 '\n' 32 | Token3 24: Bareword span: 70 .. 71 '_' 33 | Token3 25: Float span: 71 .. 73 '.3' 34 | Token3 26: Newline span: 73 .. 74 '\n' 35 | Token3 27: Bareword span: 74 .. 77 '_44' 36 | Token3 28: Float span: 77 .. 80 '.44' 37 | Token3 29: Newline span: 80 .. 81 '\n' 38 | Token3 30: Float span: 81 .. 83 '5.' 39 | Token3 31: Bareword span: 83 .. 84 '_' 40 | Token3 32: Newline span: 84 .. 85 '\n' 41 | Token3 33: Bareword span: 85 .. 86 '_' 42 | Token3 34: Dot span: 86 .. 87 '.' 43 | Token3 35: Bareword span: 87 .. 88 '_' 44 | Token3 36: Newline span: 88 .. 89 '\n' 45 | Token3 37: Eof span: 89 .. 89 '' 46 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@int.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/int.nu 5 | snapshot_kind: text 6 | --- 7 | ==== TOKENS ==== 8 | Token3 0: Int span: 0 .. 1 '0' 9 | Token3 1: Newline span: 1 .. 2 '\n' 10 | Token3 2: Int span: 2 .. 4 '00' 11 | Token3 3: Newline span: 4 .. 5 '\n' 12 | Token3 4: Int span: 5 .. 10 '0x123' 13 | Token3 5: Newline span: 10 .. 11 '\n' 14 | Token3 6: Int span: 11 .. 16 '0b101' 15 | Token3 7: Newline span: 16 .. 17 '\n' 16 | Token3 8: Int span: 17 .. 19 '0_' 17 | Token3 9: Newline span: 19 .. 20 '\n' 18 | Token3 10: Bareword span: 20 .. 23 '0bo' 19 | Token3 11: Newline span: 23 .. 24 '\n' 20 | Token3 12: Bareword span: 24 .. 26 '0x' 21 | Token3 13: Newline span: 26 .. 27 '\n' 22 | Token3 14: Bareword span: 27 .. 32 '01x10' 23 | Token3 15: Newline span: 32 .. 33 '\n' 24 | Token3 16: Eof span: 33 .. 33 '' 25 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@raw_string.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/raw_string.nu 5 | snapshot_kind: text 6 | --- 7 | ==== TOKENS ==== 8 | Token3 0: RawString span: 0 .. 9 'r#'aabb'#' 9 | Token3 1: Newline span: 9 .. 10 '\n' 10 | Token3 2: RawString span: 10 .. 25 'r##'aa\n'#\nbb'##' 11 | Token3 3: Newline span: 25 .. 26 '\n' 12 | Token3 4: RawString span: 26 .. 58 'r####'aa\nbb\ncc'##dd\n###\nddd'####' 13 | Token3 5: Newline span: 58 .. 59 '\n' 14 | Token3 6: Eof span: 59 .. 59 '' 15 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__lexer@sq_string_interp.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_lexer(path) 4 | input_file: tests/lex/sq_string_interp.nu 5 | --- 6 | ==== TOKENS ==== 7 | Token3 0: SqStringInterpStart span: 0 .. 2 '$'' 8 | Token3 1: StrInterpEnd span: 2 .. 3 ''' 9 | Token3 2: Newline span: 3 .. 4 '\n' 10 | Token3 3: SqStringInterpStart span: 4 .. 6 '$'' 11 | Token3 4: StrInterpChunk span: 6 .. 9 'foo' 12 | Token3 5: StrInterpEnd span: 9 .. 10 ''' 13 | Token3 6: Newline span: 10 .. 11 '\n' 14 | Token3 7: SqStringInterpStart span: 11 .. 13 '$'' 15 | Token3 8: StrInterpChunk span: 13 .. 17 'foo)' 16 | Token3 9: StrInterpEnd span: 17 .. 18 ''' 17 | Token3 10: Newline span: 18 .. 19 '\n' 18 | Token3 11: SqStringInterpStart span: 19 .. 21 '$'' 19 | Token3 12: StrInterpLParen span: 21 .. 22 '(' 20 | Token3 13: Int span: 22 .. 23 '1' 21 | Token3 14: StrInterpRParen span: 23 .. 24 ')' 22 | Token3 15: StrInterpChunk span: 24 .. 27 'bar' 23 | Token3 16: StrInterpEnd span: 27 .. 28 ''' 24 | Token3 17: Newline span: 28 .. 29 '\n' 25 | Token3 18: SqStringInterpStart span: 29 .. 31 '$'' 26 | Token3 19: StrInterpChunk span: 31 .. 34 'foo' 27 | Token3 20: StrInterpLParen span: 34 .. 35 '(' 28 | Token3 21: Int span: 35 .. 36 '1' 29 | Token3 22: StrInterpRParen span: 36 .. 37 ')' 30 | Token3 23: StrInterpChunk span: 37 .. 40 'bar' 31 | Token3 24: StrInterpEnd span: 40 .. 41 ''' 32 | Token3 25: Newline span: 41 .. 42 '\n' 33 | Token3 26: SqStringInterpStart span: 42 .. 44 '$'' 34 | Token3 27: StrInterpChunk span: 44 .. 47 'foo' 35 | Token3 28: StrInterpLParen span: 47 .. 48 '(' 36 | Token3 29: LParen span: 48 .. 49 '(' 37 | Token3 30: Int span: 49 .. 50 '1' 38 | Token3 31: RParen span: 50 .. 51 ')' 39 | Token3 32: StrInterpRParen span: 51 .. 52 ')' 40 | Token3 33: StrInterpChunk span: 52 .. 55 'bar' 41 | Token3 34: StrInterpEnd span: 55 .. 56 ''' 42 | Token3 35: Newline span: 56 .. 57 '\n' 43 | Token3 36: SqStringInterpStart span: 57 .. 59 '$'' 44 | Token3 37: StrInterpChunk span: 59 .. 62 'foo' 45 | Token3 38: StrInterpLParen span: 62 .. 63 '(' 46 | Token3 39: Int span: 63 .. 64 '1' 47 | Token3 40: Plus span: 65 .. 66 '+' 48 | Token3 41: LParen span: 67 .. 68 '(' 49 | Token3 42: Int span: 68 .. 69 '3' 50 | Token3 43: Plus span: 70 .. 71 '+' 51 | Token3 44: Int span: 72 .. 73 '4' 52 | Token3 45: RParen span: 73 .. 74 ')' 53 | Token3 46: StrInterpRParen span: 74 .. 75 ')' 54 | Token3 47: StrInterpChunk span: 75 .. 78 'bar' 55 | Token3 48: StrInterpEnd span: 78 .. 79 ''' 56 | Token3 49: Newline span: 79 .. 80 '\n' 57 | Token3 50: SqStringInterpStart span: 80 .. 82 '$'' 58 | Token3 51: StrInterpChunk span: 82 .. 85 'foo' 59 | Token3 52: StrInterpLParen span: 85 .. 86 '(' 60 | Token3 53: StrInterpRParen span: 86 .. 87 ')' 61 | Token3 54: StrInterpChunk span: 87 .. 90 'bar' 62 | Token3 55: StrInterpEnd span: 90 .. 91 ''' 63 | Token3 56: Newline span: 91 .. 92 '\n' 64 | Token3 57: SqStringInterpStart span: 92 .. 94 '$'' 65 | Token3 58: StrInterpChunk span: 94 .. 97 'foo' 66 | Token3 59: StrInterpLParen span: 97 .. 98 '(' 67 | Token3 60: DqStringInterpStart span: 98 .. 100 '$"' 68 | Token3 61: StrInterpLParen span: 100 .. 101 '(' 69 | Token3 62: Int span: 101 .. 102 '1' 70 | Token3 63: Plus span: 103 .. 104 '+' 71 | Token3 64: LParen span: 105 .. 106 '(' 72 | Token3 65: Int span: 106 .. 107 '2' 73 | Token3 66: Plus span: 108 .. 109 '+' 74 | Token3 67: Int span: 110 .. 111 '3' 75 | Token3 68: RParen span: 111 .. 112 ')' 76 | Token3 69: StrInterpRParen span: 112 .. 113 ')' 77 | Token3 70: StrInterpEnd span: 113 .. 114 '"' 78 | Token3 71: StrInterpRParen span: 114 .. 115 ')' 79 | Token3 72: StrInterpChunk span: 115 .. 118 'bar' 80 | Token3 73: StrInterpEnd span: 118 .. 119 ''' 81 | Token3 74: Newline span: 119 .. 120 '\n' 82 | Token3 75: Eof span: 120 .. 120 '' 83 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@alias.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/alias.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (6 to 19) ""fancy alias"" 9 | 1: Name (22 to 25) "foo" 10 | 2: Alias { new_name: NodeId(0), old_name: NodeId(1) } (0 to 25) 11 | 3: Name (27 to 32) "fancy" 12 | 4: Name (33 to 38) "alias" 13 | 5: Call { parts: [NodeId(3), NodeId(4)] } (33 to 38) 14 | 6: Block(BlockId(0)) (0 to 39) 15 | ==== SCOPE ==== 16 | 0: Frame Scope, node_id: NodeId(6) 17 | decls: [ fancy alias: NodeId(0) ] 18 | ==== TYPES ==== 19 | 0: unknown 20 | 1: unknown 21 | 2: () 22 | 3: unknown 23 | 4: string 24 | 5: stream 25 | 6: stream 26 | ==== IR ==== 27 | register_count: 0 28 | file_count: 0 29 | ==== IR ERRORS ==== 30 | Error (NodeId 2): node Alias { new_name: NodeId(0), old_name: NodeId(1) } not suported yet 31 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@binary_ops_exact.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/binary_ops_exact.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (0 to 1) "1" 9 | 1: Equal (2 to 4) 10 | 2: Int (5 to 6) "1" 11 | 3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 6) 12 | 4: True (8 to 12) 13 | 5: List([NodeId(4)]) (7 to 12) 14 | 6: Append (14 to 16) 15 | 7: False (17 to 22) 16 | 8: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(7) } (7 to 22) 17 | 9: Int (23 to 24) "1" 18 | 10: Plus (25 to 26) 19 | 11: Int (27 to 28) "1" 20 | 12: BinaryOp { lhs: NodeId(9), op: NodeId(10), rhs: NodeId(11) } (23 to 28) 21 | 13: Float (29 to 32) "1.0" 22 | 14: Plus (33 to 34) 23 | 15: Float (35 to 38) "1.0" 24 | 16: BinaryOp { lhs: NodeId(13), op: NodeId(14), rhs: NodeId(15) } (29 to 38) 25 | 17: True (39 to 43) 26 | 18: And (44 to 47) 27 | 19: False (48 to 53) 28 | 20: BinaryOp { lhs: NodeId(17), op: NodeId(18), rhs: NodeId(19) } (39 to 53) 29 | 21: String (54 to 59) ""foo"" 30 | 22: RegexMatch (60 to 62) 31 | 23: String (63 to 68) "".*o"" 32 | 24: BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) } (54 to 68) 33 | 25: Int (69 to 70) "1" 34 | 26: In (71 to 73) 35 | 27: Int (75 to 76) "1" 36 | 28: Int (78 to 79) "2" 37 | 29: List([NodeId(27), NodeId(28)]) (74 to 79) 38 | 30: BinaryOp { lhs: NodeId(25), op: NodeId(26), rhs: NodeId(29) } (69 to 79) 39 | 31: Block(BlockId(0)) (0 to 81) 40 | ==== SCOPE ==== 41 | 0: Frame Scope, node_id: NodeId(31) (empty) 42 | ==== TYPES ==== 43 | 0: int 44 | 1: forbidden 45 | 2: int 46 | 3: bool 47 | 4: bool 48 | 5: list 49 | 6: forbidden 50 | 7: bool 51 | 8: list 52 | 9: int 53 | 10: forbidden 54 | 11: int 55 | 12: int 56 | 13: float 57 | 14: forbidden 58 | 15: float 59 | 16: float 60 | 17: bool 61 | 18: forbidden 62 | 19: bool 63 | 20: bool 64 | 21: string 65 | 22: forbidden 66 | 23: string 67 | 24: bool 68 | 25: int 69 | 26: forbidden 70 | 27: int 71 | 28: int 72 | 29: list 73 | 30: bool 74 | 31: bool 75 | ==== IR ==== 76 | register_count: 2 77 | file_count: 0 78 | 0: LoadLiteral { dst: RegId(0), lit: Int(1) } 79 | 1: LoadLiteral { dst: RegId(1), lit: Int(1) } 80 | ==== IR ERRORS ==== 81 | Error (NodeId 1): unrecognized operator Equal 82 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@binary_ops_mismatch.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/binary_ops_mismatch.nu 5 | --- 6 | ==== COMPILER ==== 7 | 0: String (0 to 3) ""a"" 8 | 1: Plus (4 to 5) 9 | 2: Float (6 to 9) "1.0" 10 | 3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 9) 11 | 4: String (10 to 13) ""a"" 12 | 5: Append (14 to 16) 13 | 6: Float (17 to 20) "1.0" 14 | 7: BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) } (10 to 20) 15 | 8: True (21 to 25) 16 | 9: And (26 to 29) 17 | 10: String (30 to 33) ""a"" 18 | 11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (21 to 33) 19 | 12: True (34 to 38) 20 | 13: NotRegexMatch (39 to 41) 21 | 14: String (42 to 48) ""true"" 22 | 15: BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) } (34 to 48) 23 | 16: Block(BlockId(0)) (0 to 49) 24 | ==== SCOPE ==== 25 | 0: Frame Scope, node_id: NodeId(16) (empty) 26 | ==== TYPES ==== 27 | 0: string 28 | 1: error 29 | 2: float 30 | 3: error 31 | 4: string 32 | 5: error 33 | 6: float 34 | 7: error 35 | 8: bool 36 | 9: error 37 | 10: string 38 | 11: error 39 | 12: bool 40 | 13: error 41 | 14: string 42 | 15: error 43 | 16: error 44 | ==== TYPE ERRORS ==== 45 | Error (NodeId 1): type mismatch: unsupported addition between string and float 46 | Error (NodeId 5): type mismatch: unsupported append between string and float 47 | Error (NodeId 9): type mismatch: unsupported logical operation between bool and string 48 | Error (NodeId 13): type mismatch: unsupported string operation between bool and string 49 | ==== IR ==== 50 | register_count: 0 51 | file_count: 0 52 | ==== IR ERRORS ==== 53 | Error (NodeId 0): node String not suported yet 54 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@binary_ops_spaces.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/binary_ops_spaces.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (0 to 1) "1" 9 | 1: Plus (2 to 3) 10 | 2: Int (4 to 5) "2" 11 | 3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 5) 12 | 4: Int (6 to 7) "1" 13 | 5: Plus (7 to 8) 14 | 6: Int (9 to 10) "2" 15 | 7: BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) } (6 to 10) 16 | 8: Int (11 to 12) "1" 17 | 9: Plus (13 to 14) 18 | 10: Int (14 to 15) "2" 19 | 11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (11 to 15) 20 | 12: Int (16 to 17) "1" 21 | 13: Plus (17 to 18) 22 | 14: Int (18 to 19) "2" 23 | 15: BinaryOp { lhs: NodeId(12), op: NodeId(13), rhs: NodeId(14) } (16 to 19) 24 | 16: Block(BlockId(0)) (0 to 20) 25 | ==== COMPILER ERRORS ==== 26 | Error (NodeId 5): missing space before operator 27 | Error (NodeId 9): missing space after operator 28 | Error (NodeId 13): missing space before operator 29 | Error (NodeId 13): missing space after operator 30 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@binary_ops_subtypes.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/binary_ops_subtypes.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (0 to 1) "1" 9 | 1: Equal (2 to 4) 10 | 2: Float (5 to 8) "1.0" 11 | 3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 8) 12 | 4: String (9 to 12) ""a"" 13 | 5: Equal (13 to 15) 14 | 6: Float (16 to 19) "1.0" 15 | 7: BinaryOp { lhs: NodeId(4), op: NodeId(5), rhs: NodeId(6) } (9 to 19) 16 | 8: Int (20 to 21) "1" 17 | 9: Plus (22 to 23) 18 | 10: Float (24 to 27) "1.0" 19 | 11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (20 to 27) 20 | 12: Int (29 to 30) "1" 21 | 13: List([NodeId(12)]) (28 to 30) 22 | 14: Append (32 to 34) 23 | 15: Float (35 to 38) "1.0" 24 | 16: BinaryOp { lhs: NodeId(13), op: NodeId(14), rhs: NodeId(15) } (28 to 38) 25 | 17: Float (40 to 43) "1.0" 26 | 18: Int (44 to 45) "1" 27 | 19: List([NodeId(17), NodeId(18)]) (39 to 45) 28 | 20: Append (47 to 49) 29 | 21: String (50 to 53) ""a"" 30 | 22: BinaryOp { lhs: NodeId(19), op: NodeId(20), rhs: NodeId(21) } (39 to 53) 31 | 23: Int (56 to 57) "1" 32 | 24: List([NodeId(23)]) (55 to 57) 33 | 25: Int (60 to 61) "2" 34 | 26: List([NodeId(25)]) (59 to 61) 35 | 27: List([NodeId(24), NodeId(26)]) (54 to 62) 36 | 28: Append (64 to 66) 37 | 29: Int (69 to 70) "3" 38 | 30: List([NodeId(29)]) (68 to 70) 39 | 31: List([NodeId(30)]) (67 to 71) 40 | 32: BinaryOp { lhs: NodeId(27), op: NodeId(28), rhs: NodeId(31) } (54 to 71) 41 | 33: Int (75 to 76) "1" 42 | 34: List([NodeId(33)]) (74 to 76) 43 | 35: Int (79 to 80) "2" 44 | 36: List([NodeId(35)]) (78 to 80) 45 | 37: List([NodeId(34), NodeId(36)]) (73 to 81) 46 | 38: Append (83 to 85) 47 | 39: Float (88 to 91) "3.0" 48 | 40: List([NodeId(39)]) (87 to 91) 49 | 41: List([NodeId(40)]) (86 to 92) 50 | 42: BinaryOp { lhs: NodeId(37), op: NodeId(38), rhs: NodeId(41) } (73 to 92) 51 | 43: Int (94 to 95) "1" 52 | 44: In (96 to 98) 53 | 45: Float (100 to 103) "1.0" 54 | 46: Int (105 to 106) "1" 55 | 47: List([NodeId(45), NodeId(46)]) (99 to 106) 56 | 48: BinaryOp { lhs: NodeId(43), op: NodeId(44), rhs: NodeId(47) } (94 to 106) 57 | 49: Float (108 to 111) "2.3" 58 | 50: Modulo (112 to 115) 59 | 51: Int (116 to 117) "1" 60 | 52: BinaryOp { lhs: NodeId(49), op: NodeId(50), rhs: NodeId(51) } (108 to 117) 61 | 53: String (120 to 121) "b" 62 | 54: Int (123 to 124) "2" 63 | 55: String (126 to 127) "c" 64 | 56: Int (129 to 130) "3" 65 | 57: Record { pairs: [(NodeId(53), NodeId(54)), (NodeId(55), NodeId(56))] } (119 to 131) 66 | 58: List([NodeId(57)]) (118 to 131) 67 | 59: Append (133 to 135) 68 | 60: String (138 to 139) "a" 69 | 61: Int (141 to 142) "3" 70 | 62: String (144 to 145) "b" 71 | 63: Float (147 to 150) "1.5" 72 | 64: String (152 to 153) "c" 73 | 65: String (155 to 160) ""foo"" 74 | 66: Record { pairs: [(NodeId(60), NodeId(61)), (NodeId(62), NodeId(63)), (NodeId(64), NodeId(65))] } (137 to 161) 75 | 67: List([NodeId(66)]) (136 to 161) 76 | 68: BinaryOp { lhs: NodeId(58), op: NodeId(59), rhs: NodeId(67) } (118 to 161) 77 | 69: Block(BlockId(0)) (0 to 163) 78 | ==== SCOPE ==== 79 | 0: Frame Scope, node_id: NodeId(69) (empty) 80 | ==== TYPES ==== 81 | 0: int 82 | 1: forbidden 83 | 2: float 84 | 3: bool 85 | 4: string 86 | 5: forbidden 87 | 6: float 88 | 7: bool 89 | 8: int 90 | 9: forbidden 91 | 10: float 92 | 11: float 93 | 12: int 94 | 13: list 95 | 14: forbidden 96 | 15: float 97 | 16: list 98 | 17: float 99 | 18: int 100 | 19: list 101 | 20: forbidden 102 | 21: string 103 | 22: list 104 | 23: int 105 | 24: list 106 | 25: int 107 | 26: list 108 | 27: list> 109 | 28: forbidden 110 | 29: int 111 | 30: list 112 | 31: list> 113 | 32: list> 114 | 33: int 115 | 34: list 116 | 35: int 117 | 36: list 118 | 37: list> 119 | 38: forbidden 120 | 39: float 121 | 40: list 122 | 41: list> 123 | 42: list> 124 | 43: int 125 | 44: forbidden 126 | 45: float 127 | 46: int 128 | 47: list 129 | 48: bool 130 | 49: float 131 | 50: forbidden 132 | 51: int 133 | 52: float 134 | 53: unknown 135 | 54: int 136 | 55: unknown 137 | 56: int 138 | 57: record 139 | 58: list> 140 | 59: forbidden 141 | 60: unknown 142 | 61: int 143 | 62: unknown 144 | 63: float 145 | 64: unknown 146 | 65: string 147 | 66: record 148 | 67: list> 149 | 68: list> 150 | 69: list> 151 | ==== IR ==== 152 | register_count: 1 153 | file_count: 0 154 | 0: LoadLiteral { dst: RegId(0), lit: Int(1) } 155 | ==== IR ERRORS ==== 156 | Error (NodeId 2): node Float not suported yet 157 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@calls.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/calls.nu 5 | --- 6 | ==== COMPILER ==== 7 | 0: Name (0 to 4) "spam" 8 | 1: Name (5 to 8) "foo" 9 | 2: String (9 to 14) ""bar"" 10 | 3: Int (16 to 17) "1" 11 | 4: Plus (18 to 19) 12 | 5: Int (20 to 21) "2" 13 | 6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (16 to 21) 14 | 7: Call { parts: [NodeId(0), NodeId(1), NodeId(2), NodeId(6)] } (5 to 22) 15 | 8: Name (28 to 36) "existing" 16 | 9: Name (38 to 39) "a" 17 | 10: Name (41 to 47) "string" 18 | 11: Type { name: NodeId(10), args: None, optional: false } (41 to 47) 19 | 12: Param { name: NodeId(9), ty: Some(NodeId(11)) } (38 to 47) 20 | 13: Name (49 to 50) "b" 21 | 14: Name (52 to 58) "string" 22 | 15: Type { name: NodeId(14), args: None, optional: false } (52 to 58) 23 | 16: Param { name: NodeId(13), ty: Some(NodeId(15)) } (49 to 58) 24 | 17: Name (60 to 61) "c" 25 | 18: Name (63 to 66) "int" 26 | 19: Type { name: NodeId(18), args: None, optional: false } (63 to 66) 27 | 20: Param { name: NodeId(17), ty: Some(NodeId(19)) } (60 to 66) 28 | 21: Params([NodeId(12), NodeId(16), NodeId(20)]) (37 to 67) 29 | 22: Variable (72 to 74) "$a" 30 | 23: Variable (76 to 78) "$b" 31 | 24: Variable (80 to 82) "$c" 32 | 25: List([NodeId(22), NodeId(23), NodeId(24)]) (70 to 82) 33 | 26: Block(BlockId(0)) (68 to 85) 34 | 27: Def { name: NodeId(8), params: NodeId(21), in_out_types: None, block: NodeId(26) } (24 to 85) 35 | 28: Name (86 to 94) "existing" 36 | 29: Name (95 to 98) "foo" 37 | 30: String (100 to 104) ""ba"" 38 | 31: Plus (105 to 106) 39 | 32: String (107 to 110) ""r"" 40 | 33: BinaryOp { lhs: NodeId(30), op: NodeId(31), rhs: NodeId(32) } (100 to 110) 41 | 34: Int (112 to 113) "3" 42 | 35: Call { parts: [NodeId(28), NodeId(29), NodeId(33), NodeId(34)] } (95 to 113) 43 | 36: Name (115 to 128) "foo/bar/spam 44 | " 45 | 37: Call { parts: [NodeId(36)] } (127 to 127) 46 | 38: Block(BlockId(1)) (0 to 128) 47 | ==== SCOPE ==== 48 | 0: Frame Scope, node_id: NodeId(38) 49 | decls: [ existing: NodeId(8) ] 50 | 1: Frame Scope, node_id: NodeId(26) 51 | variables: [ a: NodeId(9), b: NodeId(13), c: NodeId(17) ] 52 | ==== TYPES ==== 53 | 0: unknown 54 | 1: string 55 | 2: string 56 | 3: int 57 | 4: forbidden 58 | 5: int 59 | 6: int 60 | 7: stream 61 | 8: unknown 62 | 9: unknown 63 | 10: unknown 64 | 11: string 65 | 12: string 66 | 13: unknown 67 | 14: unknown 68 | 15: string 69 | 16: string 70 | 17: unknown 71 | 18: unknown 72 | 19: int 73 | 20: int 74 | 21: forbidden 75 | 22: string 76 | 23: string 77 | 24: int 78 | 25: list 79 | 26: list 80 | 27: () 81 | 28: unknown 82 | 29: string 83 | 30: string 84 | 31: forbidden 85 | 32: string 86 | 33: string 87 | 34: int 88 | 35: any 89 | 36: unknown 90 | 37: stream 91 | 38: stream 92 | ==== IR ==== 93 | register_count: 0 94 | file_count: 0 95 | ==== IR ERRORS ==== 96 | Error (NodeId 7): node Call { parts: [NodeId(0), NodeId(1), NodeId(2), NodeId(6)] } not suported yet 97 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@closure.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/closure.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Name (3 to 4) "a" 9 | 1: Param { name: NodeId(0), ty: None } (3 to 4) 10 | 2: Name (6 to 7) "b" 11 | 3: Param { name: NodeId(2), ty: None } (6 to 7) 12 | 4: Params([NodeId(1), NodeId(3)]) (2 to 8) 13 | 5: Variable (9 to 11) "$a" 14 | 6: Plus (12 to 13) 15 | 7: Variable (14 to 16) "$b" 16 | 8: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(7) } (9 to 16) 17 | 9: Block(BlockId(0)) (9 to 16) 18 | 10: Closure { params: Some(NodeId(4)), block: NodeId(9) } (0 to 17) 19 | 11: Variable (18 to 20) "$a" 20 | 12: Block(BlockId(1)) (0 to 42) 21 | ==== SCOPE ==== 22 | 0: Frame Scope, node_id: NodeId(12) (empty) 23 | 1: Frame Scope, node_id: NodeId(9) 24 | variables: [ a: NodeId(0), b: NodeId(2) ] 25 | ==== SCOPE ERRORS ==== 26 | Error (NodeId 11): variable `a` not found 27 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@closure2.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/closure2.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 6) "$a" 9 | 1: Plus (7 to 8) 10 | 2: Variable (9 to 11) "$b" 11 | 3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (4 to 11) 12 | 4: Block(BlockId(0)) (4 to 12) 13 | 5: Closure { params: None, block: NodeId(4) } (0 to 13) 14 | 6: Block(BlockId(1)) (0 to 14) 15 | ==== SCOPE ==== 16 | 0: Frame Scope, node_id: NodeId(6) (empty) 17 | 1: Frame Scope, node_id: NodeId(4) (empty) 18 | ==== SCOPE ERRORS ==== 19 | Error (NodeId 0): variable `a` not found 20 | Error (NodeId 2): variable `b` not found 21 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@closure3.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/closure3.nu 5 | --- 6 | ==== COMPILER ==== 7 | 0: Variable (4 to 11) "closure" 8 | 1: Name (16 to 17) "a" 9 | 2: Name (19 to 22) "int" 10 | 3: Type { name: NodeId(2), args: None, optional: false } (19 to 22) 11 | 4: Param { name: NodeId(1), ty: Some(NodeId(3)) } (16 to 22) 12 | 5: Name (24 to 25) "b" 13 | 6: Name (27 to 30) "int" 14 | 7: Type { name: NodeId(6), args: None, optional: false } (27 to 30) 15 | 8: Param { name: NodeId(5), ty: Some(NodeId(7)) } (24 to 30) 16 | 9: Params([NodeId(4), NodeId(8)]) (15 to 31) 17 | 10: Variable (32 to 34) "$a" 18 | 11: Plus (35 to 36) 19 | 12: Variable (37 to 39) "$b" 20 | 13: LessThan (40 to 41) 21 | 14: Int (42 to 43) "5" 22 | 15: BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) } (32 to 39) 23 | 16: BinaryOp { lhs: NodeId(15), op: NodeId(13), rhs: NodeId(14) } (32 to 43) 24 | 17: Block(BlockId(0)) (32 to 43) 25 | 18: Closure { params: Some(NodeId(9)), block: NodeId(17) } (14 to 44) 26 | 19: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(18), is_mutable: false } (0 to 44) 27 | 20: Name (46 to 52) "filter" 28 | 21: Variable (53 to 61) "$closure" 29 | 22: Call { parts: [NodeId(20), NodeId(21)] } (53 to 61) 30 | 23: Block(BlockId(1)) (0 to 62) 31 | ==== SCOPE ==== 32 | 0: Frame Scope, node_id: NodeId(23) 33 | variables: [ closure: NodeId(0) ] 34 | 1: Frame Scope, node_id: NodeId(17) 35 | variables: [ a: NodeId(1), b: NodeId(5) ] 36 | ==== TYPES ==== 37 | 0: closure 38 | 1: unknown 39 | 2: unknown 40 | 3: int 41 | 4: int 42 | 5: unknown 43 | 6: unknown 44 | 7: int 45 | 8: int 46 | 9: forbidden 47 | 10: int 48 | 11: forbidden 49 | 12: int 50 | 13: forbidden 51 | 14: int 52 | 15: int 53 | 16: bool 54 | 17: bool 55 | 18: closure 56 | 19: () 57 | 20: unknown 58 | 21: closure 59 | 22: stream 60 | 23: stream 61 | ==== IR ==== 62 | register_count: 0 63 | file_count: 0 64 | ==== IR ERRORS ==== 65 | Error (NodeId 19): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(18), is_mutable: false } not suported yet 66 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@def.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/def.nu 5 | --- 6 | ==== COMPILER ==== 7 | 0: Name (4 to 7) "foo" 8 | 1: Name (9 to 10) "w" 9 | 2: Param { name: NodeId(1), ty: None } (9 to 10) 10 | 3: Name (11 to 12) "x" 11 | 4: Name (14 to 17) "int" 12 | 5: Type { name: NodeId(4), args: None, optional: false } (14 to 17) 13 | 6: Param { name: NodeId(3), ty: Some(NodeId(5)) } (11 to 17) 14 | 7: Name (19 to 20) "y" 15 | 8: Name (22 to 26) "list" 16 | 9: Name (27 to 31) "list" 17 | 10: Name (32 to 35) "int" 18 | 11: Type { name: NodeId(10), args: None, optional: false } (32 to 35) 19 | 12: TypeArgs([NodeId(11)]) (31 to 36) 20 | 13: Type { name: NodeId(9), args: Some(NodeId(12)), optional: false } (27 to 31) 21 | 14: TypeArgs([NodeId(13)]) (26 to 37) 22 | 15: Type { name: NodeId(8), args: Some(NodeId(14)), optional: false } (22 to 26) 23 | 16: Param { name: NodeId(7), ty: Some(NodeId(15)) } (19 to 26) 24 | 17: Name (39 to 40) "z" 25 | 18: Name (42 to 48) "record" 26 | 19: Name (49 to 50) "a" 27 | 20: Param { name: NodeId(19), ty: None } (49 to 50) 28 | 21: Name (52 to 53) "b" 29 | 22: Name (55 to 58) "int" 30 | 23: Type { name: NodeId(22), args: None, optional: false } (55 to 58) 31 | 24: Param { name: NodeId(21), ty: Some(NodeId(23)) } (52 to 58) 32 | 25: Params([NodeId(20), NodeId(24)]) (48 to 59) 33 | 26: RecordType { fields: NodeId(25), optional: false } (42 to 60) 34 | 27: Param { name: NodeId(17), ty: Some(NodeId(26)) } (39 to 60) 35 | 28: Params([NodeId(2), NodeId(6), NodeId(16), NodeId(27)]) (8 to 61) 36 | 29: Variable (66 to 68) "$w" 37 | 30: Variable (69 to 71) "$x" 38 | 31: Variable (73 to 75) "$y" 39 | 32: Variable (77 to 79) "$z" 40 | 33: List([NodeId(29), NodeId(30), NodeId(31), NodeId(32)]) (64 to 80) 41 | 34: Block(BlockId(0)) (62 to 83) 42 | 35: Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) } (0 to 83) 43 | 36: Block(BlockId(1)) (0 to 83) 44 | ==== SCOPE ==== 45 | 0: Frame Scope, node_id: NodeId(36) 46 | decls: [ foo: NodeId(0) ] 47 | 1: Frame Scope, node_id: NodeId(34) 48 | variables: [ w: NodeId(1), x: NodeId(3), y: NodeId(7), z: NodeId(17) ] 49 | ==== TYPES ==== 50 | 0: unknown 51 | 1: unknown 52 | 2: any 53 | 3: unknown 54 | 4: unknown 55 | 5: int 56 | 6: int 57 | 7: unknown 58 | 8: unknown 59 | 9: unknown 60 | 10: unknown 61 | 11: int 62 | 12: forbidden 63 | 13: list 64 | 14: forbidden 65 | 15: list> 66 | 16: list> 67 | 17: unknown 68 | 18: unknown 69 | 19: unknown 70 | 20: unknown 71 | 21: unknown 72 | 22: unknown 73 | 23: int 74 | 24: unknown 75 | 25: unknown 76 | 26: record 77 | 27: record 78 | 28: forbidden 79 | 29: unknown 80 | 30: int 81 | 31: list> 82 | 32: record 83 | 33: list 84 | 34: list 85 | 35: () 86 | 36: () 87 | ==== IR ==== 88 | register_count: 0 89 | file_count: 0 90 | ==== IR ERRORS ==== 91 | Error (NodeId 35): node Def { name: NodeId(0), params: NodeId(28), in_out_types: None, block: NodeId(34) } not suported yet 92 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@def_return_type.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/def_return_type.nu 5 | --- 6 | ==== COMPILER ==== 7 | 0: Name (4 to 7) "foo" 8 | 1: Params([]) (8 to 11) 9 | 2: Name (14 to 21) "nothing" 10 | 3: Type { name: NodeId(2), args: None, optional: false } (14 to 21) 11 | 4: Name (25 to 29) "list" 12 | 5: Name (30 to 33) "any" 13 | 6: Type { name: NodeId(5), args: None, optional: false } (30 to 33) 14 | 7: TypeArgs([NodeId(6)]) (29 to 34) 15 | 8: Type { name: NodeId(4), args: Some(NodeId(7)), optional: false } (25 to 29) 16 | 9: InOutType(NodeId(3), NodeId(8)) (14 to 35) 17 | 10: InOutTypes([NodeId(9)]) (14 to 35) 18 | 11: List([]) (37 to 38) 19 | 12: Block(BlockId(0)) (35 to 41) 20 | 13: Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) } (0 to 41) 21 | 14: Name (46 to 49) "bar" 22 | 15: Params([]) (50 to 53) 23 | 16: Name (58 to 64) "string" 24 | 17: Type { name: NodeId(16), args: None, optional: false } (58 to 64) 25 | 18: Name (68 to 72) "list" 26 | 19: Name (73 to 79) "string" 27 | 20: Type { name: NodeId(19), args: None, optional: false } (73 to 79) 28 | 21: TypeArgs([NodeId(20)]) (72 to 80) 29 | 22: Type { name: NodeId(18), args: Some(NodeId(21)), optional: false } (68 to 72) 30 | 23: InOutType(NodeId(17), NodeId(22)) (58 to 80) 31 | 24: Name (82 to 85) "int" 32 | 25: Type { name: NodeId(24), args: None, optional: false } (82 to 85) 33 | 26: Name (89 to 93) "list" 34 | 27: Name (94 to 97) "int" 35 | 28: Type { name: NodeId(27), args: None, optional: false } (94 to 97) 36 | 29: TypeArgs([NodeId(28)]) (93 to 98) 37 | 30: Type { name: NodeId(26), args: Some(NodeId(29)), optional: false } (89 to 93) 38 | 31: InOutType(NodeId(25), NodeId(30)) (82 to 99) 39 | 32: InOutTypes([NodeId(23), NodeId(31)]) (56 to 101) 40 | 33: List([]) (103 to 104) 41 | 34: Block(BlockId(1)) (101 to 107) 42 | 35: Def { name: NodeId(14), params: NodeId(15), in_out_types: Some(NodeId(32)), block: NodeId(34) } (42 to 107) 43 | 36: Block(BlockId(2)) (0 to 108) 44 | ==== SCOPE ==== 45 | 0: Frame Scope, node_id: NodeId(36) 46 | decls: [ bar: NodeId(14), foo: NodeId(0) ] 47 | 1: Frame Scope, node_id: NodeId(12) (empty) 48 | 2: Frame Scope, node_id: NodeId(34) (empty) 49 | ==== TYPES ==== 50 | 0: unknown 51 | 1: forbidden 52 | 2: unknown 53 | 3: unknown 54 | 4: unknown 55 | 5: unknown 56 | 6: any 57 | 7: forbidden 58 | 8: unknown 59 | 9: unknown 60 | 10: unknown 61 | 11: list 62 | 12: list 63 | 13: () 64 | 14: unknown 65 | 15: forbidden 66 | 16: unknown 67 | 17: unknown 68 | 18: unknown 69 | 19: unknown 70 | 20: string 71 | 21: forbidden 72 | 22: unknown 73 | 23: unknown 74 | 24: unknown 75 | 25: unknown 76 | 26: unknown 77 | 27: unknown 78 | 28: int 79 | 29: forbidden 80 | 30: unknown 81 | 31: unknown 82 | 32: unknown 83 | 33: list 84 | 34: list 85 | 35: () 86 | 36: () 87 | ==== IR ==== 88 | register_count: 0 89 | file_count: 0 90 | ==== IR ERRORS ==== 91 | Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(1), in_out_types: Some(NodeId(10)), block: NodeId(12) } not suported yet 92 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@for.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/for.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Int (8 to 9) "0" 10 | 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) 11 | 3: Variable (14 to 15) "i" 12 | 4: Int (20 to 21) "1" 13 | 5: Int (22 to 23) "2" 14 | 6: Int (24 to 25) "3" 15 | 7: List([NodeId(4), NodeId(5), NodeId(6)]) (19 to 25) 16 | 8: Variable (33 to 35) "$x" 17 | 9: Assignment (36 to 37) 18 | 10: Variable (38 to 40) "$x" 19 | 11: Plus (41 to 42) 20 | 12: Variable (43 to 45) "$i" 21 | 13: BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) } (38 to 45) 22 | 14: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(13) } (33 to 45) 23 | 15: Block(BlockId(0)) (27 to 47) 24 | 16: For { variable: NodeId(3), range: NodeId(7), block: NodeId(15) } (10 to 47) 25 | 17: Variable (53 to 55) "$i" 26 | 18: Int (60 to 61) "4" 27 | 19: Int (62 to 63) "5" 28 | 20: Int (64 to 65) "6" 29 | 21: List([NodeId(18), NodeId(19), NodeId(20)]) (59 to 65) 30 | 22: Variable (73 to 75) "$x" 31 | 23: Assignment (76 to 77) 32 | 24: Variable (78 to 80) "$x" 33 | 25: Plus (81 to 82) 34 | 26: Variable (83 to 85) "$i" 35 | 27: BinaryOp { lhs: NodeId(24), op: NodeId(25), rhs: NodeId(26) } (78 to 85) 36 | 28: BinaryOp { lhs: NodeId(22), op: NodeId(23), rhs: NodeId(27) } (73 to 85) 37 | 29: Block(BlockId(1)) (67 to 87) 38 | 30: For { variable: NodeId(17), range: NodeId(21), block: NodeId(29) } (49 to 87) 39 | 31: Block(BlockId(2)) (0 to 88) 40 | ==== SCOPE ==== 41 | 0: Frame Scope, node_id: NodeId(31) 42 | variables: [ x: NodeId(0) ] 43 | 1: Frame Scope, node_id: NodeId(15) 44 | variables: [ i: NodeId(3) ] 45 | 2: Frame Scope, node_id: NodeId(29) 46 | variables: [ i: NodeId(17) ] 47 | ==== TYPES ==== 48 | 0: int 49 | 1: int 50 | 2: () 51 | 3: int 52 | 4: int 53 | 5: int 54 | 6: int 55 | 7: list 56 | 8: int 57 | 9: forbidden 58 | 10: int 59 | 11: forbidden 60 | 12: int 61 | 13: int 62 | 14: () 63 | 15: () 64 | 16: () 65 | 17: int 66 | 18: int 67 | 19: int 68 | 20: int 69 | 21: list 70 | 22: int 71 | 23: forbidden 72 | 24: int 73 | 25: forbidden 74 | 26: int 75 | 27: int 76 | 28: () 77 | 29: () 78 | 30: () 79 | 31: () 80 | ==== IR ==== 81 | register_count: 0 82 | file_count: 0 83 | ==== IR ERRORS ==== 84 | Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } not suported yet 85 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@for_break_continue.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/for_break_continue.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Int (8 to 9) "0" 10 | 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) 11 | 3: Variable (14 to 15) "i" 12 | 4: Int (20 to 21) "1" 13 | 5: Int (22 to 23) "2" 14 | 6: Int (24 to 25) "3" 15 | 7: List([NodeId(4), NodeId(5), NodeId(6)]) (19 to 25) 16 | 8: Variable (36 to 38) "$x" 17 | 9: GreaterThan (39 to 40) 18 | 10: Int (41 to 42) "2" 19 | 11: BinaryOp { lhs: NodeId(8), op: NodeId(9), rhs: NodeId(10) } (36 to 42) 20 | 12: Break (53 to 58) 21 | 13: Block(BlockId(0)) (43 to 64) 22 | 14: If { condition: NodeId(11), then_block: NodeId(13), else_block: None } (33 to 64) 23 | 15: Variable (73 to 75) "$i" 24 | 16: LessThan (76 to 77) 25 | 17: Int (78 to 79) "3" 26 | 18: BinaryOp { lhs: NodeId(15), op: NodeId(16), rhs: NodeId(17) } (73 to 79) 27 | 19: Continue (90 to 98) 28 | 20: Block(BlockId(1)) (80 to 104) 29 | 21: If { condition: NodeId(18), then_block: NodeId(20), else_block: None } (70 to 104) 30 | 22: Variable (110 to 112) "$x" 31 | 23: Assignment (113 to 114) 32 | 24: Variable (115 to 117) "$x" 33 | 25: Plus (118 to 119) 34 | 26: Variable (120 to 122) "$i" 35 | 27: BinaryOp { lhs: NodeId(24), op: NodeId(25), rhs: NodeId(26) } (115 to 122) 36 | 28: BinaryOp { lhs: NodeId(22), op: NodeId(23), rhs: NodeId(27) } (110 to 122) 37 | 29: Block(BlockId(2)) (27 to 124) 38 | 30: For { variable: NodeId(3), range: NodeId(7), block: NodeId(29) } (10 to 124) 39 | 31: Block(BlockId(3)) (0 to 124) 40 | ==== SCOPE ==== 41 | 0: Frame Scope, node_id: NodeId(31) 42 | variables: [ x: NodeId(0) ] 43 | 1: Frame Scope, node_id: NodeId(29) 44 | variables: [ i: NodeId(3) ] 45 | 2: Frame Scope, node_id: NodeId(13) (empty) 46 | 3: Frame Scope, node_id: NodeId(20) (empty) 47 | ==== TYPES ==== 48 | 0: int 49 | 1: int 50 | 2: () 51 | 3: int 52 | 4: int 53 | 5: int 54 | 6: int 55 | 7: list 56 | 8: int 57 | 9: forbidden 58 | 10: int 59 | 11: bool 60 | 12: unknown 61 | 13: unknown 62 | 14: oneof<(), unknown> 63 | 15: int 64 | 16: forbidden 65 | 17: int 66 | 18: bool 67 | 19: unknown 68 | 20: unknown 69 | 21: oneof<(), unknown> 70 | 22: int 71 | 23: forbidden 72 | 24: int 73 | 25: forbidden 74 | 26: int 75 | 27: int 76 | 28: () 77 | 29: () 78 | 30: () 79 | 31: () 80 | ==== TYPE ERRORS ==== 81 | Error (NodeId 12): unsupported ast node 'Break' in typechecker 82 | Error (NodeId 19): unsupported ast node 'Continue' in typechecker 83 | ==== IR ==== 84 | register_count: 0 85 | file_count: 0 86 | ==== IR ERRORS ==== 87 | Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } not suported yet 88 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@if_.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/if_.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Int (8 to 11) "123" 10 | 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 11) 11 | 3: Variable (16 to 18) "$x" 12 | 4: LessThan (19 to 20) 13 | 5: Int (21 to 24) "100" 14 | 6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (16 to 24) 15 | 7: Int (31 to 32) "5" 16 | 8: Block(BlockId(0)) (25 to 35) 17 | 9: Variable (43 to 45) "$x" 18 | 10: GreaterThan (46 to 47) 19 | 11: Int (48 to 51) "200" 20 | 12: BinaryOp { lhs: NodeId(9), op: NodeId(10), rhs: NodeId(11) } (43 to 51) 21 | 13: Int (58 to 59) "6" 22 | 14: Block(BlockId(1)) (52 to 62) 23 | 15: Int (73 to 74) "7" 24 | 16: Block(BlockId(2)) (67 to 76) 25 | 17: If { condition: NodeId(12), then_block: NodeId(14), else_block: Some(NodeId(16)) } (40 to 76) 26 | 18: If { condition: NodeId(6), then_block: NodeId(8), else_block: Some(NodeId(17)) } (13 to 76) 27 | 19: Block(BlockId(3)) (0 to 77) 28 | ==== SCOPE ==== 29 | 0: Frame Scope, node_id: NodeId(19) 30 | variables: [ x: NodeId(0) ] 31 | 1: Frame Scope, node_id: NodeId(8) (empty) 32 | 2: Frame Scope, node_id: NodeId(14) (empty) 33 | 3: Frame Scope, node_id: NodeId(16) (empty) 34 | ==== TYPES ==== 35 | 0: int 36 | 1: int 37 | 2: () 38 | 3: int 39 | 4: forbidden 40 | 5: int 41 | 6: bool 42 | 7: int 43 | 8: int 44 | 9: int 45 | 10: forbidden 46 | 11: int 47 | 12: bool 48 | 13: int 49 | 14: int 50 | 15: int 51 | 16: int 52 | 17: int 53 | 18: int 54 | 19: int 55 | ==== IR ==== 56 | register_count: 0 57 | file_count: 0 58 | ==== IR ERRORS ==== 59 | Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } not suported yet 60 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@invalid_if.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/invalid_if.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (3 to 4) "1" 9 | 1: Int (9 to 10) "4" 10 | 2: Block(BlockId(0)) (5 to 13) 11 | 3: Int (22 to 23) "3" 12 | 4: Block(BlockId(1)) (18 to 25) 13 | 5: If { condition: NodeId(0), then_block: NodeId(2), else_block: Some(NodeId(4)) } (0 to 25) 14 | 6: Block(BlockId(2)) (0 to 26) 15 | ==== SCOPE ==== 16 | 0: Frame Scope, node_id: NodeId(6) (empty) 17 | 1: Frame Scope, node_id: NodeId(2) (empty) 18 | 2: Frame Scope, node_id: NodeId(4) (empty) 19 | ==== TYPES ==== 20 | 0: int 21 | 1: int 22 | 2: int 23 | 3: int 24 | 4: int 25 | 5: error 26 | 6: error 27 | ==== TYPE ERRORS ==== 28 | Error (NodeId 0): The condition for if branch is not a boolean 29 | ==== IR ==== 30 | register_count: 0 31 | file_count: 0 32 | ==== IR ERRORS ==== 33 | Error (NodeId 5): node If { condition: NodeId(0), then_block: NodeId(2), else_block: Some(NodeId(4)) } not suported yet 34 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@invalid_range.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/invalid_range.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (0 to 1) "1" 9 | 1: Garbage (2 to 4) 10 | 2: Int (5 to 6) "2" 11 | 3: Block(BlockId(0)) (0 to 7) 12 | ==== COMPILER ERRORS ==== 13 | Error (NodeId 1): incomplete expression 14 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@invalid_record.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/invalid_record.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (2 to 3) "a" 9 | 1: Int (5 to 6) "1" 10 | 2: String (9 to 10) "b" 11 | 3: Garbage (11 to 12) 12 | 4: Garbage (13 to 13) 13 | 5: Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(4))] } (0 to 0) 14 | 6: Block(BlockId(0)) (0 to 13) 15 | ==== COMPILER ERRORS ==== 16 | Error (NodeId 3): expected: colon ':' 17 | Error (NodeId 4): incomplete expression 18 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@invalid_types.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/invalid_types.nu 5 | --- 6 | ==== COMPILER ==== 7 | 0: Name (4 to 7) "foo" 8 | 1: Name (9 to 10) "x" 9 | 2: Name (12 to 16) "list" 10 | 3: Name (17 to 20) "int" 11 | 4: Type { name: NodeId(3), args: None, optional: false } (17 to 20) 12 | 5: Name (22 to 28) "string" 13 | 6: Type { name: NodeId(5), args: None, optional: false } (22 to 28) 14 | 7: TypeArgs([NodeId(4), NodeId(6)]) (16 to 29) 15 | 8: Type { name: NodeId(2), args: Some(NodeId(7)), optional: false } (12 to 16) 16 | 9: Param { name: NodeId(1), ty: Some(NodeId(8)) } (9 to 16) 17 | 10: Params([NodeId(9)]) (8 to 30) 18 | 11: Variable (33 to 35) "$x" 19 | 12: Block(BlockId(0)) (31 to 37) 20 | 13: Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) } (0 to 37) 21 | 14: Name (42 to 45) "bar" 22 | 15: Name (47 to 48) "y" 23 | 16: Name (50 to 54) "list" 24 | 17: TypeArgs([]) (54 to 56) 25 | 18: Type { name: NodeId(16), args: Some(NodeId(17)), optional: false } (50 to 54) 26 | 19: Param { name: NodeId(15), ty: Some(NodeId(18)) } (47 to 54) 27 | 20: Params([NodeId(19)]) (46 to 57) 28 | 21: Variable (60 to 62) "$y" 29 | 22: Block(BlockId(1)) (58 to 64) 30 | 23: Def { name: NodeId(14), params: NodeId(20), in_out_types: None, block: NodeId(22) } (38 to 64) 31 | 24: Block(BlockId(2)) (0 to 65) 32 | ==== SCOPE ==== 33 | 0: Frame Scope, node_id: NodeId(24) 34 | decls: [ bar: NodeId(14), foo: NodeId(0) ] 35 | 1: Frame Scope, node_id: NodeId(12) 36 | variables: [ x: NodeId(1) ] 37 | 2: Frame Scope, node_id: NodeId(22) 38 | variables: [ y: NodeId(15) ] 39 | ==== TYPES ==== 40 | 0: unknown 41 | 1: unknown 42 | 2: unknown 43 | 3: unknown 44 | 4: int 45 | 5: unknown 46 | 6: string 47 | 7: forbidden 48 | 8: list 49 | 9: list 50 | 10: forbidden 51 | 11: list 52 | 12: list 53 | 13: () 54 | 14: unknown 55 | 15: unknown 56 | 16: unknown 57 | 17: forbidden 58 | 18: list 59 | 19: list 60 | 20: forbidden 61 | 21: list 62 | 22: list 63 | 23: () 64 | 24: () 65 | ==== TYPE ERRORS ==== 66 | Error (NodeId 7): list must have only one type argument (to allow selection of types, use oneof -- WIP) 67 | Error (NodeId 17): list must have one type argument 68 | ==== IR ==== 69 | register_count: 0 70 | file_count: 0 71 | ==== IR ERRORS ==== 72 | Error (NodeId 13): node Def { name: NodeId(0), params: NodeId(10), in_out_types: None, block: NodeId(12) } not suported yet 73 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@let_.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/let_.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Int (8 to 11) "123" 10 | 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 11) 11 | 3: Variable (13 to 15) "$x" 12 | 4: Variable (21 to 22) "x" 13 | 5: Int (25 to 28) "123" 14 | 6: Int (31 to 34) "456" 15 | 7: Pipeline(PipelineId(0)) (25 to 34) 16 | 8: Let { variable_name: NodeId(4), ty: None, initializer: NodeId(7), is_mutable: false } (17 to 34) 17 | 9: Variable (36 to 38) "$x" 18 | 10: Block(BlockId(0)) (0 to 39) 19 | ==== SCOPE ==== 20 | 0: Frame Scope, node_id: NodeId(10) 21 | variables: [ x: NodeId(4) ] 22 | ==== TYPES ==== 23 | 0: int 24 | 1: int 25 | 2: () 26 | 3: int 27 | 4: int 28 | 5: int 29 | 6: int 30 | 7: int 31 | 8: () 32 | 9: int 33 | 10: int 34 | ==== IR ==== 35 | register_count: 0 36 | file_count: 0 37 | ==== IR ERRORS ==== 38 | Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } not suported yet 39 | 40 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@let_mismatch.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/let_mismatch.nu 5 | --- 6 | ==== COMPILER ==== 7 | 0: Variable (4 to 5) "x" 8 | 1: Name (7 to 13) "number" 9 | 2: Type { name: NodeId(1), args: None, optional: false } (7 to 13) 10 | 3: Int (16 to 18) "10" 11 | 4: Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: false } (0 to 18) 12 | 5: Variable (32 to 33) "y" 13 | 6: Name (35 to 38) "any" 14 | 7: Type { name: NodeId(6), args: None, optional: false } (35 to 38) 15 | 8: String (41 to 47) ""spam"" 16 | 9: Let { variable_name: NodeId(5), ty: Some(NodeId(7)), initializer: NodeId(8), is_mutable: false } (28 to 47) 17 | 10: Variable (60 to 61) "z" 18 | 11: Name (63 to 69) "string" 19 | 12: Type { name: NodeId(11), args: None, optional: false } (63 to 69) 20 | 13: Int (72 to 75) "123" 21 | 14: Let { variable_name: NodeId(10), ty: Some(NodeId(12)), initializer: NodeId(13), is_mutable: false } (56 to 75) 22 | 15: Variable (91 to 92) "w" 23 | 16: Name (94 to 98) "list" 24 | 17: Name (99 to 103) "list" 25 | 18: Name (104 to 107) "int" 26 | 19: Type { name: NodeId(18), args: None, optional: false } (104 to 107) 27 | 20: TypeArgs([NodeId(19)]) (103 to 108) 28 | 21: Type { name: NodeId(17), args: Some(NodeId(20)), optional: false } (99 to 103) 29 | 22: TypeArgs([NodeId(21)]) (98 to 109) 30 | 23: Type { name: NodeId(16), args: Some(NodeId(22)), optional: false } (94 to 98) 31 | 24: String (116 to 119) "'a'" 32 | 25: List([NodeId(24)]) (114 to 120) 33 | 26: List([NodeId(25)]) (112 to 122) 34 | 27: Let { variable_name: NodeId(15), ty: Some(NodeId(23)), initializer: NodeId(26), is_mutable: false } (87 to 122) 35 | 28: Variable (128 to 129) "v" 36 | 29: Name (131 to 137) "record" 37 | 30: Name (138 to 139) "a" 38 | 31: Name (141 to 144) "int" 39 | 32: Type { name: NodeId(31), args: None, optional: false } (141 to 144) 40 | 33: Param { name: NodeId(30), ty: Some(NodeId(32)) } (138 to 144) 41 | 34: Params([NodeId(33)]) (137 to 145) 42 | 35: RecordType { fields: NodeId(34), optional: false } (131 to 146) 43 | 36: String (149 to 150) "a" 44 | 37: String (152 to 157) ""foo"" 45 | 38: Record { pairs: [(NodeId(36), NodeId(37))] } (148 to 158) 46 | 39: Let { variable_name: NodeId(28), ty: Some(NodeId(35)), initializer: NodeId(38), is_mutable: false } (124 to 158) 47 | 40: Block(BlockId(0)) (0 to 159) 48 | ==== SCOPE ==== 49 | 0: Frame Scope, node_id: NodeId(40) 50 | variables: [ v: NodeId(28), w: NodeId(15), x: NodeId(0), y: NodeId(5), z: NodeId(10) ] 51 | ==== TYPES ==== 52 | 0: number 53 | 1: unknown 54 | 2: number 55 | 3: int 56 | 4: () 57 | 5: any 58 | 6: unknown 59 | 7: any 60 | 8: string 61 | 9: () 62 | 10: string 63 | 11: unknown 64 | 12: string 65 | 13: int 66 | 14: () 67 | 15: list> 68 | 16: unknown 69 | 17: unknown 70 | 18: unknown 71 | 19: int 72 | 20: forbidden 73 | 21: list 74 | 22: forbidden 75 | 23: list> 76 | 24: string 77 | 25: list 78 | 26: list> 79 | 27: () 80 | 28: record 81 | 29: unknown 82 | 30: unknown 83 | 31: unknown 84 | 32: int 85 | 33: unknown 86 | 34: unknown 87 | 35: record 88 | 36: unknown 89 | 37: string 90 | 38: record 91 | 39: () 92 | 40: () 93 | ==== TYPE ERRORS ==== 94 | Error (NodeId 13): initializer does not match declared type 95 | Error (NodeId 26): initializer does not match declared type 96 | Error (NodeId 38): initializer does not match declared type 97 | ==== IR ==== 98 | register_count: 0 99 | file_count: 0 100 | ==== IR ERRORS ==== 101 | Error (NodeId 4): node Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: false } not suported yet 102 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@list.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/list.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (1 to 2) "1" 9 | 1: Int (4 to 5) "2" 10 | 2: Int (7 to 8) "3" 11 | 3: List([NodeId(0), NodeId(1), NodeId(2)]) (0 to 8) 12 | 4: Int (11 to 12) "0" 13 | 5: Int (13 to 14) "1" 14 | 6: Int (15 to 16) "2" 15 | 7: List([NodeId(4), NodeId(5), NodeId(6)]) (10 to 16) 16 | 8: String (19 to 22) ""0"" 17 | 9: String (23 to 26) ""1"" 18 | 10: String (27 to 30) ""2"" 19 | 11: List([NodeId(8), NodeId(9), NodeId(10)]) (18 to 30) 20 | 12: Int (33 to 34) "0" 21 | 13: Int (35 to 36) "1" 22 | 14: Float (37 to 40) "2.2" 23 | 15: List([NodeId(12), NodeId(13), NodeId(14)]) (32 to 40) 24 | 16: Int (43 to 44) "0" 25 | 17: Int (45 to 46) "1" 26 | 18: String (47 to 50) ""2"" 27 | 19: List([NodeId(16), NodeId(17), NodeId(18)]) (42 to 50) 28 | 20: Block(BlockId(0)) (0 to 52) 29 | ==== SCOPE ==== 30 | 0: Frame Scope, node_id: NodeId(20) (empty) 31 | ==== TYPES ==== 32 | 0: int 33 | 1: int 34 | 2: int 35 | 3: list 36 | 4: int 37 | 5: int 38 | 6: int 39 | 7: list 40 | 8: string 41 | 9: string 42 | 10: string 43 | 11: list 44 | 12: int 45 | 13: int 46 | 14: float 47 | 15: list 48 | 16: int 49 | 17: int 50 | 18: string 51 | 19: list 52 | 20: list 53 | ==== IR ==== 54 | register_count: 0 55 | file_count: 0 56 | ==== IR ERRORS ==== 57 | Error (NodeId 3): node List([NodeId(0), NodeId(1), NodeId(2)]) not suported yet 58 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@literals.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/literals.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: True (1 to 5) 9 | 1: Null (6 to 10) 10 | 2: Int (11 to 12) "1" 11 | 3: String (13 to 16) "abc" 12 | 4: String (17 to 22) ""foo"" 13 | 5: List([NodeId(0), NodeId(1), NodeId(2), NodeId(3), NodeId(4)]) (0 to 22) 14 | 6: Block(BlockId(0)) (0 to 24) 15 | ==== SCOPE ==== 16 | 0: Frame Scope, node_id: NodeId(6) (empty) 17 | ==== TYPES ==== 18 | 0: bool 19 | 1: nothing 20 | 2: int 21 | 3: string 22 | 4: string 23 | 5: list 24 | 6: list 25 | ==== IR ==== 26 | register_count: 0 27 | file_count: 0 28 | ==== IR ERRORS ==== 29 | Error (NodeId 5): node List([NodeId(0), NodeId(1), NodeId(2), NodeId(3), NodeId(4)]) not suported yet 30 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@loop.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/loop.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Int (8 to 9) "0" 10 | 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) 11 | 3: Variable (24 to 26) "$x" 12 | 4: GreaterThan (27 to 28) 13 | 5: Int (29 to 31) "10" 14 | 6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (24 to 31) 15 | 7: Break (42 to 47) 16 | 8: Block(BlockId(0)) (32 to 53) 17 | 9: If { condition: NodeId(6), then_block: NodeId(8), else_block: None } (21 to 53) 18 | 10: Variable (59 to 61) "$x" 19 | 11: AddAssignment (62 to 64) 20 | 12: Int (65 to 66) "1" 21 | 13: BinaryOp { lhs: NodeId(10), op: NodeId(11), rhs: NodeId(12) } (59 to 66) 22 | 14: Block(BlockId(1)) (15 to 68) 23 | 15: Loop { block: NodeId(14) } (10 to 68) 24 | 16: Block(BlockId(2)) (0 to 68) 25 | ==== SCOPE ==== 26 | 0: Frame Scope, node_id: NodeId(16) 27 | variables: [ x: NodeId(0) ] 28 | 1: Frame Scope, node_id: NodeId(14) (empty) 29 | 2: Frame Scope, node_id: NodeId(8) (empty) 30 | ==== TYPES ==== 31 | 0: int 32 | 1: int 33 | 2: () 34 | 3: unknown 35 | 4: unknown 36 | 5: unknown 37 | 6: unknown 38 | 7: unknown 39 | 8: unknown 40 | 9: unknown 41 | 10: unknown 42 | 11: unknown 43 | 12: unknown 44 | 13: unknown 45 | 14: unknown 46 | 15: unknown 47 | 16: unknown 48 | ==== TYPE ERRORS ==== 49 | Error (NodeId 15): unsupported ast node 'Loop { block: NodeId(14) }' in typechecker 50 | ==== IR ==== 51 | register_count: 0 52 | file_count: 0 53 | ==== IR ERRORS ==== 54 | Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } not suported yet 55 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@match.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/match.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Int (8 to 9) "1" 10 | 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: false } (0 to 9) 11 | 3: Variable (15 to 18) "foo" 12 | 4: Variable (27 to 29) "$x" 13 | 5: Int (34 to 35) "1" 14 | 6: String (39 to 44) ""one"" 15 | 7: Int (48 to 49) "2" 16 | 8: Variable (63 to 64) "w" 17 | 9: Int (67 to 68) "3" 18 | 10: Let { variable_name: NodeId(8), ty: None, initializer: NodeId(9), is_mutable: false } (59 to 68) 19 | 11: Int (73 to 74) "2" 20 | 12: Plus (75 to 76) 21 | 13: Variable (77 to 79) "$w" 22 | 14: BinaryOp { lhs: NodeId(11), op: NodeId(12), rhs: NodeId(13) } (73 to 79) 23 | 15: Block(BlockId(0)) (59 to 82) 24 | 16: Closure { params: None, block: NodeId(15) } (53 to 83) 25 | 17: Int (87 to 88) "3" 26 | 18: Null (92 to 96) 27 | 19: String (100 to 101) "_" 28 | 20: Garbage (106 to 107) 29 | 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) 30 | 22: Let { variable_name: NodeId(3), ty: None, initializer: NodeId(21), is_mutable: false } (11 to 110) 31 | 23: Block(BlockId(1)) (0 to 111) 32 | ==== COMPILER ERRORS ==== 33 | Error (NodeId 20): use null instead of () 34 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@math.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/math.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (0 to 1) "3" 9 | 1: Plus (2 to 3) 10 | 2: Int (4 to 5) "4" 11 | 3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 5) 12 | 4: Block(BlockId(0)) (0 to 5) 13 | ==== SCOPE ==== 14 | 0: Frame Scope, node_id: NodeId(4) (empty) 15 | ==== TYPES ==== 16 | 0: int 17 | 1: forbidden 18 | 2: int 19 | 3: int 20 | 4: int 21 | ==== IR ==== 22 | register_count: 2 23 | file_count: 0 24 | 0: LoadLiteral { dst: RegId(0), lit: Int(3) } 25 | 1: LoadLiteral { dst: RegId(1), lit: Int(4) } 26 | 2: BinaryOp { lhs_dst: RegId(0), op: Math(Plus), rhs: RegId(1) } 27 | 3: Return { src: RegId(0) } 28 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@math_precedence.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/math_precedence.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (0 to 1) "1" 9 | 1: Plus (2 to 3) 10 | 2: Int (4 to 5) "2" 11 | 3: Multiply (6 to 7) 12 | 4: Int (8 to 9) "3" 13 | 5: Plus (10 to 11) 14 | 6: Int (12 to 13) "4" 15 | 7: BinaryOp { lhs: NodeId(2), op: NodeId(3), rhs: NodeId(4) } (4 to 9) 16 | 8: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(7) } (0 to 9) 17 | 9: BinaryOp { lhs: NodeId(8), op: NodeId(5), rhs: NodeId(6) } (0 to 13) 18 | 10: Block(BlockId(0)) (0 to 13) 19 | ==== SCOPE ==== 20 | 0: Frame Scope, node_id: NodeId(10) (empty) 21 | ==== TYPES ==== 22 | 0: int 23 | 1: forbidden 24 | 2: int 25 | 3: forbidden 26 | 4: int 27 | 5: forbidden 28 | 6: int 29 | 7: int 30 | 8: int 31 | 9: int 32 | 10: int 33 | ==== IR ==== 34 | register_count: 4 35 | file_count: 0 36 | 0: LoadLiteral { dst: RegId(0), lit: Int(1) } 37 | 1: LoadLiteral { dst: RegId(1), lit: Int(2) } 38 | 2: LoadLiteral { dst: RegId(2), lit: Int(3) } 39 | 3: BinaryOp { lhs_dst: RegId(1), op: Math(Multiply), rhs: RegId(2) } 40 | 4: BinaryOp { lhs_dst: RegId(0), op: Math(Plus), rhs: RegId(1) } 41 | 5: LoadLiteral { dst: RegId(3), lit: Int(4) } 42 | 6: BinaryOp { lhs_dst: RegId(0), op: Math(Plus), rhs: RegId(3) } 43 | 7: Return { src: RegId(0) } 44 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@mut_.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/mut_.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Name (7 to 10) "int" 10 | 2: Type { name: NodeId(1), args: None, optional: false } (7 to 10) 11 | 3: Int (13 to 16) "123" 12 | 4: Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: true } (0 to 16) 13 | 5: Variable (18 to 20) "$x" 14 | 6: Assignment (21 to 22) 15 | 7: Int (23 to 24) "3" 16 | 8: Plus (25 to 26) 17 | 9: Int (27 to 30) "456" 18 | 10: BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) } (23 to 30) 19 | 11: BinaryOp { lhs: NodeId(5), op: NodeId(6), rhs: NodeId(10) } (18 to 30) 20 | 12: Variable (36 to 37) "y" 21 | 13: Name (39 to 42) "int" 22 | 14: Type { name: NodeId(13), args: None, optional: false } (39 to 42) 23 | 15: Int (45 to 48) "123" 24 | 16: Int (51 to 54) "344" 25 | 17: Pipeline(PipelineId(0)) (45 to 54) 26 | 18: Let { variable_name: NodeId(12), ty: Some(NodeId(14)), initializer: NodeId(17), is_mutable: true } (32 to 54) 27 | 19: Variable (56 to 58) "$y" 28 | 20: Assignment (59 to 60) 29 | 21: Int (61 to 62) "3" 30 | 22: Plus (63 to 64) 31 | 23: Int (65 to 66) "1" 32 | 24: BinaryOp { lhs: NodeId(21), op: NodeId(22), rhs: NodeId(23) } (61 to 66) 33 | 25: BinaryOp { lhs: NodeId(19), op: NodeId(20), rhs: NodeId(24) } (56 to 66) 34 | 26: Block(BlockId(0)) (0 to 68) 35 | ==== SCOPE ==== 36 | 0: Frame Scope, node_id: NodeId(26) 37 | variables: [ x: NodeId(0), y: NodeId(12) ] 38 | ==== TYPES ==== 39 | 0: int 40 | 1: unknown 41 | 2: int 42 | 3: int 43 | 4: () 44 | 5: int 45 | 6: forbidden 46 | 7: int 47 | 8: forbidden 48 | 9: int 49 | 10: int 50 | 11: () 51 | 12: int 52 | 13: unknown 53 | 14: int 54 | 15: int 55 | 16: int 56 | 17: int 57 | 18: () 58 | 19: int 59 | 20: forbidden 60 | 21: int 61 | 22: forbidden 62 | 23: int 63 | 24: int 64 | 25: () 65 | 26: () 66 | ==== IR ==== 67 | register_count: 0 68 | file_count: 0 69 | ==== IR ERRORS ==== 70 | Error (NodeId 4): node Let { variable_name: NodeId(0), ty: Some(NodeId(2)), initializer: NodeId(3), is_mutable: true } not suported yet 71 | 72 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@pipeline.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/pipeline.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Int (0 to 1) "1" 9 | 1: Int (4 to 5) "3" 10 | 2: Int (8 to 9) "5" 11 | 3: Pipeline(PipelineId(0)) (0 to 9) 12 | 4: Int (42 to 43) "1" 13 | 5: Int (46 to 47) "2" 14 | 6: Int (50 to 51) "3" 15 | 7: Pipeline(PipelineId(1)) (42 to 51) 16 | 8: Block(BlockId(0)) (0 to 52) 17 | ==== SCOPE ==== 18 | 0: Frame Scope, node_id: NodeId(8) (empty) 19 | ==== TYPES ==== 20 | 0: int 21 | 1: int 22 | 2: int 23 | 3: int 24 | 4: int 25 | 5: int 26 | 6: int 27 | 7: int 28 | 8: int 29 | ==== IR ==== 30 | register_count: 0 31 | file_count: 0 32 | ==== IR ERRORS ==== 33 | Error (NodeId 3): node Pipeline(PipelineId(0)) not suported yet 34 | 35 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@record.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/record.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (1 to 2) "a" 9 | 1: Int (4 to 5) "1" 10 | 2: String (7 to 8) "b" 11 | 3: Int (10 to 11) "2" 12 | 4: Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(3))] } (0 to 12) 13 | 5: Block(BlockId(0)) (0 to 13) 14 | ==== SCOPE ==== 15 | 0: Frame Scope, node_id: NodeId(5) (empty) 16 | ==== TYPES ==== 17 | 0: unknown 18 | 1: int 19 | 2: unknown 20 | 3: int 21 | 4: record 22 | 5: record 23 | ==== IR ==== 24 | register_count: 0 25 | file_count: 0 26 | ==== IR ERRORS ==== 27 | Error (NodeId 4): node Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(3))] } not suported yet 28 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@record2.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/record2.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (1 to 4) ""a"" 9 | 1: Int (6 to 7) "1" 10 | 2: String (9 to 12) ""b"" 11 | 3: Int (14 to 15) "2" 12 | 4: Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(3))] } (0 to 16) 13 | 5: Block(BlockId(0)) (0 to 17) 14 | ==== SCOPE ==== 15 | 0: Frame Scope, node_id: NodeId(5) (empty) 16 | ==== TYPES ==== 17 | 0: unknown 18 | 1: int 19 | 2: unknown 20 | 3: int 21 | 4: record<"a": int, "b": int> 22 | 5: record<"a": int, "b": int> 23 | ==== IR ==== 24 | register_count: 0 25 | file_count: 0 26 | ==== IR ERRORS ==== 27 | Error (NodeId 4): node Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(3))] } not suported yet 28 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@record3.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/record3.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (2 to 3) "a" 9 | 1: Int (5 to 6) "1" 10 | 2: String (9 to 10) "b" 11 | 3: Int (12 to 13) "2" 12 | 4: Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(3))] } (0 to 16) 13 | 5: Block(BlockId(0)) (0 to 17) 14 | ==== SCOPE ==== 15 | 0: Frame Scope, node_id: NodeId(5) (empty) 16 | ==== TYPES ==== 17 | 0: unknown 18 | 1: int 19 | 2: unknown 20 | 3: int 21 | 4: record 22 | 5: record 23 | ==== IR ==== 24 | register_count: 0 25 | file_count: 0 26 | ==== IR ERRORS ==== 27 | Error (NodeId 4): node Record { pairs: [(NodeId(0), NodeId(1)), (NodeId(2), NodeId(3))] } not suported yet 28 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@reparse.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/reparse.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Name (10 to 11) "a" 10 | 2: Param { name: NodeId(1), ty: None } (10 to 11) 11 | 3: Params([NodeId(2)]) (9 to 12) 12 | 4: Variable (13 to 15) "$a" 13 | 5: Block(BlockId(0)) (13 to 16) 14 | 6: Closure { params: Some(NodeId(3)), block: NodeId(5) } (8 to 17) 15 | 7: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(6), is_mutable: false } (0 to 17) 16 | 8: Variable (22 to 23) "y" 17 | 9: String (28 to 29) "a" 18 | 10: String (31 to 32) "b" 19 | 11: Record { pairs: [(NodeId(9), NodeId(10))] } (26 to 34) 20 | 12: Let { variable_name: NodeId(8), ty: None, initializer: NodeId(11), is_mutable: false } (18 to 34) 21 | 13: Block(BlockId(1)) (0 to 34) 22 | ==== SCOPE ==== 23 | 0: Frame Scope, node_id: NodeId(13) 24 | variables: [ x: NodeId(0), y: NodeId(8) ] 25 | 1: Frame Scope, node_id: NodeId(5) 26 | variables: [ a: NodeId(1) ] 27 | ==== TYPES ==== 28 | 0: closure 29 | 1: unknown 30 | 2: any 31 | 3: forbidden 32 | 4: unknown 33 | 5: unknown 34 | 6: closure 35 | 7: () 36 | 8: record 37 | 9: unknown 38 | 10: string 39 | 11: record 40 | 12: () 41 | 13: () 42 | ==== IR ==== 43 | register_count: 0 44 | file_count: 0 45 | ==== IR ERRORS ==== 46 | Error (NodeId 7): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(6), is_mutable: false } not suported yet 47 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@string.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/string.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (0 to 13) ""hello world"" 9 | 1: String (14 to 27) "'hello world'" 10 | 2: Block(BlockId(0)) (0 to 27) 11 | ==== SCOPE ==== 12 | 0: Frame Scope, node_id: NodeId(2) (empty) 13 | ==== TYPES ==== 14 | 0: string 15 | 1: string 16 | 2: string 17 | ==== IR ==== 18 | register_count: 0 19 | file_count: 0 20 | ==== IR ERRORS ==== 21 | Error (NodeId 0): node String not suported yet 22 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@string_operation.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/string_operation.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (0 to 5) ""abc"" 9 | 1: Plus (6 to 7) 10 | 2: String (8 to 13) ""def"" 11 | 3: BinaryOp { lhs: NodeId(0), op: NodeId(1), rhs: NodeId(2) } (0 to 13) 12 | 4: Block(BlockId(0)) (0 to 13) 13 | ==== SCOPE ==== 14 | 0: Frame Scope, node_id: NodeId(4) (empty) 15 | ==== TYPES ==== 16 | 0: string 17 | 1: forbidden 18 | 2: string 19 | 3: string 20 | 4: string 21 | ==== IR ==== 22 | register_count: 0 23 | file_count: 0 24 | ==== IR ERRORS ==== 25 | Error (NodeId 0): node String not suported yet 26 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@table.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/table.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (7 to 10) ""a"" 9 | 1: String (12 to 15) ""b"" 10 | 2: List([NodeId(0), NodeId(1)]) (6 to 15) 11 | 3: Int (24 to 25) "1" 12 | 4: Int (27 to 28) "2" 13 | 5: List([NodeId(3), NodeId(4)]) (23 to 28) 14 | 6: Int (35 to 36) "3" 15 | 7: Int (38 to 39) "4" 16 | 8: List([NodeId(6), NodeId(7)]) (34 to 39) 17 | 9: Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } (0 to 41) 18 | 10: Block(BlockId(0)) (0 to 42) 19 | ==== SCOPE ==== 20 | 0: Frame Scope, node_id: NodeId(10) (empty) 21 | ==== TYPES ==== 22 | 0: unknown 23 | 1: unknown 24 | 2: unknown 25 | 3: unknown 26 | 4: unknown 27 | 5: unknown 28 | 6: unknown 29 | 7: unknown 30 | 8: unknown 31 | 9: unknown 32 | 10: unknown 33 | ==== TYPE ERRORS ==== 34 | Error (NodeId 9): unsupported ast node 'Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }' in typechecker 35 | ==== IR ==== 36 | register_count: 0 37 | file_count: 0 38 | ==== IR ERRORS ==== 39 | Error (NodeId 9): node Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } not suported yet 40 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@table2.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/table2.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: String (7 to 8) "a" 9 | 1: String (10 to 11) "b" 10 | 2: List([NodeId(0), NodeId(1)]) (6 to 11) 11 | 3: Int (20 to 21) "1" 12 | 4: Int (23 to 24) "2" 13 | 5: List([NodeId(3), NodeId(4)]) (19 to 24) 14 | 6: Int (31 to 32) "3" 15 | 7: Int (34 to 35) "4" 16 | 8: List([NodeId(6), NodeId(7)]) (30 to 35) 17 | 9: Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } (0 to 37) 18 | 10: Block(BlockId(0)) (0 to 38) 19 | ==== SCOPE ==== 20 | 0: Frame Scope, node_id: NodeId(10) (empty) 21 | ==== TYPES ==== 22 | 0: unknown 23 | 1: unknown 24 | 2: unknown 25 | 3: unknown 26 | 4: unknown 27 | 5: unknown 28 | 6: unknown 29 | 7: unknown 30 | 8: unknown 31 | 9: unknown 32 | 10: unknown 33 | ==== TYPE ERRORS ==== 34 | Error (NodeId 9): unsupported ast node 'Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] }' in typechecker 35 | ==== IR ==== 36 | register_count: 0 37 | file_count: 0 38 | ==== IR ERRORS ==== 39 | Error (NodeId 9): node Table { header: NodeId(2), rows: [NodeId(5), NodeId(8)] } not suported yet 40 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@variable_names.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/variable_names.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (0 to 4) "$abc" 9 | 1: Variable (5 to 7) "$_" 10 | 2: Variable (8 to 12) "$a_c" 11 | 3: Garbage (14 to 17) 12 | 4: Block(BlockId(0)) (0 to 18) 13 | ==== COMPILER ERRORS ==== 14 | Error (NodeId 3): variable name must be a bareword 15 | -------------------------------------------------------------------------------- /src/snapshots/new_nu_parser__test__node_output@while.nu.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/test.rs 3 | expression: evaluate_example(path) 4 | input_file: tests/while.nu 5 | snapshot_kind: text 6 | --- 7 | ==== COMPILER ==== 8 | 0: Variable (4 to 5) "x" 9 | 1: Int (8 to 9) "0" 10 | 2: Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } (0 to 9) 11 | 3: Int (16 to 17) "1" 12 | 4: LessThan (18 to 19) 13 | 5: Int (20 to 21) "2" 14 | 6: BinaryOp { lhs: NodeId(3), op: NodeId(4), rhs: NodeId(5) } (16 to 21) 15 | 7: Variable (26 to 28) "$x" 16 | 8: AddAssignment (29 to 31) 17 | 9: Int (32 to 33) "1" 18 | 10: BinaryOp { lhs: NodeId(7), op: NodeId(8), rhs: NodeId(9) } (26 to 33) 19 | 11: Block(BlockId(0)) (22 to 35) 20 | 12: While { condition: NodeId(6), block: NodeId(11) } (10 to 35) 21 | 13: Block(BlockId(1)) (0 to 36) 22 | ==== SCOPE ==== 23 | 0: Frame Scope, node_id: NodeId(13) 24 | variables: [ x: NodeId(0) ] 25 | 1: Frame Scope, node_id: NodeId(11) (empty) 26 | ==== TYPES ==== 27 | 0: int 28 | 1: int 29 | 2: () 30 | 3: int 31 | 4: forbidden 32 | 5: int 33 | 6: bool 34 | 7: int 35 | 8: forbidden 36 | 9: int 37 | 10: () 38 | 11: () 39 | 12: () 40 | 13: () 41 | ==== IR ==== 42 | register_count: 0 43 | file_count: 0 44 | ==== IR ERRORS ==== 45 | Error (NodeId 2): node Let { variable_name: NodeId(0), ty: None, initializer: NodeId(1), is_mutable: true } not suported yet 46 | -------------------------------------------------------------------------------- /src/test.rs: -------------------------------------------------------------------------------- 1 | use crate::ir_generator::IrGenerator; 2 | use crate::lexer::lex; 3 | use crate::resolver::Resolver; 4 | use crate::typechecker::Typechecker; 5 | use crate::{compiler::Compiler, parser::Parser}; 6 | 7 | use std::path::Path; 8 | 9 | fn evaluate_example(fname: &Path) -> String { 10 | let mut compiler = Compiler::new(); 11 | let contents = std::fs::read(fname).expect("We only run tests found by glob"); 12 | 13 | let span_offset = compiler.span_offset(); 14 | compiler.add_file(&fname.to_string_lossy(), &contents); 15 | 16 | let (tokens, err) = lex(&contents, span_offset); 17 | if let Err(e) = err { 18 | tokens.eprint(&contents); 19 | eprintln!("Lexing error. Error: {:?}", e); 20 | std::process::exit(1); 21 | } 22 | 23 | let parser = Parser::new(compiler, tokens); 24 | compiler = parser.parse(); 25 | 26 | let mut result = compiler.display_state(); 27 | 28 | if !compiler.errors.is_empty() { 29 | return result; 30 | } 31 | 32 | let mut resolver = Resolver::new(&compiler); 33 | resolver.resolve(); 34 | result.push_str(&resolver.display_state()); 35 | 36 | compiler.merge_name_bindings(resolver.to_name_bindings()); 37 | 38 | if !compiler.errors.is_empty() { 39 | return result; 40 | } 41 | 42 | let mut typechecker = Typechecker::new(&compiler); 43 | typechecker.typecheck(); 44 | result.push_str(&typechecker.display_state()); 45 | 46 | compiler.merge_types(typechecker.to_types()); 47 | 48 | let mut ir_generator = IrGenerator::new(&compiler); 49 | ir_generator.generate(); 50 | result.push_str(&ir_generator.display_state()); 51 | 52 | result 53 | } 54 | 55 | fn evaluate_lexer(fname: &Path) -> String { 56 | let contents = std::fs::read(fname); 57 | 58 | let Ok(contents) = contents else { 59 | panic!("Lexer: can't find file {}", fname.to_string_lossy()); 60 | }; 61 | 62 | let (tokens, err) = lex(&contents, 0); 63 | let mut res = tokens.display(&contents); 64 | 65 | if let Err(e) = err { 66 | res.push_str(&format!("Lexing error. Error: {:?}", e)); 67 | } 68 | 69 | res 70 | } 71 | 72 | #[test] 73 | fn test_node_output() { 74 | insta::glob!("../tests", "*.nu", |path| { 75 | insta::assert_snapshot!(evaluate_example(path)); 76 | }); 77 | } 78 | 79 | #[test] 80 | fn test_lexer() { 81 | insta::glob!("../tests/lex", "*.nu", |path| { 82 | insta::assert_snapshot!(evaluate_lexer(path)); 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /tests/alias.nu: -------------------------------------------------------------------------------- 1 | alias "fancy alias" = foo 2 | 3 | fancy alias 4 | -------------------------------------------------------------------------------- /tests/binary_ops_exact.nu: -------------------------------------------------------------------------------- 1 | 1 == 1 2 | [true] ++ false 3 | 1 + 1 4 | 1.0 + 1.0 5 | true and false 6 | "foo" =~ ".*o" 7 | 1 in [1, 2] 8 | -------------------------------------------------------------------------------- /tests/binary_ops_mismatch.nu: -------------------------------------------------------------------------------- 1 | "a" + 1.0 2 | "a" ++ 1.0 3 | true and "a" 4 | true !~ "true" 5 | -------------------------------------------------------------------------------- /tests/binary_ops_spaces.nu: -------------------------------------------------------------------------------- 1 | 1 + 2 2 | 1+ 2 3 | 1 +2 4 | 1+2 5 | -------------------------------------------------------------------------------- /tests/binary_ops_subtypes.nu: -------------------------------------------------------------------------------- 1 | 1 == 1.0 2 | "a" == 1.0 3 | 1 + 1.0 4 | [1] ++ 1.0 5 | [1.0 1] ++ "a" 6 | [[1] [2]] ++ [[3]] 7 | [[1] [2]] ++ [[3.0]] 8 | 1 in [1.0, 1] 9 | 2.3 mod 1 10 | [{b: 2, c: 3}] ++ [{a: 3, b: 1.5, c: "foo"}] 11 | -------------------------------------------------------------------------------- /tests/calls.nu: -------------------------------------------------------------------------------- 1 | spam foo "bar" (1 + 2) 2 | 3 | def existing [a: string, b: string, c: int] { [ $a, $b, $c] } 4 | existing foo ("ba" + "r") 3 5 | 6 | foo/bar/spam 7 | -------------------------------------------------------------------------------- /tests/closure.nu: -------------------------------------------------------------------------------- 1 | { |a, b| $a + $b} 2 | $a # doesn't leak scope -------------------------------------------------------------------------------- /tests/closure2.nu: -------------------------------------------------------------------------------- 1 | { 2 | $a + $b 3 | } 4 | -------------------------------------------------------------------------------- /tests/closure3.nu: -------------------------------------------------------------------------------- 1 | let closure = {|a :int, b :int| $a + $b < 5} 2 | 3 | filter $closure 4 | -------------------------------------------------------------------------------- /tests/def.nu: -------------------------------------------------------------------------------- 1 | def foo [w x: int, y: list>, z: record ] { [ $w $x, $y, $z ] } -------------------------------------------------------------------------------- /tests/def_return_type.nu: -------------------------------------------------------------------------------- 1 | def foo [ ] : nothing -> list { [] } 2 | def bar [ ] : [ string -> list, int -> list ] { [] } 3 | -------------------------------------------------------------------------------- /tests/for.nu: -------------------------------------------------------------------------------- 1 | mut x = 0 2 | for i in [1 2 3] { 3 | $x = $x + $i 4 | } 5 | 6 | for $i in [4 5 6] { 7 | $x = $x + $i 8 | } 9 | -------------------------------------------------------------------------------- /tests/for_break_continue.nu: -------------------------------------------------------------------------------- 1 | mut x = 0 2 | for i in [1 2 3] { 3 | if $x > 2 { 4 | break 5 | } 6 | 7 | if $i < 3 { 8 | continue 9 | } 10 | 11 | $x = $x + $i 12 | } -------------------------------------------------------------------------------- /tests/if_.nu: -------------------------------------------------------------------------------- 1 | let x = 123 2 | 3 | if $x < 100 { 4 | 5 5 | } else if $x > 200 { 6 | 6 7 | } else { 8 | 7 9 | } 10 | -------------------------------------------------------------------------------- /tests/invalid_if.nu: -------------------------------------------------------------------------------- 1 | if 1 { 2 | 4 3 | } else { 4 | 3 5 | } 6 | -------------------------------------------------------------------------------- /tests/invalid_range.nu: -------------------------------------------------------------------------------- 1 | 1 .. 2 2 | -------------------------------------------------------------------------------- /tests/invalid_record.nu: -------------------------------------------------------------------------------- 1 | { 2 | a: 1, 3 | b 4 | } 5 | -------------------------------------------------------------------------------- /tests/invalid_types.nu: -------------------------------------------------------------------------------- 1 | def foo [x: list] { $x } 2 | def bar [y: list<>] { $y } 3 | -------------------------------------------------------------------------------- /tests/let_.nu: -------------------------------------------------------------------------------- 1 | let x = 123 2 | 3 | $x 4 | 5 | let x = 123 | 456 6 | 7 | $x 8 | -------------------------------------------------------------------------------- /tests/let_mismatch.nu: -------------------------------------------------------------------------------- 1 | let x: number = 10 # match 2 | let y: any = "spam" # match 3 | let z: string = 123 # mismatch 4 | let w: list> = [ [ 'a' ] ] 5 | let v: record = {a: "foo"} 6 | -------------------------------------------------------------------------------- /tests/lex/bareword.nu: -------------------------------------------------------------------------------- 1 | _ 2 | _0 3 | foo bar 4 | foo.bar 5 | -------------------------------------------------------------------------------- /tests/lex/comment.nu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nu 2 | # comment 3 | #comment 4 | # 5 | command # comment 6 | command #comment 7 | command#call 8 | command# call 9 | command #comment 10 | -------------------------------------------------------------------------------- /tests/lex/datetime.nu: -------------------------------------------------------------------------------- 1 | 2020-12-20 2 | 2020-12-20T12:23:34 3 | 2020-12-20T12:23:34.456 4 | 2020-12-20T12:23:34.456Z 5 | 2020-12-20T12:23:34.456+02:00 6 | 2020-12-20T12:23:34.456-02:00 7 | 2020-12-20T12:23:34+02:00 8 | -------------------------------------------------------------------------------- /tests/lex/dq_string_interp.nu: -------------------------------------------------------------------------------- 1 | $"" 2 | $"foo" 3 | $"foo\(" 4 | $"foo\()" 5 | $"foo)" 6 | $"(1)bar" 7 | $"foo(1)bar" 8 | $"foo((1))bar" 9 | $"foo(1 + (2 + 3))bar" 10 | $"foo('baz')bar" 11 | $"foo()bar" 12 | $"foo($baz)bar" 13 | $"escapes\"" 14 | $"esc\"apes" 15 | $"foo($'(1 + (2 + 3))')bar" 16 | -------------------------------------------------------------------------------- /tests/lex/float.nu: -------------------------------------------------------------------------------- 1 | 1.0 2 | 01.10 3 | 1.0e1 4 | 1.0e01 5 | .2 6 | 2. 7 | .3e3 8 | 3.e3 9 | .1e-10 10 | .2e+20 11 | 45_67.8_9 12 | 45_.8_ 13 | _.3 14 | _44.44 15 | 5._ 16 | _._ 17 | -------------------------------------------------------------------------------- /tests/lex/int.nu: -------------------------------------------------------------------------------- 1 | 0 2 | 00 3 | 0x123 4 | 0b101 5 | 0_ 6 | 0bo 7 | 0x 8 | 01x10 9 | -------------------------------------------------------------------------------- /tests/lex/raw_string.nu: -------------------------------------------------------------------------------- 1 | r#'aabb'# 2 | r##'aa 3 | '# 4 | bb'## 5 | r####'aa 6 | bb 7 | cc'##dd 8 | ### 9 | ddd'#### 10 | -------------------------------------------------------------------------------- /tests/lex/sq_string_interp.nu: -------------------------------------------------------------------------------- 1 | $'' 2 | $'foo' 3 | $'foo)' 4 | $'(1)bar' 5 | $'foo(1)bar' 6 | $'foo((1))bar' 7 | $'foo(1 + (3 + 4))bar' 8 | $'foo()bar' 9 | $'foo($"(1 + (2 + 3))")bar' 10 | -------------------------------------------------------------------------------- /tests/list.nu: -------------------------------------------------------------------------------- 1 | [1, 2, 3] 2 | [0 1 2] 3 | ["0" "1" "2"] 4 | [0 1 2.2] 5 | [0 1 "2"] 6 | -------------------------------------------------------------------------------- /tests/literals.nu: -------------------------------------------------------------------------------- 1 | [true null 1 abc "foo"] 2 | -------------------------------------------------------------------------------- /tests/loop.nu: -------------------------------------------------------------------------------- 1 | mut x = 0 2 | loop { 3 | if $x > 10 { 4 | break 5 | } 6 | 7 | $x += 1 8 | } -------------------------------------------------------------------------------- /tests/match.nu: -------------------------------------------------------------------------------- 1 | let x = 1 2 | 3 | let foo = match $x { 4 | 1 => "one", 5 | 2 => { 6 | let w = 3 7 | 2 + $w 8 | }, 9 | 3 => null, 10 | _ => (), 11 | } 12 | -------------------------------------------------------------------------------- /tests/math.nu: -------------------------------------------------------------------------------- 1 | 3 + 4 -------------------------------------------------------------------------------- /tests/math_precedence.nu: -------------------------------------------------------------------------------- 1 | 1 + 2 * 3 + 4 -------------------------------------------------------------------------------- /tests/mut_.nu: -------------------------------------------------------------------------------- 1 | mut x: int = 123 2 | 3 | $x = 3 + 456 4 | 5 | mut y: int = 123 | 344 6 | 7 | $y = 3 + 1 8 | -------------------------------------------------------------------------------- /tests/pipeline.nu: -------------------------------------------------------------------------------- 1 | 1 | 3 | 5 2 | 3 | # test pipeline with multiline 4 | 1 | 5 | 2 | 6 | 3 7 | -------------------------------------------------------------------------------- /tests/record.nu: -------------------------------------------------------------------------------- 1 | {a: 1, b: 2} 2 | -------------------------------------------------------------------------------- /tests/record2.nu: -------------------------------------------------------------------------------- 1 | {"a": 1, "b": 2} 2 | -------------------------------------------------------------------------------- /tests/record3.nu: -------------------------------------------------------------------------------- 1 | { 2 | a: 1, 3 | b: 2, 4 | } 5 | -------------------------------------------------------------------------------- /tests/reparse.nu: -------------------------------------------------------------------------------- 1 | let x = {|a| $a } 2 | let y = { a: b } -------------------------------------------------------------------------------- /tests/string.nu: -------------------------------------------------------------------------------- 1 | "hello world" 2 | 'hello world' -------------------------------------------------------------------------------- /tests/string_operation.nu: -------------------------------------------------------------------------------- 1 | "abc" + "def" -------------------------------------------------------------------------------- /tests/table.nu: -------------------------------------------------------------------------------- 1 | [ 2 | ["a", "b"]; 3 | [1, 2] 4 | [3, 4] 5 | ] -------------------------------------------------------------------------------- /tests/table2.nu: -------------------------------------------------------------------------------- 1 | [ 2 | [a, b]; 3 | [1, 2] 4 | [3, 4] 5 | ] -------------------------------------------------------------------------------- /tests/variable_names.nu: -------------------------------------------------------------------------------- 1 | $abc 2 | $_ 3 | $a_c 4 | $234 5 | -------------------------------------------------------------------------------- /tests/while.nu: -------------------------------------------------------------------------------- 1 | mut x = 0 2 | while 1 < 2 { 3 | $x += 1 4 | } 5 | -------------------------------------------------------------------------------- /toolkit.nu: -------------------------------------------------------------------------------- 1 | # Prints input string with highlighted span 2 | # 3 | # Useful for debugging parser output 4 | export def span [span_start: int, span_end: int]: string -> nothing { 5 | let s = $in | encode utf8 6 | 7 | let pre = $s | bytes at ..($span_start - 1) | decode 8 | let highlighted = $s | bytes at $span_start..($span_end - 1) | decode 9 | let post = $s | bytes at ($span_end).. | decode 10 | 11 | print ($pre + $'(ansi ru)($highlighted)(ansi reset)' + $post) 12 | } 13 | 14 | # Runs the parser on all nu scripts for given list of files and directories. 15 | # When directory is passed, all *.nu scripts are found recursively. 16 | # 17 | # Returns a table with a row for every file that was parsed, the exit_code of 18 | # the parser and the time it took to parse the source. 19 | # 20 | # Example usage: 21 | # $ parse-all example.nu ../nu_scripts/ 22 | export def parse-all [ 23 | --verbose (-v)=false # Print the file name before parsing (useful when parser hangs) 24 | --binary (-b)='./target/debug/new-nu-parser' # The path to new-nu-parser binary 25 | ...sources: string # The list of nu scripts and directories to parse 26 | ] (nothing -> list>) { 27 | let files = $sources | each { |$source| 28 | if ($source | path type) == "dir" { 29 | glob ($source | path join "**/*.nu") 30 | } else { 31 | [$source] 32 | } 33 | } | flatten 34 | $files | each { |file| 35 | if $verbose { 36 | print $"Analyzing ($file) ..." 37 | } 38 | mut output = {} 39 | let time = timeit { 40 | let ret = (^$"($binary)" $file | complete) 41 | $output = $ret 42 | } 43 | { 44 | file: $file, 45 | exit_code: $output.exit_code 46 | time: $time, 47 | } 48 | } 49 | } 50 | 51 | # Summarizes the output produced by `parse-all` command. 52 | # 53 | # Example usage: 54 | # $ parse-all ../nu_scripts/ | summary 55 | export def summary [] (list> -> string) { 56 | let report = $in; 57 | let errors = $report | where exit_code == 1 58 | let crashes = $report | where exit_code != 0 and exit_code != 1 59 | let slowest = $report | sort-by time | last 60 | 61 | print $"Total files: ($report | length)" 62 | print $"Errors: ($report | where exit_code == 1 | length)" 63 | print $"Crashes: ($report | where exit_code != 1 and exit_code != 0 | length)" 64 | print $"Successes: ($report | where exit_code == 0 | length)" 65 | if ($errors | length) > 0 { 66 | print $"Sample error file: ($errors | first | get file)" 67 | } 68 | if ($crashes | length) > 0 { 69 | print $"Sample crash file: ($crashes | first | get file)" 70 | } 71 | print $"Slowest file: ($slowest | get time) ($slowest | get file)" 72 | } 73 | --------------------------------------------------------------------------------