package jetbrick.template.parser; import java.util.ArrayList; import java.util.Collections; import java.util.List; import jetbrick.template.parser.ast.AstConstant; import jetbrick.template.parser.ast.AstConstantList; import jetbrick.template.parser.ast.AstConstantMap; import jetbrick.template.parser.ast.AstConstantMapEntry; import jetbrick.template.parser.ast.AstDirectiveBreak; import jetbrick.template.parser.ast.AstDirectiveCall; import jetbrick.template.parser.ast.AstDirectiveContinue; import jetbrick.template.parser.ast.AstDirectiveFor; import jetbrick.template.parser.ast.AstDirectiveIf; import jetbrick.template.parser.ast.AstDirectiveInclude; import jetbrick.template.parser.ast.AstDirectiveMacro; import jetbrick.template.parser.ast.AstDirectiveNoop; import jetbrick.template.parser.ast.AstDirectiveReturn; import jetbrick.template.parser.ast.AstDirectiveSet; import jetbrick.template.parser.ast.AstDirectiveStop; import jetbrick.template.parser.ast.AstDirectiveTag; import jetbrick.template.parser.ast.AstExpression; import jetbrick.template.parser.ast.AstExpressionList; import jetbrick.template.parser.ast.AstIdentifier; import jetbrick.template.parser.ast.AstInvokeField; import jetbrick.template.parser.ast.AstInvokeFieldStatic; import jetbrick.template.parser.ast.AstInvokeFunction; import jetbrick.template.parser.ast.AstInvokeIndexGet; import jetbrick.template.parser.ast.AstInvokeMethod; import jetbrick.template.parser.ast.AstInvokeMethodStatic; import jetbrick.template.parser.ast.AstInvokeNewArray; import jetbrick.template.parser.ast.AstInvokeNewObject; import jetbrick.template.parser.ast.AstNode; import jetbrick.template.parser.ast.AstOperatorBinary; import jetbrick.template.parser.ast.AstOperatorEquals; import jetbrick.template.parser.ast.AstOperatorInstanceof; import jetbrick.template.parser.ast.AstOperatorNullAsDefault; import jetbrick.template.parser.ast.AstOperatorUnary; import jetbrick.template.parser.ast.AstStatement; import jetbrick.template.parser.ast.AstStatementList; import jetbrick.template.parser.ast.AstTemplate; import jetbrick.template.parser.ast.AstTernaryOperator; import jetbrick.template.parser.ast.AstTernarySimplifyOperator; import jetbrick.template.parser.ast.AstText; import jetbrick.template.parser.ast.AstType; import jetbrick.template.parser.ast.AstValue; import jetbrick.template.parser.ast.AstValueEscaped; import jetbrick.template.parser.ast.Position; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.BlockContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.ConstantContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.DirectiveContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_breakContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_callContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_continueContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_defineContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_define_expressionContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_elseContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_elseifContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_forContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_ifContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_includeContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_invalidContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_macroContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_macro_argumentsContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_optionsContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_options_expressionContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_returnContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_setContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_set_expressionContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_stopContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Directive_tagContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_array_listContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_binary_operatorContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_constantContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_fieldContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_field_staticContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_functionContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_hash_mapContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_identifierContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_index_getContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_instanceofContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_listContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_methodContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_method_staticContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_new_arrayContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_new_objectContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_nullsafe_operatorContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_primaryContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_ternary_operatorContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Expression_unary_operatorContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.Hash_map_entryContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.IdentifierContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.TemplateContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.TextContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.TypeContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParser.ValueContext; import jetbrick.template.runtime.parser.grammer.JetTemplateParserVisitor; import jetbrick.util.JavaKeywordsUtils; import jetbrick.util.StringEscapeUtils; import jetbrick.util.StringUtils; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; public final class AstCodeVisitor extends AbstractParseTreeVisitor implements JetTemplateParserVisitor { private final ParserContext parseCtx; public static Object nullObject_; public AstCodeVisitor(ParserContext parseCtx) { this.parseCtx = parseCtx; } public AstTemplate visitTemplate(JetTemplateParser.TemplateContext ctx) { AstStatementList statements = (AstStatementList)accept(ctx.getChild(0)); return new AstTemplate(statements); } public AstStatementList visitBlock(JetTemplateParser.BlockContext ctx) { List statements = accept(ctx.children); ParseTree parent = ctx.getParent(); int block; if ((parent instanceof JetTemplateParser.TemplateContext)) { block = 1; } else { int block; if ((parent instanceof JetTemplateParser.Directive_forContext)) { block = 2; } else { int block; if ((parent instanceof JetTemplateParser.Directive_ifContext)) { block = 3; } else { int block; if ((parent instanceof JetTemplateParser.Directive_elseifContext)) { block = 4; } else { int block; if ((parent instanceof JetTemplateParser.Directive_elseContext)) { block = 5; } else { int block; if ((parent instanceof JetTemplateParser.Directive_macroContext)) { block = 6; } else { int block; if ((parent instanceof JetTemplateParser.Directive_tagContext)) { block = 7; } else { throw new UnsupportedOperationException(); } } } } } } } int block; return new AstStatementList(statements, block, this.parseCtx); } public AstText visitText(JetTemplateParser.TextContext ctx) { Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); String text = token.getText(); switch (token.getType()) { case 4: text = text.substring(3, text.length() - 3); break; case 5: text = text.substring(1); } return new AstText(text, token.getLine()); } public AstNode visitValue(JetTemplateParser.ValueContext ctx) { AstExpression expression = (AstExpression)accept(ctx.expression()); Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); if (token.getType() == 8) { return new AstValueEscaped(expression); } return new AstValue(expression); } public AstNode visitDirective(JetTemplateParser.DirectiveContext ctx) { return (AstNode)ctx.getChild(0).accept(this); } public AstNode visitDirective_options(JetTemplateParser.Directive_optionsContext ctx) { accept(ctx.directive_options_expression()); return AstDirectiveNoop.INSTANCE; } public AstNode visitDirective_options_expression(JetTemplateParser.Directive_options_expressionContext ctx) { ParseTree nameNode = ctx.getChild(0); ParseTree valueNode = ctx.getChild(2); String name = nameNode.getText(); Object value = ((AstConstant)accept(valueNode)).getValue(); boolean invalidName = false; if ("import".equals(name)) { if ((value instanceof String)) { this.parseCtx.importClass((String)value); return null; } } else if ("loadmacro".equals(name)) { if ((value instanceof String)) { this.parseCtx.loadMacroFile((String)value); return null; } } else if ("strict".equals(name)) { if ((value instanceof Boolean)) { this.parseCtx.setStrict(((Boolean)value).booleanValue()); return null; } } else if ("safecall".equals(name)) { if ((value instanceof Boolean)) { this.parseCtx.setSafecall(((Boolean)value).booleanValue()); return null; } } else if ("trimLeadingWhitespaces".equals(name)) { if ((value instanceof Boolean)) { this.parseCtx.setTrimLeadingWhitespaces(((Boolean)value).booleanValue()); return null; } } else { invalidName = true; } if (invalidName) { throw new SyntaxException("#option name is unknown: %s", new Object[] { name }).set(pos(nameNode)); } throw new SyntaxException("#option value is invalid: %s", new Object[] { name }).set(pos(valueNode)); } public AstNode visitDirective_define(JetTemplateParser.Directive_defineContext ctx) { accept(ctx.directive_define_expression()); return AstDirectiveNoop.INSTANCE; } public AstNode visitDirective_define_expression(JetTemplateParser.Directive_define_expressionContext ctx) { AstType type = (AstType)accept(ctx.type()); String identifier = ctx.IDENTIFIER().getText(); validateIdentifier(identifier, true, ctx.IDENTIFIER()); Class cls = resolveClass(type); try { this.parseCtx.defineSymbol(identifier, cls); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } return null; } public AstStatementList visitDirective_set(JetTemplateParser.Directive_setContext ctx) { List statements = accept(ctx.directive_set_expression()); return new AstStatementList(statements, 8, this.parseCtx); } public AstStatement visitDirective_set_expression(JetTemplateParser.Directive_set_expressionContext ctx) { AstType type = (AstType)accept(ctx.type()); String identifier = ctx.IDENTIFIER().getText(); AstExpression expression = (AstExpression)accept(ctx.expression()); validateIdentifier(identifier, true, ctx.IDENTIFIER()); if (type != null) { Class cls = resolveClass(type); try { this.parseCtx.defineSymbol(identifier, cls, true); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } } else { try { this.parseCtx.useSymbol(identifier); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } } return new AstDirectiveSet(identifier, expression, pos(ctx)); } public AstDirectiveIf visitDirective_if(JetTemplateParser.Directive_ifContext ctx) { AstExpression conditionExpression = (AstExpression)accept(ctx.getChild(1)); AstStatementList thenStatement = (AstStatementList)accept(ctx.getChild(3)); AstStatement elseStatement = (AstStatement)accept(ctx.directive_else()); if (elseStatement == null) { elseStatement = (AstStatement)accept(ctx.directive_elseif()); } return new AstDirectiveIf(conditionExpression, thenStatement, elseStatement, pos(ctx)); } public AstDirectiveIf visitDirective_elseif(JetTemplateParser.Directive_elseifContext ctx) { AstExpression conditionExpression = (AstExpression)accept(ctx.getChild(1)); AstStatementList thenStatement = (AstStatementList)accept(ctx.getChild(3)); AstStatement elseStatement = (AstStatement)accept(ctx.directive_else()); if (elseStatement == null) { elseStatement = (AstStatement)accept(ctx.directive_elseif()); } return new AstDirectiveIf(conditionExpression, thenStatement, elseStatement, pos(ctx)); } public AstNode visitDirective_else(JetTemplateParser.Directive_elseContext ctx) { return (AstNode)ctx.getChild(1).accept(this); } public AstDirectiveFor visitDirective_for(JetTemplateParser.Directive_forContext ctx) { AstType type = (AstType)accept(ctx.type()); String identifier = ctx.IDENTIFIER().getText(); validateIdentifier(identifier, true, ctx.IDENTIFIER()); Class cls = null; if (type != null) { cls = resolveClass(type); } try { this.parseCtx.defineSymbol(identifier, cls, true); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } AstExpression expression = (AstExpression)accept(ctx.expression()); AstStatementList statement = (AstStatementList)accept(ctx.block()); AstStatementList elseStatement = (AstStatementList)accept(ctx.directive_else()); return new AstDirectiveFor(identifier, expression, statement, elseStatement, pos(ctx)); } public AstDirectiveBreak visitDirective_break(JetTemplateParser.Directive_breakContext ctx) { validateInsideOfDirectiveFor(ctx, "#break"); AstExpression expression = (AstExpression)accept(ctx.expression()); return new AstDirectiveBreak(expression, pos(ctx)); } public AstDirectiveContinue visitDirective_continue(JetTemplateParser.Directive_continueContext ctx) { validateInsideOfDirectiveFor(ctx, "#continue"); AstExpression expression = (AstExpression)accept(ctx.expression()); return new AstDirectiveContinue(expression, pos(ctx)); } public AstNode visitDirective_stop(JetTemplateParser.Directive_stopContext ctx) { AstExpression expression = (AstExpression)accept(ctx.expression()); return new AstDirectiveStop(expression, pos(ctx)); } public AstNode visitDirective_return(JetTemplateParser.Directive_returnContext ctx) { AstExpression expression = (AstExpression)accept(ctx.getChild(1)); return new AstDirectiveReturn(expression, pos(ctx)); } public AstNode visitDirective_include(JetTemplateParser.Directive_includeContext ctx) { List expressions = accept(ctx.expression()); AstExpression fileExpression = (AstExpression)expressions.get(0); AstExpression parametersExpression = null; String returnName = null; switch (expressions.size()) { case 1: break; case 2: AstExpression expr = (AstExpression)expressions.get(1); if ((expr instanceof AstConstantMap)) { parametersExpression = expr; } else if ((expr instanceof AstConstant)) { Object value = ((AstConstant)expr).getValue(); if ((value instanceof String)) { returnName = (String)value; } else { throw new SyntaxException("type mismatch: the %s argument cannot convert from %s to %s", new Object[] { "2nd", value.getClass(), "String" }).set(pos(expr)); } } else { parametersExpression = expr; } break; case 3: parametersExpression = (AstExpression)expressions.get(1); AstExpression expr = (AstExpression)expressions.get(2); Object value = ((AstConstant)expr).getValue(); if ((value instanceof String)) { returnName = (String)value; } else { throw new SyntaxException("type mismatch: the %s argument cannot convert from %s to %s", new Object[] { "3rd", value.getClass(), "String" }).set(pos(expr)); } break; default: throw new SyntaxException("arguments not match").set(pos(expressions.get(3))); } return new AstDirectiveInclude(fileExpression, parametersExpression, returnName, pos(ctx)); } public AstNode visitDirective_tag(JetTemplateParser.Directive_tagContext ctx) { Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); Position position = new Position(token.getLine(), token.getCharPositionInLine() + 5); String name = ctx.getChild(0).getText(); name = StringUtils.substringBetween(name, " ", "(").trim(); AstExpressionList expressionList = (AstExpressionList)accept(ctx.expression_list()); AstStatementList block = (AstStatementList)accept(ctx.block()); return new AstDirectiveTag(name, expressionList, block, position); } public AstNode visitDirective_call(JetTemplateParser.Directive_callContext ctx) { Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); Position position = new Position(token.getLine(), token.getCharPositionInLine() + 6); String name = ctx.getChild(0).getText(); name = StringUtils.substringBetween(name, " ", "(").trim(); AstExpressionList expressionList = (AstExpressionList)accept(ctx.expression_list()); return new AstDirectiveCall(name, expressionList, position); } public AstNode visitDirective_macro(JetTemplateParser.Directive_macroContext ctx) { Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); Position position = new Position(token.getLine(), token.getCharPositionInLine() + 7); String name = token.getText(); name = StringUtils.substringBetween(name, " ", "(").trim(); this.parseCtx.enterMacros(); accept(ctx.directive_macro_arguments()); List argumentNames = this.parseCtx.getMacroArgumentNames(); AstStatementList block = (AstStatementList)accept(ctx.block()); AstDirectiveMacro macro = new AstDirectiveMacro(name, argumentNames, this.parseCtx.getSymbols(), block, position); try { this.parseCtx.defineMacro(macro); } catch (IllegalStateException e) { throw new SyntaxException(e).set(position); } this.parseCtx.exitMacros(); return AstDirectiveNoop.INSTANCE; } public AstNode visitDirective_macro_arguments(JetTemplateParser.Directive_macro_argumentsContext ctx) { int count = ctx.getChildCount(); int i = 0; while (i < count) { ParseTree node = ctx.getChild(i++); Class cls = null; if ((node instanceof JetTemplateParser.TypeContext)) { AstType type = (AstType)accept(node); cls = resolveClass(type); node = ctx.getChild(i++); } String name = node.getText(); try { this.parseCtx.defineSymbol(name, cls); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(node)); } i++; } return null; } public AstNode visitDirective_invalid(JetTemplateParser.Directive_invalidContext ctx) { throw new SyntaxException("arguments is missing: %s", new Object[] { ctx.getText() }).set(pos(ctx)); } public AstNode visitExpression_primary(JetTemplateParser.Expression_primaryContext ctx) { return (AstNode)ctx.getChild(1).accept(this); } public AstNode visitExpression_constant(JetTemplateParser.Expression_constantContext ctx) { return (AstNode)ctx.getChild(0).accept(this); } public AstExpression visitExpression_array_list(JetTemplateParser.Expression_array_listContext ctx) { AstExpressionList expressionList = (AstExpressionList)accept(ctx.getChild(1)); if (expressionList == null) { return new AstConstant(Collections.emptyList(), pos(ctx)); } return new AstConstantList(expressionList, pos(ctx)); } public AstExpression visitExpression_hash_map(JetTemplateParser.Expression_hash_mapContext ctx) { List entries = accept(ctx.hash_map_entry()); if ((entries == null) || (entries.isEmpty())) { return new AstConstant(Collections.emptyMap(), pos(ctx)); } return new AstConstantMap(entries, pos(ctx)); } public AstConstantMapEntry visitHash_map_entry(JetTemplateParser.Hash_map_entryContext ctx) { Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); String name = token.getText(); switch (token.getType()) { case 86: case 87: name = getJavaString(token.getText(), ctx); } AstExpression valueExpression = (AstExpression)accept(ctx.getChild(2)); return new AstConstantMapEntry(name, valueExpression, pos(ctx)); } public AstNode visitExpression_unary_operator(JetTemplateParser.Expression_unary_operatorContext ctx) { Position position = pos(ctx); Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); AstExpression expression = (AstExpression)accept(ctx.getChild(1)); switch (token.getType()) { case 65: return new AstOperatorUnary(1, expression, position); case 66: return new AstOperatorUnary(2, expression, position); case 64: return new AstOperatorEquals(23, expression, null, position); case 72: return new AstOperatorUnary(22, expression, position); } throw new SyntaxException("unreachable code").set(position); } public AstNode visitExpression_binary_operator(JetTemplateParser.Expression_binary_operatorContext ctx) { Position position = pos(ctx, 1); Token token = ((TerminalNode)ctx.getChild(1)).getSymbol(); AstExpression lhs = (AstExpression)accept(ctx.getChild(0)); AstExpression rhs = (AstExpression)accept(ctx.getChild(ctx.getChildCount() - 1)); switch (token.getType()) { case 65: return new AstOperatorBinary(1, lhs, rhs, position); case 66: return new AstOperatorBinary(2, lhs, rhs, position); case 67: return new AstOperatorBinary(3, lhs, rhs, position); case 68: return new AstOperatorBinary(4, lhs, rhs, position); case 69: return new AstOperatorBinary(5, lhs, rhs, position); case 74: return new AstOperatorBinary(9, lhs, rhs, position); case 75: return new AstOperatorBinary(10, lhs, rhs, position); case 76: return new AstOperatorBinary(11, lhs, rhs, position); case 70: return new AstOperatorBinary(6, lhs, rhs, position); case 71: return new AstOperatorBinary(7, lhs, rhs, position); case 73: return new AstOperatorBinary(8, lhs, rhs, position); case 59: return new AstOperatorBinary(12, lhs, rhs, position); case 61: return new AstOperatorBinary(13, lhs, rhs, position); case 58: return new AstOperatorBinary(14, lhs, rhs, position); case 54: return new AstOperatorEquals(18, lhs, rhs, position); case 55: return new AstOperatorEquals(19, lhs, rhs, position); case 60: return new AstOperatorBinary(15, lhs, rhs, position); case 56: return new AstOperatorEquals(16, lhs, rhs, position); case 57: return new AstOperatorEquals(17, lhs, rhs, position); case 62: return new AstOperatorEquals(20, lhs, rhs, position); case 63: return new AstOperatorEquals(21, lhs, rhs, position); } throw new SyntaxException("unreachable code").set(position); } public AstNode visitExpression_nullsafe_operator(JetTemplateParser.Expression_nullsafe_operatorContext ctx) { this.parseCtx.setNullSafe(true); AstExpression objectExpression = (AstExpression)accept(ctx.getChild(0)); this.parseCtx.setNullSafe(false); AstExpression defaultExpression = (AstExpression)accept(ctx.getChild(2)); return new AstOperatorNullAsDefault(objectExpression, defaultExpression, pos(ctx, 1)); } public AstNode visitExpression_ternary_operator(JetTemplateParser.Expression_ternary_operatorContext ctx) { if (ctx.getChildCount() == 5) { AstExpression conditionExpression = (AstExpression)accept(ctx.getChild(0)); AstExpression trueExpression = (AstExpression)accept(ctx.getChild(2)); AstExpression falseExpression = (AstExpression)accept(ctx.getChild(4)); return new AstTernaryOperator(conditionExpression, trueExpression, falseExpression, pos(ctx, 1)); } AstExpression objectExpression = (AstExpression)accept(ctx.getChild(0)); AstExpression defaultExpression = (AstExpression)accept(ctx.getChild(3)); return new AstTernarySimplifyOperator(objectExpression, defaultExpression, pos(ctx, 1)); } public AstNode visitExpression_instanceof(JetTemplateParser.Expression_instanceofContext ctx) { AstExpression expression = (AstExpression)accept(ctx.getChild(0)); AstType type = (AstType)accept(ctx.getChild(2)); Class cls = resolveClass(type); return new AstOperatorInstanceof(expression, cls, pos(ctx)); } public AstNode visitExpression_new_object(JetTemplateParser.Expression_new_objectContext ctx) { AstType type = (AstType)accept(ctx.getChild(1)); AstExpressionList argumentList = (AstExpressionList)accept(ctx.expression_list()); Class cls = resolveClass(type); return new AstInvokeNewObject(cls, argumentList, pos(ctx)); } public AstNode visitExpression_new_array(JetTemplateParser.Expression_new_arrayContext ctx) { AstType type = (AstType)accept(ctx.getChild(1)); List expressions = accept(ctx.expression()); Class cls = resolveClass(type); return new AstInvokeNewArray(cls, expressions, pos(ctx)); } public AstNode visitExpression_field(JetTemplateParser.Expression_fieldContext ctx) { AstExpression objectExpression = (AstExpression)accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); return new AstInvokeField(objectExpression, name, this.parseCtx.isNullSafe(), pos(ctx, 1)); } public AstNode visitExpression_field_static(JetTemplateParser.Expression_field_staticContext ctx) { AstType type = (AstType)accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); Class cls = resolveClass(type); return new AstInvokeFieldStatic(cls, name, pos(ctx, 1)); } public AstNode visitExpression_method(JetTemplateParser.Expression_methodContext ctx) { AstExpression objectExpression = (AstExpression)accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); AstExpressionList argumentList = (AstExpressionList)accept(ctx.expression_list()); return new AstInvokeMethod(objectExpression, name, argumentList, this.parseCtx.isNullSafe(), pos(ctx, 1)); } public AstNode visitExpression_method_static(JetTemplateParser.Expression_method_staticContext ctx) { AstType type = (AstType)accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); AstExpressionList argumentList = (AstExpressionList)accept(ctx.expression_list()); Class cls = resolveClass(type); return new AstInvokeMethodStatic(cls, name, argumentList, pos(ctx, 1)); } public AstNode visitExpression_index_get(JetTemplateParser.Expression_index_getContext ctx) { AstExpression objectExpression = (AstExpression)accept(ctx.getChild(0)); AstExpression indexExpression = (AstExpression)accept(ctx.getChild(2)); return new AstInvokeIndexGet(objectExpression, indexExpression, this.parseCtx.isNullSafe(), pos(ctx, 1)); } public AstNode visitExpression_function(JetTemplateParser.Expression_functionContext ctx) { String name = ctx.getChild(0).getText(); AstExpressionList argumentList = (AstExpressionList)accept(ctx.expression_list()); return new AstInvokeFunction(name, argumentList, pos(ctx)); } public AstNode visitExpression_identifier(JetTemplateParser.Expression_identifierContext ctx) { return (AstNode)ctx.getChild(0).accept(this); } public AstIdentifier visitIdentifier(JetTemplateParser.IdentifierContext ctx) { String name = ctx.getChild(0).getText(); validateIdentifier(name, false, ctx); if ("for".equals(name)) { validateInsideOfDirectiveFor(ctx, "Local variable `for`"); } return new AstIdentifier(name, pos(ctx)); } public AstConstant visitConstant(JetTemplateParser.ConstantContext ctx) { Token token = ((TerminalNode)ctx.getChild(0)).getSymbol(); String text = token.getText(); int length = text.length(); int type = token.getType(); switch (type) { case 86: case 87: String value = getJavaString(text, ctx); return new AstConstant(value, pos(ctx)); case 83: case 84: case 85: char suffix = text.charAt(length - 1); int radix; if (type == 84) { int radix = 16; if ((suffix == 'l') || (suffix == 'L')) { text = text.substring(2, length - 1); } else { text = text.substring(2); suffix = '\000'; } } else { radix = 10; if (suffix > '9') { text = text.substring(0, length - 1); } } Object value; Object value; Object value; Object value; switch (suffix) { case 'L': case 'l': value = Long.valueOf(text, radix); break; case 'F': case 'f': value = Float.valueOf(text); break; case 'D': case 'd': value = Double.valueOf(text); break; default: Object value; if (type == 85) { value = Double.valueOf(text); } else { value = Integer.valueOf(text, radix); } break; } return new AstConstant(value, pos(ctx)); case 79: return new AstConstant(Boolean.TRUE, pos(ctx)); case 80: return new AstConstant(Boolean.FALSE, pos(ctx)); case 81: return new AstConstant(null, pos(ctx)); } throw new SyntaxException("unreachable code").set(pos(ctx)); } public AstExpressionList visitExpression_list(JetTemplateParser.Expression_listContext ctx) { List expression_list = accept(ctx.expression()); return new AstExpressionList(expression_list, pos(ctx)); } public AstType visitType(JetTemplateParser.TypeContext ctx) { String className = StringUtils.deleteWhitespace(ctx.getText()); return new AstType(className, pos(ctx)); } private Class resolveClass(AstType type) { Class cls = this.parseCtx.resolveClass(type.getClassName()); if (cls == null) { throw new SyntaxException("cannot resolve class: %s", new Object[] { type.getClassName() }).set(pos(type)); } return cls; } private T accept(ParseTree node) { if (node != null) { return (AstNode)node.accept(this); } return null; } private List accept(List nodes) { if (nodes != null) { int size = nodes.size(); if (size == 0) { return Collections.emptyList(); } List results = new ArrayList(size); for (ParseTree node : nodes) { T ast = (AstNode)node.accept(this); if (ast != null) { results.add(ast); } } return results; } return null; } private void validateIdentifier(String name, boolean defining, ParseTree node) { if (nullObject_ != null) { if (nullObject_ == null) { throw new SyntaxException("syntax error on token `%s`, It is a reserved/keyword identifier", new Object[] { name }).set(pos(node)); } } else { if (JavaKeywordsUtils.isKeyword(name)) { throw new SyntaxException("syntax error on token `%s`, It is a reserved/keyword identifier", new Object[] { name }).set(pos(node)); } if (nullObject_ != null) { throw new SyntaxException("local variable `%s` cannot start with '$', it is a reserved identifier", new Object[] { name }).set(pos(node)); } } if (nullObject_ != null) { try { this.parseCtx.useSymbol(name); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(node)); } } } private void validateInsideOfDirectiveFor(ParserRuleContext ctx, String name) { ParserRuleContext p = ctx.getParent(); while (p != null) { if ((p instanceof JetTemplateParser.Directive_forContext)) { return; } if ((p instanceof JetTemplateParser.Directive_elseContext)) { p = p.getParent(); } p = p.getParent(); } throw new SyntaxException("`%s` cannot be used outside of `#for(...)`", new Object[] { name }).set(pos(ctx)); } private String getJavaString(String text, ParserRuleContext ctx) { String value = text; value = value.substring(1, value.length() - 1); try { value = StringEscapeUtils.unescapeJava(value); } catch (StringIndexOutOfBoundsException e) { throw new SyntaxException("invalid unicode in string constant").set(pos(ctx)); } return value; } private Position pos(ParserRuleContext ctx, int childIndex) { ParseTree node = ctx.getChild(childIndex); if ((node instanceof TerminalNode)) { Token token = ((TerminalNode)node).getSymbol(); return new Position(token.getLine(), token.getCharPositionInLine()); } if ((node instanceof ParserRuleContext)) { Token token = ctx.getStart(); return new Position(token.getLine(), token.getCharPositionInLine()); } throw new UnsupportedOperationException(); } private Position pos(Object ctx) { Token token = null; if ((ctx instanceof ParserRuleContext)) { token = ((ParserRuleContext)ctx).getStart(); } else if ((ctx instanceof TerminalNode)) { token = ((TerminalNode)ctx).getSymbol(); } else if ((ctx instanceof Token)) { token = (Token)ctx; } else { if ((ctx instanceof AstExpression)) { return ((AstExpression)ctx).getPosition(); } if ((ctx instanceof AstType)) { return ((AstType)ctx).getPosition(); } } if (token != null) { return new Position(token.getLine(), token.getCharPositionInLine()); } throw new UnsupportedOperationException(); } }