com.shelloid.script.Compiler.java Source code

Java tutorial

Introduction

Here is the source code for com.shelloid.script.Compiler.java

Source

/*
 * 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 com.shelloid.script;

import com.shelloid.script.parser.ShelloidLexer;
import com.shelloid.script.parser.ShelloidParser;
import com.shelloid.script.parser.ShelloidParser.AssignStmtContext;
import com.shelloid.script.parser.ShelloidParser.DeclStmtContext;
import com.shelloid.script.parser.ShelloidParser.ExprContext;
import com.shelloid.script.parser.ShelloidParser.ExprStmtContext;
import com.shelloid.script.parser.ShelloidParser.LiteralContext;
import com.shelloid.script.parser.ShelloidParser.MethodCallContext;
import com.shelloid.script.parser.ShelloidParser.ObjExprContext;
import com.shelloid.script.parser.ShelloidParser.ObjExprSeqContext;
import com.shelloid.script.parser.ShelloidParser.ParamListContext;
import com.shelloid.script.parser.ShelloidParser.ReturnStmtContext;
import com.shelloid.script.parser.ShelloidParser.ScriptContext;
import com.shelloid.script.parser.ShelloidParser.StmtContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Set;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.Token;

/**
 *
 * @author Jayaraj Poroor
 */
public class Compiler {

    ArrayList<String> errorMsgs = new ArrayList();

    public Compiler() {
    }

    public ScriptBin compile(InputStream is, ScriptSource src, HashMap<String, Object> globals)
            throws IOException, CompilerException {
        errorMsgs.clear();
        ShelloidLexer lexer = new ShelloidLexer(new ANTLRInputStream(is));
        ShelloidParser parser = new ShelloidParser(new CommonTokenStream(lexer));
        parser.removeErrorListeners();
        parser.addErrorListener(new CustomErrorListener());
        ScriptContext script = parser.script();
        ScriptBin bin = new ScriptBin();
        if (errorMsgs.isEmpty()) {
            CompileCtx ctx = new CompileCtx(src, bin, false, globals, null);
            CompiledScript cscript = translateScript(script, ctx);
            cscript.setSrc(src);
            bin.setScript(cscript);
        }

        if (errorMsgs.isEmpty()) {
            return bin;
        } else {
            throw new CompilerException(errorMsgs);
        }
    }

    CompiledScript translateScript(ScriptContext script, CompileCtx ctx) {
        CompiledScript cscript = new CompiledScript(new SourceCtx(script.start, script.stop));
        Iterator<StmtContext> it = script.stmt().iterator();
        while (it.hasNext()) {
            StmtContext stmt = it.next();
            CompiledStmt cstmt = translateStmt(stmt, ctx);
            cscript.addStmt(cstmt);
        }
        return cscript;
    }

    CompiledStmt translateStmt(StmtContext stmt, CompileCtx ctx) {
        CompiledStmt cstmt = new CompiledStmt(new SourceCtx(stmt.start, stmt.stop));
        AssignStmtContext assignStmt = stmt.assignStmt();
        DeclStmtContext declStmt = stmt.declStmt();
        ExprStmtContext exprStmt = stmt.exprStmt();
        ReturnStmtContext retStmt = stmt.returnStmt();
        ExprContext expr = null;
        if (assignStmt != null) {
            cstmt.kind = CompiledStmt.StmtKind.ASSIGN_STMT;
            cstmt.id = assignStmt.ID().getText();
            expr = assignStmt.expr();
            if (!ctx.varIsDefined(cstmt.id)) {
                compileError(assignStmt.ID().getSymbol(), "Undefined variable: " + cstmt.id);
            } else if (ctx.isGlobal(cstmt.id) || ctx.isBuiltin(cstmt.id)) {
                compileError(assignStmt.ID().getSymbol(), "Attempt to assign global variable: " + cstmt.id);
            }
        } else if (declStmt != null) {
            cstmt.kind = CompiledStmt.StmtKind.DECL_STMT;
            cstmt.id = declStmt.ID().getText();
            expr = declStmt.expr();//may be null
            if (ctx.isGlobal(cstmt.id) || ctx.isBuiltin(cstmt.id)) {
                compileError(declStmt.ID().getSymbol(), "Attempt to redefine global/builtin variable");
            } else if (ctx.hasVar(cstmt.id)) {
                compileError(declStmt.ID().getSymbol(), "Variable is already defined.");
            } else {
                ctx.addVar(cstmt.id);
            }
        } else if (exprStmt != null) {
            cstmt.kind = CompiledStmt.StmtKind.EXPR_STMT;
            expr = exprStmt.expr();
        } else if (retStmt != null) {
            cstmt.kind = CompiledStmt.StmtKind.RET_STMT;
            expr = retStmt.expr();
        }

        if (expr != null) {
            CompiledExpr cexpr = translateExpr(expr, ctx);
            cstmt.expr = cexpr;
        }
        return cstmt;
    }

    CompiledExpr translateExpr(ExprContext expr, CompileCtx ctx) {
        if (expr.objExprSeq() != null) {
            return translateObjExprSeq(expr.objExprSeq(), ctx);
        } else if (expr.literal() != null) {
            return translateLiteral(expr.literal(), ctx);
        } else if (expr.script() != null) {
            CompiledExpr cexpr = new CompiledExpr(new SourceCtx(expr.start, expr.stop));
            boolean isAsync = (expr.ASYNC() != null);
            CompileCtx newCtx = new CompileCtx(ctx.src, ctx.bin, isAsync, ctx.globals, ctx);
            CompiledScript cscript = translateScript(expr.script(), newCtx);
            if (isAsync) {
                ArrayList<CompiledScript> asyncs = ctx.bin.getAsyncs();
                long index = asyncs.size(); //must do before calling add(...)
                asyncs.add(cscript);
                cscript.setAsyncInfo(index, ctx.src);
                cexpr.kind = CompiledExpr.ExprKind.ASYNC_INDEX_EXPR;
                cexpr.value = index;
            } else {
                cexpr.kind = CompiledExpr.ExprKind.SCRIPT_EXPR;
                cexpr.value = cscript;
            }
            return cexpr;
        } else {
            if (expr.op != null) {
                CompiledExpr cexpr = new CompiledExpr(new SourceCtx(expr.start, expr.stop));
                cexpr.kind = CompiledExpr.ExprKind.OP_EXPR;
                cexpr.value = expr.op.getText();
                cexpr.lexpr = translateExpr(expr.expr(0), ctx);
                if (expr.expr().size() > 1) {
                    cexpr.rexpr = translateExpr(expr.expr(1), ctx);
                }
                return cexpr;
            } else {
                return translateExpr(expr.expr(0), ctx);
            }
        }
    }

    CompiledExpr translateObjExprSeq(ObjExprSeqContext objExprSeq, CompileCtx ctx) {
        CompiledExpr cexpr = new CompiledExpr(new SourceCtx(objExprSeq.start, objExprSeq.stop));
        Iterator<ObjExprContext> it = objExprSeq.objExpr().iterator();
        ArrayList<CompiledObjExpr> cObjExprs = new ArrayList<CompiledObjExpr>();
        boolean rootVar = true;
        while (it.hasNext()) {
            ObjExprContext objExpr = it.next();
            CompiledObjExpr cobjExpr = translateObjExpr(objExpr, ctx);
            cObjExprs.add(cobjExpr);
            if (rootVar && cobjExpr.kind == CompiledObjExpr.ObjExprKind.OBJ_REF) {
                if (!ctx.varIsDefined(cobjExpr.id) && !ctx.isGlobal(cobjExpr.id) && !ctx.isBuiltin(cobjExpr.id)) {
                    compileError(objExpr.ID().getSymbol(), "Undefined variable: " + cobjExpr.id);
                }
            }
            rootVar = false;
        }
        cexpr.kind = CompiledExpr.ExprKind.OBJ_EXPR_SEQ;
        cexpr.value = cObjExprs;

        return cexpr;
    }

    CompiledObjExpr translateObjExpr(ObjExprContext objExpr, CompileCtx ctx) {
        CompiledObjExpr cobjExpr = new CompiledObjExpr(new SourceCtx(objExpr.start, objExpr.stop));
        cobjExpr.id = objExpr.ID().getText();
        MethodCallContext methodCall = objExpr.methodCall();
        if (methodCall == null) {
            cobjExpr.kind = CompiledObjExpr.ObjExprKind.OBJ_REF;
        } else {
            cobjExpr.kind = CompiledObjExpr.ObjExprKind.METHOD_CALL;
            ParamListContext paramList = methodCall.paramList();
            if (paramList != null) {
                ArrayList<CompiledExpr> cparams = new ArrayList<CompiledExpr>();
                Iterator<ExprContext> it = paramList.expr().iterator();
                boolean rootVar = true;
                while (it.hasNext()) {
                    ExprContext expr = it.next();
                    cparams.add(translateExpr(expr, ctx));
                }
                cobjExpr.params = cparams;
            }
        }
        return cobjExpr;
    }

    CompiledExpr translateLiteral(LiteralContext literal, CompileCtx ctx) {
        CompiledExpr cexpr = new CompiledExpr(new SourceCtx(literal.start, literal.stop));
        cexpr.kind = CompiledExpr.ExprKind.LITERAL_EXPR;
        String value = null;
        try {
            if (literal.TRUE() != null)
                cexpr.value = new Boolean(true);
            else if (literal.FALSE() != null)
                cexpr.value = new Boolean(false);
            else if (literal.INT() != null) {
                value = literal.INT().getText();
                cexpr.value = Long.parseLong(value);
            } else if (literal.FLOAT() != null) {
                value = literal.FLOAT().getText();
                cexpr.value = Double.parseDouble(value);
            } else if (literal.STRING() != null) {
                value = literal.STRING().getText();
                cexpr.value = value.substring(1, value.length() - 1);//remove quotes    
            }
        } catch (NumberFormatException e) {
            compileError(literal.start, "Invalid number format: " + value);
        }
        return cexpr;
    }

    void compileError(Token token, String msg) {
        String errorMsg = "Syntax Error at: " + token.getLine() + ": " + token.getCharPositionInLine() + " near "
                + token.getText() + ", cause: " + msg;
        errorMsgs.add(errorMsg);
    }

    class CustomErrorListener extends BaseErrorListener {
        public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line,
                int charPositionInLine, String msg, RecognitionException e) {
            String tokenText = (offendingSymbol instanceof Token) ? ((Token) offendingSymbol).getText()
                    : "<unknown token>";
            String errorMsg = "Syntax Error at: " + line + ": " + charPositionInLine + " near " + tokenText;
            errorMsgs.add(errorMsg);
        }
    }
}