Skip to content

Commit 7d7b3ce

Browse files
committed
Implement an error recovering tree parser
This is *heavily* based off of https://matklad.github.io/2023/05/21/resilient-ll-parsing-tutorial.html and https://github.com/rust-lang/rust-analyzer
1 parent 196c7fb commit 7d7b3ce

File tree

455 files changed

+10163
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

455 files changed

+10163
-1
lines changed

.run/tests_gradle.run.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</option>
1111
<option name="taskNames">
1212
<list>
13-
<option value=":ScriptingExample:test" />
13+
<option value=":test" />
1414
</list>
1515
</option>
1616
<option name="vmOptions" />

Parser/build.gradle

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,15 @@ plugins {
66
dependencies {
77
api project(':Shared')
88
api project(':CodeModel')
9+
testImplementation libs.jupiter.api
10+
testImplementation libs.junit.platform.engine
11+
testRuntimeOnly libs.jupiter.engine
912
}
13+
14+
15+
test {
16+
useJUnitPlatform()
17+
testLogging {
18+
events "PASSED", "FAILED", "SKIPPED"
19+
}
20+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.openzen.zenscript.lexer;
2+
3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.HashSet;
6+
import java.util.Set;
7+
8+
public class ZSTokenSet {
9+
10+
private static final ZSTokenSet EMPTY = new ZSTokenSet(Collections.emptySet());
11+
12+
private final Set<ZSTokenType> tokens;
13+
14+
public static ZSTokenSet empty() {
15+
return EMPTY;
16+
}
17+
18+
public static ZSTokenSet of(ZSTokenType... tokens) {
19+
return new ZSTokenSet(new HashSet<>(Arrays.asList(tokens)));
20+
}
21+
22+
private ZSTokenSet(Set<ZSTokenType> tokens) {
23+
this.tokens = tokens;
24+
}
25+
26+
public ZSTokenSet and(ZSTokenType... newTokens) {
27+
Set<ZSTokenType> tokens = new HashSet<>(this.tokens());
28+
tokens.addAll(Arrays.asList(newTokens));
29+
return new ZSTokenSet(tokens);
30+
}
31+
32+
public ZSTokenSet without(ZSTokenType... removedTokens) {
33+
Set<ZSTokenType> tokens = new HashSet<>(this.tokens());
34+
Arrays.asList(removedTokens).forEach(tokens::remove);
35+
return new ZSTokenSet(tokens);
36+
}
37+
38+
public boolean contains(ZSTokenType token) {
39+
return this.tokens().contains(token);
40+
}
41+
42+
public Set<ZSTokenType> tokens() {
43+
return tokens;
44+
}
45+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.openzen.zenscript.tree;
2+
3+
import org.openzen.zenscript.lexer.ZSTokenType;
4+
5+
class Annotation {
6+
7+
//TODO TEST
8+
static boolean parse(TreeParser p) {
9+
boolean parsed = false;
10+
while (p.at(ZSTokenType.T_SQOPEN)) {
11+
if (p.nth(1) == ZSTokenType.T_IDENTIFIER) {
12+
TreeParser.MarkOpened open = p.open();
13+
p.expect(ZSTokenType.T_SQOPEN);
14+
p.whitespace();
15+
Type.parse(p);
16+
p.whitespace();
17+
CallArguments.parse(p);
18+
p.whitespace();
19+
p.expect(ZSTokenType.T_SQCLOSE);
20+
p.close(open, TreeKind.ANNOTATION);
21+
p.whitespace();
22+
parsed = true;
23+
} else {
24+
break;
25+
}
26+
}
27+
return parsed;
28+
}
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.openzen.zenscript.tree;
2+
3+
import org.openzen.zenscript.lexer.ZSTokenType;
4+
5+
class CallArguments {
6+
7+
static void parse(TreeParser p) {
8+
if (p.at(ZSTokenType.T_BROPEN)) {
9+
TreeParser.MarkOpened open = p.open();
10+
p.expect(ZSTokenType.T_BROPEN);
11+
p.whitespace();
12+
if (!p.eat(ZSTokenType.T_BRCLOSE)) {
13+
do {
14+
// either eats nothing or eat for the comma
15+
p.whitespace();
16+
TreeParser.MarkOpened argumentOpen = p.open();
17+
Expression.parse(p);
18+
p.close(argumentOpen, TreeKind.CALL_ARGUMENT);
19+
} while (p.eat(ZSTokenType.T_COMMA) && !p.eof());
20+
p.expect(ZSTokenType.T_BRCLOSE);
21+
p.whitespace();
22+
}
23+
p.whitespace();
24+
p.close(open, TreeKind.CALL_ARGUMENTS);
25+
}
26+
}
27+
28+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package org.openzen.zenscript.tree;
2+
3+
import org.openzen.zencode.shared.CodePosition;
4+
import org.openzen.zenscript.lexer.ZSToken;
5+
import org.openzen.zenscript.lexer.ZSTokenType;
6+
import org.openzen.zenscript.tree.lexer.PositionedToken;
7+
8+
public interface Child {
9+
10+
static Child ofToken(PositionedToken<ZSTokenType, ZSToken> token) {
11+
return new TokenChild(token);
12+
}
13+
14+
static Child ofTree(Tree tree) {
15+
return new TreeChild(tree);
16+
}
17+
18+
static Child ofError(CodePosition position, String message) {
19+
return new ErrorChild(position, message);
20+
}
21+
22+
boolean isError();
23+
24+
boolean isToken();
25+
26+
boolean isTree();
27+
28+
PositionedToken<ZSTokenType, ZSToken> asToken();
29+
30+
Tree asTree();
31+
32+
ParseError asError();
33+
34+
class TokenChild implements Child {
35+
private final PositionedToken<ZSTokenType, ZSToken> token;
36+
37+
private TokenChild(PositionedToken<ZSTokenType, ZSToken> token) {
38+
this.token = token;
39+
}
40+
41+
@Override
42+
public boolean isError() {
43+
return false;
44+
}
45+
46+
@Override
47+
public boolean isToken() {
48+
return true;
49+
}
50+
51+
@Override
52+
public boolean isTree() {
53+
return false;
54+
}
55+
56+
@Override
57+
public PositionedToken<ZSTokenType, ZSToken> asToken() {
58+
return this.token;
59+
}
60+
61+
@Override
62+
public Tree asTree() {
63+
throw new IllegalStateException("Unable to convert a token to a tree!");
64+
}
65+
66+
@Override
67+
public ParseError asError() {
68+
throw new IllegalStateException("Unable to convert a token to an error !");
69+
}
70+
}
71+
72+
class TreeChild implements Child {
73+
private final Tree tree;
74+
75+
private TreeChild(Tree tree) {
76+
this.tree = tree;
77+
}
78+
79+
@Override
80+
public boolean isError() {
81+
return false;
82+
}
83+
84+
@Override
85+
public boolean isToken() {
86+
return false;
87+
}
88+
89+
@Override
90+
public boolean isTree() {
91+
return true;
92+
}
93+
94+
@Override
95+
public PositionedToken<ZSTokenType, ZSToken> asToken() {
96+
throw new IllegalStateException("Unable to convert a tree to a token!");
97+
}
98+
99+
@Override
100+
public Tree asTree() {
101+
return tree;
102+
}
103+
104+
@Override
105+
public ParseError asError() {
106+
throw new IllegalStateException("Unable to convert a tree to an error!");
107+
}
108+
}
109+
110+
class ErrorChild implements Child {
111+
private final CodePosition position;
112+
private final String message;
113+
114+
public ErrorChild(CodePosition position, String message) {
115+
this.position = position;
116+
this.message = message;
117+
}
118+
119+
@Override
120+
public boolean isError() {
121+
return true;
122+
}
123+
124+
@Override
125+
public boolean isToken() {
126+
return false;
127+
}
128+
129+
@Override
130+
public boolean isTree() {
131+
return false;
132+
}
133+
134+
@Override
135+
public PositionedToken<ZSTokenType, ZSToken> asToken() {
136+
throw new IllegalStateException("Unable to convert an error to a token!");
137+
}
138+
139+
140+
@Override
141+
public Tree asTree() {
142+
throw new IllegalStateException("Unable to convert an error to a tree!");
143+
}
144+
145+
@Override
146+
public ParseError asError() {
147+
return new ParseError(this.position, this.message);
148+
}
149+
}
150+
}

0 commit comments

Comments
 (0)