From 7a08601831b510477f5b855c8ca00c9ec76b9f7f Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Fri, 25 Oct 2019 17:25:15 +0200 Subject: [PATCH] feat: add comparison operators This commit adds the >,>=,<=,<,==,!= operators to the language. It also contains a rewrite of how tests are performed so that instead of using *.mun files, the test are written in Rust which enables testing of individual test cases with a little more ease. --- ci/azure-coverage.yml | 2 +- ci/azure-test.yml | 2 +- crates/mun_codegen/Cargo.toml | 4 +- crates/mun_codegen/src/ir/function.rs | 55 ++++- .../snapshots/test__binary_expressions.snap} | 5 + .../snapshots/test__equality_operands.snap | 79 +++++++ .../snapshots/test__function.snap} | 5 + .../snapshots/test__function_arguments.snap} | 5 + .../snapshots/test__function_calls.snap} | 5 + .../snapshots/test__invalid_binary_ops.snap | 6 + .../snapshots/test__let_statement.snap} | 5 + .../snapshots/test__return_type.snap} | 5 + .../snapshots/test__update_operators.snap} | 5 + crates/mun_codegen/src/test.rs | 217 +++++++++++++++--- .../tests/data/ir/0001_function.mun | 2 - .../tests/data/ir/0002_return_type.mun | 3 - .../tests/data/ir/0003_function_arguments.mun | 3 - .../tests/data/ir/0004_binary_expressions.mun | 11 - .../tests/data/ir/0005_let_statement.mun | 4 - .../tests/data/ir/0006_invalid_binary_ops.mun | 4 - .../tests/data/ir/0006_invalid_binary_ops.txt | 2 - .../tests/data/ir/0007_update_operators.mun | 17 -- .../tests/data/ir/0008_function_calls.mun | 13 -- crates/mun_compiler/src/lib.rs | 1 + crates/mun_hir/src/code_model.rs | 2 + crates/mun_hir/src/expr.rs | 30 ++- crates/mun_hir/src/lib.rs | 2 +- crates/mun_hir/src/name.rs | 1 + crates/mun_hir/src/ty/lower.rs | 1 + crates/mun_hir/src/ty/op.rs | 4 +- crates/mun_runtime/src/test.rs | 121 ++++++++++ crates/mun_syntax/Cargo.toml | 2 +- crates/mun_syntax/src/ast/expr_extensions.rs | 32 ++- crates/mun_syntax/src/ast/extensions.rs | 4 +- crates/mun_syntax/src/grammar.ron | 3 +- crates/mun_syntax/src/lib.rs | 2 +- crates/mun_syntax/src/parsing.rs | 37 ++- crates/mun_syntax/src/parsing/grammar.rs | 18 +- .../src/parsing/grammar/declarations.rs | 22 +- .../src/parsing/grammar/expressions.rs | 119 ++++------ .../mun_syntax/src/parsing/grammar/params.rs | 14 +- .../mun_syntax/src/parsing/grammar/paths.rs | 12 +- .../src/parsing/grammar/patterns.rs | 4 +- .../mun_syntax/src/parsing/grammar/types.rs | 2 +- crates/mun_syntax/src/parsing/parser.rs | 197 +++++++++------- .../src/parsing/text_token_source.rs | 83 +++++-- crates/mun_syntax/src/parsing/token_set.rs | 2 +- .../mun_syntax/src/syntax_kind/generated.rs | 13 +- crates/mun_syntax/src/tests.rs | 45 +--- crates/mun_syntax/src/tests/lexer.rs | 137 +++++++++++ crates/mun_syntax/src/tests/parser.rs | 141 ++++++++++++ .../tests/snapshots/lexer__binary_cmp.snap | 28 +++ .../tests/snapshots/lexer__comments.snap} | 5 + .../tests/snapshots/lexer__ident.snap} | 5 + .../tests/snapshots/lexer__keywords.snap} | 7 +- .../tests/snapshots/lexer__numbers.snap} | 7 +- .../tests/snapshots/lexer__strings.snap} | 5 + .../tests/snapshots/lexer__symbols.snap} | 8 +- .../snapshots/lexer__unclosed_string.snap | 6 + .../tests/snapshots/lexer__whitespace.snap} | 5 + .../tests/snapshots/parser__binary_expr.snap} | 5 + .../tests/snapshots/parser__block.snap} | 5 + .../snapshots/parser__compare_operands.snap | 185 +++++++++++++++ .../src/tests/snapshots/parser__empty.snap | 6 + .../parser__expression_statement.snap} | 84 +++---- .../tests/snapshots/parser__function.snap} | 5 + .../snapshots/parser__function_calls.snap} | 5 + .../tests/snapshots/parser__literals.snap} | 18 +- .../tests/snapshots/parser__patterns.snap} | 5 + .../tests/snapshots/parser__unary_expr.snap} | 33 +-- .../tests/data/lexer/0001_numbers.mun | 5 - .../tests/data/lexer/0002_comments.mun | 8 - .../tests/data/lexer/0003_whitespace.mun | 4 - .../tests/data/lexer/0004_ident.mun | 1 - .../tests/data/lexer/0005_symbols.mun | 13 -- .../tests/data/lexer/0006_strings.mun | 6 - .../tests/data/lexer/0007_keywords.mun | 3 - .../tests/data/lexer/0008_unclosed_string.mun | 1 - .../tests/data/lexer/0008_unclosed_string.txt | 1 - .../tests/data/parser/ok/0000_empty.mun | 0 .../tests/data/parser/ok/0000_empty.txt | 1 - .../tests/data/parser/ok/0001_function.mun | 7 - .../tests/data/parser/ok/0002_block.mun | 5 - .../tests/data/parser/ok/0003_literals.mun | 7 - .../tests/data/parser/ok/0004_unary_expr.mun | 4 - .../tests/data/parser/ok/0005_binary_expr.mun | 4 - .../tests/data/parser/ok/0006_expr_stmt.mun | 8 - .../tests/data/parser/ok/0007_fn_calls.mun | 4 - .../tests/data/parser/ok/0008_patterns.mun | 4 - crates/test_utils/Cargo.toml | 8 - crates/test_utils/src/lib.rs | 108 --------- 91 files changed, 1468 insertions(+), 671 deletions(-) rename crates/mun_codegen/{tests/data/ir/0004_binary_expressions.txt => src/snapshots/test__binary_expressions.snap} (62%) create mode 100644 crates/mun_codegen/src/snapshots/test__equality_operands.snap rename crates/mun_codegen/{tests/data/ir/0001_function.txt => src/snapshots/test__function.snap} (55%) rename crates/mun_codegen/{tests/data/ir/0003_function_arguments.txt => src/snapshots/test__function_arguments.snap} (52%) rename crates/mun_codegen/{tests/data/ir/0008_function_calls.txt => src/snapshots/test__function_calls.snap} (84%) create mode 100644 crates/mun_codegen/src/snapshots/test__invalid_binary_ops.snap rename crates/mun_codegen/{tests/data/ir/0005_let_statement.txt => src/snapshots/test__let_statement.snap} (54%) rename crates/mun_codegen/{tests/data/ir/0002_return_type.txt => src/snapshots/test__return_type.snap} (52%) rename crates/mun_codegen/{tests/data/ir/0007_update_operators.txt => src/snapshots/test__update_operators.snap} (50%) delete mode 100644 crates/mun_codegen/tests/data/ir/0001_function.mun delete mode 100644 crates/mun_codegen/tests/data/ir/0002_return_type.mun delete mode 100644 crates/mun_codegen/tests/data/ir/0003_function_arguments.mun delete mode 100644 crates/mun_codegen/tests/data/ir/0004_binary_expressions.mun delete mode 100644 crates/mun_codegen/tests/data/ir/0005_let_statement.mun delete mode 100644 crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.mun delete mode 100644 crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.txt delete mode 100644 crates/mun_codegen/tests/data/ir/0007_update_operators.mun delete mode 100644 crates/mun_codegen/tests/data/ir/0008_function_calls.mun create mode 100644 crates/mun_syntax/src/tests/lexer.rs create mode 100644 crates/mun_syntax/src/tests/parser.rs create mode 100644 crates/mun_syntax/src/tests/snapshots/lexer__binary_cmp.snap rename crates/mun_syntax/{tests/data/lexer/0002_comments.txt => src/tests/snapshots/lexer__comments.snap} (60%) rename crates/mun_syntax/{tests/data/lexer/0004_ident.txt => src/tests/snapshots/lexer__ident.snap} (66%) rename crates/mun_syntax/{tests/data/lexer/0007_keywords.txt => src/tests/snapshots/lexer__keywords.snap} (79%) rename crates/mun_syntax/{tests/data/lexer/0001_numbers.txt => src/tests/snapshots/lexer__numbers.snap} (59%) rename crates/mun_syntax/{tests/data/lexer/0006_strings.txt => src/tests/snapshots/lexer__strings.snap} (58%) rename crates/mun_syntax/{tests/data/lexer/0005_symbols.txt => src/tests/snapshots/lexer__symbols.snap} (85%) create mode 100644 crates/mun_syntax/src/tests/snapshots/lexer__unclosed_string.snap rename crates/mun_syntax/{tests/data/lexer/0003_whitespace.txt => src/tests/snapshots/lexer__whitespace.snap} (72%) rename crates/mun_syntax/{tests/data/parser/ok/0005_binary_expr.txt => src/tests/snapshots/parser__binary_expr.snap} (93%) rename crates/mun_syntax/{tests/data/parser/ok/0002_block.txt => src/tests/snapshots/parser__block.snap} (91%) create mode 100644 crates/mun_syntax/src/tests/snapshots/parser__compare_operands.snap create mode 100644 crates/mun_syntax/src/tests/snapshots/parser__empty.snap rename crates/mun_syntax/{tests/data/parser/ok/0006_expr_stmt.txt => src/tests/snapshots/parser__expression_statement.snap} (51%) rename crates/mun_syntax/{tests/data/parser/ok/0001_function.txt => src/tests/snapshots/parser__function.snap} (91%) rename crates/mun_syntax/{tests/data/parser/ok/0007_fn_calls.txt => src/tests/snapshots/parser__function_calls.snap} (93%) rename crates/mun_syntax/{tests/data/parser/ok/0003_literals.txt => src/tests/snapshots/parser__literals.snap} (85%) rename crates/mun_syntax/{tests/data/parser/ok/0008_patterns.txt => src/tests/snapshots/parser__patterns.snap} (92%) rename crates/mun_syntax/{tests/data/parser/ok/0004_unary_expr.txt => src/tests/snapshots/parser__unary_expr.snap} (66%) delete mode 100644 crates/mun_syntax/tests/data/lexer/0001_numbers.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0002_comments.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0003_whitespace.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0004_ident.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0005_symbols.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0006_strings.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0007_keywords.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0008_unclosed_string.mun delete mode 100644 crates/mun_syntax/tests/data/lexer/0008_unclosed_string.txt delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0000_empty.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0000_empty.txt delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0001_function.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0002_block.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0003_literals.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0004_unary_expr.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0005_binary_expr.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0006_expr_stmt.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0007_fn_calls.mun delete mode 100644 crates/mun_syntax/tests/data/parser/ok/0008_patterns.mun delete mode 100644 crates/test_utils/Cargo.toml delete mode 100644 crates/test_utils/src/lib.rs diff --git a/ci/azure-coverage.yml b/ci/azure-coverage.yml index 4c33b17f1..0228402e0 100644 --- a/ci/azure-coverage.yml +++ b/ci/azure-coverage.yml @@ -24,7 +24,7 @@ jobs: - script: | sudo apt-get install libssl-dev pkg-config cmake zlib1g-dev cargo install cargo-tarpaulin - cargo tarpaulin -v --out Xml -- --test-threads=1 + cargo tarpaulin -v --out Xml displayName: "Install and run tarpaulin" - script: bash <(curl -s https://codecov.io/bash) diff --git a/ci/azure-test.yml b/ci/azure-test.yml index 1e10a2c2b..9df03b402 100644 --- a/ci/azure-test.yml +++ b/ci/azure-test.yml @@ -29,7 +29,7 @@ jobs: - script: | echo %PATH% - cargo test -- --test-threads=1 + cargo test env: LOOM_MAX_DURATION: 10 CI: 'True' diff --git a/crates/mun_codegen/Cargo.toml b/crates/mun_codegen/Cargo.toml index 27ccac09c..8d6a106f9 100644 --- a/crates/mun_codegen/Cargo.toml +++ b/crates/mun_codegen/Cargo.toml @@ -14,7 +14,9 @@ md5="0.6.1" array-init="0.1.0" tempfile = "3" lazy_static = "1.4.0" -test_utils = { path="../test_utils"} + +[dev-dependencies] +insta = "0.12.0" [build-dependencies] lazy_static = "1.4.0" diff --git a/crates/mun_codegen/src/ir/function.rs b/crates/mun_codegen/src/ir/function.rs index 114cfd0d5..440095c1c 100644 --- a/crates/mun_codegen/src/ir/function.rs +++ b/crates/mun_codegen/src/ir/function.rs @@ -7,9 +7,10 @@ use crate::{IrDatabase, Module, OptimizationLevel}; use inkwell::builder::Builder; use inkwell::passes::{PassManager, PassManagerBuilder}; use inkwell::types::{AnyTypeEnum, BasicTypeEnum}; +use inkwell::{FloatPredicate, IntPredicate}; use mun_hir::{ - self as hir, ArithOp, BinaryOp, Body, Expr, ExprId, HirDisplay, InferenceResult, Literal, Pat, - PatId, Path, Resolution, Resolver, Statement, TypeCtor, + self as hir, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDisplay, InferenceResult, + Literal, Ordering, Pat, PatId, Path, Resolution, Resolver, Statement, TypeCtor, }; use std::collections::HashMap; use std::mem; @@ -323,6 +324,31 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { // BinaryOp::MultiplyAssign, // BinaryOp::RemainderAssign, // BinaryOp::PowerAssign, + BinaryOp::CmpOp(op) => { + let (name, predicate) = match op { + CmpOp::Eq { negated: false } => ("eq", FloatPredicate::OEQ), + CmpOp::Eq { negated: true } => ("neq", FloatPredicate::ONE), + CmpOp::Ord { + ordering: Ordering::Less, + strict: false, + } => ("lesseq", FloatPredicate::OLE), + CmpOp::Ord { + ordering: Ordering::Less, + strict: true, + } => ("less", FloatPredicate::OLT), + CmpOp::Ord { + ordering: Ordering::Greater, + strict: false, + } => ("greatereq", FloatPredicate::OGE), + CmpOp::Ord { + ordering: Ordering::Greater, + strict: true, + } => ("greater", FloatPredicate::OGT), + }; + self.builder + .build_float_compare(predicate, lhs, rhs, name) + .into() + } _ => unreachable!(), } } @@ -348,6 +374,31 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { // BinaryOp::MultiplyAssign, // BinaryOp::RemainderAssign, // BinaryOp::PowerAssign, + BinaryOp::CmpOp(op) => { + let (name, predicate) = match op { + CmpOp::Eq { negated: false } => ("eq", IntPredicate::EQ), + CmpOp::Eq { negated: true } => ("neq", IntPredicate::NE), + CmpOp::Ord { + ordering: Ordering::Less, + strict: false, + } => ("lesseq", IntPredicate::SLE), + CmpOp::Ord { + ordering: Ordering::Less, + strict: true, + } => ("less", IntPredicate::SLT), + CmpOp::Ord { + ordering: Ordering::Greater, + strict: false, + } => ("greatereq", IntPredicate::SGE), + CmpOp::Ord { + ordering: Ordering::Greater, + strict: true, + } => ("greater", IntPredicate::SGT), + }; + self.builder + .build_int_compare(predicate, lhs, rhs, name) + .into() + } _ => unreachable!(), } } diff --git a/crates/mun_codegen/tests/data/ir/0004_binary_expressions.txt b/crates/mun_codegen/src/snapshots/test__binary_expressions.snap similarity index 62% rename from crates/mun_codegen/tests/data/ir/0004_binary_expressions.txt rename to crates/mun_codegen/src/snapshots/test__binary_expressions.snap index 26b1f40d0..516137a42 100644 --- a/crates/mun_codegen/tests/data/ir/0004_binary_expressions.txt +++ b/crates/mun_codegen/src/snapshots/test__binary_expressions.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn add(a:int, b:int):int {\n a+b\n}\n\nfn subtract(a:int, b:int):int {\n a-b\n}\n\nfn multiply(a:int, b:int):int {\n a*b\n}" +--- ; ModuleID = 'main.mun' source_filename = "main.mun" @@ -18,3 +22,4 @@ body: %mul = mul i64 %a, %b ret i64 %mul } + diff --git a/crates/mun_codegen/src/snapshots/test__equality_operands.snap b/crates/mun_codegen/src/snapshots/test__equality_operands.snap new file mode 100644 index 000000000..f71eebe17 --- /dev/null +++ b/crates/mun_codegen/src/snapshots/test__equality_operands.snap @@ -0,0 +1,79 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn equals(a:int, b:int):bool { a == b }\nfn not_equals(a:int, b:int):bool { a != b }\nfn less(a:int, b:int):bool { a < b }\nfn less_equal(a:int, b:int):bool { a <= b }\nfn greater(a:int, b:int):bool { a > b }\nfn greater_equal(a:int, b:int):bool { a >= b }\nfn equalsf(a:float, b:float):bool { a == b }\nfn not_equalsf(a:float, b:float):bool { a != b }\nfn lessf(a:float, b:float):bool { a < b }\nfn less_equalf(a:float, b:float):bool { a <= b }\nfn greaterf(a:float, b:float):bool { a > b }\nfn greater_equalf(a:float, b:float):bool { a >= b }" +--- +; ModuleID = 'main.mun' +source_filename = "main.mun" + +define i1 @equals(i64 %a, i64 %b) { +body: + %eq = icmp eq i64 %a, %b + ret i1 %eq +} + +define i1 @not_equals(i64 %a, i64 %b) { +body: + %neq = icmp ne i64 %a, %b + ret i1 %neq +} + +define i1 @less(i64 %a, i64 %b) { +body: + %less = icmp slt i64 %a, %b + ret i1 %less +} + +define i1 @less_equal(i64 %a, i64 %b) { +body: + %lesseq = icmp sle i64 %a, %b + ret i1 %lesseq +} + +define i1 @greater(i64 %a, i64 %b) { +body: + %greater = icmp sgt i64 %a, %b + ret i1 %greater +} + +define i1 @greater_equal(i64 %a, i64 %b) { +body: + %greatereq = icmp sge i64 %a, %b + ret i1 %greatereq +} + +define i1 @equalsf(double %a, double %b) { +body: + %eq = fcmp oeq double %a, %b + ret i1 %eq +} + +define i1 @not_equalsf(double %a, double %b) { +body: + %neq = fcmp one double %a, %b + ret i1 %neq +} + +define i1 @lessf(double %a, double %b) { +body: + %less = fcmp olt double %a, %b + ret i1 %less +} + +define i1 @less_equalf(double %a, double %b) { +body: + %lesseq = fcmp ole double %a, %b + ret i1 %lesseq +} + +define i1 @greaterf(double %a, double %b) { +body: + %greater = fcmp ogt double %a, %b + ret i1 %greater +} + +define i1 @greater_equalf(double %a, double %b) { +body: + %greatereq = fcmp oge double %a, %b + ret i1 %greatereq +} + diff --git a/crates/mun_codegen/tests/data/ir/0001_function.txt b/crates/mun_codegen/src/snapshots/test__function.snap similarity index 55% rename from crates/mun_codegen/tests/data/ir/0001_function.txt rename to crates/mun_codegen/src/snapshots/test__function.snap index f13523cef..28335de17 100644 --- a/crates/mun_codegen/tests/data/ir/0001_function.txt +++ b/crates/mun_codegen/src/snapshots/test__function.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn main() {\n}" +--- ; ModuleID = 'main.mun' source_filename = "main.mun" @@ -5,3 +9,4 @@ define void @main() { body: ret void } + diff --git a/crates/mun_codegen/tests/data/ir/0003_function_arguments.txt b/crates/mun_codegen/src/snapshots/test__function_arguments.snap similarity index 52% rename from crates/mun_codegen/tests/data/ir/0003_function_arguments.txt rename to crates/mun_codegen/src/snapshots/test__function_arguments.snap index d360017b4..e5c52bffb 100644 --- a/crates/mun_codegen/tests/data/ir/0003_function_arguments.txt +++ b/crates/mun_codegen/src/snapshots/test__function_arguments.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn main(a:int):int {\n a\n}" +--- ; ModuleID = 'main.mun' source_filename = "main.mun" @@ -5,3 +9,4 @@ define i64 @main(i64 %a) { body: ret i64 %a } + diff --git a/crates/mun_codegen/tests/data/ir/0008_function_calls.txt b/crates/mun_codegen/src/snapshots/test__function_calls.snap similarity index 84% rename from crates/mun_codegen/tests/data/ir/0008_function_calls.txt rename to crates/mun_codegen/src/snapshots/test__function_calls.snap index 152c3c15e..cc720a8bd 100644 --- a/crates/mun_codegen/tests/data/ir/0008_function_calls.txt +++ b/crates/mun_codegen/src/snapshots/test__function_calls.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn add_impl(a:int, b:int):int {\n a+b\n}\n\nfn add(a:int, b:int):int {\n add_impl(a,b)\n}\n\nfn test():int {\n add(4,5)\n add_impl(4,5)\n add(4,5)\n}" +--- ; ModuleID = 'main.mun' source_filename = "main.mun" @@ -28,3 +32,4 @@ body: %add2 = call i64 %add_ptr1(i64 4, i64 5) ret i64 %add2 } + diff --git a/crates/mun_codegen/src/snapshots/test__invalid_binary_ops.snap b/crates/mun_codegen/src/snapshots/test__invalid_binary_ops.snap new file mode 100644 index 000000000..fdac363fb --- /dev/null +++ b/crates/mun_codegen/src/snapshots/test__invalid_binary_ops.snap @@ -0,0 +1,6 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn main() {\n let a = 3+3.0;\n let b = 3.0+3;\n}" +--- +error 2:13: mismatched type +error 3:15: mismatched type diff --git a/crates/mun_codegen/tests/data/ir/0005_let_statement.txt b/crates/mun_codegen/src/snapshots/test__let_statement.snap similarity index 54% rename from crates/mun_codegen/tests/data/ir/0005_let_statement.txt rename to crates/mun_codegen/src/snapshots/test__let_statement.snap index fd1d1068f..03915a2ee 100644 --- a/crates/mun_codegen/tests/data/ir/0005_let_statement.txt +++ b/crates/mun_codegen/src/snapshots/test__let_statement.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn main(a:int):int {\n let b = a+1\n b\n}" +--- ; ModuleID = 'main.mun' source_filename = "main.mun" @@ -6,3 +10,4 @@ body: %add = add i64 %a, 1 ret i64 %add } + diff --git a/crates/mun_codegen/tests/data/ir/0002_return_type.txt b/crates/mun_codegen/src/snapshots/test__return_type.snap similarity index 52% rename from crates/mun_codegen/tests/data/ir/0002_return_type.txt rename to crates/mun_codegen/src/snapshots/test__return_type.snap index e592d80e8..4ce17d4d7 100644 --- a/crates/mun_codegen/tests/data/ir/0002_return_type.txt +++ b/crates/mun_codegen/src/snapshots/test__return_type.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn main():int {\n 0\n}" +--- ; ModuleID = 'main.mun' source_filename = "main.mun" @@ -5,3 +9,4 @@ define i64 @main() { body: ret i64 0 } + diff --git a/crates/mun_codegen/tests/data/ir/0007_update_operators.txt b/crates/mun_codegen/src/snapshots/test__update_operators.snap similarity index 50% rename from crates/mun_codegen/tests/data/ir/0007_update_operators.txt rename to crates/mun_codegen/src/snapshots/test__update_operators.snap index fa467abfb..dfa3bec3a 100644 --- a/crates/mun_codegen/tests/data/ir/0007_update_operators.txt +++ b/crates/mun_codegen/src/snapshots/test__update_operators.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_codegen/src/test.rs +expression: "fn add(a:int, b:int):int {\n let result = a\n result += b\n result\n}\n\nfn subtract(a:int, b:int):int {\n let result = a\n result -= b\n result\n}\n\nfn multiply(a:int, b:int):int {\n let result = a\n result *= b\n result\n}" +--- ; ModuleID = 'main.mun' source_filename = "main.mun" @@ -18,3 +22,4 @@ body: %mul = mul i64 %a, %b ret i64 %a } + diff --git a/crates/mun_codegen/src/test.rs b/crates/mun_codegen/src/test.rs index 7695cc622..f21e449e1 100644 --- a/crates/mun_codegen/src/test.rs +++ b/crates/mun_codegen/src/test.rs @@ -1,42 +1,187 @@ use crate::{mock::MockDatabase, IrDatabase}; use mun_hir::diagnostics::DiagnosticSink; +use mun_hir::line_index::LineIndex; use mun_hir::Module; +use mun_hir::SourceDatabase; use std::cell::RefCell; -use std::path::PathBuf; -use test_utils::{dir_tests, project_dir}; - -fn test_data_dir() -> PathBuf { - project_dir().join("crates/mun_codegen/tests/data/") -} - -#[test] -fn ir_tests() { - dir_tests(&test_data_dir(), &["ir"], |text, _path| { - let (db, file_id) = MockDatabase::with_single_file(text); - - let messages = RefCell::new(Vec::new()); - let mut sink = DiagnosticSink::new(|diag| { - messages.borrow_mut().push(diag.message()); - }); - if let Some(module) = Module::package_modules(&db) - .iter() - .find(|m| m.file_id() == file_id) - { - module.diagnostics(&db, &mut sink) - } - drop(sink); - let messages = messages.into_inner(); - - if !messages.is_empty() { - messages.join("\n") - } else { - format!( - "{}", - db.module_ir(file_id) - .llvm_module - .print_to_string() - .to_string() - ) - } +use std::sync::Arc; + +fn test_snapshot(text: &str) { + let text = text.trim().replace("\n ", "\n"); + + let (db, file_id) = MockDatabase::with_single_file(&text); + + let line_index: Arc = db.line_index(file_id); + let messages = RefCell::new(Vec::new()); + let mut sink = DiagnosticSink::new(|diag| { + let line_col = line_index.line_col(diag.highlight_range().start()); + messages.borrow_mut().push(format!( + "error {}:{}: {}", + line_col.line + 1, + line_col.col + 1, + diag.message() + )); }); + if let Some(module) = Module::package_modules(&db) + .iter() + .find(|m| m.file_id() == file_id) + { + module.diagnostics(&db, &mut sink) + } + drop(sink); + let messages = messages.into_inner(); + + let name = if !messages.is_empty() { + messages.join("\n") + } else { + format!( + "{}", + db.module_ir(file_id) + .llvm_module + .print_to_string() + .to_string() + ) + }; + insta::assert_snapshot!(insta::_macro_support::AutoName, name, &text); +} + +#[test] +fn function() { + test_snapshot( + r#" + fn main() { + } + "#, + ); +} + +#[test] +fn return_type() { + test_snapshot( + r#" + fn main():int { + 0 + } + "#, + ); +} + +#[test] +fn function_arguments() { + test_snapshot( + r#" + fn main(a:int):int { + a + } + "#, + ); +} + +#[test] +fn binary_expressions() { + test_snapshot( + r#" + fn add(a:int, b:int):int { + a+b + } + + fn subtract(a:int, b:int):int { + a-b + } + + fn multiply(a:int, b:int):int { + a*b + } + "#, + ); +} + +#[test] +fn let_statement() { + test_snapshot( + r#" + fn main(a:int):int { + let b = a+1 + b + } + "#, + ); +} + +#[test] +fn invalid_binary_ops() { + test_snapshot( + r#" + fn main() { + let a = 3+3.0; + let b = 3.0+3; + } + "#, + ); +} + +#[test] +fn update_operators() { + test_snapshot( + r#" + fn add(a:int, b:int):int { + let result = a + result += b + result + } + + fn subtract(a:int, b:int):int { + let result = a + result -= b + result + } + + fn multiply(a:int, b:int):int { + let result = a + result *= b + result + } + "#, + ); +} + +#[test] +fn function_calls() { + test_snapshot( + r#" + fn add_impl(a:int, b:int):int { + a+b + } + + fn add(a:int, b:int):int { + add_impl(a,b) + } + + fn test():int { + add(4,5) + add_impl(4,5) + add(4,5) + } + "#, + ); +} + +#[test] +fn equality_operands() { + test_snapshot( + r#" + fn equals(a:int, b:int):bool { a == b } + fn not_equals(a:int, b:int):bool { a != b } + fn less(a:int, b:int):bool { a < b } + fn less_equal(a:int, b:int):bool { a <= b } + fn greater(a:int, b:int):bool { a > b } + fn greater_equal(a:int, b:int):bool { a >= b } + fn equalsf(a:float, b:float):bool { a == b } + fn not_equalsf(a:float, b:float):bool { a != b } + fn lessf(a:float, b:float):bool { a < b } + fn less_equalf(a:float, b:float):bool { a <= b } + fn greaterf(a:float, b:float):bool { a > b } + fn greater_equalf(a:float, b:float):bool { a >= b } + "#, + ); } diff --git a/crates/mun_codegen/tests/data/ir/0001_function.mun b/crates/mun_codegen/tests/data/ir/0001_function.mun deleted file mode 100644 index ea3850b23..000000000 --- a/crates/mun_codegen/tests/data/ir/0001_function.mun +++ /dev/null @@ -1,2 +0,0 @@ -fn main() { -} \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0002_return_type.mun b/crates/mun_codegen/tests/data/ir/0002_return_type.mun deleted file mode 100644 index 2d1d7f392..000000000 --- a/crates/mun_codegen/tests/data/ir/0002_return_type.mun +++ /dev/null @@ -1,3 +0,0 @@ -fn main():int { - 0 -} \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0003_function_arguments.mun b/crates/mun_codegen/tests/data/ir/0003_function_arguments.mun deleted file mode 100644 index c434783c8..000000000 --- a/crates/mun_codegen/tests/data/ir/0003_function_arguments.mun +++ /dev/null @@ -1,3 +0,0 @@ -fn main(a:int):int { - a -} \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0004_binary_expressions.mun b/crates/mun_codegen/tests/data/ir/0004_binary_expressions.mun deleted file mode 100644 index 2d55262bf..000000000 --- a/crates/mun_codegen/tests/data/ir/0004_binary_expressions.mun +++ /dev/null @@ -1,11 +0,0 @@ -fn add(a:int, b:int):int { - a+b -} - -fn subtract(a:int, b:int):int { - a-b -} - -fn multiply(a:int, b:int):int { - a*b -} \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0005_let_statement.mun b/crates/mun_codegen/tests/data/ir/0005_let_statement.mun deleted file mode 100644 index 5efc51382..000000000 --- a/crates/mun_codegen/tests/data/ir/0005_let_statement.mun +++ /dev/null @@ -1,4 +0,0 @@ -fn main(a:int):int { - let b = a+1 - b -} \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.mun b/crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.mun deleted file mode 100644 index 8e20a3773..000000000 --- a/crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.mun +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - let a = 3+3.0; - let b = 3.0+3; -} \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.txt b/crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.txt deleted file mode 100644 index 4faf5a73e..000000000 --- a/crates/mun_codegen/tests/data/ir/0006_invalid_binary_ops.txt +++ /dev/null @@ -1,2 +0,0 @@ -mismatched type -mismatched type \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0007_update_operators.mun b/crates/mun_codegen/tests/data/ir/0007_update_operators.mun deleted file mode 100644 index c37333aa2..000000000 --- a/crates/mun_codegen/tests/data/ir/0007_update_operators.mun +++ /dev/null @@ -1,17 +0,0 @@ -fn add(a:int, b:int):int { - let result = a - result += b - result -} - -fn subtract(a:int, b:int):int { - let result = a - result -= b - result -} - -fn multiply(a:int, b:int):int { - let result = a - result *= b - result -} \ No newline at end of file diff --git a/crates/mun_codegen/tests/data/ir/0008_function_calls.mun b/crates/mun_codegen/tests/data/ir/0008_function_calls.mun deleted file mode 100644 index 29cb164a9..000000000 --- a/crates/mun_codegen/tests/data/ir/0008_function_calls.mun +++ /dev/null @@ -1,13 +0,0 @@ -fn add_impl(a:int, b:int):int { - a+b -} - -fn add(a:int, b:int):int { - add_impl(a,b) -} - -fn test():int { - add(4,5) - add_impl(4,5) - add(4,5) -} \ No newline at end of file diff --git a/crates/mun_compiler/src/lib.rs b/crates/mun_compiler/src/lib.rs index 2e3934f48..360460133 100644 --- a/crates/mun_compiler/src/lib.rs +++ b/crates/mun_compiler/src/lib.rs @@ -241,6 +241,7 @@ pub fn main(options: &CompilerOptions) -> Result, failure::Error } let diagnostics = diagnostics(&db, file_id); + dbg!(&diagnostics); if !diagnostics.is_empty() { let mut writer = StandardStream::stderr(ColorChoice::Auto); for diagnostic in diagnostics { diff --git a/crates/mun_hir/src/code_model.rs b/crates/mun_hir/src/code_model.rs index f4c2af22f..18f700c73 100644 --- a/crates/mun_hir/src/code_model.rs +++ b/crates/mun_hir/src/code_model.rs @@ -272,6 +272,7 @@ impl Function { pub enum BuiltinType { Float, Int, + Boolean, } use crate::code_model::diagnostics::ModuleDefinitionDiagnostic; @@ -282,6 +283,7 @@ impl BuiltinType { pub(crate) const ALL: &'static [(Name, BuiltinType)] = &[ (FLOAT, BuiltinType::Float), (INT, BuiltinType::Int), + (BOOLEAN, BuiltinType::Boolean), ]; } diff --git a/crates/mun_hir/src/expr.rs b/crates/mun_hir/src/expr.rs index 31b087c22..3e193dbe5 100644 --- a/crates/mun_hir/src/expr.rs +++ b/crates/mun_hir/src/expr.rs @@ -438,15 +438,28 @@ where | op @ BinOp::Subtract | op @ BinOp::Divide | op @ BinOp::Multiply - | op @ BinOp::Remainder - | op @ BinOp::Power => { + | op @ BinOp::Equals + | op @ BinOp::NotEquals + | op @ BinOp::Less + | op @ BinOp::LessEqual + | op @ BinOp::Greater + | op @ BinOp::GreatEqual + //| op @ BinOp::Remainder + //| op @ BinOp::Power + => { let op = match op { BinOp::Add => BinaryOp::ArithOp(ArithOp::Add), BinOp::Subtract => BinaryOp::ArithOp(ArithOp::Subtract), BinOp::Divide => BinaryOp::ArithOp(ArithOp::Divide), BinOp::Multiply => BinaryOp::ArithOp(ArithOp::Multiply), - BinOp::Remainder => BinaryOp::ArithOp(ArithOp::Remainder), - BinOp::Power => BinaryOp::ArithOp(ArithOp::Power), + BinOp::Equals => BinaryOp::CmpOp(CmpOp::Eq { negated: false }), + BinOp::NotEquals => BinaryOp::CmpOp(CmpOp::Eq { negated: true }), + BinOp::Less => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true } ), + BinOp::LessEqual => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false } ), + BinOp::Greater => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true } ), + BinOp::GreatEqual => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false } ), + //BinOp::Remainder => BinaryOp::ArithOp(ArithOp::Remainder), + //BinOp::Power => BinaryOp::ArithOp(ArithOp::Power), _ => unreachable!(), }; let lhs = self.collect_expr_opt(e.lhs()); @@ -476,15 +489,16 @@ where | op @ BinOp::SubtractAssign | op @ BinOp::DivideAssign | op @ BinOp::MultiplyAssign - | op @ BinOp::RemainderAssign - | op @ BinOp::PowerAssign => { + //| op @ BinOp::RemainderAssign + //| op @ BinOp::PowerAssign + => { let op = match op { BinOp::AddAssign => BinaryOp::ArithOp(ArithOp::Add), BinOp::SubtractAssign => BinaryOp::ArithOp(ArithOp::Subtract), BinOp::DivideAssign => BinaryOp::ArithOp(ArithOp::Divide), BinOp::MultiplyAssign => BinaryOp::ArithOp(ArithOp::Multiply), - BinOp::RemainderAssign => BinaryOp::ArithOp(ArithOp::Remainder), - BinOp::PowerAssign => BinaryOp::ArithOp(ArithOp::Power), + //BinOp::RemainderAssign => BinaryOp::ArithOp(ArithOp::Remainder), + //BinOp::PowerAssign => BinaryOp::ArithOp(ArithOp::Power), _ => unreachable!(), }; let lhs = self.collect_expr_opt(e.lhs()); diff --git a/crates/mun_hir/src/lib.rs b/crates/mun_hir/src/lib.rs index fb8acf6d6..d31755a0b 100644 --- a/crates/mun_hir/src/lib.rs +++ b/crates/mun_hir/src/lib.rs @@ -32,7 +32,7 @@ pub use crate::{ display::HirDisplay, expr::{ resolver_for_expr, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, ExprScopes, Literal, - LogicOp, Pat, PatId, Statement, + LogicOp, Ordering, Pat, PatId, Statement, }, ids::ItemLoc, input::{FileId, PackageInput}, diff --git a/crates/mun_hir/src/name.rs b/crates/mun_hir/src/name.rs index d454eec64..67d429677 100644 --- a/crates/mun_hir/src/name.rs +++ b/crates/mun_hir/src/name.rs @@ -48,3 +48,4 @@ impl AsName for ast::Name { pub(crate) const FLOAT: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"float")); pub(crate) const INT: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"int")); +pub(crate) const BOOLEAN: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"bool")); diff --git a/crates/mun_hir/src/ty/lower.rs b/crates/mun_hir/src/ty/lower.rs index a4231bf1e..2e11ac394 100644 --- a/crates/mun_hir/src/ty/lower.rs +++ b/crates/mun_hir/src/ty/lower.rs @@ -122,6 +122,7 @@ fn type_for_builtin(def: BuiltinType) -> Ty { Ty::simple(match def { BuiltinType::Float => TypeCtor::Float, BuiltinType::Int => TypeCtor::Int, + BuiltinType::Boolean => TypeCtor::Bool, }) } diff --git a/crates/mun_hir/src/ty/op.rs b/crates/mun_hir/src/ty/op.rs index 936b36bae..4b4e0fa95 100644 --- a/crates/mun_hir/src/ty/op.rs +++ b/crates/mun_hir/src/ty/op.rs @@ -6,8 +6,8 @@ pub(super) fn binary_op_rhs_expectation(_op: BinaryOp, lhs_ty: Ty) -> Ty { pub(super) fn binary_op_return_ty(op: BinaryOp, rhs_ty: Ty) -> Ty { match op { - BinaryOp::ArithOp(_) | BinaryOp::CmpOp(_) => rhs_ty, - BinaryOp::LogicOp(_) => Ty::simple(TypeCtor::Bool), + BinaryOp::ArithOp(_) => rhs_ty, + BinaryOp::CmpOp(_) | BinaryOp::LogicOp(_) => Ty::simple(TypeCtor::Bool), BinaryOp::Assignment => Ty::Empty, } } diff --git a/crates/mun_runtime/src/test.rs b/crates/mun_runtime/src/test.rs index 9a2629e48..0bfa98498 100644 --- a/crates/mun_runtime/src/test.rs +++ b/crates/mun_runtime/src/test.rs @@ -86,3 +86,124 @@ fn dispatch_table() { let result: i64 = invoke_fn!(runtime, "add", a, b).unwrap(); assert_eq!(result, a + b); } + +#[test] +fn booleans() { + let compile_result = compile( + r#" + fn equal(a:int, b:int):bool { a==b } + fn equalf(a:float, b:float):bool { a==b } + fn not_equal(a:int, b:int):bool { a!=b } + fn not_equalf(a:float, b:float):bool { a!=b } + fn less(a:int, b:int):bool { ab } + fn greaterf(a:float, b:float):bool { a>b } + fn less_equal(a:int, b:int):bool { a<=b } + fn lessf_equal(a:float, b:float):bool { a<=b } + fn greater_equal(a:int, b:int):bool { a>=b } + fn greaterf_equal(a:float, b:float):bool { a>=b } + "#, + ); + let mut runtime = compile_result.new_runtime(); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "equal", 52, 764).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "equal", 64, 64).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "equalf", 123.0, 123.0).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "equalf", 123.0, 234.0).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "not_equal", 52, 764).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "not_equal", 64, 64).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "not_equalf", 123.0, 123.0).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "not_equalf", 123.0, 234.0).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "less", 52, 764).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "less", 64, 64).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "lessf", 123.0, 123.0).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "lessf", 123.0, 234.0).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greater", 52, 764).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greater", 64, 64).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greaterf", 123.0, 123.0).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greaterf", 123.0, 234.0).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "less_equal", 52, 764).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "less_equal", 64, 64).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "lessf_equal", 123.0, 123.0) + .unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "lessf_equal", 123.0, 234.0) + .unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greater_equal", 52, 764).unwrap(), + false + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greater_equal", 64, 64).unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greaterf_equal", 123.0, 123.0) + .unwrap(), + true + ); + assert_eq!( + MunRuntime::invoke_fn2::(&mut runtime, "greaterf_equal", 123.0, 234.0) + .unwrap(), + false + ); +} diff --git a/crates/mun_syntax/Cargo.toml b/crates/mun_syntax/Cargo.toml index 3fca8a954..4189449ab 100644 --- a/crates/mun_syntax/Cargo.toml +++ b/crates/mun_syntax/Cargo.toml @@ -13,4 +13,4 @@ drop_bomb = "0.1.4" mun_errors = { path = "../mun_errors"} [dev-dependencies] -test_utils = { path="../test_utils"} \ No newline at end of file +insta = "0.12.0" \ No newline at end of file diff --git a/crates/mun_syntax/src/ast/expr_extensions.rs b/crates/mun_syntax/src/ast/expr_extensions.rs index bb4038f39..3fffe0ed1 100644 --- a/crates/mun_syntax/src/ast/expr_extensions.rs +++ b/crates/mun_syntax/src/ast/expr_extensions.rs @@ -17,7 +17,7 @@ pub enum PrefixOp { impl ast::PrefixExpr { pub fn op_kind(&self) -> Option { match self.op_token()?.kind() { - T![not] => Some(PrefixOp::Not), + T![!] => Some(PrefixOp::Not), T![-] => Some(PrefixOp::Neg), _ => None, } @@ -34,15 +34,21 @@ pub enum BinOp { Subtract, Divide, Multiply, - Remainder, - Power, + // Remainder, + // Power, Assign, AddAssign, SubtractAssign, DivideAssign, MultiplyAssign, - RemainderAssign, - PowerAssign, + // RemainderAssign, + // PowerAssign, + Equals, + NotEquals, + LessEqual, + Less, + GreatEqual, + Greater, } impl BinExpr { @@ -56,15 +62,21 @@ impl BinExpr { MINUS => Some((c, BinOp::Subtract)), SLASH => Some((c, BinOp::Divide)), STAR => Some((c, BinOp::Multiply)), - PERCENT => Some((c, BinOp::Remainder)), - CARET => Some((c, BinOp::Power)), - EQ => Some((c, BinOp::Assign)), + // PERCENT => Some((c, BinOp::Remainder)), + // CARET => Some((c, BinOp::Power)), + T![=] => Some((c, BinOp::Assign)), PLUSEQ => Some((c, BinOp::AddAssign)), MINUSEQ => Some((c, BinOp::SubtractAssign)), SLASHEQ => Some((c, BinOp::DivideAssign)), STAREQ => Some((c, BinOp::MultiplyAssign)), - PERCENTEQ => Some((c, BinOp::RemainderAssign)), - CARETEQ => Some((c, BinOp::PowerAssign)), + // PERCENTEQ => Some((c, BinOp::RemainderAssign)), + // CARETEQ => Some((c, BinOp::PowerAssign)), + EQEQ => Some((c, BinOp::Equals)), + NEQ => Some((c, BinOp::NotEquals)), + LT => Some((c, BinOp::Less)), + LTEQ => Some((c, BinOp::LessEqual)), + GT => Some((c, BinOp::Greater)), + GTEQ => Some((c, BinOp::GreatEqual)), _ => None, }) } diff --git a/crates/mun_syntax/src/ast/extensions.rs b/crates/mun_syntax/src/ast/extensions.rs index 18c6a84a9..ad09ccf1b 100644 --- a/crates/mun_syntax/src/ast/extensions.rs +++ b/crates/mun_syntax/src/ast/extensions.rs @@ -1,7 +1,7 @@ use crate::ast::NameOwner; use crate::{ ast::{self, AstNode}, - SyntaxKind, T, + T, }; use crate::{SmolStr, SyntaxNode}; use text_unit::TextRange; @@ -23,7 +23,7 @@ impl ast::FunctionDef { let fn_kw = self .syntax() .children_with_tokens() - .find(|p| p.kind() == SyntaxKind::FN_KW) + .find(|p| p.kind() == T![fn]) .map(|kw| kw.text_range()); let name = self.name().map(|n| n.syntax.text_range()); let param_list = self.param_list().map(|p| p.syntax.text_range()); diff --git a/crates/mun_syntax/src/grammar.ron b/crates/mun_syntax/src/grammar.ron index 2826e529b..3b95ddddc 100644 --- a/crates/mun_syntax/src/grammar.ron +++ b/crates/mun_syntax/src/grammar.ron @@ -23,6 +23,7 @@ Grammar( [";", "SEMI"], [":", "COLON"], [",", "COMMA"], + ["!", "EXCLAMATION"], // Extended symbols ["_", "UNDERSCORE"], @@ -63,7 +64,7 @@ Grammar( "in", // "local", // We use let "nil", - "not", + //"not", // We use ! "or", "self", "super", diff --git a/crates/mun_syntax/src/lib.rs b/crates/mun_syntax/src/lib.rs index 7c842f73d..83f01377a 100644 --- a/crates/mun_syntax/src/lib.rs +++ b/crates/mun_syntax/src/lib.rs @@ -24,7 +24,7 @@ use std::{fmt::Write, marker::PhantomData, sync::Arc}; pub use crate::{ ast::AstNode, - parsing::{tokenize, Token}, + parsing::{lexer::Token, tokenize}, ptr::{AstPtr, SyntaxNodePtr}, syntax_error::{SyntaxError, SyntaxErrorKind}, syntax_kind::SyntaxKind, diff --git a/crates/mun_syntax/src/parsing.rs b/crates/mun_syntax/src/parsing.rs index eadb4e714..b4db690ed 100644 --- a/crates/mun_syntax/src/parsing.rs +++ b/crates/mun_syntax/src/parsing.rs @@ -5,25 +5,38 @@ mod token_set; mod event; mod grammar; -mod lexer; +pub mod lexer; mod parser; mod text_token_source; mod text_tree_sink; -pub use lexer::{tokenize, Token}; +pub use lexer::tokenize; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ParseError(pub String); /// `TokenSource` abstract the source of the tokens. trait TokenSource { - /// Returns the token at `pos` - fn token_kind(&self, pos: usize) -> SyntaxKind; + fn current(&self) -> Token; - /// Returns true if the token at `pos` is joined to the next token. For example - /// * `. .` -> not joined - /// * `..` -> tokens are joined - fn is_token_joint_to_next(&self, pos: usize) -> bool; + /// Lookahead n token + fn lookahead_nth(&self, n: usize) -> Token; + + /// bump cursor to next token + fn bump(&mut self); + + /// Is the current token a specified keyword? + fn is_keyword(&self, kw: &str) -> bool; +} + +/// `TokenCursor` abstracts the cursor of `TokenSource` operates one. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct Token { + /// What is the current token? + pub kind: SyntaxKind, + + /// Is the current token joined to the next one (`> >` vs `>>`). + pub is_jointed_to_next: bool, } /// `TreeSink` abstracts details of a particular syntax tree implementation. @@ -43,13 +56,13 @@ pub trait TreeSink { pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec) { let tokens = tokenize(&text); - let token_source = text_token_source::TextTokenSource::new(text, &tokens); + let mut token_source = text_token_source::TextTokenSource::new(text, &tokens); let mut tree_sink = text_tree_sink::TextTreeSink::new(text, &tokens); - parse(&token_source, &mut tree_sink); + parse(&mut token_source, &mut tree_sink); tree_sink.finish() } -fn parse_from_tokens(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink, f: F) +fn parse_from_tokens(token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink, f: F) where F: FnOnce(&mut parser::Parser), { @@ -60,6 +73,6 @@ where } /// Parse given tokens into the given sink as a rust file. -fn parse(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink) { +fn parse(token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) { parse_from_tokens(token_source, tree_sink, grammar::root); } diff --git a/crates/mun_syntax/src/parsing/grammar.rs b/crates/mun_syntax/src/parsing/grammar.rs index 75383ca4b..e2f422295 100644 --- a/crates/mun_syntax/src/parsing/grammar.rs +++ b/crates/mun_syntax/src/parsing/grammar.rs @@ -30,9 +30,9 @@ pub(crate) fn root(p: &mut Parser) { //} fn name_recovery(p: &mut Parser, recovery: TokenSet) { - if p.matches(IDENT) { + if p.at(IDENT) { let m = p.start(); - p.bump(); + p.bump(IDENT); m.complete(p, NAME); } else { p.error_recover("expected a name", recovery) @@ -44,9 +44,9 @@ fn name(p: &mut Parser) { } fn name_ref(p: &mut Parser) { - if p.matches(IDENT) { + if p.at(IDENT) { let m = p.start(); - p.bump(); + p.bump(IDENT); m.complete(p, NAME_REF); } else { p.error_and_bump("expected identifier"); @@ -54,9 +54,9 @@ fn name_ref(p: &mut Parser) { } fn opt_visibility(p: &mut Parser) -> bool { - if p.matches(EXPORT_KW) { + if p.at(EXPORT_KW) { let m = p.start(); - p.bump(); + p.bump(EXPORT_KW); m.complete(p, VISIBILITY); true } else { @@ -65,11 +65,11 @@ fn opt_visibility(p: &mut Parser) -> bool { } fn error_block(p: &mut Parser, message: &str) { - assert!(p.matches(L_CURLY)); + assert!(p.at(T!['{'])); let m = p.start(); p.error(message); - p.bump(); + p.bump(T!['{']); expressions::expr_block_contents(p); - p.eat(R_CURLY); + p.eat(T!['{']); m.complete(p, ERROR); } diff --git a/crates/mun_syntax/src/parsing/grammar/declarations.rs b/crates/mun_syntax/src/parsing/grammar/declarations.rs index ab682a3be..ebe9c0063 100644 --- a/crates/mun_syntax/src/parsing/grammar/declarations.rs +++ b/crates/mun_syntax/src/parsing/grammar/declarations.rs @@ -4,7 +4,7 @@ use crate::T; pub(super) const DECLARATION_RECOVERY_SET: TokenSet = token_set![FN_KW, EXPORT_KW]; pub(super) fn mod_contents(p: &mut Parser) { - while !p.matches(EOF) { + while !p.at(EOF) { declaration(p); } } @@ -17,14 +17,14 @@ pub(super) fn declaration(p: &mut Parser) { }; m.abandon(p); - if p.matches(L_CURLY) { + if p.at(T!['{']) { error_block(p, "expected a declaration") - } else if p.matches(R_CURLY) { + } else if p.at(T!['{']) { let e = p.start(); p.error("unmatched }"); - p.bump(); + p.bump(T!['{']); e.complete(p, ERROR); - } else if !p.matches(EOF) { + } else if !p.at(EOF) { p.error_and_bump("expected a declaration"); } else { p.error("expected a declaration"); @@ -35,7 +35,7 @@ pub(super) fn maybe_declaration(p: &mut Parser, m: Marker) -> Result<(), Marker> opt_visibility(p); match p.current() { - FN_KW => { + T![fn] => { fn_def(p); m.complete(p, FUNCTION_DEF); } @@ -45,12 +45,12 @@ pub(super) fn maybe_declaration(p: &mut Parser, m: Marker) -> Result<(), Marker> } pub(super) fn fn_def(p: &mut Parser) { - assert!(p.matches(FN_KW)); - p.bump(); + assert!(p.at(T![fn])); + p.bump(T![fn]); name_recovery(p, DECLARATION_RECOVERY_SET.union(token_set![L_PAREN])); - if p.matches(L_PAREN) { + if p.at(T!['(']) { params::param_list(p); } else { p.error("expected function arguments") @@ -62,9 +62,9 @@ pub(super) fn fn_def(p: &mut Parser) { } fn opt_fn_ret_type(p: &mut Parser) -> bool { - if p.matches(T![:]) { + if p.at(T![:]) { let m = p.start(); - p.bump(); + p.bump(T![:]); types::type_(p); m.complete(p, RET_TYPE); true diff --git a/crates/mun_syntax/src/parsing/grammar/expressions.rs b/crates/mun_syntax/src/parsing/grammar/expressions.rs index 7b066aff2..adb1133f7 100644 --- a/crates/mun_syntax/src/parsing/grammar/expressions.rs +++ b/crates/mun_syntax/src/parsing/grammar/expressions.rs @@ -7,13 +7,13 @@ const EXPR_RECOVERY_SET: TokenSet = token_set![LET_KW]; const ATOM_EXPR_FIRST: TokenSet = LITERAL_FIRST.union(token_set![IDENT, L_PAREN]); -const LHS_FIRST: TokenSet = ATOM_EXPR_FIRST.union(token_set![NOT_KW, MINUS]); +const LHS_FIRST: TokenSet = ATOM_EXPR_FIRST.union(token_set![EXCLAMATION, MINUS]); const EXPR_FIRST: TokenSet = LHS_FIRST; pub(crate) fn expr_block_contents(p: &mut Parser) { - while !p.matches(EOF) && !p.matches(R_CURLY) { - if p.eat(SEMI) { + while !p.at(EOF) && !p.at(T!['}']) { + if p.eat(T![;]) { continue; } @@ -23,14 +23,14 @@ pub(crate) fn expr_block_contents(p: &mut Parser) { /// Parses a block statement pub(crate) fn block(p: &mut Parser) { - if !p.matches(L_CURLY) { + if !p.at(T!['{']) { p.error("expected a block"); return; } let m = p.start(); - p.bump(); + p.bump(T!['{']); expr_block_contents(p); - p.expect(R_CURLY); + p.expect(T!['}']); m.complete(p, BLOCK); } @@ -39,7 +39,7 @@ pub(super) fn stmt(p: &mut Parser) { let m = p.start(); // Encounters let keyword, so we know it's a let stmt - if p.matches(LET_KW) { + if p.at(T![let]) { let_stmt(p, m); return; } @@ -47,7 +47,7 @@ pub(super) fn stmt(p: &mut Parser) { let cm = expr_stmt(p); let kind = cm.as_ref().map(|cm| cm.kind()).unwrap_or(ERROR); - if p.matches(R_CURLY) { + if p.at(T!['}']) { if let Some(cm) = cm { cm.undo_completion(p).abandon(p); m.complete(p, kind); @@ -55,23 +55,23 @@ pub(super) fn stmt(p: &mut Parser) { m.abandon(p); } } else { - p.eat(SEMI); + p.eat(T![;]); m.complete(p, EXPR_STMT); } } fn let_stmt(p: &mut Parser, m: Marker) { - assert!(p.matches(LET_KW)); - p.bump(); + assert!(p.at(T![let])); + p.bump(T![let]); patterns::pattern(p); - if p.matches(COLON) { + if p.at(T![:]) { types::ascription(p); } - if p.eat(EQ) { + if p.eat(T![=]) { expressions::expr(p); } - p.eat(SEMI); // Semicolon at the end of statement belongs to the statement + p.eat(T![;]); // Semicolon at the end of statement belongs to the statement m.complete(p, LET_STMT); } @@ -97,12 +97,7 @@ fn expr_bp(p: &mut Parser, bp: u8) -> Option { } let m = lhs.precede(p); - match op { - Op::Simple => p.bump(), - Op::Composite(kind, n) => { - p.bump_compound(kind, n); - } - } + p.bump(op); expr_bp(p, op_bp + 1); lhs = m.complete(p, BIN_EXPR); @@ -111,43 +106,33 @@ fn expr_bp(p: &mut Parser, bp: u8) -> Option { Some(lhs) } -enum Op { - Simple, - Composite(SyntaxKind, u8), -} - -fn current_op(p: &Parser) -> (u8, Op) { - if let Some(t) = p.current2() { - match t { - (PLUS, EQ) => return (1, Op::Composite(PLUSEQ, 2)), - (MINUS, EQ) => return (1, Op::Composite(MINUSEQ, 2)), - (STAR, EQ) => return (1, Op::Composite(STAREQ, 2)), - (SLASH, EQ) => return (1, Op::Composite(SLASHEQ, 2)), - (CARET, EQ) => return (1, Op::Composite(CARETEQ, 2)), - (PERCENT, EQ) => return (1, Op::Composite(PERCENTEQ, 2)), - (LT, EQ) => return (5, Op::Composite(LTEQ, 2)), - (GT, EQ) => return (5, Op::Composite(GTEQ, 2)), - _ => (), - } +fn current_op(p: &Parser) -> (u8, SyntaxKind) { + match p.current() { + T![+] if p.at(T![+=]) => (1, T![+=]), + T![+] => (10, T![+]), + T![-] if p.at(T![-=]) => (1, T![-=]), + T![-] => (10, T![-]), + T![*] if p.at(T![*=]) => (1, T![*=]), + T![*] => (11, T![*]), + T![/] if p.at(T![/=]) => (1, T![/=]), + T![/] => (11, T![/]), + T![=] if p.at(T![==]) => (5, T![==]), + T![=] => (1, T![=]), + T![!] if p.at(T![!=]) => (5, T![!=]), + T![>] if p.at(T![>=]) => (5, T![>=]), + T![>] => (5, T![>]), + T![<] if p.at(T![<=]) => (5, T![<=]), + T![<] => (5, T![<]), + _ => (0, T![_]), } - - let bp = match p.current() { - EQ => 1, - EQEQ | NEQ | LT | GT => 5, - MINUS | PLUS => 10, - STAR | SLASH | PERCENT => 11, - CARET => 12, - _ => 0, - }; - (bp, Op::Simple) } fn lhs(p: &mut Parser) -> Option { let m; let kind = match p.current() { - MINUS | NOT_KW => { + T![-] | T![!] => { m = p.start(); - p.bump(); + p.bump_any(); PREFIX_EXPR } _ => { @@ -162,7 +147,7 @@ fn lhs(p: &mut Parser) -> Option { fn postfix_expr(p: &mut Parser, mut lhs: CompletedMarker) -> CompletedMarker { loop { lhs = match p.current() { - L_PAREN => call_expr(p, lhs), + T!['('] => call_expr(p, lhs), _ => break, } } @@ -170,28 +155,28 @@ fn postfix_expr(p: &mut Parser, mut lhs: CompletedMarker) -> CompletedMarker { } fn call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { - assert!(p.matches(L_PAREN)); + assert!(p.at(T!['('])); let m = lhs.precede(p); arg_list(p); m.complete(p, CALL_EXPR) } fn arg_list(p: &mut Parser) { - assert!(p.matches(L_PAREN)); + assert!(p.at(T!['('])); let m = p.start(); - p.bump(); - while !p.matches(R_PAREN) && !p.matches(EOF) { - if !p.matches_any(EXPR_FIRST) { + p.bump(T!['(']); + while !p.at(T![')']) && !p.at(EOF) { + if !p.at_ts(EXPR_FIRST) { p.error("expected expression"); break; } expr(p); - if !p.matches(R_PAREN) && !p.expect(COMMA) { + if !p.at(T![')']) && !p.expect(T![,]) { break; } } - p.eat(R_PAREN); + p.eat(T![')']); m.complete(p, ARG_LIST); } @@ -204,14 +189,8 @@ fn atom_expr(p: &mut Parser) -> Option { return Some(path_expr(p)); } - if p.matches(IDENT) { - let m = p.start(); - p.bump(); - return Some(m.complete(p, NAME_REF)); - } - let marker = match p.current() { - L_PAREN => paren_expr(p), + T!['('] => paren_expr(p), _ => { p.error_recover("expected expression", EXPR_RECOVERY_SET); return None; @@ -227,19 +206,19 @@ fn path_expr(p: &mut Parser) -> CompletedMarker { } fn literal(p: &mut Parser) -> Option { - if !p.matches_any(LITERAL_FIRST) { + if !p.at_ts(LITERAL_FIRST) { return None; } let m = p.start(); - p.bump(); + p.bump_any(); Some(m.complete(p, LITERAL)) } fn paren_expr(p: &mut Parser) -> CompletedMarker { - assert!(p.matches(L_PAREN)); + assert!(p.at(T!['('])); let m = p.start(); - p.bump(); + p.bump(T!['(']); expr(p); - p.expect(R_PAREN); + p.expect(T![')']); m.complete(p, PAREN_EXPR) } diff --git a/crates/mun_syntax/src/parsing/grammar/params.rs b/crates/mun_syntax/src/parsing/grammar/params.rs index d86e423cd..7025d5cc8 100644 --- a/crates/mun_syntax/src/parsing/grammar/params.rs +++ b/crates/mun_syntax/src/parsing/grammar/params.rs @@ -5,20 +5,20 @@ pub(super) fn param_list(p: &mut Parser) { } fn list(p: &mut Parser) { - assert!(p.matches(L_PAREN)); + assert!(p.at(T!['('])); let m = p.start(); - p.bump(); - while !p.matches(EOF) && !p.matches(R_PAREN) { - if !p.matches_any(VALUE_PARAMETER_FIRST) { + p.bump(T!['(']); + while !p.at(EOF) && !p.at(T![')']) { + if !p.at_ts(VALUE_PARAMETER_FIRST) { p.error("expected value parameter"); break; } param(p); - if !p.matches(R_PAREN) { - p.expect(COMMA); + if !p.at(T![')']) { + p.expect(T![,]); } } - p.expect(R_PAREN); + p.expect(T![')']); m.complete(p, PARAM_LIST); } diff --git a/crates/mun_syntax/src/parsing/grammar/paths.rs b/crates/mun_syntax/src/parsing/grammar/paths.rs index e88b7d889..bd048dc42 100644 --- a/crates/mun_syntax/src/parsing/grammar/paths.rs +++ b/crates/mun_syntax/src/parsing/grammar/paths.rs @@ -4,7 +4,7 @@ pub(super) const PATH_FIRST: TokenSet = token_set![IDENT, SELF_KW, SUPER_KW, COL pub(super) fn is_path_start(p: &Parser) -> bool { match p.current() { - IDENT | COLONCOLON => true, + IDENT | T![::] => true, _ => false, } } @@ -28,12 +28,12 @@ fn path(p: &mut Parser, mode: Mode) { let mut qualifier = path.complete(p, PATH); loop { let import_tree = match p.nth(1) { - STAR | L_CURLY => true, + T![*] | T!['{'] => true, _ => false, }; - if p.matches(COLONCOLON) && !import_tree { + if p.at(T![::]) && !import_tree { let path = qualifier.precede(p); - p.bump(); + p.bump(T![::]); path_segment(p, mode, false); let path = path.complete(p, PATH); qualifier = path; @@ -46,13 +46,13 @@ fn path(p: &mut Parser, mode: Mode) { fn path_segment(p: &mut Parser, _mode: Mode, first: bool) { let m = p.start(); if first { - p.eat(COLONCOLON); + p.eat(T![::]); } match p.current() { IDENT => { name_ref(p); } - SELF_KW | SUPER_KW => p.bump(), + T![self] | T![super] => p.bump_any(), _ => p.error_recover( "expected identifier", declarations::DECLARATION_RECOVERY_SET, diff --git a/crates/mun_syntax/src/parsing/grammar/patterns.rs b/crates/mun_syntax/src/parsing/grammar/patterns.rs index 16d457a9e..97be71f25 100644 --- a/crates/mun_syntax/src/parsing/grammar/patterns.rs +++ b/crates/mun_syntax/src/parsing/grammar/patterns.rs @@ -29,9 +29,9 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option { } fn placeholder_pat(p: &mut Parser) -> CompletedMarker { - assert!(p.matches(T![_])); + assert!(p.at(T![_])); let m = p.start(); - p.bump(); + p.bump(T![_]); m.complete(p, PLACEHOLDER_PAT) } diff --git a/crates/mun_syntax/src/parsing/grammar/types.rs b/crates/mun_syntax/src/parsing/grammar/types.rs index 9d1f52499..7b5aae4b1 100644 --- a/crates/mun_syntax/src/parsing/grammar/types.rs +++ b/crates/mun_syntax/src/parsing/grammar/types.rs @@ -3,7 +3,7 @@ use super::*; pub(super) const TYPE_RECOVERY_SET: TokenSet = token_set![R_PAREN, COMMA]; pub(super) fn ascription(p: &mut Parser) { - p.expect(COLON); + p.expect(T![:]); type_(p); } diff --git a/crates/mun_syntax/src/parsing/parser.rs b/crates/mun_syntax/src/parsing/parser.rs index b7199665d..c8bf6c967 100644 --- a/crates/mun_syntax/src/parsing/parser.rs +++ b/crates/mun_syntax/src/parsing/parser.rs @@ -12,17 +12,15 @@ use std::cell::Cell; /// events of the form 'start expression, consume number literal, finish espression'. See `Event` /// docs for more info. pub(crate) struct Parser<'t> { - token_source: &'t dyn TokenSource, - token_pos: usize, + token_source: &'t mut dyn TokenSource, events: Vec, steps: Cell, } impl<'t> Parser<'t> { - pub(super) fn new(token_source: &'t dyn TokenSource) -> Parser<'t> { + pub(super) fn new(token_source: &'t mut dyn TokenSource) -> Parser<'t> { Parser { token_source, - token_pos: 0, events: Vec::new(), steps: Cell::new(0), } @@ -38,53 +36,78 @@ impl<'t> Parser<'t> { self.nth(0) } - /// Returns the kind of the current two tokens, if they are not separated by trivia. - pub(crate) fn current2(&self) -> Option<(SyntaxKind, SyntaxKind)> { - let c1 = self.nth(0); - let c2 = self.nth(1); - - if self.token_source.is_token_joint_to_next(self.token_pos) { - Some((c1, c2)) - } else { - None - } - } - - /// Lookahead operation: returns the kind of the next nth token. + /// Lookahead operation: returns the kind of the next nth + /// token. pub(crate) fn nth(&self, n: usize) -> SyntaxKind { + assert!(n <= 3); + let steps = self.steps.get(); - assert!(steps <= 10_000, "the parser seems stuck"); + assert!(steps <= 10_000_000, "the parser seems stuck"); self.steps.set(steps + 1); - let mut i = 0; - let mut count = 0; - loop { - let mut kind = self.token_source.token_kind(self.token_pos + i); - if let Some((composited, step)) = self.is_composite(kind, i) { - kind = composited; - i += step; - } else { - i += 1; - } + self.token_source.lookahead_nth(n).kind + } - match kind { - EOF => return EOF, - _ if count == n => return kind, - _ => count += 1, - } + // Checks if the current token is `kind`. + pub(crate) fn at(&self, kind: SyntaxKind) -> bool { + self.nth_at(0, kind) + } + + pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool { + match kind { + T![-=] => self.at_composite2(n, T![-], T![=]), + //T![->] => self.at_composite2(n, T![-], T![>]), + T![::] => self.at_composite2(n, T![:], T![:]), + T![!=] => self.at_composite2(n, T![!], T![=]), + T![..] => self.at_composite2(n, T![.], T![.]), + T![*=] => self.at_composite2(n, T![*], T![=]), + T![/=] => self.at_composite2(n, T![/], T![=]), + //T![&&] => self.at_composite2(n, T![&], T![&]), + //T![&=] => self.at_composite2(n, T![&], T![=]), + //T![%=] => self.at_composite2(n, T![%], T![=]), + //T![^=] => self.at_composite2(n, T![^], T![=]), + T![+=] => self.at_composite2(n, T![+], T![=]), + //T![<<] => self.at_composite2(n, T![<], T![<]), + T![<=] => self.at_composite2(n, T![<], T![=]), + T![==] => self.at_composite2(n, T![=], T![=]), + //T![=>] => self.at_composite2(n, T![=], T![>]), + T![>=] => self.at_composite2(n, T![>], T![=]), + //T![>>] => self.at_composite2(n, T![>], T![>]), + //T![|=] => self.at_composite2(n, T![|], T![=]), + //T![||] => self.at_composite2(n, T![|], T![|]), + T![...] => self.at_composite3(n, T![.], T![.], T![.]), + //T![..=] => self.at_composite3(n, T![.], T![.], T![=]), + //T![<<=] => self.at_composite3(n, T![<], T![<], T![=]), + //T![>>=] => self.at_composite3(n, T![>], T![>], T![=]), + _ => self.token_source.lookahead_nth(n).kind == kind, } } - /// Checks if the current token is `kind` - pub(crate) fn matches(&self, kind: SyntaxKind) -> bool { - self.current() == kind + fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { + let t1 = self.token_source.lookahead_nth(n + 0); + let t2 = self.token_source.lookahead_nth(n + 1); + t1.kind == k1 && t1.is_jointed_to_next && t2.kind == k2 + } + + fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool { + let t1 = self.token_source.lookahead_nth(n + 0); + let t2 = self.token_source.lookahead_nth(n + 1); + let t3 = self.token_source.lookahead_nth(n + 2); + (t1.kind == k1 && t1.is_jointed_to_next) + && (t2.kind == k2 && t2.is_jointed_to_next) + && t3.kind == k3 } - /// Checks if the current tokens is in `kinds` - pub(crate) fn matches_any(&self, kinds: TokenSet) -> bool { + /// Checks if the current token is in `kinds`. + pub(crate) fn at_ts(&self, kinds: TokenSet) -> bool { kinds.contains(self.current()) } + // /// Checks if the current token is contextual keyword with text `t`. + // pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool { + // self.token_source.is_keyword(kw) + // } + /// Starts a new node in the syntax tree. All nodes and tokens consumed between the `start` and /// the corresponding `Marker::complete` belong to the same node. pub(crate) fn start(&mut self) -> Marker { @@ -93,57 +116,24 @@ impl<'t> Parser<'t> { Marker::new(pos) } - pub(crate) fn bump(&mut self) { + /// Consume the next token if `kind` matches. + pub(crate) fn bump(&mut self, kind: SyntaxKind) { + assert!(self.eat(kind), "kind != {:?}", kind); + } + + /// Advances the parser by one token with composite puncts handled + pub(crate) fn bump_any(&mut self) { let kind = self.nth(0); if kind == EOF { return; } - - use SyntaxKind::*; - match kind { - DOTDOTDOT => { - self.bump_compound(kind, 3); - } - DOTDOT | COLONCOLON | EQEQ => { - self.bump_compound(kind, 2); - } - _ => { - self.do_bump(kind, 1); - } - } - } - - pub fn is_composite(&self, kind: SyntaxKind, n: usize) -> Option<(SyntaxKind, usize)> { - let joint1 = self.token_source.is_token_joint_to_next(self.token_pos + n); - let kind1 = self.token_source.token_kind(self.token_pos + n + 1); - let joint2 = self - .token_source - .is_token_joint_to_next(self.token_pos + n + 1); - let kind2 = self.token_source.token_kind(self.token_pos + n + 2); - - use SyntaxKind::*; - - // This does not match all the multi character symbols because they are still context - // sensitive (for example `+=` and `..=`). - match kind { - DOT if joint1 && kind1 == DOT && joint2 && kind2 == DOT => Some((DOTDOTDOT, 3)), - DOT if joint1 && kind1 == DOT => Some((DOTDOT, 2)), - - COLON if joint1 && kind1 == COLON => Some((COLONCOLON, 2)), - EQ if joint2 && kind1 == EQ => Some((EQEQ, 2)), - - _ => None, - } - } - - /// Advances the parser by `n` tokens, remapping its kind. This is useful to create compound - /// tokens from parts. For example an `::` is two consecutive remapped `:` tokens. - pub(crate) fn bump_compound(&mut self, kind: SyntaxKind, n: u8) { - self.do_bump(kind, n); + self.do_bump(kind, 1) } fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) { - self.token_pos += n_raw_tokens as usize; + for _ in 0..n_raw_tokens { + self.token_source.bump(); + } self.push_event(Event::Token { kind, n_raw_tokens }); } @@ -160,12 +150,12 @@ impl<'t> Parser<'t> { /// Create an error node and consume the next token. pub(crate) fn error_recover(&mut self, message: &str, recovery: TokenSet) { - if self.matches(L_CURLY) || self.matches(R_CURLY) || self.matches_any(recovery) { + if self.at(T!['{']) || self.at(T!['{']) || self.at_ts(recovery) { self.error(message); } else { let m = self.start(); self.error(message); - self.bump(); + self.bump_any(); m.complete(self, ERROR); } } @@ -176,12 +166,41 @@ impl<'t> Parser<'t> { /// Consume the next token if `kind` matches. pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool { - if self.matches(kind) { - self.bump(); - true - } else { - false + if !self.at(kind) { + return false; } + let n_raw_tokens = match kind { + T![-=] + //| T![->] + | T![::] + | T![!=] + | T![..] + | T![*=] + | T![/=] + //| T![&&] + //| T![&=] + //| T![%=] + //| T![^=] + | T![+=] + //| T![<<] + | T![<=] + | T![==] + //| T![=>] + | T![>=] + //| T![>>] + //| T![|=] + //| T![||] + => 2, + + T![...] + //| T![..=] + //| T![<<=] + //| T![>>=] + => 3, + _ => 1, + }; + self.do_bump(kind, n_raw_tokens); + true } /// Consume the next token if it is `kind` or emit an error otherwise. diff --git a/crates/mun_syntax/src/parsing/text_token_source.rs b/crates/mun_syntax/src/parsing/text_token_source.rs index 2cf120c80..a82a30200 100644 --- a/crates/mun_syntax/src/parsing/text_token_source.rs +++ b/crates/mun_syntax/src/parsing/text_token_source.rs @@ -1,39 +1,79 @@ use crate::{ - parsing::{lexer::Token, TokenSource}, - SyntaxKind::{self, *}, + parsing::{lexer::Token, Token as PToken, TokenSource}, + SyntaxKind::*, TextUnit, }; +use text_unit::TextRange; /// An implementation of `TokenSource` for text. -pub(crate) struct TextTokenSource { - /// Holds the start position of each token +pub(crate) struct TextTokenSource<'t> { + text: &'t str, + /// start position of each token(expect whitespace and comment) + /// ```non-rust + /// struct Foo; + /// ^------^--- + /// | | ^- + /// 0 7 10 + /// ``` + /// (token, start_offset): `[(struct, 0), (Foo, 7), (;, 10)]` start_offsets: Vec, - - /// Non-whitespace/comment tokens + /// non-whitespace/comment tokens + /// ```non-rust + /// struct Foo {} + /// ^^^^^^ ^^^ ^^ + /// ``` + /// tokens: `[struct, Foo, {, }]` tokens: Vec, + + /// Current token and position + curr: (PToken, usize), } -impl TokenSource for TextTokenSource { - fn token_kind(&self, pos: usize) -> SyntaxKind { - if pos >= self.tokens.len() { - EOF - } else { - self.tokens[pos].kind +impl<'t> TokenSource for TextTokenSource<'t> { + fn current(&self) -> PToken { + self.curr.0 + } + + fn lookahead_nth(&self, n: usize) -> PToken { + mk_token(self.curr.1 + n, &self.start_offsets, &self.tokens) + } + + fn bump(&mut self) { + if self.curr.0.kind == EOF { + return; } + + let pos = self.curr.1 + 1; + self.curr = (mk_token(pos, &self.start_offsets, &self.tokens), pos); } - fn is_token_joint_to_next(&self, pos: usize) -> bool { - if (pos + 1) >= self.tokens.len() { - true - } else { - self.start_offsets[pos] + self.tokens[pos].len == self.start_offsets[pos + 1] + fn is_keyword(&self, kw: &str) -> bool { + let pos = self.curr.1; + if !(pos < self.tokens.len()) { + return false; } + let range = TextRange::offset_len(self.start_offsets[pos], self.tokens[pos].len); + self.text[range] == *kw + } +} + +fn mk_token(pos: usize, start_offsets: &[TextUnit], tokens: &[Token]) -> PToken { + let kind = tokens.get(pos).map(|t| t.kind).unwrap_or(EOF); + let is_jointed_to_next = if pos + 1 < start_offsets.len() { + start_offsets[pos] + tokens[pos].len == start_offsets[pos + 1] + } else { + false + }; + + PToken { + kind, + is_jointed_to_next, } } -impl TextTokenSource { - /// Generate input for tokens (expect comment and whitespace). - pub fn new(_text: &str, raw_tokens: &[Token]) -> Self { +impl<'t> TextTokenSource<'t> { + /// Generate input from tokens(expect comment and whitespace). + pub fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> { let mut tokens = Vec::new(); let mut start_offsets = Vec::new(); let mut len = 0.into(); @@ -45,9 +85,12 @@ impl TextTokenSource { len += token.len; } + let first = mk_token(0, &start_offsets, &tokens); TextTokenSource { + text, start_offsets, tokens, + curr: (first, 0), } } } diff --git a/crates/mun_syntax/src/parsing/token_set.rs b/crates/mun_syntax/src/parsing/token_set.rs index a9091c459..69d8b0d15 100644 --- a/crates/mun_syntax/src/parsing/token_set.rs +++ b/crates/mun_syntax/src/parsing/token_set.rs @@ -37,6 +37,6 @@ fn token_set_works_for_tokens() { use crate::SyntaxKind::*; let ts = token_set![EOF, EQ]; assert!(ts.contains(EOF)); - assert!(ts.contains(EQ)); + assert!(ts.contains(T![=])); assert!(!ts.contains(PLUS)); } diff --git a/crates/mun_syntax/src/syntax_kind/generated.rs b/crates/mun_syntax/src/syntax_kind/generated.rs index 42f8aa12a..b02420e2b 100644 --- a/crates/mun_syntax/src/syntax_kind/generated.rs +++ b/crates/mun_syntax/src/syntax_kind/generated.rs @@ -35,6 +35,7 @@ pub enum SyntaxKind { SEMI, COLON, COMMA, + EXCLAMATION, UNDERSCORE, EQEQ, NEQ, @@ -60,7 +61,6 @@ pub enum SyntaxKind { IF_KW, IN_KW, NIL_KW, - NOT_KW, OR_KW, SELF_KW, SUPER_KW, @@ -133,6 +133,7 @@ macro_rules! T { (;) => { $crate::SyntaxKind::SEMI }; (:) => { $crate::SyntaxKind::COLON }; (,) => { $crate::SyntaxKind::COMMA }; + (!) => { $crate::SyntaxKind::EXCLAMATION }; (_) => { $crate::SyntaxKind::UNDERSCORE }; (==) => { $crate::SyntaxKind::EQEQ }; (!=) => { $crate::SyntaxKind::NEQ }; @@ -158,7 +159,6 @@ macro_rules! T { (if) => { $crate::SyntaxKind::IF_KW }; (in) => { $crate::SyntaxKind::IN_KW }; (nil) => { $crate::SyntaxKind::NIL_KW }; - (not) => { $crate::SyntaxKind::NOT_KW }; (or) => { $crate::SyntaxKind::OR_KW }; (self) => { $crate::SyntaxKind::SELF_KW }; (super) => { $crate::SyntaxKind::SUPER_KW }; @@ -201,7 +201,6 @@ impl SyntaxKind { | IF_KW | IN_KW | NIL_KW - | NOT_KW | OR_KW | SELF_KW | SUPER_KW @@ -243,6 +242,7 @@ impl SyntaxKind { | SEMI | COLON | COMMA + | EXCLAMATION | UNDERSCORE | EQEQ | NEQ @@ -295,6 +295,7 @@ impl SyntaxKind { SEMI => &SyntaxInfo { name: "SEMI" }, COLON => &SyntaxInfo { name: "COLON" }, COMMA => &SyntaxInfo { name: "COMMA" }, + EXCLAMATION => &SyntaxInfo { name: "EXCLAMATION" }, UNDERSCORE => &SyntaxInfo { name: "UNDERSCORE" }, EQEQ => &SyntaxInfo { name: "EQEQ" }, NEQ => &SyntaxInfo { name: "NEQ" }, @@ -320,7 +321,6 @@ impl SyntaxKind { IF_KW => &SyntaxInfo { name: "IF_KW" }, IN_KW => &SyntaxInfo { name: "IN_KW" }, NIL_KW => &SyntaxInfo { name: "NIL_KW" }, - NOT_KW => &SyntaxInfo { name: "NOT_KW" }, OR_KW => &SyntaxInfo { name: "OR_KW" }, SELF_KW => &SyntaxInfo { name: "SELF_KW" }, SUPER_KW => &SyntaxInfo { name: "SUPER_KW" }, @@ -383,7 +383,6 @@ impl SyntaxKind { "if" => IF_KW, "in" => IN_KW, "nil" => NIL_KW, - "not" => NOT_KW, "or" => OR_KW, "self" => SELF_KW, "super" => SUPER_KW, @@ -425,9 +424,13 @@ impl SyntaxKind { ';' => SEMI, ':' => COLON, ',' => COMMA, + '!' => EXCLAMATION, '_' => UNDERSCORE, _ => return None, }; Some(tok) } } + + + diff --git a/crates/mun_syntax/src/tests.rs b/crates/mun_syntax/src/tests.rs index b56e91620..5333bc75f 100644 --- a/crates/mun_syntax/src/tests.rs +++ b/crates/mun_syntax/src/tests.rs @@ -1,43 +1,2 @@ -use crate::SourceFile; -use std::{fmt::Write, path::PathBuf}; -use test_utils::{dir_tests, project_dir}; - -#[test] -fn lexer_tests() { - dir_tests(&test_data_dir(), &["lexer"], |text, _| { - let tokens = crate::tokenize(text); - dump_tokens(&tokens, text) - }); -} - -#[test] -fn parser_tests() { - dir_tests(&test_data_dir(), &["parser/ok"], |text, path| { - let file = SourceFile::parse(text); - let errors = file.errors(); - assert_eq!( - &*errors, - &[] as &[crate::SyntaxError], - "There should be no errors in the file {:?}", - path.display() - ); - file.debug_dump() - }) -} - -fn test_data_dir() -> PathBuf { - project_dir().join("crates/mun_syntax/tests/data") -} - -fn dump_tokens(tokens: &[crate::Token], text: &str) -> String { - let mut acc = String::new(); - let mut offset = 0; - for token in tokens { - let len: u32 = token.len.into(); - let len = len as usize; - let token_text = &text[offset..offset + len]; - offset += len; - write!(acc, "{:?} {} {:?}\n", token.kind, token.len, token_text).unwrap() - } - acc -} +mod lexer; +mod parser; diff --git a/crates/mun_syntax/src/tests/lexer.rs b/crates/mun_syntax/src/tests/lexer.rs new file mode 100644 index 000000000..7897d17cd --- /dev/null +++ b/crates/mun_syntax/src/tests/lexer.rs @@ -0,0 +1,137 @@ +use std::fmt::Write; + +fn dump_tokens(tokens: &[crate::Token], text: &str) -> String { + let mut acc = String::new(); + let mut offset = 0; + for token in tokens { + let len: u32 = token.len.into(); + let len = len as usize; + let token_text = &text[offset..offset + len]; + offset += len; + write!(acc, "{:?} {} {:?}\n", token.kind, token.len, token_text).unwrap() + } + acc +} + +fn lex_snapshot(text: &str) { + let text = text.trim().replace("\n ", "\n"); + let tokens = crate::tokenize(&text); + insta::assert_snapshot!( + insta::_macro_support::AutoName, + dump_tokens(&tokens, &text), + &text + ); +} + +#[test] +fn numbers() { + lex_snapshot( + r#" + 1.34 + 0x3Af + 1e-3 + 100_000"#, + ) +} + +#[test] +fn comments() { + lex_snapshot( + r#" + // hello, world! + /**/ + /* block comment */ + /* multi + line + comment */ + /* /* nested */ */ + /* unclosed comment"#, + ) +} + +#[test] +fn whitespace() { + lex_snapshot( + r#" + h e ll o + w + + o r l d"#, + ) +} + +#[test] +fn ident() { + lex_snapshot( + r#" + hello world_ _a2 _ __ x 即可编著课程 + "#, + ) +} + +#[test] +fn symbols() { + lex_snapshot( + r#" + # ( ) { } [ ] ; , + = == + != + < <= + > >= + . .. ... ..= + + += + - -= + * *= + / /= + ^ ^= + % %= + : :: + "#, + ) +} + +#[test] +fn strings() { + lex_snapshot( + r#" + "Hello, world!" + 'Hello, world!' + "\n" + "\"\\" + "multi + line" + "#, + ) +} + +#[test] +fn keywords() { + lex_snapshot( + r#" + and break do else false for fn if in nil or + return then true while let mut class public protected + private + "#, + ) +} + +#[test] +fn unclosed_string() { + lex_snapshot( + r#" + "test + "#, + ) +} + +#[test] +fn binary_cmp() { + lex_snapshot( + r#" + a==b + a ==b + a== b + a == b + "#, + ) +} diff --git a/crates/mun_syntax/src/tests/parser.rs b/crates/mun_syntax/src/tests/parser.rs new file mode 100644 index 000000000..61c289660 --- /dev/null +++ b/crates/mun_syntax/src/tests/parser.rs @@ -0,0 +1,141 @@ +use crate::SourceFile; + +fn ok_snapshot_test(text: &str) { + let text = text.trim().replace("\n ", "\n"); + let file = SourceFile::parse(&text); + let errors = file.errors(); + assert_eq!( + &*errors, + &[] as &[crate::SyntaxError], + "There should be no errors\nAST:\n{}", + file.debug_dump() + ); + insta::assert_snapshot!(insta::_macro_support::AutoName, file.debug_dump(), &text); +} + +#[test] +fn empty() { + ok_snapshot_test(r#""#); +} + +#[test] +fn function() { + ok_snapshot_test( + r#" + // Source file comment + + // Comment that belongs to the function + fn a() {} + fn b(value:number) {} + export fn c() {} + fn b(value:number):number {}"#, + ); +} + +#[test] +fn block() { + ok_snapshot_test( + r#" + fn foo() { + let a; + let b:i32; + let c:string; + }"#, + ); +} + +#[test] +fn literals() { + ok_snapshot_test( + r#" + fn foo() { + let a = true; + let b = false; + let c = 1; + let d = 1.12; + let e = "Hello, world!" + } + "#, + ); +} + +#[test] +fn unary_expr() { + ok_snapshot_test( + r#" + fn foo() { + let a = --3; + let b = !!true; + } + "#, + ) +} + +#[test] +fn binary_expr() { + ok_snapshot_test( + r#" + fn foo() { + let a = 3+4*5 + let b = 3*4+10/2 + } + "#, + ) +} + +#[test] +fn expression_statement() { + ok_snapshot_test( + r#" + fn foo() { + let a = "hello" + let b = "world" + let c + b = "Hello, world!" + !-5+2*(a+b); + -3 + } + "#, + ) +} + +#[test] +fn function_calls() { + ok_snapshot_test( + r#" + fn bar(i:number) { } + fn foo(i:number) { + bar(i+1) + } + "#, + ) +} + +#[test] +fn patterns() { + ok_snapshot_test( + r#" + fn main(_:number) { + let a = 0; + let _ = a; + } + "#, + ) +} + +#[test] +fn compare_operands() { + ok_snapshot_test( + r#" + fn main() { + let _ = a==b; + let _ = a == b; + let _ = a != b; + let _ = a < b; + let _ = a > b; + let _ = a <= b; + let _ = a >= b; + } + "#, + ) +} diff --git a/crates/mun_syntax/src/tests/snapshots/lexer__binary_cmp.snap b/crates/mun_syntax/src/tests/snapshots/lexer__binary_cmp.snap new file mode 100644 index 000000000..bc4813a9f --- /dev/null +++ b/crates/mun_syntax/src/tests/snapshots/lexer__binary_cmp.snap @@ -0,0 +1,28 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "a==b\na ==b\na== b\na == b" +--- +IDENT 1 "a" +EQ 1 "=" +EQ 1 "=" +IDENT 1 "b" +WHITESPACE 1 "\n" +IDENT 1 "a" +WHITESPACE 1 " " +EQ 1 "=" +EQ 1 "=" +IDENT 1 "b" +WHITESPACE 1 "\n" +IDENT 1 "a" +EQ 1 "=" +EQ 1 "=" +WHITESPACE 1 " " +IDENT 1 "b" +WHITESPACE 1 "\n" +IDENT 1 "a" +WHITESPACE 1 " " +EQ 1 "=" +EQ 1 "=" +WHITESPACE 1 " " +IDENT 1 "b" + diff --git a/crates/mun_syntax/tests/data/lexer/0002_comments.txt b/crates/mun_syntax/src/tests/snapshots/lexer__comments.snap similarity index 60% rename from crates/mun_syntax/tests/data/lexer/0002_comments.txt rename to crates/mun_syntax/src/tests/snapshots/lexer__comments.snap index 0002349e9..57db7f49a 100644 --- a/crates/mun_syntax/tests/data/lexer/0002_comments.txt +++ b/crates/mun_syntax/src/tests/snapshots/lexer__comments.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "// hello, world!\n/**/\n/* block comment */\n/* multi\n line\n comment */\n/* /* nested */ */\n/* unclosed comment" +--- COMMENT 16 "// hello, world!" WHITESPACE 1 "\n" COMMENT 4 "/**/" @@ -9,3 +13,4 @@ WHITESPACE 1 "\n" COMMENT 18 "/* /* nested */ */" WHITESPACE 1 "\n" COMMENT 19 "/* unclosed comment" + diff --git a/crates/mun_syntax/tests/data/lexer/0004_ident.txt b/crates/mun_syntax/src/tests/snapshots/lexer__ident.snap similarity index 66% rename from crates/mun_syntax/tests/data/lexer/0004_ident.txt rename to crates/mun_syntax/src/tests/snapshots/lexer__ident.snap index 7e8c913b7..4e5359b5c 100644 --- a/crates/mun_syntax/tests/data/lexer/0004_ident.txt +++ b/crates/mun_syntax/src/tests/snapshots/lexer__ident.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: hello world_ _a2 _ __ x 即可编著课程 +--- IDENT 5 "hello" WHITESPACE 1 " " IDENT 6 "world_" @@ -11,3 +15,4 @@ WHITESPACE 1 " " IDENT 1 "x" WHITESPACE 1 " " IDENT 18 "即可编著课程" + diff --git a/crates/mun_syntax/tests/data/lexer/0007_keywords.txt b/crates/mun_syntax/src/tests/snapshots/lexer__keywords.snap similarity index 79% rename from crates/mun_syntax/tests/data/lexer/0007_keywords.txt rename to crates/mun_syntax/src/tests/snapshots/lexer__keywords.snap index 239ea9ba7..d10f45b48 100644 --- a/crates/mun_syntax/tests/data/lexer/0007_keywords.txt +++ b/crates/mun_syntax/src/tests/snapshots/lexer__keywords.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "and break do else false for fn if in nil or\nreturn then true while let mut class public protected\nprivate" +--- AND_KW 3 "and" WHITESPACE 1 " " BREAK_KW 5 "break" @@ -18,8 +22,6 @@ IN_KW 2 "in" WHITESPACE 1 " " NIL_KW 3 "nil" WHITESPACE 1 " " -NOT_KW 3 "not" -WHITESPACE 1 " " OR_KW 2 "or" WHITESPACE 1 "\n" RETURN_KW 6 "return" @@ -41,3 +43,4 @@ WHITESPACE 1 " " PROTECTED_KW 9 "protected" WHITESPACE 1 "\n" PRIVATE_KW 7 "private" + diff --git a/crates/mun_syntax/tests/data/lexer/0001_numbers.txt b/crates/mun_syntax/src/tests/snapshots/lexer__numbers.snap similarity index 59% rename from crates/mun_syntax/tests/data/lexer/0001_numbers.txt rename to crates/mun_syntax/src/tests/snapshots/lexer__numbers.snap index a86c7e7f2..04dfdedd8 100644 --- a/crates/mun_syntax/tests/data/lexer/0001_numbers.txt +++ b/crates/mun_syntax/src/tests/snapshots/lexer__numbers.snap @@ -1,5 +1,7 @@ -INT_NUMBER 1 "1" -WHITESPACE 1 "\n" +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "1.34\n0x3Af\n1e-3\n100_000" +--- FLOAT_NUMBER 4 "1.34" WHITESPACE 1 "\n" INT_NUMBER 5 "0x3Af" @@ -7,3 +9,4 @@ WHITESPACE 1 "\n" FLOAT_NUMBER 4 "1e-3" WHITESPACE 1 "\n" INT_NUMBER 7 "100_000" + diff --git a/crates/mun_syntax/tests/data/lexer/0006_strings.txt b/crates/mun_syntax/src/tests/snapshots/lexer__strings.snap similarity index 58% rename from crates/mun_syntax/tests/data/lexer/0006_strings.txt rename to crates/mun_syntax/src/tests/snapshots/lexer__strings.snap index 5400f20d6..83b79dd62 100644 --- a/crates/mun_syntax/tests/data/lexer/0006_strings.txt +++ b/crates/mun_syntax/src/tests/snapshots/lexer__strings.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "\"Hello, world!\"\n'Hello, world!'\n\"\\n\"\n\"\\\"\\\\\"\n\"multi\nline\"" +--- STRING 15 "\"Hello, world!\"" WHITESPACE 1 "\n" STRING 15 "\'Hello, world!\'" @@ -7,3 +11,4 @@ WHITESPACE 1 "\n" STRING 6 "\"\\\"\\\\\"" WHITESPACE 1 "\n" STRING 12 "\"multi\nline\"" + diff --git a/crates/mun_syntax/tests/data/lexer/0005_symbols.txt b/crates/mun_syntax/src/tests/snapshots/lexer__symbols.snap similarity index 85% rename from crates/mun_syntax/tests/data/lexer/0005_symbols.txt rename to crates/mun_syntax/src/tests/snapshots/lexer__symbols.snap index 5a4d8f0ae..b4c0b6419 100644 --- a/crates/mun_syntax/tests/data/lexer/0005_symbols.txt +++ b/crates/mun_syntax/src/tests/snapshots/lexer__symbols.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "# ( ) { } [ ] ; ,\n= ==\n!=\n< <=\n> >=\n. .. ... ..=\n+ +=\n- -=\n* *=\n/ /=\n^ ^=\n% %=\n: ::" +--- HASH 1 "#" WHITESPACE 1 " " L_PAREN 1 "(" @@ -21,7 +25,8 @@ WHITESPACE 1 " " EQ 1 "=" EQ 1 "=" WHITESPACE 1 "\n" -NEQ 2 "!=" +EXCLAMATION 1 "!" +EQ 1 "=" WHITESPACE 1 "\n" LT 1 "<" WHITESPACE 1 " " @@ -80,3 +85,4 @@ COLON 1 ":" WHITESPACE 1 " " COLON 1 ":" COLON 1 ":" + diff --git a/crates/mun_syntax/src/tests/snapshots/lexer__unclosed_string.snap b/crates/mun_syntax/src/tests/snapshots/lexer__unclosed_string.snap new file mode 100644 index 000000000..086d15bdb --- /dev/null +++ b/crates/mun_syntax/src/tests/snapshots/lexer__unclosed_string.snap @@ -0,0 +1,6 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "\"test" +--- +STRING 5 "\"test" + diff --git a/crates/mun_syntax/tests/data/lexer/0003_whitespace.txt b/crates/mun_syntax/src/tests/snapshots/lexer__whitespace.snap similarity index 72% rename from crates/mun_syntax/tests/data/lexer/0003_whitespace.txt rename to crates/mun_syntax/src/tests/snapshots/lexer__whitespace.snap index 4348b5df2..89e789034 100644 --- a/crates/mun_syntax/tests/data/lexer/0003_whitespace.txt +++ b/crates/mun_syntax/src/tests/snapshots/lexer__whitespace.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/lexer.rs +expression: "h e ll o\nw\n\no r l d" +--- IDENT 1 "h" WHITESPACE 1 " " IDENT 1 "e" @@ -15,3 +19,4 @@ WHITESPACE 5 " " IDENT 1 "l" WHITESPACE 3 " " IDENT 1 "d" + diff --git a/crates/mun_syntax/tests/data/parser/ok/0005_binary_expr.txt b/crates/mun_syntax/src/tests/snapshots/parser__binary_expr.snap similarity index 93% rename from crates/mun_syntax/tests/data/parser/ok/0005_binary_expr.txt rename to crates/mun_syntax/src/tests/snapshots/parser__binary_expr.snap index b1f3a76d4..08696b2bd 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0005_binary_expr.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__binary_expr.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn foo() {\n let a = 3+4*5\n let b = 3*4+10/2\n}" +--- SOURCE_FILE@[0; 51) FUNCTION_DEF@[0; 51) FN_KW@[0; 2) "fn" @@ -56,3 +60,4 @@ SOURCE_FILE@[0; 51) INT_NUMBER@[48; 49) "2" WHITESPACE@[49; 50) "\n" R_CURLY@[50; 51) "}" + diff --git a/crates/mun_syntax/tests/data/parser/ok/0002_block.txt b/crates/mun_syntax/src/tests/snapshots/parser__block.snap similarity index 91% rename from crates/mun_syntax/tests/data/parser/ok/0002_block.txt rename to crates/mun_syntax/src/tests/snapshots/parser__block.snap index 0d13b7e53..7b3c17031 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0002_block.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__block.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn foo() {\n let a;\n let b:i32;\n let c:string;\n}" +--- SOURCE_FILE@[0; 56) FUNCTION_DEF@[0; 56) FN_KW@[0; 2) "fn" @@ -48,3 +52,4 @@ SOURCE_FILE@[0; 56) SEMI@[53; 54) ";" WHITESPACE@[54; 55) "\n" R_CURLY@[55; 56) "}" + diff --git a/crates/mun_syntax/src/tests/snapshots/parser__compare_operands.snap b/crates/mun_syntax/src/tests/snapshots/parser__compare_operands.snap new file mode 100644 index 000000000..01e0144f2 --- /dev/null +++ b/crates/mun_syntax/src/tests/snapshots/parser__compare_operands.snap @@ -0,0 +1,185 @@ +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn main() {\n let _ = a==b;\n let _ = a == b;\n let _ = a != b;\n let _ = a < b;\n let _ = a > b;\n let _ = a <= b;\n let _ = a >= b;\n}" +--- +SOURCE_FILE@[0; 149) + FUNCTION_DEF@[0; 149) + FN_KW@[0; 2) "fn" + WHITESPACE@[2; 3) " " + NAME@[3; 7) + IDENT@[3; 7) "main" + PARAM_LIST@[7; 9) + L_PAREN@[7; 8) "(" + R_PAREN@[8; 9) ")" + WHITESPACE@[9; 10) " " + BLOCK@[10; 149) + L_CURLY@[10; 11) "{" + WHITESPACE@[11; 16) "\n " + LET_STMT@[16; 29) + LET_KW@[16; 19) "let" + WHITESPACE@[19; 20) " " + PLACEHOLDER_PAT@[20; 21) + UNDERSCORE@[20; 21) "_" + WHITESPACE@[21; 22) " " + EQ@[22; 23) "=" + WHITESPACE@[23; 24) " " + BIN_EXPR@[24; 28) + PATH_EXPR@[24; 25) + PATH@[24; 25) + PATH_SEGMENT@[24; 25) + NAME_REF@[24; 25) + IDENT@[24; 25) "a" + EQEQ@[25; 27) "==" + PATH_EXPR@[27; 28) + PATH@[27; 28) + PATH_SEGMENT@[27; 28) + NAME_REF@[27; 28) + IDENT@[27; 28) "b" + SEMI@[28; 29) ";" + WHITESPACE@[29; 34) "\n " + LET_STMT@[34; 49) + LET_KW@[34; 37) "let" + WHITESPACE@[37; 38) " " + PLACEHOLDER_PAT@[38; 39) + UNDERSCORE@[38; 39) "_" + WHITESPACE@[39; 40) " " + EQ@[40; 41) "=" + WHITESPACE@[41; 42) " " + BIN_EXPR@[42; 48) + PATH_EXPR@[42; 43) + PATH@[42; 43) + PATH_SEGMENT@[42; 43) + NAME_REF@[42; 43) + IDENT@[42; 43) "a" + WHITESPACE@[43; 44) " " + EQEQ@[44; 46) "==" + WHITESPACE@[46; 47) " " + PATH_EXPR@[47; 48) + PATH@[47; 48) + PATH_SEGMENT@[47; 48) + NAME_REF@[47; 48) + IDENT@[47; 48) "b" + SEMI@[48; 49) ";" + WHITESPACE@[49; 54) "\n " + LET_STMT@[54; 69) + LET_KW@[54; 57) "let" + WHITESPACE@[57; 58) " " + PLACEHOLDER_PAT@[58; 59) + UNDERSCORE@[58; 59) "_" + WHITESPACE@[59; 60) " " + EQ@[60; 61) "=" + WHITESPACE@[61; 62) " " + BIN_EXPR@[62; 68) + PATH_EXPR@[62; 63) + PATH@[62; 63) + PATH_SEGMENT@[62; 63) + NAME_REF@[62; 63) + IDENT@[62; 63) "a" + WHITESPACE@[63; 64) " " + NEQ@[64; 66) "!=" + WHITESPACE@[66; 67) " " + PATH_EXPR@[67; 68) + PATH@[67; 68) + PATH_SEGMENT@[67; 68) + NAME_REF@[67; 68) + IDENT@[67; 68) "b" + SEMI@[68; 69) ";" + WHITESPACE@[69; 74) "\n " + LET_STMT@[74; 88) + LET_KW@[74; 77) "let" + WHITESPACE@[77; 78) " " + PLACEHOLDER_PAT@[78; 79) + UNDERSCORE@[78; 79) "_" + WHITESPACE@[79; 80) " " + EQ@[80; 81) "=" + WHITESPACE@[81; 82) " " + BIN_EXPR@[82; 87) + PATH_EXPR@[82; 83) + PATH@[82; 83) + PATH_SEGMENT@[82; 83) + NAME_REF@[82; 83) + IDENT@[82; 83) "a" + WHITESPACE@[83; 84) " " + LT@[84; 85) "<" + WHITESPACE@[85; 86) " " + PATH_EXPR@[86; 87) + PATH@[86; 87) + PATH_SEGMENT@[86; 87) + NAME_REF@[86; 87) + IDENT@[86; 87) "b" + SEMI@[87; 88) ";" + WHITESPACE@[88; 93) "\n " + LET_STMT@[93; 107) + LET_KW@[93; 96) "let" + WHITESPACE@[96; 97) " " + PLACEHOLDER_PAT@[97; 98) + UNDERSCORE@[97; 98) "_" + WHITESPACE@[98; 99) " " + EQ@[99; 100) "=" + WHITESPACE@[100; 101) " " + BIN_EXPR@[101; 106) + PATH_EXPR@[101; 102) + PATH@[101; 102) + PATH_SEGMENT@[101; 102) + NAME_REF@[101; 102) + IDENT@[101; 102) "a" + WHITESPACE@[102; 103) " " + GT@[103; 104) ">" + WHITESPACE@[104; 105) " " + PATH_EXPR@[105; 106) + PATH@[105; 106) + PATH_SEGMENT@[105; 106) + NAME_REF@[105; 106) + IDENT@[105; 106) "b" + SEMI@[106; 107) ";" + WHITESPACE@[107; 112) "\n " + LET_STMT@[112; 127) + LET_KW@[112; 115) "let" + WHITESPACE@[115; 116) " " + PLACEHOLDER_PAT@[116; 117) + UNDERSCORE@[116; 117) "_" + WHITESPACE@[117; 118) " " + EQ@[118; 119) "=" + WHITESPACE@[119; 120) " " + BIN_EXPR@[120; 126) + PATH_EXPR@[120; 121) + PATH@[120; 121) + PATH_SEGMENT@[120; 121) + NAME_REF@[120; 121) + IDENT@[120; 121) "a" + WHITESPACE@[121; 122) " " + LTEQ@[122; 124) "<=" + WHITESPACE@[124; 125) " " + PATH_EXPR@[125; 126) + PATH@[125; 126) + PATH_SEGMENT@[125; 126) + NAME_REF@[125; 126) + IDENT@[125; 126) "b" + SEMI@[126; 127) ";" + WHITESPACE@[127; 132) "\n " + LET_STMT@[132; 147) + LET_KW@[132; 135) "let" + WHITESPACE@[135; 136) " " + PLACEHOLDER_PAT@[136; 137) + UNDERSCORE@[136; 137) "_" + WHITESPACE@[137; 138) " " + EQ@[138; 139) "=" + WHITESPACE@[139; 140) " " + BIN_EXPR@[140; 146) + PATH_EXPR@[140; 141) + PATH@[140; 141) + PATH_SEGMENT@[140; 141) + NAME_REF@[140; 141) + IDENT@[140; 141) "a" + WHITESPACE@[141; 142) " " + GTEQ@[142; 144) ">=" + WHITESPACE@[144; 145) " " + PATH_EXPR@[145; 146) + PATH@[145; 146) + PATH_SEGMENT@[145; 146) + NAME_REF@[145; 146) + IDENT@[145; 146) "b" + SEMI@[146; 147) ";" + WHITESPACE@[147; 148) "\n" + R_CURLY@[148; 149) "}" + diff --git a/crates/mun_syntax/src/tests/snapshots/parser__empty.snap b/crates/mun_syntax/src/tests/snapshots/parser__empty.snap new file mode 100644 index 000000000..2bd6d433c --- /dev/null +++ b/crates/mun_syntax/src/tests/snapshots/parser__empty.snap @@ -0,0 +1,6 @@ +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: file.debug_dump() +--- +SOURCE_FILE@[0; 0) + diff --git a/crates/mun_syntax/tests/data/parser/ok/0006_expr_stmt.txt b/crates/mun_syntax/src/tests/snapshots/parser__expression_statement.snap similarity index 51% rename from crates/mun_syntax/tests/data/parser/ok/0006_expr_stmt.txt rename to crates/mun_syntax/src/tests/snapshots/parser__expression_statement.snap index ab09bd51d..83e3ad90e 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0006_expr_stmt.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__expression_statement.snap @@ -1,5 +1,9 @@ -SOURCE_FILE@[0; 113) - FUNCTION_DEF@[0; 113) +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn foo() {\n let a = \"hello\"\n let b = \"world\"\n let c\n b = \"Hello, world!\"\n !-5+2*(a+b);\n -3\n}" +--- +SOURCE_FILE@[0; 110) + FUNCTION_DEF@[0; 110) FN_KW@[0; 2) "fn" WHITESPACE@[2; 3) " " NAME@[3; 6) @@ -8,7 +12,7 @@ SOURCE_FILE@[0; 113) L_PAREN@[6; 7) "(" R_PAREN@[7; 8) ")" WHITESPACE@[8; 9) " " - BLOCK@[9; 113) + BLOCK@[9; 110) L_CURLY@[9; 10) "{" WHITESPACE@[10; 15) "\n " LET_STMT@[15; 30) @@ -55,40 +59,40 @@ SOURCE_FILE@[0; 113) LITERAL@[69; 84) STRING@[69; 84) "\"Hello, world!\"" WHITESPACE@[84; 89) "\n " - EXPR_STMT@[89; 104) - BIN_EXPR@[89; 103) - PREFIX_EXPR@[89; 95) - NOT_KW@[89; 92) "not" - WHITESPACE@[92; 93) " " - PREFIX_EXPR@[93; 95) - MINUS@[93; 94) "-" - LITERAL@[94; 95) - INT_NUMBER@[94; 95) "5" - PLUS@[95; 96) "+" - BIN_EXPR@[96; 103) - LITERAL@[96; 97) - INT_NUMBER@[96; 97) "2" - STAR@[97; 98) "*" - PAREN_EXPR@[98; 103) - L_PAREN@[98; 99) "(" - BIN_EXPR@[99; 102) - PATH_EXPR@[99; 100) - PATH@[99; 100) - PATH_SEGMENT@[99; 100) - NAME_REF@[99; 100) - IDENT@[99; 100) "a" - PLUS@[100; 101) "+" - PATH_EXPR@[101; 102) - PATH@[101; 102) - PATH_SEGMENT@[101; 102) - NAME_REF@[101; 102) - IDENT@[101; 102) "b" - R_PAREN@[102; 103) ")" - SEMI@[103; 104) ";" - WHITESPACE@[104; 109) "\n " - PREFIX_EXPR@[109; 111) - MINUS@[109; 110) "-" - LITERAL@[110; 111) - INT_NUMBER@[110; 111) "3" - WHITESPACE@[111; 112) "\n" - R_CURLY@[112; 113) "}" + EXPR_STMT@[89; 101) + BIN_EXPR@[89; 100) + PREFIX_EXPR@[89; 92) + EXCLAMATION@[89; 90) "!" + PREFIX_EXPR@[90; 92) + MINUS@[90; 91) "-" + LITERAL@[91; 92) + INT_NUMBER@[91; 92) "5" + PLUS@[92; 93) "+" + BIN_EXPR@[93; 100) + LITERAL@[93; 94) + INT_NUMBER@[93; 94) "2" + STAR@[94; 95) "*" + PAREN_EXPR@[95; 100) + L_PAREN@[95; 96) "(" + BIN_EXPR@[96; 99) + PATH_EXPR@[96; 97) + PATH@[96; 97) + PATH_SEGMENT@[96; 97) + NAME_REF@[96; 97) + IDENT@[96; 97) "a" + PLUS@[97; 98) "+" + PATH_EXPR@[98; 99) + PATH@[98; 99) + PATH_SEGMENT@[98; 99) + NAME_REF@[98; 99) + IDENT@[98; 99) "b" + R_PAREN@[99; 100) ")" + SEMI@[100; 101) ";" + WHITESPACE@[101; 106) "\n " + PREFIX_EXPR@[106; 108) + MINUS@[106; 107) "-" + LITERAL@[107; 108) + INT_NUMBER@[107; 108) "3" + WHITESPACE@[108; 109) "\n" + R_CURLY@[109; 110) "}" + diff --git a/crates/mun_syntax/tests/data/parser/ok/0001_function.txt b/crates/mun_syntax/src/tests/snapshots/parser__function.snap similarity index 91% rename from crates/mun_syntax/tests/data/parser/ok/0001_function.txt rename to crates/mun_syntax/src/tests/snapshots/parser__function.snap index e30757668..e7233c4cb 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0001_function.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__function.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "// Source file comment\n\n// Comment that belongs to the function\nfn a() {}\nfn b(value:number) {}\nexport fn c() {}\nfn b(value:number):number {}" +--- SOURCE_FILE@[0; 141) COMMENT@[0; 22) "// Source file comment" WHITESPACE@[22; 24) "\n\n" @@ -84,3 +88,4 @@ SOURCE_FILE@[0; 141) BLOCK@[139; 141) L_CURLY@[139; 140) "{" R_CURLY@[140; 141) "}" + diff --git a/crates/mun_syntax/tests/data/parser/ok/0007_fn_calls.txt b/crates/mun_syntax/src/tests/snapshots/parser__function_calls.snap similarity index 93% rename from crates/mun_syntax/tests/data/parser/ok/0007_fn_calls.txt rename to crates/mun_syntax/src/tests/snapshots/parser__function_calls.snap index a7f784105..0d05ada09 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0007_fn_calls.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__function_calls.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn bar(i:number) { }\nfn foo(i:number) {\n bar(i+1)\n}" +--- SOURCE_FILE@[0; 52) FUNCTION_DEF@[0; 20) FN_KW@[0; 2) "fn" @@ -65,3 +69,4 @@ SOURCE_FILE@[0; 52) R_PAREN@[49; 50) ")" WHITESPACE@[50; 51) "\n" R_CURLY@[51; 52) "}" + diff --git a/crates/mun_syntax/tests/data/parser/ok/0003_literals.txt b/crates/mun_syntax/src/tests/snapshots/parser__literals.snap similarity index 85% rename from crates/mun_syntax/tests/data/parser/ok/0003_literals.txt rename to crates/mun_syntax/src/tests/snapshots/parser__literals.snap index 74adb7abc..e4f893ad0 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0003_literals.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__literals.snap @@ -1,5 +1,9 @@ -SOURCE_FILE@[0; 111) - FUNCTION_DEF@[0; 111) +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn foo() {\n let a = true;\n let b = false;\n let c = 1;\n let d = 1.12;\n let e = \"Hello, world!\"\n}" +--- +SOURCE_FILE@[0; 110) + FUNCTION_DEF@[0; 110) FN_KW@[0; 2) "fn" WHITESPACE@[2; 3) " " NAME@[3; 6) @@ -8,7 +12,7 @@ SOURCE_FILE@[0; 111) L_PAREN@[6; 7) "(" R_PAREN@[7; 8) ")" WHITESPACE@[8; 9) " " - BLOCK@[9; 111) + BLOCK@[9; 110) L_CURLY@[9; 10) "{" WHITESPACE@[10; 15) "\n " LET_STMT@[15; 28) @@ -63,7 +67,7 @@ SOURCE_FILE@[0; 111) FLOAT_NUMBER@[75; 79) "1.12" SEMI@[79; 80) ";" WHITESPACE@[80; 85) "\n " - LET_STMT@[85; 109) + LET_STMT@[85; 108) LET_KW@[85; 88) "let" WHITESPACE@[88; 89) " " BIND_PAT@[89; 90) @@ -74,6 +78,6 @@ SOURCE_FILE@[0; 111) WHITESPACE@[92; 93) " " LITERAL@[93; 108) STRING@[93; 108) "\"Hello, world!\"" - SEMI@[108; 109) ";" - WHITESPACE@[109; 110) "\n" - R_CURLY@[110; 111) "}" + WHITESPACE@[108; 109) "\n" + R_CURLY@[109; 110) "}" + diff --git a/crates/mun_syntax/tests/data/parser/ok/0008_patterns.txt b/crates/mun_syntax/src/tests/snapshots/parser__patterns.snap similarity index 92% rename from crates/mun_syntax/tests/data/parser/ok/0008_patterns.txt rename to crates/mun_syntax/src/tests/snapshots/parser__patterns.snap index 040ce9cb1..992724a17 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0008_patterns.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__patterns.snap @@ -1,3 +1,7 @@ +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn main(_:number) {\n let a = 0;\n let _ = a;\n}" +--- SOURCE_FILE@[0; 49) FUNCTION_DEF@[0; 49) FN_KW@[0; 2) "fn" @@ -49,3 +53,4 @@ SOURCE_FILE@[0; 49) SEMI@[46; 47) ";" WHITESPACE@[47; 48) "\n" R_CURLY@[48; 49) "}" + diff --git a/crates/mun_syntax/tests/data/parser/ok/0004_unary_expr.txt b/crates/mun_syntax/src/tests/snapshots/parser__unary_expr.snap similarity index 66% rename from crates/mun_syntax/tests/data/parser/ok/0004_unary_expr.txt rename to crates/mun_syntax/src/tests/snapshots/parser__unary_expr.snap index b7a814f80..ee9971e45 100644 --- a/crates/mun_syntax/tests/data/parser/ok/0004_unary_expr.txt +++ b/crates/mun_syntax/src/tests/snapshots/parser__unary_expr.snap @@ -1,5 +1,9 @@ -SOURCE_FILE@[0; 55) - FUNCTION_DEF@[0; 55) +--- +source: crates/mun_syntax/src/tests/parser.rs +expression: "fn foo() {\n let a = --3;\n let b = !!true;\n}" +--- +SOURCE_FILE@[0; 49) + FUNCTION_DEF@[0; 49) FN_KW@[0; 2) "fn" WHITESPACE@[2; 3) " " NAME@[3; 6) @@ -8,7 +12,7 @@ SOURCE_FILE@[0; 55) L_PAREN@[6; 7) "(" R_PAREN@[7; 8) ")" WHITESPACE@[8; 9) " " - BLOCK@[9; 55) + BLOCK@[9; 49) L_CURLY@[9; 10) "{" WHITESPACE@[10; 15) "\n " LET_STMT@[15; 27) @@ -28,7 +32,7 @@ SOURCE_FILE@[0; 55) INT_NUMBER@[25; 26) "3" SEMI@[26; 27) ";" WHITESPACE@[27; 32) "\n " - LET_STMT@[32; 53) + LET_STMT@[32; 47) LET_KW@[32; 35) "let" WHITESPACE@[35; 36) " " BIND_PAT@[36; 37) @@ -37,14 +41,13 @@ SOURCE_FILE@[0; 55) WHITESPACE@[37; 38) " " EQ@[38; 39) "=" WHITESPACE@[39; 40) " " - PREFIX_EXPR@[40; 52) - NOT_KW@[40; 43) "not" - WHITESPACE@[43; 44) " " - PREFIX_EXPR@[44; 52) - NOT_KW@[44; 47) "not" - WHITESPACE@[47; 48) " " - LITERAL@[48; 52) - TRUE_KW@[48; 52) "true" - SEMI@[52; 53) ";" - WHITESPACE@[53; 54) "\n" - R_CURLY@[54; 55) "}" + PREFIX_EXPR@[40; 46) + EXCLAMATION@[40; 41) "!" + PREFIX_EXPR@[41; 46) + EXCLAMATION@[41; 42) "!" + LITERAL@[42; 46) + TRUE_KW@[42; 46) "true" + SEMI@[46; 47) ";" + WHITESPACE@[47; 48) "\n" + R_CURLY@[48; 49) "}" + diff --git a/crates/mun_syntax/tests/data/lexer/0001_numbers.mun b/crates/mun_syntax/tests/data/lexer/0001_numbers.mun deleted file mode 100644 index e031dfcc8..000000000 --- a/crates/mun_syntax/tests/data/lexer/0001_numbers.mun +++ /dev/null @@ -1,5 +0,0 @@ -1 -1.34 -0x3Af -1e-3 -100_000 \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0002_comments.mun b/crates/mun_syntax/tests/data/lexer/0002_comments.mun deleted file mode 100644 index 9f4d38110..000000000 --- a/crates/mun_syntax/tests/data/lexer/0002_comments.mun +++ /dev/null @@ -1,8 +0,0 @@ -// hello, world! -/**/ -/* block comment */ -/* multi - line - comment */ -/* /* nested */ */ -/* unclosed comment \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0003_whitespace.mun b/crates/mun_syntax/tests/data/lexer/0003_whitespace.mun deleted file mode 100644 index 8938fbe71..000000000 --- a/crates/mun_syntax/tests/data/lexer/0003_whitespace.mun +++ /dev/null @@ -1,4 +0,0 @@ -h e ll o -w - -o r l d \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0004_ident.mun b/crates/mun_syntax/tests/data/lexer/0004_ident.mun deleted file mode 100644 index 88e4e7900..000000000 --- a/crates/mun_syntax/tests/data/lexer/0004_ident.mun +++ /dev/null @@ -1 +0,0 @@ -hello world_ _a2 _ __ x 即可编著课程 \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0005_symbols.mun b/crates/mun_syntax/tests/data/lexer/0005_symbols.mun deleted file mode 100644 index cffddfaba..000000000 --- a/crates/mun_syntax/tests/data/lexer/0005_symbols.mun +++ /dev/null @@ -1,13 +0,0 @@ -# ( ) { } [ ] ; , -= == -!= -< <= -> >= -. .. ... ..= -+ += -- -= -* *= -/ /= -^ ^= -% %= -: :: \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0006_strings.mun b/crates/mun_syntax/tests/data/lexer/0006_strings.mun deleted file mode 100644 index f41bccd53..000000000 --- a/crates/mun_syntax/tests/data/lexer/0006_strings.mun +++ /dev/null @@ -1,6 +0,0 @@ -"Hello, world!" -'Hello, world!' -"\n" -"\"\\" -"multi -line" \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0007_keywords.mun b/crates/mun_syntax/tests/data/lexer/0007_keywords.mun deleted file mode 100644 index 6603f0205..000000000 --- a/crates/mun_syntax/tests/data/lexer/0007_keywords.mun +++ /dev/null @@ -1,3 +0,0 @@ -and break do else false for fn if in nil not or -return then true while let mut class public protected -private \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0008_unclosed_string.mun b/crates/mun_syntax/tests/data/lexer/0008_unclosed_string.mun deleted file mode 100644 index cc7cf9cd9..000000000 --- a/crates/mun_syntax/tests/data/lexer/0008_unclosed_string.mun +++ /dev/null @@ -1 +0,0 @@ -"test \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/lexer/0008_unclosed_string.txt b/crates/mun_syntax/tests/data/lexer/0008_unclosed_string.txt deleted file mode 100644 index 6940637d6..000000000 --- a/crates/mun_syntax/tests/data/lexer/0008_unclosed_string.txt +++ /dev/null @@ -1 +0,0 @@ -STRING 5 "\"test" diff --git a/crates/mun_syntax/tests/data/parser/ok/0000_empty.mun b/crates/mun_syntax/tests/data/parser/ok/0000_empty.mun deleted file mode 100644 index e69de29bb..000000000 diff --git a/crates/mun_syntax/tests/data/parser/ok/0000_empty.txt b/crates/mun_syntax/tests/data/parser/ok/0000_empty.txt deleted file mode 100644 index 08f5a942f..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0000_empty.txt +++ /dev/null @@ -1 +0,0 @@ -SOURCE_FILE@[0; 0) diff --git a/crates/mun_syntax/tests/data/parser/ok/0001_function.mun b/crates/mun_syntax/tests/data/parser/ok/0001_function.mun deleted file mode 100644 index 011faf427..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0001_function.mun +++ /dev/null @@ -1,7 +0,0 @@ -// Source file comment - -// Comment that belongs to the function -fn a() {} -fn b(value:number) {} -export fn c() {} -fn b(value:number):number {} \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/parser/ok/0002_block.mun b/crates/mun_syntax/tests/data/parser/ok/0002_block.mun deleted file mode 100644 index b904ba195..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0002_block.mun +++ /dev/null @@ -1,5 +0,0 @@ -fn foo() { - let a; - let b:i32; - let c:string; -} \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/parser/ok/0003_literals.mun b/crates/mun_syntax/tests/data/parser/ok/0003_literals.mun deleted file mode 100644 index 1f9842ce1..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0003_literals.mun +++ /dev/null @@ -1,7 +0,0 @@ -fn foo() { - let a = true; - let b = false; - let c = 1; - let d = 1.12; - let e = "Hello, world!"; -} \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/parser/ok/0004_unary_expr.mun b/crates/mun_syntax/tests/data/parser/ok/0004_unary_expr.mun deleted file mode 100644 index 92967b6dc..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0004_unary_expr.mun +++ /dev/null @@ -1,4 +0,0 @@ -fn foo() { - let a = --3; - let b = not not true; -} \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/parser/ok/0005_binary_expr.mun b/crates/mun_syntax/tests/data/parser/ok/0005_binary_expr.mun deleted file mode 100644 index bdc54c5d6..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0005_binary_expr.mun +++ /dev/null @@ -1,4 +0,0 @@ -fn foo() { - let a = 3+4*5 - let b = 3*4+10/2 -} \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/parser/ok/0006_expr_stmt.mun b/crates/mun_syntax/tests/data/parser/ok/0006_expr_stmt.mun deleted file mode 100644 index 552e61201..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0006_expr_stmt.mun +++ /dev/null @@ -1,8 +0,0 @@ -fn foo() { - let a = "hello" - let b = "world" - let c - b = "Hello, world!" - not -5+2*(a+b); - -3 -} \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/parser/ok/0007_fn_calls.mun b/crates/mun_syntax/tests/data/parser/ok/0007_fn_calls.mun deleted file mode 100644 index 1ef621632..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0007_fn_calls.mun +++ /dev/null @@ -1,4 +0,0 @@ -fn bar(i:number) { } -fn foo(i:number) { - bar(i+1) -} \ No newline at end of file diff --git a/crates/mun_syntax/tests/data/parser/ok/0008_patterns.mun b/crates/mun_syntax/tests/data/parser/ok/0008_patterns.mun deleted file mode 100644 index 01b4ce126..000000000 --- a/crates/mun_syntax/tests/data/parser/ok/0008_patterns.mun +++ /dev/null @@ -1,4 +0,0 @@ -fn main(_:number) { - let a = 0; - let _ = a; -} \ No newline at end of file diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml deleted file mode 100644 index 1d4033871..000000000 --- a/crates/test_utils/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "test_utils" -version = "0.1.0" -authors = ["Bas Zalmstra "] -edition = "2018" - -[dependencies] -difference = "2.0.0" diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs deleted file mode 100644 index 76b772c23..000000000 --- a/crates/test_utils/src/lib.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::{ - fs, - path::{Path, PathBuf}, -}; - -#[macro_export] -macro_rules! assert_eq_text { - ($left:expr, $right:expr) => { - assert_eq_text!($left, $right,) - }; - ($left:expr, $right:expr, $($tt:tt)*) => {{ - let left = $left; - let right = $right; - if left != right { - if left.trim() == right.trim() { - eprintln!("Left:\n{:?}\n\nRight:\n{:?}\n\nWhitespace difference\n", left, right); - } else { - let changeset = difference::Changeset::new(right, left, "\n"); - eprintln!("Left:\n{}\n\nRight:\n{}\n\nDiff:\n{}\n", left, right, changeset); - } - eprintln!($($tt)*); - panic!("text differs"); - } - }}; -} - -pub fn dir_tests(test_data_dir: &Path, paths: &[&str], f: F) -where - F: Fn(&str, &Path) -> String, -{ - for (path, input_code) in collect_tests(test_data_dir, paths) { - let actual = f(&input_code, &path); - let path = path.with_extension("txt"); - if !path.exists() { - println!("\nfile: {}", path.display()); - println!("No .txt file with expected result, creating...\n"); - println!("{}\n{}", input_code, actual); - fs::write(&path, &actual).unwrap(); - panic!("No expected result"); - } - let expected = read_text(&path); - let expected = expected.as_str(); - let actual = actual.as_str(); - assert_equal_text(expected, actual, &path); - } -} - -pub fn collect_tests(test_data_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> { - paths - .iter() - .flat_map(|path| { - let path = test_data_dir.to_owned().join(path); - test_from_dir(&path).into_iter() - }) - .map(|path| { - let text = read_text(&path); - (path, text) - }) - .collect() -} - -fn read_text(path: &Path) -> String { - fs::read_to_string(path) - .expect(&format!("File at {:?} should be valid", path)) - .replace("\r\n", "\n") -} - -fn test_from_dir(dir: &Path) -> Vec { - let mut acc = Vec::new(); - for file in fs::read_dir(&dir).unwrap() { - let file = file.unwrap(); - let path = file.path(); - if path.extension().unwrap_or_default() == "mun" { - acc.push(path) - } - } - acc.sort(); - acc -} - -pub fn project_dir() -> PathBuf { - let dir = env!("CARGO_MANIFEST_DIR"); - PathBuf::from(dir) - .parent() - .unwrap() - .parent() - .unwrap() - .to_owned() -} - -fn assert_equal_text(expected: &str, actual: &str, path: &Path) { - if expected == actual { - return; - } - let dir = project_dir(); - let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path); - if expected.trim() == actual.trim() { - println!("whitespace difference, rewriting"); - println!("file: {}\n", pretty_path.display()); - fs::write(path, actual).unwrap(); - return; - } - // println!("rewriting {}", pretty_path.display()); - // fs::write(path, actual).unwrap(); - // return; - - assert_eq_text!(expected, actual, "file: {}", pretty_path.display()); -}