Implement and test Parser (with temporary error handling)

This commit is contained in:
Riccardo Azzolini 2019-01-27 18:24:27 +01:00
parent b959fac770
commit 6ab69a1613
2 changed files with 762 additions and 0 deletions

View File

@ -0,0 +1,262 @@
package it.cavallium.warppi.math.rules.dsl.frontend;
import it.cavallium.warppi.math.MathematicalSymbols;
import it.cavallium.warppi.math.rules.RuleType;
import it.cavallium.warppi.math.rules.dsl.Pattern;
import it.cavallium.warppi.math.rules.dsl.PatternRule;
import it.cavallium.warppi.math.rules.dsl.patterns.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*;
/**
* Converts a list of tokens to a list of <code>PatternRule</code>s.
*/
public class Parser {
private final List<Token> tokens;
private int current = 0;
public Parser(List<Token> tokens) {
this.tokens = tokens;
}
public List<PatternRule> parse() {
return rules();
}
// rules = { rule } , EOF ;
private List<PatternRule> rules() {
List<PatternRule> rules = new ArrayList<>();
while (!atEnd()) {
rules.add(rule());
}
return rules;
}
// rule = rule header , rule body ;
// rule header = rule type , IDENTIFIER , COLON ;
// rule body = pattern , ARROW , replacements ;
private PatternRule rule() {
RuleType type = ruleType();
String name = matchOrFail(IDENTIFIER).lexeme;
matchOrFail(COLON);
Pattern target = pattern();
matchOrFail(ARROW);
List<Pattern> replacements = replacements();
return new PatternRule(name, type, target, replacements);
}
// rule type = REDUCTION | EXPANSION | CALCULATION | EXISTENCE ;
private RuleType ruleType() {
switch (pop().type) {
case REDUCTION:
return RuleType.REDUCTION;
case EXPANSION:
return RuleType.EXPANSION;
case CALCULATION:
return RuleType.CALCULATION;
case EXISTENCE:
return RuleType.EXISTENCE;
}
throw new RuntimeException("Expected rule type");
}
// pattern = equation ;
private Pattern pattern() {
return equation();
}
// replacements = pattern
// | LEFT_BRACKET , patterns , RIGHT_BRACKET ;
// patterns = [ pattern , { COMMA , pattern } , [ COMMA ] ] ;
private List<Pattern> replacements() {
if (match(LEFT_BRACKET) == null) {
return Collections.singletonList(pattern());
}
if (match(RIGHT_BRACKET) != null) {
return Collections.emptyList();
} else {
List<Pattern> pats = new ArrayList<>();
do {
pats.add(pattern());
} while (match(COMMA) != null && peek().type != RIGHT_BRACKET);
matchOrFail(RIGHT_BRACKET);
return pats;
}
}
// equation = sum , [ EQUALS , sum ] ;
private Pattern equation() {
Pattern pat = sum();
if (match(EQUALS) != null) {
pat = new EquationPattern(pat, sum());
}
return pat;
}
// sum = product , { ( PLUS | MINUS | PLUS_MINUS ) product } ;
private Pattern sum() {
return matchLeftAssoc(this::product, Map.ofEntries(
Map.entry(PLUS, SumPattern::new),
Map.entry(MINUS, SubtractionPattern::new),
Map.entry(PLUS_MINUS, SumSubtractionPattern::new)
));
}
// product = unary , { ( TIMES | DIVIDE ) unary } ;
private Pattern product() {
return matchLeftAssoc(this::unary, Map.ofEntries(
Map.entry(TIMES, MultiplicationPattern::new),
Map.entry(DIVIDE, DivisionPattern::new)
));
}
// unary = ( PLUS | MINUS ) unary
// | power ;
private Pattern unary() {
if (match(PLUS) != null) {
return unary();
} else if (match(MINUS) != null) {
return new NegativePattern(unary());
} else {
return power();
}
}
// power = ( function | primary ) , [ POWER , power ] ;
private Pattern power() {
Pattern pat = functionOrPrimary();
if (match(POWER) != null) {
pat = new PowerPattern(pat, power());
}
return pat;
}
private Pattern functionOrPrimary() {
Pattern function = tryFunction();
return function != null ? function : primary();
}
// function = ( ARCCOS | ARCSIN | ARCTAN | COS | SIN | SQRT | TAN ) , LEFT_PAREN , sum , RIGHT_PAREN
// | ( LOG | ROOT ) LEFT_PAREN , sum , COMMA , sum , RIGHT_PAREN ;
private Pattern tryFunction() {
final Map<TokenType, Function<Pattern, Pattern>> oneArg = Map.ofEntries(
Map.entry(ARCCOS, ArcCosinePattern::new),
Map.entry(ARCSIN, ArcSinePattern::new),
Map.entry(ARCTAN, ArcTangentPattern::new),
Map.entry(COS, CosinePattern::new),
Map.entry(SIN, SinePattern::new),
Map.entry(SQRT, arg -> new RootPattern(new NumberPattern(new BigDecimal(2)), arg)),
Map.entry(TAN, TangentPattern::new)
);
final Map<TokenType, BiFunction<Pattern, Pattern, Pattern>> twoArg = Map.ofEntries(
Map.entry(LOG, LogarithmPattern::new),
Map.entry(ROOT, RootPattern::new)
);
final TokenType curType = peek().type;
if (oneArg.containsKey(curType)) {
pop();
return oneArgFunction(oneArg.get(curType));
} else if (twoArg.containsKey(curType)) {
pop();
return twoArgFunction(twoArg.get(curType));
}
return null;
}
private Pattern oneArgFunction(final Function<Pattern, Pattern> constructor) {
matchOrFail(LEFT_PAREN);
final Pattern arg = pattern();
matchOrFail(RIGHT_PAREN);
return constructor.apply(arg);
}
private Pattern twoArgFunction(final BiFunction<Pattern, Pattern, Pattern> constructor) {
matchOrFail(LEFT_PAREN);
final Pattern firstArg = pattern();
matchOrFail(COMMA);
final Pattern secondArg = pattern();
matchOrFail(RIGHT_PAREN);
return constructor.apply(firstArg, secondArg);
}
// primary = NUMBER | constant | IDENTIFIER | UNDEFINED
// | LEFT_PAREN sum RIGHT_PAREN ;
// constant = PI | E ;
private Pattern primary() {
Token curToken = pop();
switch (curToken.type) {
case PI:
return new ConstantPattern(MathematicalSymbols.PI);
case E:
return new ConstantPattern(MathematicalSymbols.EULER_NUMBER);
case UNDEFINED:
return new UndefinedPattern();
case NUMBER:
return new NumberPattern(new BigDecimal(curToken.lexeme));
case IDENTIFIER:
return new SubFunctionPattern(curToken.lexeme);
case LEFT_PAREN:
final Pattern grouped = sum();
matchOrFail(RIGHT_PAREN);
return grouped;
}
throw new RuntimeException("Unexpected " + curToken);
}
private Pattern matchLeftAssoc(
final Supplier<Pattern> operandParser,
final Map<TokenType, BiFunction<Pattern, Pattern, Pattern>> operators
) {
Pattern pat = operandParser.get();
while (operators.containsKey(peek().type)) {
final Token operatorToken = pop();
final BiFunction<Pattern, Pattern, Pattern> constructor = operators.get(operatorToken.type);
pat = constructor.apply(pat, operandParser.get());
}
return pat;
}
private Token matchOrFail(final TokenType expectedType) {
final Token matched = match(expectedType);
if (matched == null) {
throw new RuntimeException("Expected " + expectedType);
}
return matched;
}
private Token match(final TokenType expectedType) {
final Token curToken = tokens.get(current);
if (curToken.type == expectedType) {
current++;
return curToken;
} else {
return null;
}
}
private Token pop() {
final Token curToken = tokens.get(current);
current++;
return curToken;
}
private Token peek() {
return tokens.get(current);
}
private boolean atEnd() {
return tokens.get(current).type == EOF;
}
}

View File

@ -0,0 +1,500 @@
package it.cavallium.warppi.math.rules.dsl.frontend;
import it.cavallium.warppi.math.MathematicalSymbols;
import it.cavallium.warppi.math.rules.RuleType;
import it.cavallium.warppi.math.rules.dsl.Pattern;
import it.cavallium.warppi.math.rules.dsl.PatternRule;
import it.cavallium.warppi.math.rules.dsl.patterns.*;
import org.junit.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*;
import static org.junit.Assert.*;
public class ParserTest {
@Test
public void noRules() {
final List<Token> tokens = Collections.singletonList(
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
assertEquals(Collections.emptyList(), parser.parse());
}
@Test
public void validRuleMultipleReplacements() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "TestRule_123", 10),
new Token(COLON, ":", 22),
new Token(IDENTIFIER, "x", 26),
new Token(PLUS, "+", 28),
new Token(IDENTIFIER, "y", 30),
new Token(TIMES, "*", 32),
new Token(IDENTIFIER, "z", 34),
new Token(EQUALS, "=", 36),
new Token(MINUS, "-", 38),
new Token(LEFT_PAREN, "(", 39),
new Token(IDENTIFIER, "a_123", 40),
new Token(PLUS_MINUS, "+-", 46),
new Token(NUMBER, "3", 49),
new Token(DIVIDE, "/", 51),
new Token(NUMBER, "2.2", 53),
new Token(RIGHT_PAREN, ")", 56),
new Token(ARROW, "->", 58),
new Token(LEFT_BRACKET, "[", 61),
new Token(IDENTIFIER, "x", 67),
new Token(POWER, "^", 68),
new Token(IDENTIFIER, "a_123", 69),
new Token(EQUALS, "=", 75),
new Token(COS, "cos", 77),
new Token(LEFT_PAREN, "(", 80),
new Token(PI, "pi", 81),
new Token(RIGHT_PAREN, ")", 83),
new Token(MINUS, "-", 85),
new Token(LOG, "log", 87),
new Token(LEFT_PAREN, "(", 90),
new Token(E, "e", 91),
new Token(COMMA, ",", 92),
new Token(E, "e", 94),
new Token(RIGHT_PAREN, ")", 95),
new Token(COMMA, ",", 96),
new Token(UNDEFINED, "undefined", 113),
new Token(COMMA, ",", 122),
new Token(RIGHT_BRACKET, "]", 138),
new Token(EOF, "", 140)
);
final Parser parser = new Parser(tokens);
// x + y * z = -(a_123 +- 3 / 2.2)
final Pattern target = new EquationPattern(
new SumPattern(
new SubFunctionPattern("x"),
new MultiplicationPattern(
new SubFunctionPattern("y"),
new SubFunctionPattern("z")
)
),
new NegativePattern(new SumSubtractionPattern(
new SubFunctionPattern("a_123"),
new DivisionPattern(
new NumberPattern(new BigDecimal(3)),
new NumberPattern(new BigDecimal("2.2"))
)
))
);
// x^a_123 = cos(pi) - log(e, e)
final Pattern replacement1 = new EquationPattern(
new PowerPattern(
new SubFunctionPattern("x"),
new SubFunctionPattern("a_123")
),
new SubtractionPattern(
new CosinePattern(new ConstantPattern(MathematicalSymbols.PI)),
new LogarithmPattern(
new ConstantPattern(MathematicalSymbols.EULER_NUMBER),
new ConstantPattern(MathematicalSymbols.EULER_NUMBER)
)
)
);
final Pattern replacement2 = new UndefinedPattern();
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
"TestRule_123",
RuleType.REDUCTION,
target,
replacement1,
replacement2
));
assertEquals(expected, parser.parse());
}
@Test
public void validRuleNoReplacements() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(PLUS, "+", 0),
new Token(IDENTIFIER, "y", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
"test",
RuleType.EXISTENCE,
new SumPattern(
new SubFunctionPattern("x"),
new SubFunctionPattern("y")
)
));
assertEquals(expected, parser.parse());
}
@Test
public void validRuleOneReplacement() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(MINUS, "-", 0),
new Token(IDENTIFIER, "x", 0),
new Token(TIMES, "*", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(MINUS, "-", 0),
new Token(IDENTIFIER, "x", 0),
new Token(POWER, "^", 0),
new Token(NUMBER, "2", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
"test",
RuleType.REDUCTION,
new MultiplicationPattern(
new NegativePattern(new SubFunctionPattern("x")),
new SubFunctionPattern("x")
),
new NegativePattern(new PowerPattern(
new SubFunctionPattern("x"),
new NumberPattern(new BigDecimal(2))
))
));
assertEquals(expected, parser.parse());
}
@Test
public void validRuleOneReplacementBrackets() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(TIMES, "*", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(IDENTIFIER, "x", 0),
new Token(POWER, "^", 0),
new Token(NUMBER, "2", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
"test",
RuleType.REDUCTION,
new MultiplicationPattern(
new SubFunctionPattern("x"),
new SubFunctionPattern("x")
),
new PowerPattern(
new SubFunctionPattern("x"),
new NumberPattern(new BigDecimal(2))
)
));
assertEquals(expected, parser.parse());
}
@Test
public void multipleValidRules() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "test1", 0),
new Token(COLON, ":", 0),
new Token(PLUS, "+", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(IDENTIFIER, "x", 0),
new Token(EXPANSION, "expansion", 0),
new Token(IDENTIFIER, "test2", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(MINUS, "-", 0),
new Token(MINUS, "-", 0),
new Token(IDENTIFIER, "x", 0),
new Token(CALCULATION, "calculation", 0),
new Token(IDENTIFIER, "test3", 0),
new Token(COLON, ":", 0),
new Token(NUMBER, "1", 0),
new Token(PLUS, "+", 0),
new Token(NUMBER, "1", 0),
new Token(ARROW, "->", 0),
new Token(NUMBER, "2", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
final List<PatternRule> expected = Arrays.asList(
new PatternRule(
"test1",
RuleType.REDUCTION,
new SubFunctionPattern("x"),
new SubFunctionPattern("x")
),
new PatternRule(
"test2",
RuleType.EXPANSION,
new SubFunctionPattern("x"),
new NegativePattern(new NegativePattern(new SubFunctionPattern("x")))
),
new PatternRule(
"test3",
RuleType.CALCULATION,
new SumPattern(
new NumberPattern(new BigDecimal(1)),
new NumberPattern(new BigDecimal(1))
),
new NumberPattern(new BigDecimal(2))
)
);
assertEquals(expected, parser.parse());
}
// The EOF token is inserted by the lexer, therefore it can only be missing
// in case of programming errors, and not directly because of user input.
@Test(expected = RuntimeException.class)
public void missingEof() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(PLUS, "+", 0),
new Token(IDENTIFIER, "y", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void incompleteRule() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(PLUS, "+", 0),
new Token(IDENTIFIER, "y", 0),
new Token(ARROW, "->", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingRuleType() {
final List<Token> tokens = Arrays.asList(
new Token(IDENTIFIER, "test", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void unexpectedTokenPrimary() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(PLUS, "+", 0),
new Token(CALCULATION, "calculation", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingRuleName() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(COLON, ":", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingColon() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "test", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(IDENTIFIER, "x", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingArrow() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(IDENTIFIER, "x", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingRightBracket() {
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(IDENTIFIER, "x", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingOneArgFunctionLeftParen() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(SIN, "sin", 0),
new Token(IDENTIFIER, "x", 0),
new Token(RIGHT_PAREN, ")", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingOneArgFunctionRightParen() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(SIN, "sin", 0),
new Token(LEFT_PAREN, "(", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingTwoArgFunctionLeftParen() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(LOG, "log", 0),
new Token(IDENTIFIER, "x", 0),
new Token(COMMA, ",", 0),
new Token(IDENTIFIER, "y", 0),
new Token(RIGHT_PAREN, ")", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingTwoArgFunctionComma() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(LOG, "log", 0),
new Token(LEFT_PAREN, "(", 0),
new Token(IDENTIFIER, "x", 0),
new Token(IDENTIFIER, "y", 0),
new Token(RIGHT_PAREN, ")", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingTwoArgFunctionRightParen() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(LOG, "log", 0),
new Token(LEFT_PAREN, "(", 0),
new Token(IDENTIFIER, "x", 0),
new Token(COMMA, ",", 0),
new Token(IDENTIFIER, "y", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
@Test(expected = RuntimeException.class)
public void missingExpressionRightParen() {
final List<Token> tokens = Arrays.asList(
new Token(EXISTENCE, "existence", 0),
new Token(IDENTIFIER, "test", 0),
new Token(COLON, ":", 0),
new Token(LEFT_PAREN, "(", 0),
new Token(IDENTIFIER, "x", 0),
new Token(ARROW, "->", 0),
new Token(LEFT_BRACKET, "[", 0),
new Token(RIGHT_BRACKET, "]", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens);
parser.parse();
}
}