Skip to content

Commit 0bde695

Browse files
committed
Merge #438 from branch '421-throwAntlrExceptionInFlux' of github.com:metafacture/metafacture-core
2 parents 66a1826 + 5d05dbc commit 0bde695

File tree

4 files changed

+158
-4
lines changed

4 files changed

+158
-4
lines changed

metafacture-flux/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
api project(':metafacture-framework')
2424
implementation project(':metafacture-commons')
2525
implementation project(':metafacture-io')
26+
implementation project(':metafacture-plumbing')
2627
antlr 'org.antlr:antlr:3.5.2'
2728
testImplementation 'junit:junit:4.12'
2829
}

metafacture-flux/src/main/antlr/org/metafacture/flux/parser/Flux.g

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,53 @@ tokens {
3333

3434
@header {
3535
package org.metafacture.flux.parser;
36+
37+
import org.metafacture.flux.FluxParseException;
3638
}
3739

3840
@lexer::header {
3941
package org.metafacture.flux.parser;
4042
}
4143

44+
@parser::members {
45+
// ensure throwing an exception
46+
@Override
47+
protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException {
48+
{
49+
RecognitionException e = new MismatchedTokenException(ttype, input);
50+
// if next token is what we are looking for then "delete" this token
51+
if ( mismatchIsUnwantedToken(input, ttype) ) {
52+
e = new UnwantedTokenException(ttype, input);
53+
beginResync();
54+
input.consume(); // simply delete extra token
55+
endResync();
56+
reportError(e); // report after consuming so AW sees the token in the exception
57+
// we want to return the token we're actually matching
58+
Object matchedSymbol = getCurrentInputSymbol(input);
59+
input.consume(); // move past ttype token as if all were ok
60+
}
61+
// can't recover with single token deletion, try insertion
62+
if ( mismatchIsMissingToken(input, follow) ) {
63+
Object inserted = getMissingSymbol(input, e, ttype, follow);
64+
e = new MissingTokenException(ttype, input, inserted);
65+
reportError(e); // report after inserting so AW sees the token in the exception
66+
}
67+
throw e;
68+
}
69+
}
70+
}
71+
4272
flux
4373
:
4474
varDef* flow*
4575
;
76+
catch [RecognitionException re] {
77+
reportError(re);
78+
recover(input,re);
79+
retval.tree = (CommonTree)adaptor.errorNode(input, retval.start, input.LT(-1), re);
80+
String msg = getErrorMessage(re, this.getTokenNames()) + " in Flux";
81+
throw new FluxParseException(msg, re);
82+
}
4683

4784
varDef
4885
:
@@ -53,6 +90,9 @@ varDef
5390
->
5491
^(ASSIGN Identifier exp)
5592
;
93+
catch [RecognitionException re] {
94+
throw re;
95+
}
5696

5797
flow
5898
:
@@ -63,6 +103,9 @@ flow
63103
)
64104
'|'! flowtail ('|'! Wormhole)? ';'!
65105
;
106+
catch [RecognitionException re] {
107+
throw re;
108+
}
66109

67110
tee
68111
:
@@ -73,6 +116,9 @@ tee
73116
^(SUBFLOW flowtail)+
74117
)
75118
;
119+
catch [RecognitionException re] {
120+
throw re;
121+
}
76122

77123
flowtail
78124
:
@@ -88,6 +134,9 @@ flowtail
88134
)
89135
)*
90136
;
137+
catch [RecognitionException re] {
138+
throw re;
139+
}
91140

92141
StdIn
93142
:
@@ -105,6 +154,9 @@ exp
105154
:
106155
atom ('+'^ atom)*
107156
;
157+
catch [RecognitionException re] {
158+
throw re;
159+
}
108160

109161
atom
110162
:
@@ -122,6 +174,9 @@ pipeArgs
122174
)
123175
(','! namedArg)*
124176
;
177+
catch [RecognitionException re] {
178+
throw re;
179+
}
125180

126181
namedArg
127182
:

metafacture-flux/src/main/java/org/metafacture/flux/parser/Flow.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void addElement(final Receiver nextElement) {
7575
}
7676
}
7777
else {
78-
throw new FluxParseException(element.getClass().getCanonicalName() + "is not a sender");
78+
throw new FluxParseException(element.getClass().getCanonicalName() + " is not a sender");
7979
}
8080
element = nextElement;
8181
}

metafacture-flux/src/test/java/org/metafacture/flux/FluxGrammarTest.java

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.antlr.runtime.RecognitionException;
2929
import org.junit.Before;
3030
import org.junit.Test;
31+
import org.metafacture.commons.reflection.ReflectionException;
3132
import org.metafacture.flux.parser.FluxProgramm;
3233

3334
/**
@@ -54,7 +55,7 @@ public void setup() {
5455
@Test
5556
public void shouldAllowEmptyCommentInLastLineOfFile()
5657
throws RecognitionException, IOException {
57-
final String script = "\"test\"|write(\"stdout\"); //";
58+
final String script = "\"test\"|print; //";
5859

5960
FluxCompiler.compile(createInputStream(script), emptyMap());
6061

@@ -65,7 +66,7 @@ public void shouldAllowEmptyCommentInLastLineOfFile()
6566
@Test
6667
public void shouldAllowEmptyCommentInFile()
6768
throws RecognitionException, IOException {
68-
final String script = "\"test\"|write(\"stdout\"); //\n";
69+
final String script = "\"test\"|print; //\n";
6970

7071
FluxCompiler.compile(createInputStream(script), emptyMap());
7172

@@ -78,7 +79,7 @@ public void shouldReplaceJavaEscapeSequences()
7879
throws IOException, RecognitionException {
7980
final String script =
8081
"\"quot=\\\" octal1=\\7 octal2=\\60 octal3=\\103 unicode=\\u00f8 tab=[\\t]\"" +
81-
"|write(\"stdout\");";
82+
"|print;";
8283

8384
final FluxProgramm program = FluxCompiler.compile(
8485
createInputStream(script), emptyMap());
@@ -89,6 +90,103 @@ public void shouldReplaceJavaEscapeSequences()
8990
stdoutBuffer.toString());
9091
}
9192

93+
@Test(expected = FluxParseException.class)
94+
public void issue421_shouldThrowFluxParseExceptionWhenSemicolonInFlowIsMissing()
95+
throws RecognitionException, IOException {
96+
final String script = "\"test\"|print";
97+
try {
98+
FluxCompiler.compile(createInputStream(script), emptyMap());
99+
} catch (FluxParseException fpe) {
100+
assertEquals("mismatched input '<EOF>' expecting ';' in Flux", fpe.getMessage());
101+
throw fpe;
102+
}
103+
}
104+
105+
@Test(expected = FluxParseException.class)
106+
public void issue421_shouldThrowFluxParseExceptionWhenSemicolonInVarDefIsMissing()
107+
throws RecognitionException, IOException {
108+
final String script = "foo=42";
109+
try {
110+
FluxCompiler.compile(createInputStream(script), emptyMap());
111+
} catch (FluxParseException re) {
112+
assertEquals("mismatched input '<EOF>' expecting ';' in Flux", re.getMessage());
113+
throw re;
114+
}
115+
}
116+
117+
@Test(expected = ReflectionException.class)
118+
public void issue421_shouldThrowReflectionExceptionWhenCommandIsNotFound()
119+
throws RecognitionException, IOException {
120+
final String script = "\"test\"|prin;";
121+
try {
122+
FluxCompiler.compile(createInputStream(script), emptyMap());
123+
} catch (ReflectionException re) {
124+
assertEquals("Class not found: prin", re.getMessage());
125+
throw re;
126+
}
127+
}
128+
129+
@Test(expected = FluxParseException.class)
130+
public void issue421_shouldThrowFluxParseExceptionWhenInputIsMissingAfterPipe1()
131+
throws RecognitionException, IOException {
132+
final String script = "\"test\"|";
133+
try {
134+
FluxCompiler.compile(createInputStream(script), emptyMap());
135+
} catch (FluxParseException re) {
136+
assertEquals("no viable alternative at input '<EOF>' in Flux", re.getMessage());
137+
throw re;
138+
}
139+
}
140+
141+
@Test(expected = FluxParseException.class)
142+
public void issue421_shouldThrowFluxParseExceptionWhenInputIsMissingAfterPipe2()
143+
throws RecognitionException, IOException {
144+
final String script = "\"test\"|;";
145+
try {
146+
FluxCompiler.compile(createInputStream(script), emptyMap());
147+
} catch (FluxParseException re) {
148+
assertEquals("no viable alternative at input ';' in Flux", re.getMessage());
149+
throw re;
150+
}
151+
}
152+
153+
@Test(expected = FluxParseException.class)
154+
public void issue421_shouldThrowFluxParseExceptionWhenTeeStructureOccursWithouATeeCommand()
155+
throws RecognitionException, IOException {
156+
final String script = "\"test\"|{print}{print} ;";
157+
try {
158+
FluxCompiler.compile(createInputStream(script), emptyMap());
159+
} catch (FluxParseException re) {
160+
assertEquals("Flow cannot be split without a tee-element.", re.getMessage());
161+
throw re;
162+
}
163+
}
164+
165+
@Test(expected = FluxParseException.class)
166+
public void issue421_shouldThrowFluxParseExceptionWhenTeeIsNotASender()
167+
throws RecognitionException, IOException {
168+
final String script = "\"test\"|print|object-tee|{print}{print} ;";
169+
try {
170+
FluxCompiler.compile(createInputStream(script), emptyMap());
171+
} catch (FluxParseException re) {
172+
assertEquals("org.metafacture.io.ObjectStdoutWriter is not a sender", re.getMessage());
173+
throw re;
174+
}
175+
}
176+
177+
@Test(expected = FluxParseException.class)
178+
public void issue421_shouldInsertMissingSymbolsWhenTeeIsStructurallyInvalid()
179+
throws RecognitionException, IOException {
180+
final String script = "\"test\"|object-tee|{object-tee{print{print} ;";
181+
try {
182+
FluxCompiler.compile(createInputStream(script), emptyMap());
183+
String tmp=stdoutBuffer.toString();
184+
} catch (FluxParseException re) {
185+
assertEquals("missing '}' at '{' in Flux", re.getMessage());
186+
throw re;
187+
}
188+
}
189+
92190
private ByteArrayInputStream createInputStream(String script) {
93191
return new ByteArrayInputStream(script.getBytes(StandardCharsets.UTF_8));
94192
}

0 commit comments

Comments
 (0)