Search in sources :

Example 1 with ParseTree

use of com.laytonsmith.core.ParseTree in project CommandHelper by EngineHub.

the class CompilerObject method compile0.

void compile0() throws ConfigCompileException {
    Token t = consume();
    if (t.type == TType.NEWLINE) {
        return;
    }
    if (t.type == TType.CONST_START) {
        StringBuilder constName = new StringBuilder();
        while ((t = consume()).type != TType.RCURLY_BRACKET) {
            if (t.type != TType.BARE_STRING && t.type != TType.CONCAT) {
                throw new ConfigCompileException("Constant names may only contain names and dots.", t.getTarget());
            }
            constName.append(t.val());
        }
        Construct constant = env.getConstant(constName.toString());
        if (constant == null) {
            throw new ConfigCompileException("Expected the constant ${" + constName.toString() + "} to be provided in the compilation options, but it wasn't.", t.getTarget());
        }
        t = new Token(TType.STRING, constant.val(), constant.getTarget());
    }
    if (t.type == TType.BARE_STRING && peek().type == TType.FUNC_START) {
        consume();
        CFunction f = new CFunction(t.val(), t.getTarget());
        functionLines.add(peek().getTarget());
        pushNode(f);
        return;
    }
    if (t.type == TType.FUNC_END || t.type == TType.COMMA) {
        if (autoConcatCounter > 0) {
            autoConcatCounter--;
            popNode(t.getTarget());
        }
    }
    if (t.type == TType.COMMA) {
        return;
    }
    if (t.type == TType.FUNC_END) {
        // We're done with this child, so push it up
        popNode(t.getTarget());
        functionLines.pop();
        return;
    }
    if (t.type == TType.LSQUARE_BRACKET) {
        CFunction f = new CFunction("__cbracket__", Target.UNKNOWN);
        pushNode(f);
        bracketCounter++;
        bracketLines.push(t.getTarget());
        return;
    }
    if (t.type == TType.RSQUARE_BRACKET) {
        if (bracketCounter == 0) {
            throw new ConfigCompileException("Unexpected right bracket. (Did you have too many right square brackets (]) in your code?)", t.getTarget());
        }
        bracketCounter--;
        bracketLines.pop();
        popNode(t.getTarget());
        return;
    }
    if (t.type == TType.LCURLY_BRACKET) {
        CFunction f = new CFunction("__cbrace__", Target.UNKNOWN);
        pushNode(f);
        braceCounter++;
        braceLines.push(t.getTarget());
        return;
    }
    if (t.type == TType.RCURLY_BRACKET) {
        if (braceCounter == 0) {
            throw new ConfigCompileException("Unexpected right brace. (Did you have too many right braces (}) in your code?)", t.getTarget());
        }
        braceCounter--;
        braceLines.pop();
        popNode(t.getTarget());
        return;
    }
    // If the next token ISN'T a ) , } ] we need to autoconcat this
    if (peek().type != TType.FUNC_END && peek().type != TType.COMMA && peek().type != TType.RCURLY_BRACKET && peek().type != TType.RSQUARE_BRACKET) {
        // ... unless we're already in an autoconcat
        if (!(pointer.getData() instanceof CFunction && ((CFunction) pointer.getData()).val().equals("__autoconcat__"))) {
            CFunction f = new CFunction("__autoconcat__", Target.UNKNOWN);
            pushNode(f);
            autoConcatCounter++;
        }
    }
    if (t.type == TType.BARE_STRING && peek().type == TType.LABEL) {
        consume();
        pointer.addChild(new ParseTree(new CLabel(new CString(t.val(), t.getTarget())), stream.getFileOptions()));
        return;
    }
    if (t.type.isIdentifier()) {
        // If it's an atomic, put it in a construct and parse tree, then add it
        pointer.addChild(new ParseTree(resolveIdentifier(t), stream.getFileOptions()));
        return;
    }
    if (t.type.isSymbol()) {
        pointer.addChild(new ParseTree(new CSymbol(t.val(), t.type, t.getTarget()), stream.getFileOptions()));
        return;
    }
// Now we have to check ahead for commas and other division parameters.
}
Also used : CLabel(com.laytonsmith.core.constructs.CLabel) CSymbol(com.laytonsmith.core.constructs.CSymbol) Construct(com.laytonsmith.core.constructs.Construct) CFunction(com.laytonsmith.core.constructs.CFunction) Token(com.laytonsmith.core.constructs.Token) ConfigCompileException(com.laytonsmith.core.exceptions.ConfigCompileException) ParseTree(com.laytonsmith.core.ParseTree) CString(com.laytonsmith.core.constructs.CString)

Example 2 with ParseTree

use of com.laytonsmith.core.ParseTree in project CommandHelper by EngineHub.

the class NewMethodScriptCompiler method preprocess.

public static List<NewScript> preprocess(TokenStream tokenStream, Environment compilerEnvironment) throws ConfigCompileException {
    List<NewScript> scripts = new ArrayList<NewScript>();
    // We need to split the command definition and the pure mscript parts. First,
    // we split on newlines, those are each going to be our alias definitions
    List<List<Token>> commands = new ArrayList<List<Token>>();
    List<Token> working = new ArrayList<Token>();
    for (int i = 0; i < tokenStream.size(); i++) {
        Token t = tokenStream.get(i);
        if (t.type == Token.TType.NEWLINE) {
            commands.add(working);
            working = new ArrayList<Token>();
            continue;
        }
        working.add(t);
    }
    // Now they are split into individual aliases
    for (List<Token> stream : commands) {
        // We need to make constructs from the left, and compile the right
        // Compiling the right can be simply passed off to the compile
        // function, but we need to parse the left ourselves
        // We *should* only have (bare) strings, numbers, brackets on the left
        List<Token> left = new ArrayList<Token>();
        TokenStream right = new TokenStream(new ArrayList<Token>(), tokenStream.getFileOptions());
        boolean inLeft = true;
        boolean hasLabel = false;
        for (Token t : stream) {
            if (t.type == Token.TType.ALIAS_END) {
                inLeft = false;
                continue;
            }
            if (t.type == TType.LABEL) {
                hasLabel = true;
            }
            if (inLeft) {
                left.add(t);
            } else {
                right.add(t);
            }
        }
        ParseTree cright = compile(right, compilerEnvironment);
        List<Construct> cleft = new ArrayList<Construct>();
        boolean atFinalVar = false;
        boolean atOptionalVars = false;
        boolean pastLabel = false;
        String label = "";
        try {
            for (int i = 0; i < left.size(); i++) {
                Token t = left.get(i);
                if (hasLabel && !pastLabel) {
                    if (t.type == TType.LABEL) {
                        pastLabel = true;
                        continue;
                    }
                    label += t.val();
                    continue;
                }
                if (atFinalVar) {
                    throw new ConfigCompileException("The final var must be the last declaration in the alias", t.getTarget());
                }
                if (t.type == TType.LSQUARE_BRACKET) {
                    Token tname = left.get(i + 1);
                    atOptionalVars = true;
                    if (tname.val().equals("$")) {
                        atFinalVar = true;
                    }
                    if (tname.type != TType.VARIABLE && tname.type != TType.FINAL_VAR) {
                        throw new ConfigCompileException("Expecting a variable, but found " + tname.val(), tname.getTarget());
                    }
                    i++;
                    Token next = left.get(i + 1);
                    if (next.type != TType.OPT_VAR_ASSIGN && next.type != TType.RSQUARE_BRACKET) {
                        throw new ConfigCompileException("Expecting either a variable assignment or right square bracket, but found " + next.val(), next.getTarget());
                    }
                    i++;
                    String defaultVal = "";
                    if (next.type == TType.OPT_VAR_ASSIGN) {
                        // We have an assignment here
                        Token val = left.get(i + 1);
                        i++;
                        defaultVal = val.val();
                        next = left.get(i + 1);
                    }
                    if (next.type != TType.RSQUARE_BRACKET) {
                        throw new ConfigCompileException("Expecting a right square bracket, but found " + next.val() + " instead. (Did you forget to quote a multi word string?)", next.getTarget());
                    }
                    i++;
                    Variable v = new Variable(tname.val(), defaultVal, true, (tname.val().equals("$")), tname.getTarget());
                    cleft.add(v);
                    continue;
                }
                if (t.type == TType.VARIABLE || t.type == TType.FINAL_VAR) {
                    // Required variable
                    if (atOptionalVars) {
                        throw new ConfigCompileException("Only optional variables may come after the first optional variable", t.getTarget());
                    }
                    if (t.val().equals("$")) {
                        atFinalVar = true;
                    }
                    Variable v = new Variable(t.val(), "", false, t.val().equals("$"), t.getTarget());
                    cleft.add(v);
                    continue;
                }
                cleft.add(tokenToConstruct(t));
            }
        } catch (IndexOutOfBoundsException e) {
            throw new ConfigCompileException("Expecting more tokens, but reached end of alias signature before tokens were resolved.", left.get(0).getTarget());
        }
        if (!cleft.isEmpty()) {
            link(cright, compilerEnvironment);
            scripts.add(new NewScript(cleft, cright, label));
        }
    }
    return scripts;
}
Also used : Variable(com.laytonsmith.core.constructs.Variable) ArrayList(java.util.ArrayList) Token(com.laytonsmith.core.constructs.Token) CBareString(com.laytonsmith.core.constructs.CBareString) CString(com.laytonsmith.core.constructs.CString) ConfigCompileException(com.laytonsmith.core.exceptions.ConfigCompileException) Construct(com.laytonsmith.core.constructs.Construct) ArrayList(java.util.ArrayList) List(java.util.List) ParseTree(com.laytonsmith.core.ParseTree)

Example 3 with ParseTree

use of com.laytonsmith.core.ParseTree in project CommandHelper by EngineHub.

the class SimpleBlockKeywordFunction method doProcess.

/**
 * This is the standalone version of the {@link #process(java.util.List, int)} function. All values must be passed
 * in.
 *
 * @param keywordName The keyword name
 * @param functionArgumentCount The function clause argument count
 * @param isStandaloneFunction Whether or not this is a standalone function (that is, it can be used without a block
 * following it)
 * @param list The current list
 * @param keywordPosition The keyword position
 * @return
 * @throws ConfigCompileException
 */
public static int doProcess(String keywordName, Integer[] functionArgumentCount, boolean isStandaloneFunction, List<ParseTree> list, int keywordPosition) throws ConfigCompileException {
    Target t = list.get(keywordPosition).getTarget();
    if (list.size() > keywordPosition + 1) {
        ParseTree code = list.get(keywordPosition + 1);
        if (isCodeBlock(code)) {
            // This is a valid format, but we need to make sure that there is only one argument passed
            // to the while so far.
            Integer[] validArgs = functionArgumentCount;
            // If this is null, we don't care about argument count.
            if (validArgs != null) {
                // If the valid argument count is only 1, we will use that value
                // in the error message to make it more precise. Otherwise, use a more
                // generic error message
                int firstClauseArgumentCount = list.get(keywordPosition).getChildren().size();
                if (validArgs.length == 1) {
                    if (firstClauseArgumentCount != validArgs[0]) {
                        throw new ConfigCompileException("\"" + keywordName + "\" blocks " + (firstClauseArgumentCount > validArgs[0] ? "may only" : "must") + " have " + validArgs[0] + " argument" + (validArgs[0] == 1 ? "" : "s") + " passed to the" + " " + keywordName + " condition, " + firstClauseArgumentCount + " found.", t);
                    }
                } else {
                    boolean error = true;
                    for (int i : validArgs) {
                        if (firstClauseArgumentCount == i) {
                            error = false;
                            break;
                        }
                    }
                    if (error) {
                        throw new ConfigCompileException("\"" + keywordName + "\" blocks may not have " + firstClauseArgumentCount + " argument" + (firstClauseArgumentCount == 1 ? "" : "s") + " passed to the " + keywordName + " condition", t);
                    }
                }
            }
            list.get(keywordPosition).addChild(getArgumentOrNull(code));
            list.remove(keywordPosition + 1);
        }
    } else {
        if (!isStandaloneFunction) {
            throw new ConfigCompileException("Missing code block, following \"" + keywordName + "\"", t);
        }
    }
    return keywordPosition;
}
Also used : Target(com.laytonsmith.core.constructs.Target) ConfigCompileException(com.laytonsmith.core.exceptions.ConfigCompileException) ParseTree(com.laytonsmith.core.ParseTree)

Example 4 with ParseTree

use of com.laytonsmith.core.ParseTree in project CommandHelper by EngineHub.

the class TryKeyword method process.

@Override
public int process(List<ParseTree> list, int keywordPosition) throws ConfigCompileException {
    // If it's the old version, and a function
    if (list.get(keywordPosition).getData() instanceof CFunction && list.get(keywordPosition).getData().val().equals("try")) {
        return keywordPosition;
    }
    // Otherwise it's not, and we can continue on, assuming keyword usage.
    this.validateCodeBlock(list.get(keywordPosition + 1), "Expecting braces after try keyword");
    ParseTree complex_try = new ParseTree(new CFunction(COMPLEX_TRY, list.get(keywordPosition).getTarget()), list.get(keywordPosition).getFileOptions());
    complex_try.addChild(getArgumentOrNull(list.get(keywordPosition + 1)));
    // Remove the keyword and the try block
    list.remove(keywordPosition);
    list.remove(keywordPosition);
    // For now, we won't allow try {}, so this must be followed by a catch keyword. This restriction is somewhat artificial, and
    // if we want to remove it in the future, we can do so by removing this code block.
    {
        if (!(list.size() > keywordPosition && (nodeIsCatchKeyword(list.get(keywordPosition)) || nodeIsFinallyKeyword(list.get(keywordPosition))))) {
            throw new ConfigCompileException("Expecting \"catch\" or \"finally\" keyword to follow try, but none found", complex_try.getTarget());
        }
    }
    // We can have any number of catch statements after the try, so we loop through until we run out.
    for (int i = keywordPosition; i < list.size(); i++) {
        if (!nodeIsCatchKeyword(list.get(i)) && !nodeIsFinallyKeyword(list.get(i))) {
            // End of the chain, stop processing.
            break;
        }
        if (list.size() > i + 1) {
            this.validateCodeBlock(list.get(i + 1), "Expecting code block after catch, but none found");
        } else {
            throw new ConfigCompileException("catch must be followed by a code block, but none was found", list.get(i).getTarget());
        }
        if (list.get(i).getData() instanceof CFunction) {
            // We have something like catch(Exception @e) { }
            ParseTree n = list.get(i);
            if (n.getChildren().size() != 1) {
                throw new ConfigCompileException("Unexpected parameters passed to the \"catch\" clause." + " Exactly one argument must be passed.", n.getTarget());
            }
            complex_try.addChild(n.getChildAt(0));
            complex_try.addChild(getArgumentOrNull(list.get(i + 1)));
        } else {
            // clause statement, and we need to verify that there isn't a catch following it.
            if (list.size() > i + 2) {
                if (nodeIsCatchKeyword(list.get(i + 2))) {
                    throw new ConfigCompileException("A finally block must be the final" + " clause in the try/[catch]/finally statement", list.get(i + 2).getTarget());
                }
            }
            // Passed the inspection.
            complex_try.addChild(getArgumentOrNull(list.get(i + 1)));
        }
        // remove the catch keyword and the code block
        list.remove(i);
        list.remove(i);
        --i;
    }
    // Set the new function into place
    list.add(keywordPosition, complex_try);
    return keywordPosition;
}
Also used : CFunction(com.laytonsmith.core.constructs.CFunction) ConfigCompileException(com.laytonsmith.core.exceptions.ConfigCompileException) ParseTree(com.laytonsmith.core.ParseTree)

Example 5 with ParseTree

use of com.laytonsmith.core.ParseTree in project CommandHelper by EngineHub.

the class IfKeyword method process.

@Override
public int process(List<ParseTree> list, int keywordPosition) throws ConfigCompileException {
    ParseTree node = list.get(keywordPosition);
    Target t = node.getTarget();
    if (list.size() > keywordPosition + 1) {
        if (this.isValidCodeBlock(list.get(keywordPosition + 1))) {
            // is a compile error.
            if (node.getChildren().size() != 1) {
                throw new ConfigCompileException("Unexpected parameters passed to \"if\" clause, exactly 1 argument" + " must be provided", t);
            }
            // use ifelse from the outset
            try {
                if (nodeIsElseKeyword(list.get(keywordPosition + 2)) && nodeIsIfFunction(list.get(keywordPosition + 3))) {
                    // It is, convert this into an ifelse
                    ParseTree newNode = new ParseTree(new CFunction(IFELSE, t), node.getFileOptions());
                    newNode.setChildren(node.getChildren());
                    list.set(keywordPosition, newNode);
                    node = newNode;
                }
            } catch (IndexOutOfBoundsException ex) {
            // Doesn't matter, we're apparently at the end of the stream
            }
            node.addChild(getArgumentOrNull(list.get(keywordPosition + 1)));
            list.remove(keywordPosition + 1);
        }
        while (list.size() > keywordPosition + 1) {
            // Now check for elses. Since we've removed the cbrace following the if from the tree, we can continue from keywordPostion + 1
            if (nodeIsElseKeyword(list.get(keywordPosition + 1))) {
                try {
                    if (isCodeBlock(list.get(keywordPosition + 2))) {
                        // So ends the chain
                        validateCodeBlock(list.get(keywordPosition + 2), "");
                        node.addChild(getArgumentOrNull(list.get(keywordPosition + 2)));
                        // remove the else keyword + the brace
                        list.remove(keywordPosition + 1);
                        list.remove(keywordPosition + 1);
                        break;
                    } else if (nodeIsIfFunction(list.get(keywordPosition + 2))) {
                        // if(@a){ } else if(@b, @c)
                        if (list.get(keywordPosition + 2).getChildren().size() != 1) {
                            throw new ConfigCompileException("Unexpected parameters passed to \"if\" clause, exactly 1 argument" + " must be provided", list.get(keywordPosition + 2).getTarget());
                        }
                        if (!isCodeBlock(list.get(keywordPosition + 3))) {
                            throw new ConfigCompileException("Expecting braces after \"if\" clause", list.get(keywordPosition + 3).getTarget());
                        }
                        // Ok, checks are complete, so we can actually construct the arguments now
                        node.addChild(list.get(keywordPosition + 2).getChildAt(0));
                        node.addChild(getArgumentOrNull(list.get(keywordPosition + 3)));
                        // Remove the else, if function, and braces
                        list.remove(keywordPosition + 1);
                        list.remove(keywordPosition + 1);
                        list.remove(keywordPosition + 1);
                    } else {
                        // Anything else is unexpected.
                        throw new IndexOutOfBoundsException();
                    }
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConfigCompileException("Expecting either braces, or continuing if statement after \"else\" keyword", list.get(keywordPosition + 1).getTarget());
                }
            } else {
                // Done with the if else chain
                break;
            }
        }
    }
    return keywordPosition;
}
Also used : Target(com.laytonsmith.core.constructs.Target) CFunction(com.laytonsmith.core.constructs.CFunction) ConfigCompileException(com.laytonsmith.core.exceptions.ConfigCompileException) ParseTree(com.laytonsmith.core.ParseTree)

Aggregations

ParseTree (com.laytonsmith.core.ParseTree)30 CFunction (com.laytonsmith.core.constructs.CFunction)16 ConfigCompileException (com.laytonsmith.core.exceptions.ConfigCompileException)16 CString (com.laytonsmith.core.constructs.CString)5 Target (com.laytonsmith.core.constructs.Target)5 GlobalEnv (com.laytonsmith.core.environments.GlobalEnv)5 ArrayList (java.util.ArrayList)5 CArray (com.laytonsmith.core.constructs.CArray)4 Construct (com.laytonsmith.core.constructs.Construct)4 CancelCommandException (com.laytonsmith.core.exceptions.CancelCommandException)4 ConfigRuntimeException (com.laytonsmith.core.exceptions.ConfigRuntimeException)4 CKeyword (com.laytonsmith.core.constructs.CKeyword)3 IVariable (com.laytonsmith.core.constructs.IVariable)3 Variable (com.laytonsmith.core.constructs.Variable)3 Environment (com.laytonsmith.core.environments.Environment)3 CRECastException (com.laytonsmith.core.exceptions.CRE.CRECastException)3 ConfigCompileGroupException (com.laytonsmith.core.exceptions.ConfigCompileGroupException)3 FunctionReturnException (com.laytonsmith.core.exceptions.FunctionReturnException)3 MethodScriptComplete (com.laytonsmith.core.MethodScriptComplete)2 TokenStream (com.laytonsmith.core.compiler.TokenStream)2