From 4fa4fd7c5aff70044b84dede905152bd475b7475 Mon Sep 17 00:00:00 2001 From: myfreess Date: Mon, 17 Aug 2026 11:59:50 +0800 Subject: [PATCH 1/2] untyped_cst: add metavar parsing option --- untyped_cst/README.mbt.md | 7 +++--- untyped_cst/api.mbt | 30 ++++++++++++++++++++---- untyped_cst/api_test.mbt | 43 ++++++++++++++++++++++++++++++++++ untyped_cst/pkg.generated.mbti | 4 ++-- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/untyped_cst/README.mbt.md b/untyped_cst/README.mbt.md index 516ba4c8..dff82a9d 100644 --- a/untyped_cst/README.mbt.md +++ b/untyped_cst/README.mbt.md @@ -5,9 +5,10 @@ syntax tree that preserves tokens, comments, delimiters, separators, and source spans. It is intended for tooling that needs source-faithful structure before or alongside the existing typed syntax AST. -Use `parse_structure(source, name?)` for a MoonBit source file and -`parse_expression(source, name?)` for a standalone expression. Both return a -`ParseResult`. +Use `parse_structure(source, name?, enable_metavar?)` for a MoonBit source file +and `parse_expression(source, name?, enable_metavar?)` for a standalone +expression. Both return a `ParseResult`. Metavariable syntax is disabled by +default; pass `enable_metavar=true` to enable it. - `parse_structure` returns a root whose kind is `Impls`. - `parse_expression` returns a root whose kind is `Expression`. diff --git a/untyped_cst/api.mbt b/untyped_cst/api.mbt index a6f29c61..70af07e4 100644 --- a/untyped_cst/api.mbt +++ b/untyped_cst/api.mbt @@ -20,9 +20,15 @@ fn parse_with_state( fn parse_source( source : String, name : String, + enable_metavar : Bool, parse_root : (ParserState) -> CstNode, ) -> ParseResult { - let lex = @lexer.tokens_from_string(source, name~, comment=true) + let lex = @lexer.tokens_from_string( + source, + name~, + enable_metavar~, + comment=true, + ) let diagnostics = reports_from_lex_errors(lex.errors) parse_with_state(lex.tokens, source, diagnostics, lex.docstrings, parse_root) } @@ -30,17 +36,31 @@ fn parse_source( ///| /// Parses a MoonBit source file into a concrete syntax tree. /// +/// Metavariable syntax is disabled by default. Set `enable_metavar=true` to +/// enable it. +/// /// The returned root node has kind `Impls`. Its source spans use UTF-16 /// code-unit offsets suitable for slicing the original `source` string. -pub fn parse_structure(source : String, name? : String = "") -> ParseResult { - parse_source(source, name, parse_source_file) +pub fn parse_structure( + source : String, + name? : String = "", + enable_metavar? : Bool = false, +) -> ParseResult { + parse_source(source, name, enable_metavar, parse_source_file) } ///| /// Parses a MoonBit expression into a concrete syntax tree. /// +/// Metavariable syntax is disabled by default. Set `enable_metavar=true` to +/// enable it. +/// /// The returned root node has kind `Expression`. Its source spans use /// UTF-16 code-unit offsets suitable for slicing the original `source` string. -pub fn parse_expression(source : String, name? : String = "") -> ParseResult { - parse_source(source, name, parse_expr_fragment) +pub fn parse_expression( + source : String, + name? : String = "", + enable_metavar? : Bool = false, +) -> ParseResult { + parse_source(source, name, enable_metavar, parse_expr_fragment) } diff --git a/untyped_cst/api_test.mbt b/untyped_cst/api_test.mbt index 451899ad..10ccfade 100644 --- a/untyped_cst/api_test.mbt +++ b/untyped_cst/api_test.mbt @@ -159,6 +159,49 @@ test "parse entry points expose distinct roots and propagate names" { @test.assert_eq(expression.root().loc.end.fname, "expression.mbt") } +///| +test "parse entry points gate metavariable syntax" { + let expression_source = "$(value:exp)" + @test.assert_eq( + @untyped_cst.parse_expression(expression_source) + .diagnostics_view() + .is_empty(), + false, + ) + let expression = @untyped_cst.parse_expression( + expression_source, + enable_metavar=true, + ) + @test.assert_eq(expression.diagnostics_view().is_empty(), true) + @test.assert_eq( + api_has_string_value( + expression.root(), + fn(kind) { kind is Syntax_Identifier }, + expression_source, + ), + true, + ) + + let structure_source = "let $(value:pat) = 1" + @test.assert_eq( + @untyped_cst.parse_structure(structure_source).diagnostics_view().is_empty(), + false, + ) + let structure = @untyped_cst.parse_structure( + structure_source, + enable_metavar=true, + ) + @test.assert_eq(structure.diagnostics_view().is_empty(), true) + @test.assert_eq( + api_has_string_value( + structure.root(), + fn(kind) { kind is Syntax_Identifier }, + "$(value:pat)", + ), + true, + ) +} + ///| test "constant kinds and normalized payloads are parser semantics" { let cases : Array[(String, String, String)] = [ diff --git a/untyped_cst/pkg.generated.mbti b/untyped_cst/pkg.generated.mbti index 831cc14e..416832be 100644 --- a/untyped_cst/pkg.generated.mbti +++ b/untyped_cst/pkg.generated.mbti @@ -10,9 +10,9 @@ import { } // Values -pub fn parse_expression(String, name? : String) -> ParseResult +pub fn parse_expression(String, name? : String, enable_metavar? : Bool) -> ParseResult -pub fn parse_structure(String, name? : String) -> ParseResult +pub fn parse_structure(String, name? : String, enable_metavar? : Bool) -> ParseResult // Errors From 78a518b6563783c74865cb121b1d2ad3d2f2a5a3 Mon Sep 17 00:00:00 2001 From: myfreess Date: Mon, 17 Aug 2026 12:40:21 +0800 Subject: [PATCH 2/2] update parser version --- moon.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moon.mod b/moon.mod index a4d5f591..270b57d2 100644 --- a/moon.mod +++ b/moon.mod @@ -1,6 +1,6 @@ name = "moonbitlang/parser" -version = "0.3.14" +version = "0.3.15" import { "moonbitlang/x@0.4.39",