Skip to content

Commit 6c8f3d8

Browse files
committed
Lkt: fix the handling of lexing rules with many matchers
1 parent e009c32 commit 6c8f3d8

File tree

10 files changed

+158
-6
lines changed

10 files changed

+158
-6
lines changed

langkit/dsl_unparse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,7 @@ def unparse_rule_matchers(rule_set):
18721872
return unparse_matcher(rule_set[0][0].matcher)
18731873
elif len(rule_set) > 1:
18741874
return "or($i$hl{}$d$hl)".format("$hl".join(
1875-
unparse_matcher(rule.matcher) for rule, _ in rule_set
1875+
f"| {unparse_matcher(rule.matcher)}" for rule, _ in rule_set
18761876
))
18771877
return "bordel"
18781878

langkit/lkt_lowering.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,11 +1511,12 @@ def process_token_rule(
15111511
assert isinstance(r.f_decl, L.GrammarRuleDecl)
15121512
matcher_expr = r.f_decl.f_expr
15131513
if matcher_expr is not None:
1514-
rule = (lower_matcher(matcher_expr), token)
1515-
if is_pre:
1516-
pre_rules.append(rule)
1517-
else:
1518-
rules.append(rule)
1514+
for matcher in lower_matcher_list(matcher_expr):
1515+
rule = (matcher, token)
1516+
if is_pre:
1517+
pre_rules.append(rule)
1518+
else:
1519+
rules.append(rule)
15191520

15201521
def process_pattern(full_decl: L.FullDecl) -> None:
15211522
"""
@@ -1545,6 +1546,25 @@ def process_pattern(full_decl: L.FullDecl) -> None:
15451546
decl.f_val.p_denoted_value, Location.from_lkt_node(decl)
15461547
)
15471548

1549+
def lower_matcher_list(expr: L.GrammarExpr) -> list[Matcher]:
1550+
"""
1551+
Lower a list of token matchers to our internals.
1552+
1553+
Lists of token matchers are made up of "atomic" matchers nested in "or"
1554+
grammar expressions.
1555+
"""
1556+
if isinstance(expr, L.GrammarOrExpr):
1557+
result = []
1558+
for child_list in expr.f_sub_exprs:
1559+
with ctx.lkt_context(child_list):
1560+
check_source_language(
1561+
len(child_list) == 1, "exactly one matcher expected"
1562+
)
1563+
result += lower_matcher_list(child_list[0])
1564+
return result
1565+
else:
1566+
return [lower_matcher(expr)]
1567+
15481568
def lower_matcher(expr: L.GrammarExpr) -> Matcher:
15491569
"""
15501570
Lower a token matcher to our internals.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import common
2+
3+
lexer foo_lexer {
4+
example <- or("example" "foo" | "bar")
5+
}
6+
7+
@with_lexer(foo_lexer)
8+
grammar foo_grammar {
9+
@main_rule main_rule <- Example("example")
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import common
2+
3+
lexer foo_lexer {
4+
example <- or("example" | or ("foo" | /))
5+
}
6+
7+
@with_lexer(foo_lexer)
8+
grammar foo_grammar {
9+
@main_rule main_rule <- Example("example")
10+
}

testsuite/tests/grammar/invalid_lexers/test.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ family_1.lkt:4:5: error: Only lexer rules allowed in family blocks
5555
== foo.lkt ==
5656
Code generation was successful
5757

58+
== or_1.lkt ==
59+
or_1.lkt:4:19: error: exactly one matcher expected
60+
4 | example <- or("example" "foo" | "bar")
61+
| ^^^^^^^^^^^^^^^
62+
63+
64+
== or_2.lkt ==
65+
or_2.lkt:4:43: error: Invalid lexing expression
66+
4 | example <- or("example" | or ("foo" | /))
67+
| ^
68+
69+
5870
== pattern_1.lkt ==
5971
pattern_1.lkt:4:16: error: unbalanced square bracket
6072
4 | example <- p"["
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
lexer foo_lexer {
2+
3+
@trivia() whitespace <- p"[ \n\r\t]+"
4+
id <- p"[a-zA-Z]+"
5+
number <- p"[0-9]+"
6+
assign <- or(
7+
| "="
8+
| ":="
9+
)
10+
}
11+
@with_lexer(foo_lexer)
12+
grammar foo_grammar {
13+
@main_rule expr <- list*(Assignment(Identifier(@id) "=" Number(@number)))
14+
}
15+
16+
@abstract class FooNode implements Node[FooNode] {
17+
}
18+
19+
class Assignment: FooNode {
20+
@parse_field name: Identifier
21+
@parse_field value: Number
22+
}
23+
24+
class Identifier: FooNode implements TokenNode {
25+
}
26+
27+
class Number: FooNode implements TokenNode {
28+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import sys
2+
3+
import libfoolang
4+
5+
6+
print("main.py: Running...")
7+
8+
text = b"""\
9+
a = 1
10+
b := 24
11+
"""
12+
13+
ctx = libfoolang.AnalysisContext()
14+
u = ctx.get_from_buffer("main.txt", buffer=text)
15+
if u.diagnostics:
16+
for d in u.diagnostics:
17+
print(d)
18+
sys.exit(1)
19+
u.root.dump()
20+
21+
print("main.py: Done.")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
main.py: Running...
2+
AssignmentList main.txt:1:1-2:8
3+
|item_0:
4+
| Assignment main.txt:1:1-1:6
5+
| |f_name:
6+
| | Identifier main.txt:1:1-1:2: a
7+
| |f_value:
8+
| | Number main.txt:1:5-1:6: 1
9+
|item_1:
10+
| Assignment main.txt:2:1-2:8
11+
| |f_name:
12+
| | Identifier main.txt:2:1-2:2: b
13+
| |f_value:
14+
| | Number main.txt:2:6-2:8: 24
15+
main.py: Done.
16+
Done
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Check that the case insenvitity feature works as expected.
3+
"""
4+
5+
from langkit.compiled_types import T
6+
from langkit.dsl import ASTNode, Field
7+
8+
from utils import build_and_run, unparse_all_script
9+
10+
11+
class FooNode(ASTNode):
12+
pass
13+
14+
15+
class Assignment(FooNode):
16+
name = Field(type=T.Identifier)
17+
value = Field(type=T.Number)
18+
19+
20+
class Number(FooNode):
21+
token_node = True
22+
23+
24+
class Identifier(FooNode):
25+
token_node = True
26+
27+
28+
build_and_run(
29+
lkt_file='expected_concrete_syntax.lkt',
30+
unparse_script=unparse_all_script,
31+
py_script='main.py',
32+
types_from_lkt=True,
33+
)
34+
print('Done')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
driver: python

0 commit comments

Comments
 (0)