/** * Copyright 2013-2018 Guoqiang Chen, Shanghai, China. All rights reserved. * * Author: Guoqiang Chen * Email: subchen@gmail.com * WebURL: https://github.com/subchen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrick.template.parser; import java.util.*; import jetbrick.template.Errors; import jetbrick.template.parser.ast.*; import jetbrick.template.runtime.parser.grammer.*; 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.util.*; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.*; /** * ANTLR 4 鐨� parse tree 鐨� visitor. (鏍规嵁 parse tree 鐢熸垚 AST) */ public final class AstCodeVisitor extends AbstractParseTreeVisitor implements JetTemplateParserVisitor { private final ParserContext parseCtx; public AstCodeVisitor(ParserContext parseCtx) { this.parseCtx = parseCtx; } @Override public AstTemplate visitTemplate(TemplateContext ctx) { AstStatementList statements = accept(ctx.getChild(0)); return new AstTemplate(statements); } @Override public AstStatementList visitBlock(BlockContext ctx) { List statements = accept(ctx.children); int block; ParseTree parent = ctx.getParent(); if (parent instanceof TemplateContext) { block = Tokens.AST_BLOCK_TEMPLATE; } else if (parent instanceof Directive_forContext) { block = Tokens.AST_BLOCK_FOR; } else if (parent instanceof Directive_ifContext) { block = Tokens.AST_BLOCK_IF; } else if (parent instanceof Directive_elseifContext) { block = Tokens.AST_BLOCK_ELSEIF; } else if (parent instanceof Directive_elseContext) { block = Tokens.AST_BLOCK_ELSE; } else if (parent instanceof Directive_macroContext) { block = Tokens.AST_BLOCK_MACRO; } else if (parent instanceof Directive_tagContext) { block = Tokens.AST_BLOCK_TAG; } else { throw new UnsupportedOperationException(); } return new AstStatementList(statements, block, parseCtx); } @Override public AstText visitText(TextContext ctx) { Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); String text = token.getText(); switch (token.getType()) { case JetTemplateLexer.TEXT_CDATA: text = text.substring(3, text.length() - 3); break; case JetTemplateLexer.TEXT_CHAR_ESCAPED: text = text.substring(1); break; } return new AstText(text, token.getLine()); } @Override public AstNode visitValue(ValueContext ctx) { AstExpression expression = accept(ctx.expression()); Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); if (token.getType() == JetTemplateLexer.VALUE_OPEN_ESCAPED) { return new AstValueEscaped(expression); } else { return new AstValue(expression); } } @Override public AstNode visitDirective(DirectiveContext ctx) { return ctx.getChild(0).accept(this); } @Override public AstNode visitDirective_options(Directive_optionsContext ctx) { accept(ctx.directive_options_expression()); return AstDirectiveNoop.INSTANCE; } @Override public AstNode visitDirective_options_expression(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 (Symbols.OPTION_IMPORT.equals(name)) { if (value instanceof String) { parseCtx.importClass((String) value); return null; } } else if (Symbols.OPTION_LOAD_MACRO.equals(name)) { if (value instanceof String) { parseCtx.loadMacroFile((String) value); return null; } } else if (Symbols.OPTION_STRICT.equals(name)) { if (value instanceof Boolean) { parseCtx.setStrict((Boolean) value); return null; } } else if (Symbols.OPTION_SAFECALL.equals(name)) { if (value instanceof Boolean) { parseCtx.setSafecall((Boolean) value); return null; } } else if (Symbols.OPTION_TRIM_LEADING_WHITESPACES.equals(name)) { if (value instanceof Boolean) { parseCtx.setTrimLeadingWhitespaces((Boolean) value); return null; } } else { invalidName = true; } if (invalidName) { throw new SyntaxException(Errors.OPTION_NAME_INVALID, name).set(pos(nameNode)); } else { throw new SyntaxException(Errors.OPTION_VALUE_INVALID, name).set(pos(valueNode)); } } @Override public AstNode visitDirective_define(Directive_defineContext ctx) { accept(ctx.directive_define_expression()); return AstDirectiveNoop.INSTANCE; } @Override public AstNode visitDirective_define_expression(Directive_define_expressionContext ctx) { AstType type = accept(ctx.type()); String identifier = ctx.IDENTIFIER().getText(); validateIdentifier(identifier, true, ctx.IDENTIFIER()); // resolve class Class cls = resolveClass(type); // define try { parseCtx.defineSymbol(identifier, cls); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } return null; } @Override public AstStatementList visitDirective_set(Directive_setContext ctx) { List statements = accept(ctx.directive_set_expression()); return new AstStatementList(statements, Tokens.AST_BLOCK_SET, parseCtx); } @Override public AstStatement visitDirective_set_expression(Directive_set_expressionContext ctx) { AstType type = accept(ctx.type()); String identifier = ctx.IDENTIFIER().getText(); AstExpression expression = accept(ctx.expression()); validateIdentifier(identifier, true, ctx.IDENTIFIER()); if (type != null) { // resolve class Class cls = resolveClass(type); // define try { parseCtx.defineSymbol(identifier, cls, true); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } } else { try { parseCtx.useSymbol(identifier); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } } return new AstDirectiveSet(identifier, expression, pos(ctx)); } @Override public AstDirectiveIf visitDirective_if(Directive_ifContext ctx) { AstExpression conditionExpression = accept(ctx.getChild(1)); AstStatementList thenStatement = accept(ctx.getChild(3)); AstStatement elseStatement = accept(ctx.directive_else()); if (elseStatement == null) { elseStatement = accept(ctx.directive_elseif()); } return new AstDirectiveIf(conditionExpression, thenStatement, elseStatement, pos(ctx)); } @Override public AstDirectiveIf visitDirective_elseif(Directive_elseifContext ctx) { AstExpression conditionExpression = accept(ctx.getChild(1)); AstStatementList thenStatement = accept(ctx.getChild(3)); AstStatement elseStatement = accept(ctx.directive_else()); if (elseStatement == null) { elseStatement = accept(ctx.directive_elseif()); } return new AstDirectiveIf(conditionExpression, thenStatement, elseStatement, pos(ctx)); } @Override public AstNode visitDirective_else(Directive_elseContext ctx) { return ctx.getChild(1).accept(this); } @Override public AstDirectiveFor visitDirective_for(Directive_forContext ctx) { AstType type = accept(ctx.type()); String identifier = ctx.IDENTIFIER().getText(); validateIdentifier(identifier, true, ctx.IDENTIFIER()); Class cls = null; if (type != null) { cls = resolveClass(type); } // define try { parseCtx.defineSymbol(identifier, cls, true); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(ctx)); } AstExpression expression = accept(ctx.expression()); AstStatementList statement = accept(ctx.block()); AstStatementList elseStatement = accept(ctx.directive_else()); return new AstDirectiveFor(identifier, expression, statement, elseStatement, pos(ctx)); } @Override public AstDirectiveBreak visitDirective_break(Directive_breakContext ctx) { validateInsideOfDirectiveFor(ctx, "#break"); AstExpression expression = accept(ctx.expression()); return new AstDirectiveBreak(expression, pos(ctx)); } @Override public AstDirectiveContinue visitDirective_continue(Directive_continueContext ctx) { validateInsideOfDirectiveFor(ctx, "#continue"); AstExpression expression = accept(ctx.expression()); return new AstDirectiveContinue(expression, pos(ctx)); } @Override public AstNode visitDirective_stop(Directive_stopContext ctx) { AstExpression expression = accept(ctx.expression()); return new AstDirectiveStop(expression, pos(ctx)); } @Override public AstNode visitDirective_return(Directive_returnContext ctx) { AstExpression expression = accept(ctx.getChild(1)); return new AstDirectiveReturn(expression, pos(ctx)); } @Override public AstNode visitDirective_include(Directive_includeContext ctx) { List expressions = accept(ctx.expression()); AstExpression fileExpression = expressions.get(0); AstExpression parametersExpression = null; String returnName = null; switch (expressions.size()) { case 1: break; case 2: { AstExpression expr = 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(Errors.VARIABLE_TYPE_MISMATCH, "2nd", value.getClass(), "String").set(pos(expr)); } } else { parametersExpression = expr; } break; } case 3: { parametersExpression = expressions.get(1); AstExpression expr = expressions.get(2); Object value = ((AstConstant) expr).getValue(); if (value instanceof String) { returnName = (String) value; } else { throw new SyntaxException(Errors.VARIABLE_TYPE_MISMATCH, "3rd", value.getClass(), "String").set(pos(expr)); } break; } default: throw new SyntaxException(Errors.ARGUMENTS_NOT_MATCH).set(pos(expressions.get(3))); } return new AstDirectiveInclude(fileExpression, parametersExpression, returnName, pos(ctx)); } @Override public AstNode visitDirective_tag(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 = accept(ctx.expression_list()); AstStatementList block = accept(ctx.block()); return new AstDirectiveTag(name, expressionList, block, position); } @Override public AstNode visitDirective_call(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 = accept(ctx.expression_list()); return new AstDirectiveCall(name, expressionList, position); } @Override public AstNode visitDirective_macro(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(); parseCtx.enterMacros(); // 澶勭悊鍙傛暟 accept(ctx.directive_macro_arguments()); // 绔嬪嵆鑾峰彇鍙傛暟鍒楄〃(鍦� body 澶勭悊涔嬪墠) List argumentNames = parseCtx.getMacroArgumentNames(); AstStatementList block = accept(ctx.block()); AstDirectiveMacro macro = new AstDirectiveMacro(name, argumentNames, parseCtx.getSymbols(), block, position); try { parseCtx.defineMacro(macro); } catch (IllegalStateException e) { throw new SyntaxException(e).set(position); } parseCtx.exitMacros(); return AstDirectiveNoop.INSTANCE; } @Override public AstNode visitDirective_macro_arguments(Directive_macro_argumentsContext ctx) { int count = ctx.getChildCount(); int i = 0; while (i < count) { ParseTree node = ctx.getChild(i++); // get type and resolve Class cls = null; if (node instanceof TypeContext) { AstType type = accept(node); cls = resolveClass(type); // get next node as name node = ctx.getChild(i++); } // get name String name = node.getText(); // define arguments try { parseCtx.defineSymbol(name, cls); } catch (IllegalStateException e) { throw new SyntaxException(e).set(pos(node)); } // skip next "," if exists i++; } return null; } @Override public AstNode visitDirective_invalid(Directive_invalidContext ctx) { throw new SyntaxException(Errors.ARGUMENTS_MISSING, ctx.getText()).set(pos(ctx)); } @Override public AstNode visitExpression_primary(Expression_primaryContext ctx) { return ctx.getChild(1).accept(this); } @Override public AstNode visitExpression_constant(Expression_constantContext ctx) { return ctx.getChild(0).accept(this); } @Override public AstExpression visitExpression_array_list(Expression_array_listContext ctx) { AstExpressionList expressionList = accept(ctx.getChild(1)); if (expressionList == null) { return new AstConstant(Collections.emptyList(), pos(ctx)); } else { return new AstConstantList(expressionList, pos(ctx)); } } @Override public AstExpression visitExpression_hash_map(Expression_hash_mapContext ctx) { List entries = accept(ctx.hash_map_entry()); if (entries == null || entries.isEmpty()) { return new AstConstant(Collections.emptyMap(), pos(ctx)); } else { return new AstConstantMap(entries, pos(ctx)); } } @Override public AstConstantMapEntry visitHash_map_entry(Hash_map_entryContext ctx) { Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); String name = token.getText(); switch (token.getType()) { case JetTemplateLexer.STRING_DOUBLE: case JetTemplateLexer.STRING_SINGLE: name = getJavaString(token.getText(), ctx); break; } AstExpression valueExpression = accept(ctx.getChild(2)); return new AstConstantMapEntry(name, valueExpression, pos(ctx)); } @Override public AstNode visitExpression_unary_operator(Expression_unary_operatorContext ctx) { Position position = pos(ctx); Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); AstExpression expression = accept(ctx.getChild(1)); switch (token.getType()) { case JetTemplateLexer.PLUS: return new AstOperatorUnary(Tokens.PLUS, expression, position); case JetTemplateLexer.MINUS: return new AstOperatorUnary(Tokens.MINUS, expression, position); case JetTemplateLexer.NOT: return new AstOperatorEquals(Tokens.NOT, expression, null, position); case JetTemplateLexer.BIT_NOT: return new AstOperatorUnary(Tokens.BIT_NOT, expression, position); } throw new SyntaxException(Errors.UNREACHABLE_CODE).set(position); } @Override public AstNode visitExpression_binary_operator(Expression_binary_operatorContext ctx) { Position position = pos(ctx, 1); Token token = ((TerminalNode) ctx.getChild(1)).getSymbol(); AstExpression lhs = accept(ctx.getChild(0)); // first AstExpression rhs = accept(ctx.getChild(ctx.getChildCount() - 1)); // last switch (token.getType()) { case JetTemplateLexer.PLUS: return new AstOperatorBinary(Tokens.PLUS, lhs, rhs, position); case JetTemplateLexer.MINUS: return new AstOperatorBinary(Tokens.MINUS, lhs, rhs, position); case JetTemplateLexer.MUL: return new AstOperatorBinary(Tokens.MUL, lhs, rhs, position); case JetTemplateLexer.DIV: return new AstOperatorBinary(Tokens.DIV, lhs, rhs, position); case JetTemplateLexer.MOD: return new AstOperatorBinary(Tokens.MOD, lhs, rhs, position); case JetTemplateLexer.BIT_SHL: return new AstOperatorBinary(Tokens.BIT_SHL, lhs, rhs, position); case JetTemplateLexer.BIT_SHR: return new AstOperatorBinary(Tokens.BIT_SHR, lhs, rhs, position); case JetTemplateLexer.BIT_USHR: return new AstOperatorBinary(Tokens.BIT_USHR, lhs, rhs, position); case JetTemplateLexer.BIT_AND: return new AstOperatorBinary(Tokens.BIT_AND, lhs, rhs, position); case JetTemplateLexer.BIT_OR: return new AstOperatorBinary(Tokens.BIT_OR, lhs, rhs, position); case JetTemplateLexer.BIT_XOR: return new AstOperatorBinary(Tokens.BIT_XOR, lhs, rhs, position); case JetTemplateLexer.LT: return new AstOperatorBinary(Tokens.LT, lhs, rhs, position); case JetTemplateLexer.LE: return new AstOperatorBinary(Tokens.LE, lhs, rhs, position); case JetTemplateLexer.GT: return new AstOperatorBinary(Tokens.GT, lhs, rhs, position); case JetTemplateLexer.IDENTICALLY_EQUAL: return new AstOperatorEquals(Tokens.IDENTICALLY_EQUAL, lhs, rhs, position); case JetTemplateLexer.IDENTICALLY_EQUAL_NOT: return new AstOperatorEquals(Tokens.IDENTICALLY_EQUAL_NOT, lhs, rhs, position); case JetTemplateLexer.GE: return new AstOperatorBinary(Tokens.GE, lhs, rhs, position); case JetTemplateLexer.EQ: return new AstOperatorEquals(Tokens.EQ, lhs, rhs, position); case JetTemplateLexer.NE: return new AstOperatorEquals(Tokens.NE, lhs, rhs, position); case JetTemplateLexer.AND: return new AstOperatorEquals(Tokens.AND, lhs, rhs, position); case JetTemplateLexer.OR: return new AstOperatorEquals(Tokens.OR, lhs, rhs, position); } throw new SyntaxException(Errors.UNREACHABLE_CODE).set(position); } @Override public AstNode visitExpression_nullsafe_operator(Expression_nullsafe_operatorContext ctx) { parseCtx.setNullSafe(true); AstExpression objectExpression = accept(ctx.getChild(0)); parseCtx.setNullSafe(false); AstExpression defaultExpression = accept(ctx.getChild(2)); return new AstOperatorNullAsDefault(objectExpression, defaultExpression, pos(ctx, 1)); } @Override public AstNode visitExpression_ternary_operator(Expression_ternary_operatorContext ctx) { if (ctx.getChildCount() == 5) { // a ? b : c AstExpression conditionExpression = accept(ctx.getChild(0)); AstExpression trueExpression = accept(ctx.getChild(2)); AstExpression falseExpression = accept(ctx.getChild(4)); return new AstTernaryOperator(conditionExpression, trueExpression, falseExpression, pos(ctx, 1)); } else { // a ?: b AstExpression objectExpression = accept(ctx.getChild(0)); AstExpression defaultExpression = accept(ctx.getChild(3)); return new AstTernarySimplifyOperator(objectExpression, defaultExpression, pos(ctx, 1)); } } @Override public AstNode visitExpression_instanceof(Expression_instanceofContext ctx) { AstExpression expression = accept(ctx.getChild(0)); AstType type = accept(ctx.getChild(2)); Class cls = resolveClass(type); return new AstOperatorInstanceof(expression, cls, pos(ctx)); } @Override public AstNode visitExpression_new_object(Expression_new_objectContext ctx) { AstType type = accept(ctx.getChild(1)); AstExpressionList argumentList = accept(ctx.expression_list()); Class cls = resolveClass(type); return new AstInvokeNewObject(cls, argumentList, pos(ctx)); } @Override public AstNode visitExpression_new_array(Expression_new_arrayContext ctx) { AstType type = accept(ctx.getChild(1)); List expressions = accept(ctx.expression()); Class cls = resolveClass(type); return new AstInvokeNewArray(cls, expressions, pos(ctx)); } @Override public AstNode visitExpression_field(Expression_fieldContext ctx) { AstExpression objectExpression = accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); return new AstInvokeField(objectExpression, name, parseCtx.isNullSafe(), pos(ctx, 1)); } @Override public AstNode visitExpression_field_static(Expression_field_staticContext ctx) { AstType type = accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); Class cls = resolveClass(type); return new AstInvokeFieldStatic(cls, name, pos(ctx, 1)); } @Override public AstNode visitExpression_method(Expression_methodContext ctx) { AstExpression objectExpression = accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); AstExpressionList argumentList = accept(ctx.expression_list()); return new AstInvokeMethod(objectExpression, name, argumentList, parseCtx.isNullSafe(), pos(ctx, 1)); } @Override public AstNode visitExpression_method_static(Expression_method_staticContext ctx) { AstType type = accept(ctx.getChild(0)); String name = ctx.getChild(2).getText(); AstExpressionList argumentList = accept(ctx.expression_list()); Class cls = resolveClass(type); return new AstInvokeMethodStatic(cls, name, argumentList, pos(ctx, 1)); } @Override public AstNode visitExpression_index_get(Expression_index_getContext ctx) { AstExpression objectExpression = accept(ctx.getChild(0)); AstExpression indexExpression = accept(ctx.getChild(2)); return new AstInvokeIndexGet(objectExpression, indexExpression, parseCtx.isNullSafe(), pos(ctx, 1)); } @Override public AstNode visitExpression_function(Expression_functionContext ctx) { String name = ctx.getChild(0).getText(); AstExpressionList argumentList = accept(ctx.expression_list()); return new AstInvokeFunction(name, argumentList, pos(ctx)); } @Override public AstNode visitExpression_identifier(Expression_identifierContext ctx) { return ctx.getChild(0).accept(this); } @Override public AstIdentifier visitIdentifier(IdentifierContext ctx) { String name = ctx.getChild(0).getText(); validateIdentifier(name, false, ctx); if (Symbols.FOR.equals(name)) { validateInsideOfDirectiveFor(ctx, "Local variable `" + Symbols.FOR + "`"); } return new AstIdentifier(name, pos(ctx)); } @Override public AstConstant visitConstant(ConstantContext ctx) { Token token = ((TerminalNode) ctx.getChild(0)).getSymbol(); String text = token.getText(); int length = text.length(); int type = token.getType(); switch (type) { case JetTemplateLexer.STRING_DOUBLE: case JetTemplateLexer.STRING_SINGLE: { String value = getJavaString(text, ctx); return new AstConstant(value, pos(ctx)); } case JetTemplateLexer.INTEGER: case JetTemplateLexer.INTEGER_HEX: case JetTemplateLexer.FLOATING_POINT: { Object value; int radix; char suffix = text.charAt(length - 1); if (type == JetTemplateLexer.INTEGER_HEX) { radix = 16; if (suffix == 'l' || suffix == 'L') { text = text.substring(2, length - 1); } else { text = text.substring(2); suffix = 0; // clear } } else { radix = 10; if (suffix > '9') { text = text.substring(0, length - 1); } } 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: if (type == JetTemplateLexer.FLOATING_POINT) { value = Double.valueOf(text); } else { value = Integer.valueOf(text, radix); } } return new AstConstant(value, pos(ctx)); } case JetTemplateLexer.TRUE: return new AstConstant(Boolean.TRUE, pos(ctx)); case JetTemplateLexer.FALSE: return new AstConstant(Boolean.FALSE, pos(ctx)); case JetTemplateLexer.NULL: return new AstConstant(null, pos(ctx)); } throw new SyntaxException(Errors.UNREACHABLE_CODE).set(pos(ctx)); } @Override public AstExpressionList visitExpression_list(Expression_listContext ctx) { List expression_list = accept(ctx.expression()); return new AstExpressionList(expression_list, pos(ctx)); } @Override public AstType visitType(TypeContext ctx) { String className = StringUtils.deleteWhitespace(ctx.getText()); return new AstType(className, pos(ctx)); } private Class resolveClass(AstType type) { Class cls = parseCtx.resolveClass(type.getClassName()); if (cls == null) { throw new SyntaxException(Errors.CLASS_NOT_FOUND, type.getClassName()).set(pos(type)); } return cls; } @SuppressWarnings("unchecked") private T accept(ParseTree node) { if (node != null) { return (T) node.accept(this); } return null; } @SuppressWarnings("unchecked") private List accept(List nodes) { if (nodes != null) { int size = nodes.size(); if (size == 0) { return Collections.emptyList(); } else { List results = new ArrayList(size); for (ParseTree node : nodes) { T ast = (T) node.accept(this); if (ast != null) { results.add(ast); } } return results; } } return null; } private void validateIdentifier(String name, boolean defining, ParseTree node) { if (Symbols.FOR.equals(name)) { if (defining) { throw new SyntaxException(Errors.VARIABLE_IS_KEYWORD, name).set(pos(node)); } } else if (JavaKeywordsUtils.isKeyword(name)) { throw new SyntaxException(Errors.VARIABLE_IS_KEYWORD, name).set(pos(node)); } else if (name.startsWith("$")) { throw new SyntaxException(Errors.VARIABLE_IS_RESERVED, name).set(pos(node)); } if (!defining) { try { 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 Directive_forContext) { return; } if (p instanceof Directive_elseContext) { p = p.getParent(); } p = p.getParent(); } throw new SyntaxException(Errors.DIRECTIVE_OUTSIDE_OF_FOR, 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(Errors.UNICODE_STRING_INVALID).set(pos(ctx)); } return value; } // 鑾峰彇涓�涓� Position 淇℃伅 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()); } else if (node instanceof ParserRuleContext) { Token token = ctx.getStart(); return new Position(token.getLine(), token.getCharPositionInLine()); } throw new UnsupportedOperationException(); } // 鑾峰彇涓�涓� Position 淇℃伅 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(); } else if (ctx instanceof AstType) { return ((AstType) ctx).getPosition(); } if (token != null) { return new Position(token.getLine(), token.getCharPositionInLine()); } throw new UnsupportedOperationException(); } }