mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Добавлен оператор объединения с null
This commit is contained in:
parent
2e439d73b5
commit
0bd2aa10d5
@ -83,6 +83,7 @@ public final class Lexer {
|
||||
OPERATORS.put("**", TokenType.STARSTAR);
|
||||
OPERATORS.put("^^", TokenType.CARETCARET);
|
||||
OPERATORS.put("?:", TokenType.QUESTIONCOLON);
|
||||
OPERATORS.put("??", TokenType.QUESTIONQUESTION);
|
||||
}
|
||||
|
||||
private static final Map<String, TokenType> KEYWORDS;
|
||||
|
@ -17,7 +17,7 @@ import java.util.Map;
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class Parser {
|
||||
|
||||
|
||||
public static Statement parse(List<Token> tokens) {
|
||||
final Parser parser = new Parser(tokens);
|
||||
final Statement program = parser.parse();
|
||||
@ -26,7 +26,7 @@ public final class Parser {
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
private static final Token EOF = new Token(TokenType.EOF, "", -1, -1);
|
||||
|
||||
private static final EnumMap<TokenType, BinaryExpression.Operator> ASSIGN_OPERATORS;
|
||||
@ -52,7 +52,7 @@ public final class Parser {
|
||||
private final int size;
|
||||
private final ParseErrors parseErrors;
|
||||
private Statement parsedStatement;
|
||||
|
||||
|
||||
private int pos;
|
||||
|
||||
public Parser(List<Token> tokens) {
|
||||
@ -60,15 +60,15 @@ public final class Parser {
|
||||
size = tokens.size();
|
||||
parseErrors = new ParseErrors();
|
||||
}
|
||||
|
||||
|
||||
public Statement getParsedStatement() {
|
||||
return parsedStatement;
|
||||
}
|
||||
|
||||
|
||||
public ParseErrors getParseErrors() {
|
||||
return parseErrors;
|
||||
}
|
||||
|
||||
|
||||
public Statement parse() {
|
||||
parseErrors.clear();
|
||||
final BlockStatement result = new BlockStatement();
|
||||
@ -83,13 +83,13 @@ public final class Parser {
|
||||
parsedStatement = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private int getErrorLine() {
|
||||
if (size == 0) return 0;
|
||||
if (pos >= size) return tokens.get(size - 1).getRow();
|
||||
return tokens.get(pos).getRow();
|
||||
}
|
||||
|
||||
|
||||
private void recover() {
|
||||
int preRecoverPosition = pos;
|
||||
for (int i = preRecoverPosition; i <= size; i++) {
|
||||
@ -104,7 +104,7 @@ public final class Parser {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Statement block() {
|
||||
final BlockStatement block = new BlockStatement();
|
||||
consume(TokenType.LBRACE);
|
||||
@ -113,12 +113,12 @@ public final class Parser {
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
private Statement statementOrBlock() {
|
||||
if (lookMatch(0, TokenType.LBRACE)) return block();
|
||||
return statement();
|
||||
}
|
||||
|
||||
|
||||
private Statement statement() {
|
||||
if (match(TokenType.PRINT)) {
|
||||
return new PrintStatement(expression());
|
||||
@ -164,7 +164,7 @@ public final class Parser {
|
||||
}
|
||||
return assignmentStatement();
|
||||
}
|
||||
|
||||
|
||||
private Statement assignmentStatement() {
|
||||
if (match(TokenType.EXTRACT)) {
|
||||
return destructuringAssignment();
|
||||
@ -175,7 +175,7 @@ public final class Parser {
|
||||
}
|
||||
throw new ParseException("Unknown statement: " + get(0));
|
||||
}
|
||||
|
||||
|
||||
private DestructuringAssignmentStatement destructuringAssignment() {
|
||||
// extract(var1, var2, ...) = ...
|
||||
consume(TokenType.LPAREN);
|
||||
@ -191,7 +191,7 @@ public final class Parser {
|
||||
consume(TokenType.EQ);
|
||||
return new DestructuringAssignmentStatement(variables, expression());
|
||||
}
|
||||
|
||||
|
||||
private Statement ifElse() {
|
||||
final Expression condition = expression();
|
||||
final Statement ifStatement = statementOrBlock();
|
||||
@ -203,20 +203,20 @@ public final class Parser {
|
||||
}
|
||||
return new IfStatement(condition, ifStatement, elseStatement);
|
||||
}
|
||||
|
||||
|
||||
private Statement whileStatement() {
|
||||
final Expression condition = expression();
|
||||
final Statement statement = statementOrBlock();
|
||||
return new WhileStatement(condition, statement);
|
||||
}
|
||||
|
||||
|
||||
private Statement doWhileStatement() {
|
||||
final Statement statement = statementOrBlock();
|
||||
consume(TokenType.WHILE);
|
||||
final Expression condition = expression();
|
||||
return new DoWhileStatement(condition, statement);
|
||||
}
|
||||
|
||||
|
||||
private Statement forStatement() {
|
||||
int foreachIndex = lookMatch(0, TokenType.LPAREN) ? 1 : 0;
|
||||
if (lookMatch(foreachIndex, TokenType.WORD)
|
||||
@ -243,7 +243,7 @@ public final class Parser {
|
||||
final Statement statement = statementOrBlock();
|
||||
return new ForStatement(initialization, termination, increment, statement);
|
||||
}
|
||||
|
||||
|
||||
private ForeachArrayStatement foreachArrayStatement() {
|
||||
// for x : arr
|
||||
boolean optParentheses = match(TokenType.LPAREN);
|
||||
@ -256,7 +256,7 @@ public final class Parser {
|
||||
final Statement statement = statementOrBlock();
|
||||
return new ForeachArrayStatement(variable, container, statement);
|
||||
}
|
||||
|
||||
|
||||
private ForeachMapStatement foreachMapStatement() {
|
||||
// for k, v : map
|
||||
boolean optParentheses = match(TokenType.LPAREN);
|
||||
@ -271,7 +271,7 @@ public final class Parser {
|
||||
final Statement statement = statementOrBlock();
|
||||
return new ForeachMapStatement(key, value, container, statement);
|
||||
}
|
||||
|
||||
|
||||
private FunctionDefineStatement functionDefine() {
|
||||
// def name(arg1, arg2 = value) { ... } || def name(args) = expr
|
||||
final String name = consume(TokenType.WORD).getText();
|
||||
@ -279,7 +279,7 @@ public final class Parser {
|
||||
final Statement body = statementBody();
|
||||
return new FunctionDefineStatement(name, arguments, body);
|
||||
}
|
||||
|
||||
|
||||
private Arguments arguments() {
|
||||
// (arg1, arg2, arg3 = expr1, arg4 = expr2)
|
||||
final Arguments arguments = new Arguments();
|
||||
@ -299,14 +299,14 @@ public final class Parser {
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
||||
private Statement statementBody() {
|
||||
if (match(TokenType.EQ)) {
|
||||
return new ReturnStatement(expression());
|
||||
}
|
||||
return statementOrBlock();
|
||||
}
|
||||
|
||||
|
||||
private Expression functionChain(Expression qualifiedNameExpr) {
|
||||
// f1()()() || f1().f2().f3() || f1().key
|
||||
final Expression expr = function(qualifiedNameExpr);
|
||||
@ -339,7 +339,7 @@ public final class Parser {
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
|
||||
private Expression array() {
|
||||
// [value1, value2, ...]
|
||||
consume(TokenType.LBRACKET);
|
||||
@ -350,7 +350,7 @@ public final class Parser {
|
||||
}
|
||||
return new ArrayExpression(elements);
|
||||
}
|
||||
|
||||
|
||||
private Expression map() {
|
||||
// {key1 : value1, key2 : value2, ...}
|
||||
consume(TokenType.LBRACE);
|
||||
@ -364,7 +364,7 @@ public final class Parser {
|
||||
}
|
||||
return new MapExpression(elements);
|
||||
}
|
||||
|
||||
|
||||
private MatchExpression match() {
|
||||
// match expression {
|
||||
// case pattern1: result1
|
||||
@ -378,12 +378,12 @@ public final class Parser {
|
||||
MatchExpression.Pattern pattern = null;
|
||||
final Token current = get(0);
|
||||
if (match(TokenType.NUMBER)) {
|
||||
// case 0.5:
|
||||
// case 0.5:
|
||||
pattern = new MatchExpression.ConstantPattern(
|
||||
NumberValue.of(createNumber(current.getText(), 10))
|
||||
);
|
||||
} else if (match(TokenType.HEX_NUMBER)) {
|
||||
// case #FF:
|
||||
// case #FF:
|
||||
pattern = new MatchExpression.ConstantPattern(
|
||||
NumberValue.of(createNumber(current.getText(), 16))
|
||||
);
|
||||
@ -393,7 +393,7 @@ public final class Parser {
|
||||
new StringValue(current.getText())
|
||||
);
|
||||
} else if (match(TokenType.WORD)) {
|
||||
// case value:
|
||||
// case value:
|
||||
pattern = new MatchExpression.VariablePattern(current.getText());
|
||||
} else if (match(TokenType.LBRACKET)) {
|
||||
// case [x :: xs]:
|
||||
@ -417,7 +417,7 @@ public final class Parser {
|
||||
}
|
||||
pattern = tuplePattern;
|
||||
}
|
||||
|
||||
|
||||
if (pattern == null) {
|
||||
throw new ParseException("Wrong pattern in match expression: " + current);
|
||||
}
|
||||
@ -425,7 +425,7 @@ public final class Parser {
|
||||
// case e if e > 0:
|
||||
pattern.optCondition = expression();
|
||||
}
|
||||
|
||||
|
||||
consume(TokenType.COLON);
|
||||
if (lookMatch(0, TokenType.LBRACE)) {
|
||||
pattern.result = block();
|
||||
@ -434,14 +434,14 @@ public final class Parser {
|
||||
}
|
||||
patterns.add(pattern);
|
||||
} while (!match(TokenType.RBRACE));
|
||||
|
||||
|
||||
return new MatchExpression(expression, patterns);
|
||||
}
|
||||
|
||||
|
||||
private Expression expression() {
|
||||
return assignment();
|
||||
}
|
||||
|
||||
|
||||
private Expression assignment() {
|
||||
final Expression assignment = assignmentStrict();
|
||||
if (assignment != null) {
|
||||
@ -449,7 +449,7 @@ public final class Parser {
|
||||
}
|
||||
return ternary();
|
||||
}
|
||||
|
||||
|
||||
private Expression assignmentStrict() {
|
||||
// x[0].prop += ...
|
||||
final int position = pos;
|
||||
@ -465,16 +465,16 @@ public final class Parser {
|
||||
return null;
|
||||
}
|
||||
match(currentType);
|
||||
|
||||
|
||||
final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType);
|
||||
final Expression expression = expression();
|
||||
|
||||
|
||||
return new AssignmentExpression(op, (Accessible) targetExpr, expression);
|
||||
}
|
||||
|
||||
|
||||
private Expression ternary() {
|
||||
Expression result = logicalOr();
|
||||
|
||||
Expression result = nullCoalesce();
|
||||
|
||||
if (match(TokenType.QUESTION)) {
|
||||
final Expression trueExpr = expression();
|
||||
consume(TokenType.COLON);
|
||||
@ -486,10 +486,24 @@ public final class Parser {
|
||||
}
|
||||
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() {
|
||||
Expression result = logicalAnd();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.BARBAR)) {
|
||||
result = new ConditionalExpression(ConditionalExpression.Operator.OR, result, logicalAnd());
|
||||
@ -497,13 +511,13 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Expression logicalAnd() {
|
||||
Expression result = bitwiseOr();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.AMPAMP)) {
|
||||
result = new ConditionalExpression(ConditionalExpression.Operator.AND, result, bitwiseOr());
|
||||
@ -511,13 +525,13 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Expression bitwiseOr() {
|
||||
Expression expression = bitwiseXor();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.BAR)) {
|
||||
expression = new BinaryExpression(BinaryExpression.Operator.OR, expression, bitwiseXor());
|
||||
@ -525,13 +539,13 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
|
||||
private Expression bitwiseXor() {
|
||||
Expression expression = bitwiseAnd();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.CARET)) {
|
||||
expression = new BinaryExpression(BinaryExpression.Operator.XOR, expression, bitwiseAnd());
|
||||
@ -539,13 +553,13 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
|
||||
private Expression bitwiseAnd() {
|
||||
Expression expression = equality();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.AMP)) {
|
||||
expression = new BinaryExpression(BinaryExpression.Operator.AND, expression, equality());
|
||||
@ -553,26 +567,26 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
|
||||
private Expression equality() {
|
||||
Expression result = conditional();
|
||||
|
||||
|
||||
if (match(TokenType.EQEQ)) {
|
||||
return new ConditionalExpression(ConditionalExpression.Operator.EQUALS, result, conditional());
|
||||
}
|
||||
if (match(TokenType.EXCLEQ)) {
|
||||
return new ConditionalExpression(ConditionalExpression.Operator.NOT_EQUALS, result, conditional());
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Expression conditional() {
|
||||
Expression result = shift();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.LT)) {
|
||||
result = new ConditionalExpression(ConditionalExpression.Operator.LT, result, shift());
|
||||
@ -592,13 +606,13 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Expression shift() {
|
||||
Expression expression = additive();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.LTLT)) {
|
||||
expression = new BinaryExpression(BinaryExpression.Operator.LSHIFT, expression, additive());
|
||||
@ -618,13 +632,13 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
|
||||
private Expression additive() {
|
||||
Expression result = multiplicative();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.PLUS)) {
|
||||
result = new BinaryExpression(BinaryExpression.Operator.ADD, result, multiplicative());
|
||||
@ -648,13 +662,13 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Expression multiplicative() {
|
||||
Expression result = unary();
|
||||
|
||||
|
||||
while (true) {
|
||||
if (match(TokenType.STAR)) {
|
||||
result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, unary());
|
||||
@ -674,10 +688,10 @@ public final class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Expression unary() {
|
||||
if (match(TokenType.PLUSPLUS)) {
|
||||
return new UnaryExpression(UnaryExpression.Operator.INCREMENT_PREFIX, primary());
|
||||
@ -699,14 +713,14 @@ public final class Parser {
|
||||
}
|
||||
return primary();
|
||||
}
|
||||
|
||||
|
||||
private Expression primary() {
|
||||
if (match(TokenType.LPAREN)) {
|
||||
Expression result = expression();
|
||||
consume(TokenType.RPAREN);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
if (match(TokenType.COLONCOLON)) {
|
||||
// ::method reference
|
||||
final String functionName = consume(TokenType.WORD).getText();
|
||||
@ -729,7 +743,7 @@ public final class Parser {
|
||||
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
|
||||
return functionChain(new ValueExpression(consume(TokenType.WORD).getText()));
|
||||
}
|
||||
|
||||
|
||||
final Expression qualifiedNameExpr = qualifiedName();
|
||||
if (qualifiedNameExpr != null) {
|
||||
// variable(args) || arr["key"](args) || obj.key(args)
|
||||
@ -745,7 +759,7 @@ public final class Parser {
|
||||
}
|
||||
return qualifiedNameExpr;
|
||||
}
|
||||
|
||||
|
||||
if (lookMatch(0, TokenType.LBRACKET)) {
|
||||
return array();
|
||||
}
|
||||
@ -754,12 +768,12 @@ public final class Parser {
|
||||
}
|
||||
return value();
|
||||
}
|
||||
|
||||
|
||||
private Expression qualifiedName() {
|
||||
// var || var.key[index].key2
|
||||
final Token current = get(0);
|
||||
if (!match(TokenType.WORD)) return null;
|
||||
|
||||
|
||||
final List<Expression> indices = variableSuffix();
|
||||
if (indices == null || indices.isEmpty()) {
|
||||
return new VariableExpression(current.getText());
|
||||
@ -786,7 +800,7 @@ public final class Parser {
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
|
||||
private Expression value() {
|
||||
final Token current = get(0);
|
||||
if (match(TokenType.NUMBER)) {
|
||||
@ -816,7 +830,7 @@ public final class Parser {
|
||||
}
|
||||
throw new ParseException("Unknown expression: " + current);
|
||||
}
|
||||
|
||||
|
||||
private Number createNumber(String text, int radix) {
|
||||
// Double
|
||||
if (text.contains(".")) {
|
||||
@ -829,7 +843,7 @@ public final class Parser {
|
||||
return Long.parseLong(text, radix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Token consume(TokenType type) {
|
||||
final Token current = get(0);
|
||||
if (type != current.getType()) {
|
||||
@ -838,7 +852,7 @@ public final class Parser {
|
||||
pos++;
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
private boolean match(TokenType type) {
|
||||
final Token current = get(0);
|
||||
if (type != current.getType()) {
|
||||
@ -847,11 +861,11 @@ public final class Parser {
|
||||
pos++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean lookMatch(int pos, TokenType type) {
|
||||
return get(pos).getType() == type;
|
||||
}
|
||||
|
||||
|
||||
private Token get(int relativePosition) {
|
||||
final int position = pos + relativePosition;
|
||||
if (position >= size) return EOF;
|
||||
|
@ -69,6 +69,7 @@ public enum TokenType {
|
||||
DOTDOT, // ..
|
||||
STARSTAR, // **
|
||||
QUESTIONCOLON, // ?:
|
||||
QUESTIONQUESTION, // ??
|
||||
|
||||
TILDE, // ~
|
||||
CARET, // ^
|
||||
|
@ -10,19 +10,21 @@ import com.annimon.ownlang.lib.Value;
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class ConditionalExpression implements Expression {
|
||||
|
||||
|
||||
public enum Operator {
|
||||
EQUALS("=="),
|
||||
NOT_EQUALS("!="),
|
||||
|
||||
|
||||
LT("<"),
|
||||
LTEQ("<="),
|
||||
GT(">"),
|
||||
GTEQ(">="),
|
||||
|
||||
|
||||
AND("&&"),
|
||||
OR("||");
|
||||
|
||||
OR("||"),
|
||||
|
||||
NULL_COALESCE("??");
|
||||
|
||||
private final String name;
|
||||
|
||||
private Operator(String name) {
|
||||
@ -33,7 +35,7 @@ public final class ConditionalExpression implements Expression {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final Expression expr1, expr2;
|
||||
public final Operator operation;
|
||||
|
||||
@ -45,17 +47,24 @@ public final class ConditionalExpression implements Expression {
|
||||
|
||||
@Override
|
||||
public Value eval() {
|
||||
final Value value1 = expr1.eval();
|
||||
switch (operation) {
|
||||
case AND: return NumberValue.fromBoolean(
|
||||
(value1.asInt() != 0) && (expr2.eval().asInt() != 0) );
|
||||
case OR: return NumberValue.fromBoolean(
|
||||
(value1.asInt() != 0) || (expr2.eval().asInt() != 0) );
|
||||
case AND:
|
||||
return NumberValue.fromBoolean((expr1AsInt() != 0) && (expr2AsInt() != 0));
|
||||
case OR:
|
||||
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();
|
||||
|
||||
|
||||
double number1, number2;
|
||||
if (value1.type() == Types.NUMBER) {
|
||||
number1 = value1.asNumber();
|
||||
@ -64,23 +73,42 @@ public final class ConditionalExpression implements Expression {
|
||||
number1 = value1.compareTo(value2);
|
||||
number2 = 0;
|
||||
}
|
||||
|
||||
boolean result;
|
||||
|
||||
switch (operation) {
|
||||
case EQUALS: result = number1 == number2; break;
|
||||
case NOT_EQUALS: result = number1 != number2; break;
|
||||
|
||||
case LT: result = number1 < number2; break;
|
||||
case LTEQ: result = number1 <= number2; break;
|
||||
case GT: result = number1 > number2; break;
|
||||
case GTEQ: result = number1 >= number2; break;
|
||||
|
||||
case EQUALS: return number1 == number2;
|
||||
case NOT_EQUALS: return number1 != number2;
|
||||
|
||||
case LT: return number1 < number2;
|
||||
case LTEQ: return number1 <= number2;
|
||||
case GT: return number1 > number2;
|
||||
case GTEQ: return number1 >= number2;
|
||||
|
||||
default:
|
||||
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
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
|
27
src/test/resources/expressions/nullCoalesce.own
Normal file
27
src/test/resources/expressions/nullCoalesce.own
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user