Добавлен оператор объединения с null

This commit is contained in:
Victor 2019-07-06 20:32:31 +03:00
parent 2e439d73b5
commit 0bd2aa10d5
5 changed files with 180 additions and 109 deletions

View File

@ -83,6 +83,7 @@ public final class Lexer {
OPERATORS.put("**", TokenType.STARSTAR); OPERATORS.put("**", TokenType.STARSTAR);
OPERATORS.put("^^", TokenType.CARETCARET); OPERATORS.put("^^", TokenType.CARETCARET);
OPERATORS.put("?:", TokenType.QUESTIONCOLON); OPERATORS.put("?:", TokenType.QUESTIONCOLON);
OPERATORS.put("??", TokenType.QUESTIONQUESTION);
} }
private static final Map<String, TokenType> KEYWORDS; private static final Map<String, TokenType> KEYWORDS;

View File

@ -17,7 +17,7 @@ import java.util.Map;
* @author aNNiMON * @author aNNiMON
*/ */
public final class Parser { public final class Parser {
public static Statement parse(List<Token> tokens) { public static Statement parse(List<Token> tokens) {
final Parser parser = new Parser(tokens); final Parser parser = new Parser(tokens);
final Statement program = parser.parse(); final Statement program = parser.parse();
@ -26,7 +26,7 @@ public final class Parser {
} }
return program; return program;
} }
private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); private static final Token EOF = new Token(TokenType.EOF, "", -1, -1);
private static final EnumMap<TokenType, BinaryExpression.Operator> ASSIGN_OPERATORS; private static final EnumMap<TokenType, BinaryExpression.Operator> ASSIGN_OPERATORS;
@ -52,7 +52,7 @@ public final class Parser {
private final int size; private final int size;
private final ParseErrors parseErrors; private final ParseErrors parseErrors;
private Statement parsedStatement; private Statement parsedStatement;
private int pos; private int pos;
public Parser(List<Token> tokens) { public Parser(List<Token> tokens) {
@ -60,15 +60,15 @@ public final class Parser {
size = tokens.size(); size = tokens.size();
parseErrors = new ParseErrors(); parseErrors = new ParseErrors();
} }
public Statement getParsedStatement() { public Statement getParsedStatement() {
return parsedStatement; return parsedStatement;
} }
public ParseErrors getParseErrors() { public ParseErrors getParseErrors() {
return parseErrors; return parseErrors;
} }
public Statement parse() { public Statement parse() {
parseErrors.clear(); parseErrors.clear();
final BlockStatement result = new BlockStatement(); final BlockStatement result = new BlockStatement();
@ -83,13 +83,13 @@ public final class Parser {
parsedStatement = result; parsedStatement = result;
return result; return result;
} }
private int getErrorLine() { private int getErrorLine() {
if (size == 0) return 0; if (size == 0) return 0;
if (pos >= size) return tokens.get(size - 1).getRow(); if (pos >= size) return tokens.get(size - 1).getRow();
return tokens.get(pos).getRow(); return tokens.get(pos).getRow();
} }
private void recover() { private void recover() {
int preRecoverPosition = pos; int preRecoverPosition = pos;
for (int i = preRecoverPosition; i <= size; i++) { for (int i = preRecoverPosition; i <= size; i++) {
@ -104,7 +104,7 @@ public final class Parser {
} }
} }
} }
private Statement block() { private Statement block() {
final BlockStatement block = new BlockStatement(); final BlockStatement block = new BlockStatement();
consume(TokenType.LBRACE); consume(TokenType.LBRACE);
@ -113,12 +113,12 @@ public final class Parser {
} }
return block; return block;
} }
private Statement statementOrBlock() { private Statement statementOrBlock() {
if (lookMatch(0, TokenType.LBRACE)) return block(); if (lookMatch(0, TokenType.LBRACE)) return block();
return statement(); return statement();
} }
private Statement statement() { private Statement statement() {
if (match(TokenType.PRINT)) { if (match(TokenType.PRINT)) {
return new PrintStatement(expression()); return new PrintStatement(expression());
@ -164,7 +164,7 @@ public final class Parser {
} }
return assignmentStatement(); return assignmentStatement();
} }
private Statement assignmentStatement() { private Statement assignmentStatement() {
if (match(TokenType.EXTRACT)) { if (match(TokenType.EXTRACT)) {
return destructuringAssignment(); return destructuringAssignment();
@ -175,7 +175,7 @@ public final class Parser {
} }
throw new ParseException("Unknown statement: " + get(0)); throw new ParseException("Unknown statement: " + get(0));
} }
private DestructuringAssignmentStatement destructuringAssignment() { private DestructuringAssignmentStatement destructuringAssignment() {
// extract(var1, var2, ...) = ... // extract(var1, var2, ...) = ...
consume(TokenType.LPAREN); consume(TokenType.LPAREN);
@ -191,7 +191,7 @@ public final class Parser {
consume(TokenType.EQ); consume(TokenType.EQ);
return new DestructuringAssignmentStatement(variables, expression()); return new DestructuringAssignmentStatement(variables, expression());
} }
private Statement ifElse() { private Statement ifElse() {
final Expression condition = expression(); final Expression condition = expression();
final Statement ifStatement = statementOrBlock(); final Statement ifStatement = statementOrBlock();
@ -203,20 +203,20 @@ public final class Parser {
} }
return new IfStatement(condition, ifStatement, elseStatement); return new IfStatement(condition, ifStatement, elseStatement);
} }
private Statement whileStatement() { private Statement whileStatement() {
final Expression condition = expression(); final Expression condition = expression();
final Statement statement = statementOrBlock(); final Statement statement = statementOrBlock();
return new WhileStatement(condition, statement); return new WhileStatement(condition, statement);
} }
private Statement doWhileStatement() { private Statement doWhileStatement() {
final Statement statement = statementOrBlock(); final Statement statement = statementOrBlock();
consume(TokenType.WHILE); consume(TokenType.WHILE);
final Expression condition = expression(); final Expression condition = expression();
return new DoWhileStatement(condition, statement); return new DoWhileStatement(condition, statement);
} }
private Statement forStatement() { private Statement forStatement() {
int foreachIndex = lookMatch(0, TokenType.LPAREN) ? 1 : 0; int foreachIndex = lookMatch(0, TokenType.LPAREN) ? 1 : 0;
if (lookMatch(foreachIndex, TokenType.WORD) if (lookMatch(foreachIndex, TokenType.WORD)
@ -243,7 +243,7 @@ public final class Parser {
final Statement statement = statementOrBlock(); final Statement statement = statementOrBlock();
return new ForStatement(initialization, termination, increment, statement); return new ForStatement(initialization, termination, increment, statement);
} }
private ForeachArrayStatement foreachArrayStatement() { private ForeachArrayStatement foreachArrayStatement() {
// for x : arr // for x : arr
boolean optParentheses = match(TokenType.LPAREN); boolean optParentheses = match(TokenType.LPAREN);
@ -256,7 +256,7 @@ public final class Parser {
final Statement statement = statementOrBlock(); final Statement statement = statementOrBlock();
return new ForeachArrayStatement(variable, container, statement); return new ForeachArrayStatement(variable, container, statement);
} }
private ForeachMapStatement foreachMapStatement() { private ForeachMapStatement foreachMapStatement() {
// for k, v : map // for k, v : map
boolean optParentheses = match(TokenType.LPAREN); boolean optParentheses = match(TokenType.LPAREN);
@ -271,7 +271,7 @@ public final class Parser {
final Statement statement = statementOrBlock(); final Statement statement = statementOrBlock();
return new ForeachMapStatement(key, value, container, statement); return new ForeachMapStatement(key, value, container, statement);
} }
private FunctionDefineStatement functionDefine() { private FunctionDefineStatement functionDefine() {
// def name(arg1, arg2 = value) { ... } || def name(args) = expr // def name(arg1, arg2 = value) { ... } || def name(args) = expr
final String name = consume(TokenType.WORD).getText(); final String name = consume(TokenType.WORD).getText();
@ -279,7 +279,7 @@ public final class Parser {
final Statement body = statementBody(); final Statement body = statementBody();
return new FunctionDefineStatement(name, arguments, body); return new FunctionDefineStatement(name, arguments, body);
} }
private Arguments arguments() { private Arguments arguments() {
// (arg1, arg2, arg3 = expr1, arg4 = expr2) // (arg1, arg2, arg3 = expr1, arg4 = expr2)
final Arguments arguments = new Arguments(); final Arguments arguments = new Arguments();
@ -299,14 +299,14 @@ public final class Parser {
} }
return arguments; return arguments;
} }
private Statement statementBody() { private Statement statementBody() {
if (match(TokenType.EQ)) { if (match(TokenType.EQ)) {
return new ReturnStatement(expression()); return new ReturnStatement(expression());
} }
return statementOrBlock(); return statementOrBlock();
} }
private Expression functionChain(Expression qualifiedNameExpr) { private Expression functionChain(Expression qualifiedNameExpr) {
// f1()()() || f1().f2().f3() || f1().key // f1()()() || f1().f2().f3() || f1().key
final Expression expr = function(qualifiedNameExpr); final Expression expr = function(qualifiedNameExpr);
@ -339,7 +339,7 @@ public final class Parser {
} }
return function; return function;
} }
private Expression array() { private Expression array() {
// [value1, value2, ...] // [value1, value2, ...]
consume(TokenType.LBRACKET); consume(TokenType.LBRACKET);
@ -350,7 +350,7 @@ public final class Parser {
} }
return new ArrayExpression(elements); return new ArrayExpression(elements);
} }
private Expression map() { private Expression map() {
// {key1 : value1, key2 : value2, ...} // {key1 : value1, key2 : value2, ...}
consume(TokenType.LBRACE); consume(TokenType.LBRACE);
@ -364,7 +364,7 @@ public final class Parser {
} }
return new MapExpression(elements); return new MapExpression(elements);
} }
private MatchExpression match() { private MatchExpression match() {
// match expression { // match expression {
// case pattern1: result1 // case pattern1: result1
@ -378,12 +378,12 @@ public final class Parser {
MatchExpression.Pattern pattern = null; MatchExpression.Pattern pattern = null;
final Token current = get(0); final Token current = get(0);
if (match(TokenType.NUMBER)) { if (match(TokenType.NUMBER)) {
// case 0.5: // case 0.5:
pattern = new MatchExpression.ConstantPattern( pattern = new MatchExpression.ConstantPattern(
NumberValue.of(createNumber(current.getText(), 10)) NumberValue.of(createNumber(current.getText(), 10))
); );
} else if (match(TokenType.HEX_NUMBER)) { } else if (match(TokenType.HEX_NUMBER)) {
// case #FF: // case #FF:
pattern = new MatchExpression.ConstantPattern( pattern = new MatchExpression.ConstantPattern(
NumberValue.of(createNumber(current.getText(), 16)) NumberValue.of(createNumber(current.getText(), 16))
); );
@ -393,7 +393,7 @@ public final class Parser {
new StringValue(current.getText()) new StringValue(current.getText())
); );
} else if (match(TokenType.WORD)) { } else if (match(TokenType.WORD)) {
// case value: // case value:
pattern = new MatchExpression.VariablePattern(current.getText()); pattern = new MatchExpression.VariablePattern(current.getText());
} else if (match(TokenType.LBRACKET)) { } else if (match(TokenType.LBRACKET)) {
// case [x :: xs]: // case [x :: xs]:
@ -417,7 +417,7 @@ public final class Parser {
} }
pattern = tuplePattern; pattern = tuplePattern;
} }
if (pattern == null) { if (pattern == null) {
throw new ParseException("Wrong pattern in match expression: " + current); throw new ParseException("Wrong pattern in match expression: " + current);
} }
@ -425,7 +425,7 @@ public final class Parser {
// case e if e > 0: // case e if e > 0:
pattern.optCondition = expression(); pattern.optCondition = expression();
} }
consume(TokenType.COLON); consume(TokenType.COLON);
if (lookMatch(0, TokenType.LBRACE)) { if (lookMatch(0, TokenType.LBRACE)) {
pattern.result = block(); pattern.result = block();
@ -434,14 +434,14 @@ public final class Parser {
} }
patterns.add(pattern); patterns.add(pattern);
} while (!match(TokenType.RBRACE)); } while (!match(TokenType.RBRACE));
return new MatchExpression(expression, patterns); return new MatchExpression(expression, patterns);
} }
private Expression expression() { private Expression expression() {
return assignment(); return assignment();
} }
private Expression assignment() { private Expression assignment() {
final Expression assignment = assignmentStrict(); final Expression assignment = assignmentStrict();
if (assignment != null) { if (assignment != null) {
@ -449,7 +449,7 @@ public final class Parser {
} }
return ternary(); return ternary();
} }
private Expression assignmentStrict() { private Expression assignmentStrict() {
// x[0].prop += ... // x[0].prop += ...
final int position = pos; final int position = pos;
@ -465,16 +465,16 @@ public final class Parser {
return null; return null;
} }
match(currentType); match(currentType);
final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType); final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType);
final Expression expression = expression(); final Expression expression = expression();
return new AssignmentExpression(op, (Accessible) targetExpr, expression); return new AssignmentExpression(op, (Accessible) targetExpr, expression);
} }
private Expression ternary() { private Expression ternary() {
Expression result = logicalOr(); Expression result = nullCoalesce();
if (match(TokenType.QUESTION)) { if (match(TokenType.QUESTION)) {
final Expression trueExpr = expression(); final Expression trueExpr = expression();
consume(TokenType.COLON); consume(TokenType.COLON);
@ -486,10 +486,24 @@ public final class Parser {
} }
return result; return result;
} }
private Expression nullCoalesce() {
Expression result = logicalOr();
while (true) {
if (match(TokenType.QUESTIONQUESTION)) {
result = new ConditionalExpression(ConditionalExpression.Operator.NULL_COALESCE, result, expression());
continue;
}
break;
}
return result;
}
private Expression logicalOr() { private Expression logicalOr() {
Expression result = logicalAnd(); Expression result = logicalAnd();
while (true) { while (true) {
if (match(TokenType.BARBAR)) { if (match(TokenType.BARBAR)) {
result = new ConditionalExpression(ConditionalExpression.Operator.OR, result, logicalAnd()); result = new ConditionalExpression(ConditionalExpression.Operator.OR, result, logicalAnd());
@ -497,13 +511,13 @@ public final class Parser {
} }
break; break;
} }
return result; return result;
} }
private Expression logicalAnd() { private Expression logicalAnd() {
Expression result = bitwiseOr(); Expression result = bitwiseOr();
while (true) { while (true) {
if (match(TokenType.AMPAMP)) { if (match(TokenType.AMPAMP)) {
result = new ConditionalExpression(ConditionalExpression.Operator.AND, result, bitwiseOr()); result = new ConditionalExpression(ConditionalExpression.Operator.AND, result, bitwiseOr());
@ -511,13 +525,13 @@ public final class Parser {
} }
break; break;
} }
return result; return result;
} }
private Expression bitwiseOr() { private Expression bitwiseOr() {
Expression expression = bitwiseXor(); Expression expression = bitwiseXor();
while (true) { while (true) {
if (match(TokenType.BAR)) { if (match(TokenType.BAR)) {
expression = new BinaryExpression(BinaryExpression.Operator.OR, expression, bitwiseXor()); expression = new BinaryExpression(BinaryExpression.Operator.OR, expression, bitwiseXor());
@ -525,13 +539,13 @@ public final class Parser {
} }
break; break;
} }
return expression; return expression;
} }
private Expression bitwiseXor() { private Expression bitwiseXor() {
Expression expression = bitwiseAnd(); Expression expression = bitwiseAnd();
while (true) { while (true) {
if (match(TokenType.CARET)) { if (match(TokenType.CARET)) {
expression = new BinaryExpression(BinaryExpression.Operator.XOR, expression, bitwiseAnd()); expression = new BinaryExpression(BinaryExpression.Operator.XOR, expression, bitwiseAnd());
@ -539,13 +553,13 @@ public final class Parser {
} }
break; break;
} }
return expression; return expression;
} }
private Expression bitwiseAnd() { private Expression bitwiseAnd() {
Expression expression = equality(); Expression expression = equality();
while (true) { while (true) {
if (match(TokenType.AMP)) { if (match(TokenType.AMP)) {
expression = new BinaryExpression(BinaryExpression.Operator.AND, expression, equality()); expression = new BinaryExpression(BinaryExpression.Operator.AND, expression, equality());
@ -553,26 +567,26 @@ public final class Parser {
} }
break; break;
} }
return expression; return expression;
} }
private Expression equality() { private Expression equality() {
Expression result = conditional(); Expression result = conditional();
if (match(TokenType.EQEQ)) { if (match(TokenType.EQEQ)) {
return new ConditionalExpression(ConditionalExpression.Operator.EQUALS, result, conditional()); return new ConditionalExpression(ConditionalExpression.Operator.EQUALS, result, conditional());
} }
if (match(TokenType.EXCLEQ)) { if (match(TokenType.EXCLEQ)) {
return new ConditionalExpression(ConditionalExpression.Operator.NOT_EQUALS, result, conditional()); return new ConditionalExpression(ConditionalExpression.Operator.NOT_EQUALS, result, conditional());
} }
return result; return result;
} }
private Expression conditional() { private Expression conditional() {
Expression result = shift(); Expression result = shift();
while (true) { while (true) {
if (match(TokenType.LT)) { if (match(TokenType.LT)) {
result = new ConditionalExpression(ConditionalExpression.Operator.LT, result, shift()); result = new ConditionalExpression(ConditionalExpression.Operator.LT, result, shift());
@ -592,13 +606,13 @@ public final class Parser {
} }
break; break;
} }
return result; return result;
} }
private Expression shift() { private Expression shift() {
Expression expression = additive(); Expression expression = additive();
while (true) { while (true) {
if (match(TokenType.LTLT)) { if (match(TokenType.LTLT)) {
expression = new BinaryExpression(BinaryExpression.Operator.LSHIFT, expression, additive()); expression = new BinaryExpression(BinaryExpression.Operator.LSHIFT, expression, additive());
@ -618,13 +632,13 @@ public final class Parser {
} }
break; break;
} }
return expression; return expression;
} }
private Expression additive() { private Expression additive() {
Expression result = multiplicative(); Expression result = multiplicative();
while (true) { while (true) {
if (match(TokenType.PLUS)) { if (match(TokenType.PLUS)) {
result = new BinaryExpression(BinaryExpression.Operator.ADD, result, multiplicative()); result = new BinaryExpression(BinaryExpression.Operator.ADD, result, multiplicative());
@ -648,13 +662,13 @@ public final class Parser {
} }
break; break;
} }
return result; return result;
} }
private Expression multiplicative() { private Expression multiplicative() {
Expression result = unary(); Expression result = unary();
while (true) { while (true) {
if (match(TokenType.STAR)) { if (match(TokenType.STAR)) {
result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, unary()); result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, unary());
@ -674,10 +688,10 @@ public final class Parser {
} }
break; break;
} }
return result; return result;
} }
private Expression unary() { private Expression unary() {
if (match(TokenType.PLUSPLUS)) { if (match(TokenType.PLUSPLUS)) {
return new UnaryExpression(UnaryExpression.Operator.INCREMENT_PREFIX, primary()); return new UnaryExpression(UnaryExpression.Operator.INCREMENT_PREFIX, primary());
@ -699,14 +713,14 @@ public final class Parser {
} }
return primary(); return primary();
} }
private Expression primary() { private Expression primary() {
if (match(TokenType.LPAREN)) { if (match(TokenType.LPAREN)) {
Expression result = expression(); Expression result = expression();
consume(TokenType.RPAREN); consume(TokenType.RPAREN);
return result; return result;
} }
if (match(TokenType.COLONCOLON)) { if (match(TokenType.COLONCOLON)) {
// ::method reference // ::method reference
final String functionName = consume(TokenType.WORD).getText(); final String functionName = consume(TokenType.WORD).getText();
@ -729,7 +743,7 @@ public final class Parser {
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
return functionChain(new ValueExpression(consume(TokenType.WORD).getText())); return functionChain(new ValueExpression(consume(TokenType.WORD).getText()));
} }
final Expression qualifiedNameExpr = qualifiedName(); final Expression qualifiedNameExpr = qualifiedName();
if (qualifiedNameExpr != null) { if (qualifiedNameExpr != null) {
// variable(args) || arr["key"](args) || obj.key(args) // variable(args) || arr["key"](args) || obj.key(args)
@ -745,7 +759,7 @@ public final class Parser {
} }
return qualifiedNameExpr; return qualifiedNameExpr;
} }
if (lookMatch(0, TokenType.LBRACKET)) { if (lookMatch(0, TokenType.LBRACKET)) {
return array(); return array();
} }
@ -754,12 +768,12 @@ public final class Parser {
} }
return value(); return value();
} }
private Expression qualifiedName() { private Expression qualifiedName() {
// var || var.key[index].key2 // var || var.key[index].key2
final Token current = get(0); final Token current = get(0);
if (!match(TokenType.WORD)) return null; if (!match(TokenType.WORD)) return null;
final List<Expression> indices = variableSuffix(); final List<Expression> indices = variableSuffix();
if (indices == null || indices.isEmpty()) { if (indices == null || indices.isEmpty()) {
return new VariableExpression(current.getText()); return new VariableExpression(current.getText());
@ -786,7 +800,7 @@ public final class Parser {
} }
return indices; return indices;
} }
private Expression value() { private Expression value() {
final Token current = get(0); final Token current = get(0);
if (match(TokenType.NUMBER)) { if (match(TokenType.NUMBER)) {
@ -816,7 +830,7 @@ public final class Parser {
} }
throw new ParseException("Unknown expression: " + current); throw new ParseException("Unknown expression: " + current);
} }
private Number createNumber(String text, int radix) { private Number createNumber(String text, int radix) {
// Double // Double
if (text.contains(".")) { if (text.contains(".")) {
@ -829,7 +843,7 @@ public final class Parser {
return Long.parseLong(text, radix); return Long.parseLong(text, radix);
} }
} }
private Token consume(TokenType type) { private Token consume(TokenType type) {
final Token current = get(0); final Token current = get(0);
if (type != current.getType()) { if (type != current.getType()) {
@ -838,7 +852,7 @@ public final class Parser {
pos++; pos++;
return current; return current;
} }
private boolean match(TokenType type) { private boolean match(TokenType type) {
final Token current = get(0); final Token current = get(0);
if (type != current.getType()) { if (type != current.getType()) {
@ -847,11 +861,11 @@ public final class Parser {
pos++; pos++;
return true; return true;
} }
private boolean lookMatch(int pos, TokenType type) { private boolean lookMatch(int pos, TokenType type) {
return get(pos).getType() == type; return get(pos).getType() == type;
} }
private Token get(int relativePosition) { private Token get(int relativePosition) {
final int position = pos + relativePosition; final int position = pos + relativePosition;
if (position >= size) return EOF; if (position >= size) return EOF;

View File

@ -69,6 +69,7 @@ public enum TokenType {
DOTDOT, // .. DOTDOT, // ..
STARSTAR, // ** STARSTAR, // **
QUESTIONCOLON, // ?: QUESTIONCOLON, // ?:
QUESTIONQUESTION, // ??
TILDE, // ~ TILDE, // ~
CARET, // ^ CARET, // ^

View File

@ -10,19 +10,21 @@ import com.annimon.ownlang.lib.Value;
* @author aNNiMON * @author aNNiMON
*/ */
public final class ConditionalExpression implements Expression { public final class ConditionalExpression implements Expression {
public enum Operator { public enum Operator {
EQUALS("=="), EQUALS("=="),
NOT_EQUALS("!="), NOT_EQUALS("!="),
LT("<"), LT("<"),
LTEQ("<="), LTEQ("<="),
GT(">"), GT(">"),
GTEQ(">="), GTEQ(">="),
AND("&&"), AND("&&"),
OR("||"); OR("||"),
NULL_COALESCE("??");
private final String name; private final String name;
private Operator(String name) { private Operator(String name) {
@ -33,7 +35,7 @@ public final class ConditionalExpression implements Expression {
return name; return name;
} }
} }
public final Expression expr1, expr2; public final Expression expr1, expr2;
public final Operator operation; public final Operator operation;
@ -45,17 +47,24 @@ public final class ConditionalExpression implements Expression {
@Override @Override
public Value eval() { public Value eval() {
final Value value1 = expr1.eval();
switch (operation) { switch (operation) {
case AND: return NumberValue.fromBoolean( case AND:
(value1.asInt() != 0) && (expr2.eval().asInt() != 0) ); return NumberValue.fromBoolean((expr1AsInt() != 0) && (expr2AsInt() != 0));
case OR: return NumberValue.fromBoolean( case OR:
(value1.asInt() != 0) || (expr2.eval().asInt() != 0) ); return NumberValue.fromBoolean((expr1AsInt() != 0) || (expr2AsInt() != 0));
case NULL_COALESCE:
return nullCoalesce();
default:
return NumberValue.fromBoolean(evalAndCompare());
} }
}
private boolean evalAndCompare() {
final Value value1 = expr1.eval();
final Value value2 = expr2.eval(); final Value value2 = expr2.eval();
double number1, number2; double number1, number2;
if (value1.type() == Types.NUMBER) { if (value1.type() == Types.NUMBER) {
number1 = value1.asNumber(); number1 = value1.asNumber();
@ -64,23 +73,42 @@ public final class ConditionalExpression implements Expression {
number1 = value1.compareTo(value2); number1 = value1.compareTo(value2);
number2 = 0; number2 = 0;
} }
boolean result;
switch (operation) { switch (operation) {
case EQUALS: result = number1 == number2; break; case EQUALS: return number1 == number2;
case NOT_EQUALS: result = number1 != number2; break; case NOT_EQUALS: return number1 != number2;
case LT: result = number1 < number2; break; case LT: return number1 < number2;
case LTEQ: result = number1 <= number2; break; case LTEQ: return number1 <= number2;
case GT: result = number1 > number2; break; case GT: return number1 > number2;
case GTEQ: result = number1 >= number2; break; case GTEQ: return number1 >= number2;
default: default:
throw new OperationIsNotSupportedException(operation); throw new OperationIsNotSupportedException(operation);
} }
return NumberValue.fromBoolean(result);
} }
private Value nullCoalesce() {
Value value1;
try {
value1 = expr1.eval();
} catch (NullPointerException npe) {
value1 = null;
}
if (value1 == null) {
return expr2.eval();
}
return value1;
}
private int expr1AsInt() {
return expr1.eval().asInt();
}
private int expr2AsInt() {
return expr2.eval().asInt();
}
@Override @Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -0,0 +1,27 @@
def testZero() {
assertEquals(0, 0 ?? 1)
x = 0
assertEquals(0, x ?? 2)
}
def testObject() {
obj = {"a": 12}
assertEquals(12, obj.a ?? 10)
}
def testObjectMissingKey() {
obj = {"a": 12}
assertEquals(10, obj.test ?? 10)
}
def testNestedObjects() {
obj = {"a": {"b": 12}}
assertEquals(12, obj.a.b ?? 10)
}
def testNestedObjectsMissingKey() {
obj = {"a": {"b": 12}}
assertEquals(1, obj.test ?? 1)
assertEquals(2, obj.a.test ?? 2)
assertEquals(3, obj.test1.test2 ?? 3)
}