Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jasy.lang; import jasy.lang.antlr4.JasyBaseVisitor; import jasy.lang.antlr4.JasyLexer; import jasy.lang.antlr4.JasyParser; import jasy.lang.antlr4.JasyParser.AnnotationContext; import jasy.lang.antlr4.JasyParser.InvocationContext; import jasy.lang.antlr4.JasyParser.LookupContext; import jasy.lang.antlr4.JasyParser.ProgramContext; import jasy.lang.antlr4.JasyParser.QualifiedLookupContext; import jasy.lang.antlr4.JasyParser.StatementContext; import jasy.lang.antlr4.JasyParser.StatementsContext; import jasy.lang.antlr4.JasyParser.UnqualifiedLookupContext; import jasy.lang.ast.AmbiguousNameAST; import jasy.lang.ast.BinaryExpressionAST; import jasy.lang.ast.BlockAST; import jasy.lang.ast.BooleanLiteralAST; import jasy.lang.ast.ClassAST; import jasy.lang.ast.ExpressionAST; import jasy.lang.ast.FieldAST; import jasy.lang.ast.FieldSelectorAST; import jasy.lang.ast.MemberAST; import jasy.lang.ast.MethodAST; import jasy.lang.ast.MethodSelectorAST; import jasy.lang.ast.ModuleAST; import jasy.lang.ast.ReturnAST; import jasy.lang.ast.MetaExpressionAST; import jasy.lang.ast.CodeAST; import jasy.lang.ast.CodePrinter; import jasy.lang.ast.DoubleLiteralAST; import jasy.lang.ast.RootExpressionAST; import jasy.lang.ast.FieldGetAST; import jasy.lang.ast.FloatLiteralAST; import jasy.lang.ast.IfElseAST; import jasy.lang.ast.IncDecExpression; import jasy.lang.ast.InjectAST; import jasy.lang.ast.IntLiteralAST; import jasy.lang.ast.InvocationAST; import jasy.lang.ast.LongLiteralAST; import jasy.lang.ast.LookupAST; import jasy.lang.ast.MetaCodeAST; import jasy.lang.ast.MetaProcessor; import jasy.lang.ast.MetaScope; import jasy.lang.ast.NameTypeAST; import jasy.lang.ast.NewAST; import jasy.lang.ast.Parameter; import jasy.lang.ast.QuoteAST; import jasy.lang.ast.QuoteFlattener; import jasy.lang.ast.StatementFlattener; import jasy.lang.ast.StatementProcessor; import jasy.lang.ast.StringLiteralAST; import jasy.lang.ast.TypeAST; import jasy.lang.ast.UnaryExpression; import jasy.lang.ast.VariableAssignmentAST; import jasy.lang.ast.VariableDeclarationAST; import jasy.lang.ast.WhileAST; import jasy.runtime.asm.ClassTransformer; import jasy.runtime.asm.ClassTransformerSequence; import jasy.runtime.asm.CommonClassTransformer; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.TerminalNode; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; public class ASMCompiler { private ClassResolver classResolver; public ASMCompiler(ClassResolver classResolver) { this.classResolver = classResolver; } public JasyParser createParser(InputStream sourceCode) throws IOException { CharStream charStream = new ANTLRInputStream(sourceCode); JasyLexer lexer = new JasyLexer(charStream); CommonTokenStream tokenStream = new CommonTokenStream(lexer); return new JasyParser(tokenStream); } public CodeAST compileStatements(InputStream sourceCode) throws IOException { JasyParser parser = createParser(sourceCode); StatementsContext ctx = parser.statements(); List<CodeAST> statements = getStatements(ctx, new MetaProcessing(new Hashtable<>())); return new BlockAST(null, statements); } public ModuleAST compile(InputStream sourceCode) throws IOException { ArrayList<ClassAST> classes = new ArrayList<>(); JasyParser parser = createParser(sourceCode); JasyParser.ProgramContext program = parser.program(); Hashtable<String, TypeAST> patternVariables = new Hashtable<>(); MetaProcessing mp = new MetaProcessing(patternVariables); program.accept(new JasyBaseVisitor<Object>() { @Override public Object visitClassTransformer(JasyParser.ClassTransformerContext ctx) { List<jasy.lang.ast.AnnotationAST> annotations = ctx.annotations().annotation().stream() .map(aCtx -> new jasy.lang.ast.AnnotationAST(new Region(aCtx), aCtx.PLUS() != null, new NameTypeAST(new Region(aCtx), aCtx.typeQualifier().getText()))) .collect(Collectors.toList()); try { if (ctx.variableId != null) { String variableName = ctx.variableId.getText(); Region r = new Region(ctx.variableId); Class<?> c = ClassNode.class; NameTypeAST t = new NameTypeAST(r, c); patternVariables.put(variableName, t); } } catch (Exception e) { e.toString(); } Integer accessModifer = null; if (ctx.accessModifier() != null) accessModifer = getAccessModifier(ctx.accessModifier(), null); NameTypeAST type = ctx.typeQualifier() != null ? new NameTypeAST(new Region(ctx), ctx.typeQualifier().getText()) : null; ArrayList<MemberAST> members = new ArrayList<>(); if (ctx.members != null) { for (JasyParser.ClassTransformerMemberDefinitionContext memberCtx : ctx.members .classTransformerMemberDefinition()) { boolean isAdd = memberCtx.PLUS() != null; String variableId = memberCtx.variableId != null ? memberCtx.variableId.getText() : null; memberCtx.member.accept(new JasyBaseVisitor<Object>() { @Override public Object visitClassTransformerMemberField( JasyParser.ClassTransformerMemberFieldContext ctx) { if (variableId != null) { String variableName = variableId; Region r = new Region(ctx); Class<?> c = List.class; NameTypeAST t = new NameTypeAST(null, c, new TypeAST[] { new NameTypeAST(null, FieldNode.class) }); patternVariables.put(variableName, t); } Integer accessModifier = ctx.accessModifier() != null ? getAccessModifier(ctx.accessModifier(), null) : null; boolean isStatic = ctx.modStatic() != null; TypeAST fieldType = ctx.typeQualifier() != null ? new NameTypeAST(new Region(ctx), ctx.typeQualifier().getText()) : null; String name = ctx.identifier() != null ? ctx.identifier().getText() : null; ExpressionAST value = null; if (ctx.value != null) { MetaProcessing mp = new MetaProcessing(patternVariables); value = getExpression(ctx.value, mp); } FieldAST field = new FieldAST(new Region(ctx), variableId, isAdd, new FieldSelectorAST(accessModifier, isStatic, fieldType, name), value); members.add(field); return null; } @Override public Object visitClassTransformerMemberMethod( JasyParser.ClassTransformerMemberMethodContext ctx) { Integer accessModifier = ctx.accessModifier() != null ? getAccessModifier(ctx.accessModifier(), null) : null; boolean isStatic = ctx.modStatic() != null; String returnTypeName = ctx.typeQualifier().getText(); TypeAST returnType = new NameTypeAST(new Region(ctx), returnTypeName); boolean isVoid = returnTypeName.equals("void"); String name = ctx.identifier().getText(); List<Parameter> parameters = ctx.parameters().parameter().stream().map(pCtx -> { String pname = pCtx.identifier().getText(); TypeAST type = new NameTypeAST(new Region(ctx), pCtx.typeQualifier().getText()); return new Parameter(pname, type); }).collect(Collectors.toList()); CodeAST body; // List<jasy.lang.ast.CodeAST> body = null; if (ctx.body.block() != null) { // TODO: Wrap into meta block, that returns the block quoted List<jasy.lang.ast.CodeAST> statements = getStatements( ctx.body.block().statements(), mp); if (isVoid) statements.add(new ReturnAST(new Region(ctx.body), null)); CodeAST bodyBlock = new BlockAST(new Region(ctx.body), statements); body = new ReturnAST(new Region(ctx.body), new QuoteAST(new Region(ctx.body), bodyBlock)); // body = getStatements(ctx.body.block().statements(), mp); } else { List<jasy.lang.ast.CodeAST> statements = getStatements( ctx.body.metaBlock().statements(), mp); CodeAST metaBodyBlock = new BlockAST(new Region(ctx.body), statements); body = metaBodyBlock; // CodeAST metaCode = new MetaCodeAST(new Region(ctx.body), metaBodyBlock); // body = metaCode; // ArrayList<CodeAST> bodyStatements = new ArrayList<>(); // bodyStatements.add(metaCode); // if(isVoid) // bodyStatements.add(new ReturnAST(new Region(ctx.body), null)); // CodeAST bodyBlock = new BlockAST(new Region(ctx.body), bodyStatements); //// body = new ReturnAST(new Region(ctx.body), new QuoteAST(new Region(ctx.body), metaCode)); // body = new ReturnAST(new Region(ctx.body), new QuoteAST(new Region(ctx.body), bodyBlock)); } System.out.println("Transformation body:"); body.accept(new CodePrinter(System.out)); System.out.println(); // ArrayList<CodeAST> newBlockStatements = new ArrayList<>(); // body.accept(new StatementFlattener(new QuoteFlattener(newBlockStatements))); // body = new BlockAST(body.getRegion(), newBlockStatements); // body = body.accept(new StatementProcessor(new MetaProcessor())); MethodAST method = new MethodAST(new Region(ctx), isAdd, new MethodSelectorAST( accessModifier, isStatic, returnType, name, parameters), body, mp); members.add(method); return null; } }); } } String variableId = ctx.variableId != null ? ctx.variableId.getText() : null; classes.add(new ClassAST(new Region(ctx), variableId, annotations, accessModifer, type, members)); return null; } }); ModuleAST moduleCtx = new ModuleAST(new Region(program), classes); return moduleCtx; } public static class MetaProcessing { public int generatorCount; public MetaScope metaScope; public MetaProcessing(Hashtable<String, TypeAST> patternVariables) { metaScope = new MetaScope(patternVariables); } } private static List<jasy.lang.ast.CodeAST> getStatements(JasyParser.StatementsContext ctx, MetaProcessing mp) { return getStatements(ctx.statement(), mp); } private static <T extends ParserRuleContext> List<jasy.lang.ast.CodeAST> getStatements(List<T> ctxs, MetaProcessing mp) { return ctxs.stream().map(sCtx -> getStatement(sCtx, mp)).collect(Collectors.toList()); } private static jasy.lang.ast.CodeAST getStatement(ParserRuleContext ctx, MetaProcessing mp) { jasy.lang.ast.CodeAST r = ctx.accept(new JasyBaseVisitor<jasy.lang.ast.CodeAST>() { @Override public CodeAST visitStatement(JasyParser.StatementContext ctx) { if (ctx.nonDelimitedStatement() != null) return ctx.nonDelimitedStatement().accept(this); if (ctx.delimitedStatement() != null) return ctx.delimitedStatement().accept(this); if (ctx.metaBlock() != null) return ctx.metaBlock().accept(this); return null; } @Override public CodeAST visitDelimitedStatement(JasyParser.DelimitedStatementContext ctx) { if (ctx.expression() != null) { ExpressionAST expression = getExpression(ctx.expression(), mp); return new RootExpressionAST(new Region(ctx), expression); } return ctx.getChild(0).accept(this); } @Override public CodeAST visitReturnStatement(JasyParser.ReturnStatementContext ctx) { ExpressionAST expression = getExpression(ctx.expression(), mp); return new ReturnAST(new Region(ctx), expression); } @Override public CodeAST visitVariableDeclaration(JasyParser.VariableDeclarationContext ctx) { String type = ctx.typeQualifier().getText(); String name = ctx.identifier().getText(); ExpressionAST value; if (ctx.expression() != null) value = getExpression(ctx.expression(), mp); else value = null; return new VariableDeclarationAST(new Region(ctx), name, new NameTypeAST(new Region(ctx.typeQualifier()), type), value); } @Override public CodeAST visitMetaBlock(JasyParser.MetaBlockContext ctx) { List<CodeAST> statements = getStatements(ctx.statements(), mp); return new MetaCodeAST(null, new BlockAST(null, statements)); } // @Override // public CodeAST visitInjectStatement(JasyParser.InjectStatementContext ctx) { // ExpressionAST expression = getExpression(ctx.expression(), mp); // // return new InjectAST(new Region(ctx), expression); // } @Override public CodeAST visitIfElseStatement(JasyParser.IfElseStatementContext ctx) { ExpressionAST condition = getExpression(ctx.condition, mp); CodeAST ifTrueBody = getBody(ctx.ifTrueBlock); CodeAST ifFalseBody = ctx.ifFalseBlock != null ? getBody(ctx.ifFalseBlock) : null; return new IfElseAST(new Region(ctx), condition, ifTrueBody, ifFalseBody); } @Override public CodeAST visitWhileStatement(JasyParser.WhileStatementContext ctx) { ExpressionAST condition = getExpression(ctx.condition, mp); CodeAST body = getBody(ctx.whileTrueBlock); return new WhileAST(new Region(ctx), condition, body); } @Override public CodeAST visitForStatement(JasyParser.ForStatementContext ctx) { List<CodeAST> initialization = ctx.initialization != null ? getStatements(ctx.initialization.delimitedStatement(), mp) : null; ExpressionAST condition = ctx.condition != null ? getExpression(ctx.condition, mp) : null; List<CodeAST> update = ctx.update != null ? getStatements(ctx.update.delimitedStatement(), mp) : null; List<CodeAST> body = getStatements(ctx.whileTrueBlock.statements(), mp); ArrayList<CodeAST> statements = new ArrayList<>(); statements.addAll(initialization); ArrayList<CodeAST> whileBody = new ArrayList<>(); whileBody.addAll(body); whileBody.addAll(update); statements.add(new WhileAST(new Region(ctx), condition, new BlockAST(new Region(ctx.whileTrueBlock), whileBody))); return new BlockAST(new Region(ctx), statements); // return new WhileAST(new Region(ctx), condition, body); } private CodeAST getBody(JasyParser.SingleOrMultiStatementContext ctx) { return ctx.statement() != null ? getStatement(ctx.statement(), mp) : new BlockAST(new Region(ctx), getStatements(ctx.statements(), mp)); } }); return r; } private static class ContextToExpression extends JasyBaseVisitor<ExpressionAST> { private MetaProcessing mp; public ContextToExpression(MetaProcessing mp) { this.mp = mp; } @Override public ExpressionAST visitStringLiteral(JasyParser.StringLiteralContext ctx) { String value = ctx.getText().substring(1, ctx.getText().length() - 1); value = value.replace("\\\\", "\\").replace("\\\"", "\"").replace("\\n", "\n").replace("\\r", "\r") .replace("\\t", "\t"); return new StringLiteralAST(new Region(ctx), value); } @Override public ExpressionAST visitIntegerLiteral(JasyParser.IntegerLiteralContext ctx) { int value = Integer.parseInt(ctx.getText()); return new IntLiteralAST(new Region(ctx), value); } @Override public ExpressionAST visitLongLiteral(JasyParser.LongLiteralContext ctx) { String valueStr = ctx.getText().substring(0, ctx.getText().length() - 1); long value = Long.parseLong(valueStr); return new LongLiteralAST(new Region(ctx), value); } @Override public ExpressionAST visitFloatLiteral(JasyParser.FloatLiteralContext ctx) { String valueStr = ctx.getText().substring(0, ctx.getText().length() - 1); float value = Float.parseFloat(valueStr); return new FloatLiteralAST(new Region(ctx), value); } @Override public ExpressionAST visitDoubleLiteral(JasyParser.DoubleLiteralContext ctx) { double value = Double.parseDouble(ctx.getText()); return new DoubleLiteralAST(new Region(ctx), value); } @Override public ExpressionAST visitLeafExpression(JasyParser.LeafExpressionContext ctx) { ExpressionAST result = ctx.getChild(0).accept(this); for (int i = 0; i < ctx.leafExpressionChain().getChildCount(); i++) { if (ctx.leafExpressionChain().getChild(i) instanceof ParserRuleContext) { ParserRuleContext chainCtx = (ParserRuleContext) ctx.leafExpressionChain().getChild(i); switch (chainCtx.getRuleIndex()) { case JasyParser.RULE_lookup: LookupContext lookupCtx = (LookupContext) chainCtx; ExpressionAST fieldName = getExpression(lookupCtx, mp); result = new FieldGetAST(new Region(chainCtx), result, fieldName); break; case JasyParser.RULE_invocation: InvocationContext invocationCtx = (InvocationContext) chainCtx; String methodName = invocationCtx.identifier().getText(); List<ExpressionAST> arguments = invocationCtx.arguments().expression().stream() .map(eCtx -> getExpression(eCtx, mp)).collect(Collectors.toList()); result = new InvocationAST(new Region(chainCtx), result, /*null, */methodName, arguments/*, null*/); break; } } } return result; } private <V extends ParserRuleContext, O extends ParserRuleContext> ExpressionAST parseBinaryExpressionRule( V first, List<V> rest, List<O> operators, Function<O, Integer> operatorFunc) { ExpressionAST result = first.accept(this); for (int i = 1; i < rest.size(); i++) { ExpressionAST lhs = result; ExpressionAST rhs = rest.get(i).accept(this); int operator = operatorFunc.apply(operators.get(i - 1)); result = new BinaryExpressionAST(new Region(lhs.getRegion().start, rhs.getRegion().end), operator, lhs, rhs); } return result; } @Override public ExpressionAST visitEqualityExpression(JasyParser.EqualityExpressionContext ctx) { return parseBinaryExpressionRule(ctx.first, ctx.relationalExpression(), ctx.equalityOperator(), equalityOperator -> { switch (equalityOperator.operator.getType()) { case JasyLexer.EQUALS: return BinaryExpressionAST.OPERATOR_EQ; case JasyLexer.NOT_EQUALS: return BinaryExpressionAST.OPERATOR_NE; default: return -1; } }); } @Override public ExpressionAST visitRelationalExpression(JasyParser.RelationalExpressionContext ctx) { return parseBinaryExpressionRule(ctx.first, ctx.additiveExpression(), ctx.relationalOperator(), relationalOperator -> { switch (relationalOperator.operator.getType()) { case JasyLexer.LT: return BinaryExpressionAST.OPERATOR_LT; case JasyLexer.LTE: return BinaryExpressionAST.OPERATOR_LTE; case JasyLexer.GT: return BinaryExpressionAST.OPERATOR_GT; case JasyLexer.GTE: return BinaryExpressionAST.OPERATOR_GTE; default: return -1; } }); } @Override public ExpressionAST visitEmbeddedExpression(JasyParser.EmbeddedExpressionContext ctx) { return ctx.expression().accept(this); } @Override public ExpressionAST visitAdditiveExpression(JasyParser.AdditiveExpressionContext ctx) { return parseBinaryExpressionRule(ctx.first, ctx.multiplicativeExpression(), ctx.additiveOperator(), additiveOperator -> { switch (additiveOperator.operator.getType()) { case JasyLexer.PLUS: return BinaryExpressionAST.OPERATOR_ADD; case JasyLexer.MINUS: return BinaryExpressionAST.OPERATOR_SUB; default: return -1; } }); } @Override public ExpressionAST visitMultiplicativeExpression(JasyParser.MultiplicativeExpressionContext ctx) { return parseBinaryExpressionRule(ctx.first, ctx.unaryPrefixExpression(), ctx.multiplicativeOperator(), multiplicativeOperator -> { switch (multiplicativeOperator.operator.getType()) { case JasyLexer.MULT: return BinaryExpressionAST.OPERATOR_MULT; case JasyLexer.DIV: return BinaryExpressionAST.OPERATOR_DIV; case JasyLexer.REM: return BinaryExpressionAST.OPERATOR_REM; default: return -1; } }); } @Override public ExpressionAST visitUnaryPrefixExpression(JasyParser.UnaryPrefixExpressionContext ctx) { if (ctx.unaryPrefixOperator() != null) { int operatorType = ctx.unaryPrefixOperator().operator.getType(); ExpressionAST operand = ctx.operand.accept(this); switch (operatorType) { case JasyLexer.INC: case JasyLexer.DEC: { int operator; switch (operatorType) { case JasyLexer.INC: operator = IncDecExpression.OPERATOR_INC; break; case JasyLexer.DEC: operator = IncDecExpression.OPERATOR_DEC; break; default: operator = -1; } return new IncDecExpression(null, IncDecExpression.TIMING_PRE, operator, operand); } case JasyLexer.PLUS: case JasyLexer.MINUS: case JasyLexer.TILDE: case JasyLexer.EXCLA: { int operator; switch (operatorType) { case JasyLexer.PLUS: operator = UnaryExpression.OPERATOR_SIGN_POS; break; case JasyLexer.MINUS: operator = UnaryExpression.OPERATOR_SIGN_NEG; break; case JasyLexer.TILDE: operator = UnaryExpression.OPERATOR_BIN_COMP; break; case JasyLexer.EXCLA: operator = UnaryExpression.OPERATOR_LOG_COMP; break; default: operator = -1; } return new UnaryExpression(new Region(ctx), operator, operand); } } } return ctx.getChild(0).accept(this); } @Override public ExpressionAST visitUnaryPostfixExpression(JasyParser.UnaryPostfixExpressionContext ctx) { ExpressionAST expression = ctx.leafExpression().accept(this); if (ctx.unaryPostfixOperator() != null) { ExpressionAST operand = expression; int operatorType = ctx.unaryPostfixOperator().operator.getType(); int operator; switch (operatorType) { case JasyLexer.INC: operator = IncDecExpression.OPERATOR_INC; break; case JasyLexer.DEC: operator = IncDecExpression.OPERATOR_DEC; break; default: operator = -1; } return new IncDecExpression(null, IncDecExpression.TIMING_POST, operator, operand); } return expression; } @Override public ExpressionAST visitMetaExpression(JasyParser.MetaExpressionContext ctx) { ExpressionAST body = null; if (ctx.expression() != null) { // only support expressions for now ExpressionAST exprCtx = getExpression(ctx.expression(), mp); body = exprCtx; } else { // Not possible; only expressions are supported here!!! } return new MetaExpressionAST(new Region(ctx), body); } // @Override // public ExpressionAST visitLookup(JasyParser.LookupContext ctx) { // String name = ctx.getText(); // return new LookupAST(new Region(ctx), name); // } @Override public ExpressionAST visitUnqualifiedLookup(JasyParser.UnqualifiedLookupContext ctx) { String name = ctx.getText(); return new LookupAST(new Region(ctx), new StringLiteralAST(new Region(ctx), name)); } @Override public ExpressionAST visitQualifiedLookup(JasyParser.QualifiedLookupContext ctx) { ExpressionAST name = getExpression(ctx.expression(), mp); return new LookupAST(new Region(ctx), name); } @Override public ExpressionAST visitVariableAssignment(JasyParser.VariableAssignmentContext ctx) { if (ctx.assignmentOperator() != null) { String name = ctx.identifier().getText(); ExpressionAST value = getExpression(ctx.value, mp); int binaryExpresionOperator = -1; switch (ctx.assignmentOperator().operator.getType()) { case JasyLexer.ASSIGN_ADD: binaryExpresionOperator = BinaryExpressionAST.OPERATOR_ADD; break; case JasyLexer.ASSIGN_SUB: binaryExpresionOperator = BinaryExpressionAST.OPERATOR_SUB; break; case JasyLexer.ASSIGN_MULT: binaryExpresionOperator = BinaryExpressionAST.OPERATOR_MULT; break; case JasyLexer.ASSIGN_DIV: binaryExpresionOperator = BinaryExpressionAST.OPERATOR_DIV; break; } if (binaryExpresionOperator != -1) { ExpressionAST lhs = new LookupAST(new Region(ctx.value), new StringLiteralAST(new Region(ctx.value), name)); value = new BinaryExpressionAST(new Region(ctx.value), binaryExpresionOperator, lhs, value); } return new VariableAssignmentAST(new Region(ctx), name, value); } else return ctx.equalityExpression().accept(this); } @Override public ExpressionAST visitQuotedExpression(JasyParser.QuotedExpressionContext ctx) { CodeAST code = null; if (ctx.delimitedStatement() != null) code = getStatement(ctx.delimitedStatement(), mp); else if (ctx.nonDelimitedStatement() != null) code = getStatement(ctx.nonDelimitedStatement(), mp); else if (ctx.block() != null) { List<CodeAST> statements = getStatements(ctx.block().statements(), mp); code = new BlockAST(new Region(ctx.block()), statements); } else if (ctx.expression() != null) code = getExpression(ctx.expression(), mp); return new QuoteAST(new Region(ctx), code); } @Override public ExpressionAST visitBooleanLiteral(JasyParser.BooleanLiteralContext ctx) { String valueStr = ctx.getText(); boolean value = Boolean.parseBoolean(valueStr); return new BooleanLiteralAST(new Region(ctx), value); } @Override public ExpressionAST visitNewExpression(JasyParser.NewExpressionContext ctx) { String className = ctx.className.getText(); TypeAST c = new NameTypeAST(new Region(ctx.className), className); List<ExpressionAST> arguments = ctx.expression().stream().map(eCtx -> getExpression(eCtx, mp)) .collect(Collectors.toList()); return new NewAST(new Region(ctx), c, arguments); } @Override public ExpressionAST visitAmbigousName(JasyParser.AmbigousNameContext ctx) { List<ParserRuleContext> partCtxs = getAmbigousNameParts(ctx); // Last part is assumed to be either a field access or an invocation. ParserRuleContext lastPartCtx = partCtxs.get(partCtxs.size() - 1); // Remove last part; now, the actual parts of the ambiguous name remains. // - These parts are assumed to be only lookups. partCtxs.remove(partCtxs.size() - 1); if (partCtxs.size() > 0) { Region targetRegion = new Region(new Position(partCtxs.get(0).getStart()), new Position(partCtxs.get(partCtxs.size() - 1).getStop())); List<ExpressionAST> parts = partCtxs.stream() .map(p -> (ExpressionAST) getExpression(p, mp) /*p assumed to be lookup ctx*/) .collect(Collectors.toList()); AmbiguousNameAST target = new AmbiguousNameAST(targetRegion, parts); return getAmbiguousNameUsage(lastPartCtx, target); } // Unify ambiguous names and lookups? return getExpression(lastPartCtx, mp); } private ExpressionAST getAmbiguousNameUsage(ParserRuleContext lastPartCtx, AmbiguousNameAST target) { return lastPartCtx.accept(new JasyBaseVisitor<ExpressionAST>() { @Override public ExpressionAST visitLookup(LookupContext ctx) { ExpressionAST fieldName = getExpression(ctx, mp); // Field access return new FieldGetAST(new Region(ctx), target, fieldName); } @Override public ExpressionAST visitInvocation(InvocationContext ctx) { String methodName = ctx.identifier().getText(); List<ExpressionAST> arguments = ctx.arguments().expression().stream() .map(a -> getExpression(a, mp)).collect(Collectors.toList()); return new InvocationAST(new Region(ctx), target, methodName, arguments); } }); } private List<ParserRuleContext> getAmbigousNameParts(JasyParser.AmbigousNameContext ctx) { ArrayList<ParserRuleContext> parts = new ArrayList<>(); JasyParser.AmbigousNameContext currentPartCtx = ctx; while (currentPartCtx != null) { ParserRuleContext part = currentPartCtx.invocation() != null ? currentPartCtx.invocation() : currentPartCtx.lookup(); parts.add(part); currentPartCtx = currentPartCtx.next; } return parts; } } private static ExpressionAST getExpression(ParserRuleContext ctx, MetaProcessing mp) { return ctx.accept(new ContextToExpression(mp)); } public ClassTransformer compile(InputStream sourceCode, ArrayList<Message> errorMessages) throws IOException { CharStream charStream = new ANTLRInputStream(sourceCode); JasyLexer lexer = new JasyLexer(charStream); CommonTokenStream tokenStream = new CommonTokenStream(lexer); JasyParser parser = new JasyParser(tokenStream); JasyParser.ProgramContext program = parser.program(); ClassTransformerSequence classTransformers = new ClassTransformerSequence(); validateProgram(program, errorMessages); program.accept(new JasyBaseVisitor<Object>() { @Override public Object visitClassTransformer(JasyParser.ClassTransformerContext ctx) { CommonClassTransformer transformer = new CommonClassTransformer(); List<AnnotationContext> aCtxs = ctx.annotations().annotation().stream() .filter(pa -> pa.PLUS() == null).collect(Collectors.toList()); transformer.addPredicate(classNode -> { return aCtxs.stream().allMatch(pa -> { String annotationName = pa.typeQualifier().getText().replace(".", "/"); if (classNode.visibleAnnotations != null && classNode.visibleAnnotations.size() > 0) return classNode.visibleAnnotations.stream() .anyMatch(a -> ((AnnotationNode) a).desc.equals(annotationName)); return false; }); }); if (ctx.typeQualifier() != null) { String className = ctx.typeQualifier().getText().replace(".", "/"); transformer.addPredicate(classNode -> { return classNode.name.equals(className); }); } if (ctx.members != null) { // for(DejainParser.ClassTransformerMemberContext memberCtx: ctx.members.classTransformerMember()) { // memberCtx.accept(new DejainBaseVisitor<Object>() { // @Override // public Object visitClassTransformerMemberFieldAdd(DejainParser.ClassTransformerMemberFieldAddContext ctx) { // try { // int fieldAccess = getAccessModifier(ctx.accessModifier(), ctx.modStatic()); // String fieldName = ctx.identifier().getText(); // String fieldTypeName = ctx.typeQualifier().getText(); // String fieldDescriptor = Type.getDescriptor(classResolver.resolveType(fieldTypeName)); // // transformer.addAction(classNode -> // { // classNode.fields.add(new FieldNode(fieldAccess, fieldName, fieldDescriptor, null, null)); // }); // // return null; // } catch (ClassNotFoundException ex) { // Logger.getLogger(ASMCompiler.class.getName()).log(Level.SEVERE, null, ex); // return null; // } // } // // @Override // public Object visitClassTransformerMemberMethod(DejainParser.ClassTransformerMemberMethodContext ctx) { // try { // int methodAccess = getAccessModifier(ctx.accessModifier(), ctx.modStatic()); // String methodName = ctx.identifier().getText(); // Class<?> returnType = classResolver.resolveType(ctx.typeQualifier().getText()); // String methodDescriptor = getMethodDescriptor(ctx.typeQualifier(), ctx.parameters()); // // transformer.addPredicate(classNode -> { // System.out.println(methodName + " " + methodDescriptor); // return classNode.methods.stream().anyMatch(m -> // ((MethodNode)m).name.equals(methodName) && // ((MethodNode)m).desc.equals(methodDescriptor)); // }); // // transformer.addAction(classNode -> // { // MethodNode methodNode = new MethodNode(methodAccess, methodName, methodDescriptor, null, null); // // OptionalInt existingMethodIndex = // IntStream.range(0, classNode.methods.size()) // .filter(i -> // ((MethodNode)classNode.methods.get(i)).name.equals(methodName) && // ((MethodNode)classNode.methods.get(i)).desc.equals(methodDescriptor)) // .findFirst(); // // MethodNode methodNodeOriginal = (MethodNode)classNode.methods.get(existingMethodIndex.getAsInt()); // // InsnList originalIl = new InsnList(); // originalIl.add(methodNodeOriginal.instructions); // // MethodCodeGenerator generator = new MethodCodeGenerator(methodNode, returnType); // // generator.start(); // treeToCode(ctx, generator, originalIl, false); // generator.end(); // // methodNode.visitEnd(); // // classNode.methods.remove(existingMethodIndex.getAsInt()); // classNode.methods.add(methodNode); // }); // // return null; // } catch (ClassNotFoundException ex) { // Logger.getLogger(ASMCompiler.class.getName()).log(Level.SEVERE, null, ex); // return null; // } // } // // @Override // public Object visitClassTransformerMemberMethodAdd(DejainParser.ClassTransformerMemberMethodAddContext ctx) { // try { // int methodAccess = getAccessModifier(ctx.methodDefinition().accessModifier(), ctx.methodDefinition().modStatic()); // String methodName = ctx.methodDefinition().identifier().getText(); // Class<?> returnType = classResolver.resolveType(ctx.methodDefinition().typeQualifier().getText()); // String methodDescriptor = getMethodDescriptor(ctx.methodDefinition().typeQualifier(), ctx.methodDefinition().parameters()); // // transformer.addAction(classNode -> // { // MethodNode methodNode = new MethodNode(methodAccess, methodName, methodDescriptor, null, null); // MethodCodeGenerator generator = new MethodCodeGenerator(methodNode, returnType); // // generator.start(); // treeToCode(ctx, generator, new InsnList() /*Something that generates a default values for non-void returns?*/, false); // generator.end(); // // methodNode.visitEnd(); // // OptionalInt existingMethodIndex = // IntStream.range(0, classNode.methods.size()) // .filter(i -> // ((MethodNode)classNode.methods.get(i)).name.equals(methodName) && // ((MethodNode)classNode.methods.get(i)).desc.equals(methodDescriptor)) // .findFirst(); // if(existingMethodIndex.isPresent()) // classNode.methods.remove(existingMethodIndex.getAsInt()); // // classNode.methods.add(methodNode); // }); // // return null; // } catch (ClassNotFoundException ex) { // Logger.getLogger(ASMCompiler.class.getName()).log(Level.SEVERE, null, ex); // return null; // } // } // }); // } } transformer.addAction(classNode -> { ctx.annotations().annotation().stream().filter(pa -> pa.PLUS() != null).forEach(pa -> { String annotationName = pa.typeQualifier().getText().replace(".", "/"); try { classNode.visitAnnotation(annotationName, true); } catch (Exception e) { e.printStackTrace(); } }); }); classTransformers.addTransformer(transformer); return null; } }); return classTransformers; } private String getMethodDescriptor(JasyParser.TypeQualifierContext returnType, JasyParser.ParametersContext paramsCtx, ClassLoader classLoader) throws ClassNotFoundException { String returnDescriptor = Type.getDescriptor(classResolver.resolveType(classLoader, returnType.getText())); String parametersDescriptor = paramsCtx.parameter().stream().map(pCtx -> { String parameterDescriptor; try { parameterDescriptor = Type .getDescriptor(classResolver.resolveType(classLoader, pCtx.typeQualifier().getText())); return parameterDescriptor + ";"; } catch (ClassNotFoundException ex) { Logger.getLogger(ASMCompiler.class.getName()).log(Level.SEVERE, null, ex); return null; } }).collect(Collectors.joining()); return "(" + parametersDescriptor + ")" + returnDescriptor + ""; } private static int getAccessModifier(JasyParser.AccessModifierContext ctx, JasyParser.ModStaticContext statCtx) { int mod = Opcodes.ACC_PRIVATE; if (ctx != null) { switch (((TerminalNode) ctx.getChild(0)).getSymbol().getType()) { case JasyLexer.ACC_MOD_PRIVATE: mod = Opcodes.ACC_PRIVATE; break; case JasyLexer.ACC_MOD_PROTECTED: mod = Opcodes.ACC_PROTECTED; break; case JasyLexer.ACC_MOD_PUBLIC: mod = Opcodes.ACC_PUBLIC; break; } } if (statCtx != null) return mod | Opcodes.ACC_STATIC; return mod; } public static class Region { public Position start; public Position end; public Region(Position start, Position end) { this.start = start; this.end = end; } public Region(ParserRuleContext ctx) { start = new Position(ctx.getStart()); end = new Position(ctx.getStop() != null ? ctx.getStop() : ctx.getStart()); } } public static class Position { private int line; private int column; public Position(Token token) { this.line = token.getLine(); this.column = token.getCharPositionInLine(); } public Position(int line, int column) { this.line = line; this.column = column; } @Override public String toString() { return "" + line + "," + column; } } public static class Message { private Region region; private String text; public Message(ParserRuleContext ctx, String text) { region = new Region(new Position(ctx.getStart()), new Position(ctx.getStop())); this.text = text; } public Message(Region region, String text) { this.region = region; this.text = text; } public Region getRegion() { return region; } public String getText() { return text; } @Override public String toString() { return "" + region.start + "-" + region.end + ": " + text; } } private void validateProgram(ProgramContext ctx, ArrayList<Message> errorMessages) { ctx.accept(new JasyBaseVisitor<Object>() { @Override public Object visitClassTransformer(JasyParser.ClassTransformerContext ctx) { if (ctx.members != null) { // for(DejainParser.ClassTransformerMemberContext memberCtx: ctx.members.classTransformerMember()) { // memberCtx.accept(new DejainBaseVisitor<Object>() { // @Override // public Object visitClassTransformerMemberMethod(DejainParser.ClassTransformerMemberMethodContext ctx) { // try { // String methodName = ctx.identifier().getText(); // Class<?> returnType = classResolver.resolveType(ctx.typeQualifier().getText()); // validateMethod(ctx, methodName, returnType, errorMessages); // } catch (ClassNotFoundException ex) { // Logger.getLogger(ASMCompiler.class.getName()).log(Level.SEVERE, null, ex); // } // // return null; // } // // @Override // public Object visitClassTransformerMemberMethodAdd(DejainParser.ClassTransformerMemberMethodAddContext ctx) { // try { // String methodName = ctx.methodDefinition().identifier().getText(); // Class<?> returnType = classResolver.resolveType(ctx.methodDefinition().typeQualifier().getText()); // validateMethod(ctx, methodName, returnType, errorMessages); // } catch (ClassNotFoundException ex) { // Logger.getLogger(ASMCompiler.class.getName()).log(Level.SEVERE, null, ex); // } // // return null; // } // }); // } } return null; } }); } private static class VariableInfo { public Class<?> type; public boolean isAssigned; public VariableInfo(Class<?> type, boolean isAssigned) { this.type = type; this.isAssigned = isAssigned; } } private void validateMethod(ParserRuleContext ctx, String methodName, Class<?> returnTypeClass, ArrayList<Message> errorMessages) { // ctx.accept(new JasyBaseVisitor<Object>() { // HashMap<String, VariableInfo> locals = new HashMap<>(); // // @Override // public Object visitVariableDeclaration(JasyParser.VariableDeclarationContext ctx) { // String name = ctx.id.getText(); // if(locals.containsKey(name)) // errorMessages.add(new Message(ctx, "Variable " + name + " is already declared in method " + methodName + ".")); // else { // try { // Class<?> fieldType = classResolver.resolveType(ctx.typeQualifier().getText()); // // if(ctx.expression() != null) { // Class<?> expressionType = validateExpression(ctx.expression(), errorMessages); // errorMessages.add(new Message(ctx, "Variable " + name + " is already declared in method " + methodName + ".")); // if(!fieldType.isAssignableFrom(expressionType)) { // errorMessages.add(new Message(ctx, "" + expressionType + " is not assignable to " + fieldType)); // } else { // locals.put(name, new VariableInfo(fieldType, true)); // } // } else { // locals.put(name, new VariableInfo(fieldType, false)); // } // } catch (ClassNotFoundException ex) { // errorMessages.add(new Message(ctx, "Could not resolve type " + ctx.typeQualifier().getText() + ".")); // } // } // // return super.visitVariableDeclaration(ctx); //To change body of generated methods, choose Tools | Templates. // } // // @Override // public Object visitProceedStatement(JasyParser.ProceedStatementContext ctx) { // return returnTypeClass; // } // // @Override // public Object visitReturnStatement(JasyParser.ReturnStatementContext ctx) { // Class<?> expressionType = validateExpression(ctx.expression(), errorMessages); // if(!returnTypeClass.isAssignableFrom(expressionType)) // errorMessages.add(new Message(ctx, "" + expressionType + " is not assignable to " + returnTypeClass)); // return null; // } // }); } private static Class<?> validateExpression(ParserRuleContext ctx, ArrayList<Message> errorMessages) { return ctx.accept(new JasyBaseVisitor<Class<?>>() { @Override public Class<?> visitStringLiteral(JasyParser.StringLiteralContext ctx) { return String.class; } @Override public Class<?> visitIntegerLiteral(JasyParser.IntegerLiteralContext ctx) { return int.class; } }); } private static boolean isReturn(int opcode) { switch (opcode) { case Opcodes.ARETURN: case Opcodes.DRETURN: case Opcodes.FRETURN: case Opcodes.IRETURN: case Opcodes.RET: case Opcodes.RETURN: return true; } return false; } public static class MethodCodeGenerator { private static class VariableInfo { public int index; public Class<?> type; public VariableInfo(int index, Class<?> type) { this.index = index; this.type = type; } } public MethodNode methodNode; private Class<?> returnType; private HashMap<String, VariableInfo> localNameToIndexMap = new HashMap<>(); private Label start; private Label end; public MethodCodeGenerator(MethodNode methodNode, Class<?> returnType) { this.methodNode = methodNode; this.returnType = returnType; this.start = new Label(); this.end = new Label(); } public void start() { methodNode.visitLabel(start); } public void end() { methodNode.visitLabel(end); } public int declareVariable(String name, String desc, Class<?> type) { int index = localNameToIndexMap.size(); localNameToIndexMap.put(name, new VariableInfo(index, type)); methodNode.visitLocalVariable(name, desc, null, start, end, index); return index; } public int getVariableIndex(String name) { return localNameToIndexMap.get(name).index; } private boolean isVariable(String name) { return localNameToIndexMap.containsKey(name); } private Class<?> getVariableType(String name) { return localNameToIndexMap.get(name).type; } private Class<?> getReturnType() { return returnType; } } private static int getReturn(Class<?> type) { String typeDescriptor = Type.getDescriptor(type); switch (typeDescriptor) { case "V": return Opcodes.RETURN; case "J": return Opcodes.LRETURN; case "D": return Opcodes.DRETURN; case "F": return Opcodes.FRETURN; case "I": case "Z": case "B": case "C": case "S": return Opcodes.IRETURN; default: return Opcodes.ARETURN; } } private static int getLoad(Class<?> type) { String typeDescriptor = Type.getDescriptor(type); switch (typeDescriptor) { case "V": return -1; case "J": return Opcodes.LLOAD; case "D": return Opcodes.DLOAD; case "F": return Opcodes.FLOAD; case "I": case "Z": case "B": case "C": case "S": return Opcodes.ILOAD; default: return Opcodes.ALOAD; } } }