Skip to content

Commit acf1144

Browse files
committed
Base tests working.
1 parent 5bbcc2d commit acf1144

File tree

6 files changed

+268
-18
lines changed

6 files changed

+268
-18
lines changed

src/main/java/uk/modl/interpreter/parser/Parser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ private ModlValue parsePairValue(@NonNull final TokenStream s) {
289289
throw new ParserException(String.format("Unexpected token: '%s'", firstToken));
290290
}
291291

292-
if (peek != null) {
292+
if (peek == null || peek.getType() == TokenType.STRUCT_SEP || peek.getType() == TokenType.RPAREN
293+
|| peek.getType() == TokenType.RBRACKET) {
293294
// Its simply a string or quoted string
294295
if (firstToken.getType() == TokenType.STRING) {
295296
return new ModlString((String) firstToken.getValue());

src/main/java/uk/modl/interpreter/tokeniser/Context.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,21 @@ LinkedList<Token> parse() {
116116
*/
117117
private boolean next() {
118118
// Skip white spaces.
119-
char ws = this.s.charAt(this.tokStart);
120-
while (WS.indexOf(ws) > -1) {
119+
while (this.tokStart < s.length()) {
120+
char ws = this.s.charAt(this.tokStart);
121+
if (WS.indexOf(ws) < 0) {
122+
break;
123+
}
121124
this.tokStart++;
122-
ws = this.s.charAt(this.tokStart);
123125
}
124126

125127
TokenType tokType;
126128

129+
if (this.tokStart >= this.s.length()) {
130+
// Nothing left to parse.
131+
return false;
132+
}
133+
127134
int tokEnd;
128135
switch (this.s.charAt(this.tokStart)) {
129136
case '(': {
@@ -176,7 +183,7 @@ private boolean next() {
176183
this.tokens.add(new Token(TokenType.INTEGER, number, this.tokStart, tokEnd));
177184
} else if (FLOAT_REGEX.matcher(tokValue)
178185
.matches()) {
179-
final double number = Double.parseDouble(tokValue);
186+
final float number = Float.parseFloat(tokValue);
180187
this.tokens.add(new Token(TokenType.FLOAT, number, this.tokStart, tokEnd));
181188
} else if (tokValue.equals("null")) {
182189
this.tokens.add(new Token(TokenType.NULL, null, this.tokStart, tokEnd));

src/main/java/uk/modl/interpreter/tokeniser/Token.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
import lombok.Value;
2424

2525
@Value
26-
public
27-
class Token {
26+
public class Token {
2827
TokenType type;
2928
Object value;
3029
int from, to;

src/main/java/uk/modl/utils/UnicodeEscapeReplacer.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ static String convertUnicodeSequences(final String str) {
4646
int start = 0;
4747
StringBuilder result = new StringBuilder();
4848
m = reg.matcher(str);
49-
if (m.find()) {
50-
do {
49+
do {
50+
if (m.find()) {
5151
if (m.start() > 0 && (str.charAt(m.start() - 1) == '\\' || str.charAt(m.start() - 1) == '~')) {
5252
// Its escaped so copy over as-is, without the leading slash
5353
if (start < m.start() - 1) {
@@ -56,25 +56,23 @@ static String convertUnicodeSequences(final String str) {
5656
}
5757
// Copy from after the slash
5858
result.append(str, m.start(), m.start() + UNICODE_SEQ_LEN);
59-
60-
// Point to the next character after the current escape sequence
6159
} else {
6260
if (start < m.start()) {
6361
// Copy up to the current escape sequence
6462
result.append(str, start, m.start());
6563
}
6664

6765
// Append the converted character
68-
final int c = Integer.parseInt(str.substring(m.start() + 2, 4), HEX);
66+
final int c = Integer.parseInt(str.substring(m.start() + 2, m.start() + 6), HEX);
6967
result.append(Character.toChars(c));
70-
71-
// Point to the next character after the current escape sequence
7268
}
69+
// Point to the next character after the current escape sequence
7370
start = m.start() + UNICODE_SEQ_LEN;
74-
} while (m.find());
75-
} else {
76-
result.append(str.substring(start));
77-
}
71+
} else {
72+
result.append(str.substring(start));
73+
break;
74+
}
75+
} while (true);
7876

7977
return result.toString();
8078
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2020 NUM Technology Ltd
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8+
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9+
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
12+
* the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
15+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
16+
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18+
*
19+
*/
20+
21+
package uk.modl.interpreter;
22+
23+
import static org.junit.Assert.fail;
24+
25+
import java.io.FileInputStream;
26+
import java.io.IOException;
27+
import java.io.InputStream;
28+
import java.util.ArrayList;
29+
import java.util.Collections;
30+
import java.util.LinkedList;
31+
import java.util.List;
32+
import java.util.function.Function;
33+
34+
import com.fasterxml.jackson.core.type.TypeReference;
35+
import com.fasterxml.jackson.databind.JsonNode;
36+
import com.fasterxml.jackson.databind.ObjectMapper;
37+
import lombok.Data;
38+
import lombok.extern.log4j.Log4j2;
39+
import org.junit.Test;
40+
import uk.modl.interpreter.model.Modl;
41+
42+
@Log4j2
43+
public class InterpreterBaseTests {
44+
45+
private final List<String> errors = new ArrayList<>();
46+
47+
private final ObjectMapper mapper = new ObjectMapper();
48+
49+
/**
50+
* Read a set of tests from a file
51+
*/
52+
private final Function<String, List<TestInput>> load = (filename) -> {
53+
try (final InputStream fileStream = new FileInputStream(filename)) {
54+
return mapper.readValue(fileStream, new TypeReference<LinkedList<TestInput>>() {
55+
56+
});
57+
} catch (IOException e) {
58+
log.error("Error accessing file: {}", filename, e);
59+
}
60+
return Collections.emptyList();
61+
};
62+
63+
@Test
64+
public void testBaseTest() {
65+
66+
final List<TestInput> list = load.apply("../grammar/tests/base_tests.json");
67+
int testNumber = 1;
68+
final int startFromTestNumber = 0;// Use this to skip tests manually to make it easier for debugging a specific
69+
// test.
70+
for (final TestInput testInput : list) {
71+
if (testNumber >= startFromTestNumber && !testInput.input.equals("DELETED")) {
72+
checkValidTestInput(testInput);
73+
}
74+
testNumber++;
75+
}
76+
77+
if (errors.size() > 0) {
78+
log.info("--------------- Errors ----------------");
79+
for (final String error : errors) {
80+
log.error(error);
81+
}
82+
fail("Errors found.");
83+
}
84+
}
85+
86+
private void checkValidTestInput(final TestInput testInput) {
87+
try {
88+
final Modl interpreted = new Interpreter().interpret(testInput.input);
89+
90+
if (interpreted != null) {
91+
final JsonNode jsonResult = ModlToJson.convert(interpreted);
92+
93+
final String output = mapper.writeValueAsString(jsonResult);
94+
if (output != null) {
95+
96+
final String expected = testInput.expected_output.replace(" ", "")
97+
.replace("\n", "")
98+
.replace("\r", "");
99+
final String actual = output.replace(" ", "")
100+
.replace("\n", "")
101+
.replace("\r", "");
102+
103+
if (!expected.equals(actual)) {
104+
log.info("Running test number: " + testInput.id);
105+
log.info("Input : " + testInput.input);
106+
log.info("Minimised : " + testInput.minimised_modl);
107+
log.info("Expected : " + testInput.expected_output);
108+
log.info("Actual : " + output);
109+
110+
errors.add("Test: " + testInput.id + "\nExpected: " + testInput.expected_output + "\n"
111+
+ "Actual : "
112+
+ output + "\n");
113+
} else {
114+
log.info("Test: " + testInput.id + " - no errors\n");
115+
}
116+
117+
} else {
118+
errors.add("Test: " + testInput.id + "\nExpected: " + testInput.expected_output + "\n"
119+
+ "Actual : null\n");
120+
}
121+
122+
} else {
123+
log.error("Test: " + testInput.id + " - no result\n");
124+
125+
}
126+
} catch (final Exception e) {
127+
log.error("Test: " + testInput.id + " - exception: " + e, e);
128+
errors.add("Test: " + testInput.id + "\nExpected: " + testInput.expected_output + "\n" + "Actual : "
129+
+ e.getMessage() + "\n");
130+
}
131+
}
132+
133+
@Test
134+
public void testExtraTests() {
135+
// Go through grammar_test/error_tests.json making sure all tests raise an error
136+
// of some kind
137+
final List<TestInput> list = load.apply("../grammar/tests/extra_tests.json");
138+
int testNumber = 1;
139+
final int startFromTestNumber = 0;// Use this to skip tests manually to make it easier for debugging a specific
140+
// test.
141+
for (final TestInput testInput : list) {
142+
if (testNumber >= startFromTestNumber && !testInput.input.equals("DELETED")) {
143+
checkValidTestInput(testInput);
144+
}
145+
testNumber++;
146+
}
147+
148+
if (errors.size() > 0) {
149+
log.info("--------------- Errors ----------------");
150+
for (final String error : errors) {
151+
log.error(error);
152+
}
153+
fail("Errors found.");
154+
}
155+
}
156+
157+
@Data
158+
public static class TestInput {
159+
160+
public String input;
161+
162+
public String minimised_modl;
163+
164+
public String expected_output;
165+
166+
public String[] tested_features;
167+
168+
public int id;
169+
170+
}
171+
172+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2020 NUM Technology Ltd
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8+
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9+
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
12+
* the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
15+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
16+
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18+
*
19+
*/
20+
21+
package uk.modl.interpreter;
22+
23+
import com.fasterxml.jackson.core.JsonProcessingException;
24+
import com.fasterxml.jackson.databind.JsonNode;
25+
import com.fasterxml.jackson.databind.ObjectMapper;
26+
import org.junit.Assert;
27+
import org.junit.Test;
28+
import uk.modl.interpreter.model.Modl;
29+
30+
public class InterpreterTest {
31+
32+
private static final ObjectMapper mapper = new ObjectMapper();
33+
34+
@Test(expected = RuntimeException.class)
35+
public void parseBad() {
36+
new Interpreter().interpretToJsonString("x;xx");
37+
}
38+
39+
@Test
40+
public void testInterpreterConvenienceMethods_1() {
41+
final Interpreter interpreter = new Interpreter();
42+
final Modl modl = interpreter.interpret("a=b");
43+
Assert.assertNotNull(modl);
44+
Assert.assertEquals(1, modl.getStructures()
45+
.get()
46+
.size());
47+
}
48+
49+
@Test
50+
public void testInterpreterConvenienceMethods_2() throws JsonProcessingException {
51+
final Interpreter interpreter = new Interpreter();
52+
final JsonNode jsonNode = interpreter.interpretToJsonObject("a=b");
53+
Assert.assertNotNull(jsonNode);
54+
Assert.assertEquals("{\"a\":\"b\"}", mapper.writeValueAsString(jsonNode));
55+
}
56+
57+
@Test
58+
public void testInterpreterConvenienceMethods_3() {
59+
final Interpreter interpreter = new Interpreter();
60+
final String json = interpreter.interpretToJsonString("a=b");
61+
Assert.assertNotNull(json);
62+
Assert.assertEquals("{\"a\":\"b\"}", json);
63+
}
64+
65+
@Test
66+
public void testInterpreterConvenienceMethods_4() {
67+
final Interpreter interpreter = new Interpreter();
68+
final String json = interpreter.interpretToPrettyJsonString("a=b");
69+
Assert.assertNotNull(json);
70+
Assert.assertEquals("{\n \"a\" : \"b\"\n}", json);
71+
}
72+
73+
}

0 commit comments

Comments
 (0)