Search in sources :

Example 1 with Variable

use of com.laytonsmith.core.constructs.Variable in project CommandHelper by EngineHub.

the class Script method run.

public void run(final List<Variable> vars, Environment myEnv, final MethodScriptComplete done) {
    // Some things, such as the label are determined at compile time
    this.CurrentEnv = myEnv;
    this.CurrentEnv.getEnv(GlobalEnv.class).SetLabel(this.label);
    MCCommandSender p = myEnv.getEnv(CommandHelperEnvironment.class).GetCommandSender();
    if (!hasBeenCompiled || compilerError) {
        Target target = Target.UNKNOWN;
        if (left.size() >= 1) {
            try {
                target = new Target(left.get(0).line_num, left.get(0).file, left.get(0).column);
            } catch (NullPointerException e) {
            // Oh well, we tried to get more information
            }
        }
        throw ConfigRuntimeException.CreateUncatchableException("Unable to run command, script not yet compiled," + " or a compiler error occurred for that command. To see the compile error, run /reloadaliases", target);
    }
    enforceLabelPermissions();
    try {
        for (ParseTree rootNode : cright) {
            if (rootNode == null) {
                continue;
            }
            for (Construct tempNode : rootNode.getAllData()) {
                if (tempNode instanceof Variable) {
                    if (left_vars == null) {
                        throw ConfigRuntimeException.CreateUncatchableException("$variables may not be used in this context." + " Only @variables may be.", tempNode.getTarget());
                    }
                    Construct c = Static.resolveDollarVar(left_vars.get(((Variable) tempNode).getVariableName()), vars);
                    ((Variable) tempNode).setVal(new CString(c.toString(), tempNode.getTarget()));
                }
            }
            MethodScriptCompiler.registerAutoIncludes(CurrentEnv, this);
            MethodScriptCompiler.execute(rootNode, CurrentEnv, done, this);
        }
    } catch (ConfigRuntimeException ex) {
        // We don't know how to handle this really, so let's pass it up the chain.
        throw ex;
    } catch (CancelCommandException e) {
    // p.sendMessage(e.getMessage());
    // The message in the exception is actually empty
    } catch (LoopBreakException e) {
        if (p != null) {
            p.sendMessage("The break() function must be used inside a for() or foreach() loop");
        }
        StreamUtils.GetSystemOut().println("The break() function must be used inside a for() or foreach() loop");
    } catch (LoopContinueException e) {
        if (p != null) {
            p.sendMessage("The continue() function must be used inside a for() or foreach() loop");
        }
        StreamUtils.GetSystemOut().println("The continue() function must be used inside a for() or foreach() loop");
    } catch (FunctionReturnException e) {
        if (myEnv.getEnv(GlobalEnv.class).GetEvent() != null) {
            // Oh, we're running in an event handler. Those know how to catch it too.
            throw e;
        }
        if (p != null) {
            p.sendMessage("The return() function must be used inside a procedure.");
        }
        StreamUtils.GetSystemOut().println("The return() function must be used inside a procedure.");
    } catch (Throwable t) {
        StreamUtils.GetSystemOut().println("An unexpected exception occurred during the execution of a script.");
        t.printStackTrace();
        if (p != null) {
            p.sendMessage("An unexpected exception occurred during the execution of your script." + " Please check the console for more information.");
        }
    }
    if (done != null) {
        done.done(null);
    }
}
Also used : IVariable(com.laytonsmith.core.constructs.IVariable) Variable(com.laytonsmith.core.constructs.Variable) ConfigRuntimeException(com.laytonsmith.core.exceptions.ConfigRuntimeException) MCCommandSender(com.laytonsmith.abstraction.MCCommandSender) CString(com.laytonsmith.core.constructs.CString) Target(com.laytonsmith.core.constructs.Target) LoopContinueException(com.laytonsmith.core.exceptions.LoopContinueException) CancelCommandException(com.laytonsmith.core.exceptions.CancelCommandException) CommandHelperEnvironment(com.laytonsmith.core.environments.CommandHelperEnvironment) Construct(com.laytonsmith.core.constructs.Construct) LoopBreakException(com.laytonsmith.core.exceptions.LoopBreakException) GlobalEnv(com.laytonsmith.core.environments.GlobalEnv) FunctionReturnException(com.laytonsmith.core.exceptions.FunctionReturnException)

Example 2 with Variable

use of com.laytonsmith.core.constructs.Variable in project CommandHelper by EngineHub.

the class Script method match.

public boolean match(String command) {
    if (cleft == null) {
        // we can't match it, nor can we even tell if it's what they intended for us to run.
        return false;
    }
    boolean case_sensitive = Prefs.CaseSensitive();
    String[] cmds = command.split(" ");
    List<String> args = new ArrayList<>(Arrays.asList(cmds));
    boolean isAMatch = true;
    StringBuilder lastVar = new StringBuilder();
    int lastJ = 0;
    for (int j = 0; j < cleft.size(); j++) {
        if (!isAMatch) {
            break;
        }
        lastJ = j;
        Construct c = cleft.get(j);
        if (args.size() <= j) {
            if (c.getCType() != ConstructType.VARIABLE || !((Variable) c).isOptional()) {
                isAMatch = false;
            }
            break;
        }
        String arg = args.get(j);
        if (c.getCType() != ConstructType.VARIABLE) {
            if (case_sensitive && !c.val().equals(arg) || !case_sensitive && !c.val().equalsIgnoreCase(arg)) {
                isAMatch = false;
                continue;
            }
        } else {
            // args isn't greater than the size of cleft, it's a match
            if (((Variable) c).isOptional()) {
                if (args.size() <= cleft.size()) {
                    return true;
                } else {
                    Construct fin = cleft.get(cleft.size() - 1);
                    if (fin instanceof Variable) {
                        if (((Variable) fin).isFinal()) {
                            return true;
                        }
                    }
                    return false;
                }
            }
        }
        if (j == cleft.size() - 1) {
            if (cleft.get(j).getCType() == ConstructType.VARIABLE) {
                Variable lv = (Variable) cleft.get(j);
                if (lv.isFinal()) {
                    for (int a = j; a < args.size(); a++) {
                        if (lastVar.length() == 0) {
                            lastVar.append(args.get(a));
                        } else {
                            lastVar.append(" ").append(args.get(a));
                        }
                    }
                }
            }
        }
    }
    boolean lastIsFinal = false;
    if (cleft.get(cleft.size() - 1) instanceof Variable) {
        Variable v = (Variable) cleft.get(cleft.size() - 1);
        if (v.isFinal()) {
            lastIsFinal = true;
        }
    }
    if ((cleft.get(lastJ) instanceof Variable && ((Variable) cleft.get(lastJ)).isOptional())) {
        return true;
    }
    if (cleft.size() != cmds.length && !lastIsFinal) {
        isAMatch = false;
    }
    return isAMatch;
}
Also used : IVariable(com.laytonsmith.core.constructs.IVariable) Variable(com.laytonsmith.core.constructs.Variable) ArrayList(java.util.ArrayList) Construct(com.laytonsmith.core.constructs.Construct) CString(com.laytonsmith.core.constructs.CString) ProfilePoint(com.laytonsmith.core.profiler.ProfilePoint)

Example 3 with Variable

use of com.laytonsmith.core.constructs.Variable in project CommandHelper by EngineHub.

the class Script method getVariables.

public List<Variable> getVariables(String command) {
    String[] cmds = command.split(" ");
    List<String> args = new ArrayList<>(Arrays.asList(cmds));
    StringBuilder lastVar = new StringBuilder();
    ArrayList<Variable> vars = new ArrayList<>();
    Variable v = null;
    for (int j = 0; j < cleft.size(); j++) {
        try {
            if (cleft.get(j).getCType() == ConstructType.VARIABLE) {
                if (((Variable) cleft.get(j)).getVariableName().equals("$")) {
                    for (int k = j; k < args.size(); k++) {
                        lastVar.append(args.get(k).trim()).append(" ");
                    }
                    v = new Variable(((Variable) cleft.get(j)).getVariableName(), lastVar.toString().trim(), Target.UNKNOWN);
                } else {
                    v = new Variable(((Variable) cleft.get(j)).getVariableName(), args.get(j), Target.UNKNOWN);
                }
            }
        } catch (IndexOutOfBoundsException e) {
            v = new Variable(((Variable) cleft.get(j)).getVariableName(), ((Variable) cleft.get(j)).getDefault(), Target.UNKNOWN);
        }
        if (v != null) {
            vars.add(v);
        }
    }
    return vars;
}
Also used : IVariable(com.laytonsmith.core.constructs.IVariable) Variable(com.laytonsmith.core.constructs.Variable) ArrayList(java.util.ArrayList) CString(com.laytonsmith.core.constructs.CString) ProfilePoint(com.laytonsmith.core.profiler.ProfilePoint)

Example 4 with Variable

use of com.laytonsmith.core.constructs.Variable in project CommandHelper by EngineHub.

the class MethodScriptCompiler method compile.

/**
 * Compiles the token stream into a valid ParseTree. This also includes optimization and reduction.
 *
 * @param stream The token stream, as generated by {@link #lex(String, File, boolean) lex}
 * @return A fully compiled, optimized, and reduced parse tree. If {@code stream} is null or empty, null is
 * returned.
 * @throws ConfigCompileException If the script contains syntax errors. Additionally, during optimization, certain
 * methods may cause compile errors. Any function that can optimize static occurrences and throws a
 * {@link ConfigRuntimeException} will have that exception converted to a ConfigCompileException.
 */
public static ParseTree compile(TokenStream stream) throws ConfigCompileException, ConfigCompileGroupException {
    Set<ConfigCompileException> compilerErrors = new HashSet<>();
    if (stream == null || stream.isEmpty()) {
        return null;
    }
    Target unknown;
    try {
        // Instead of using Target.UNKNOWN, we can at least set the file.
        unknown = new Target(0, stream.get(0).target.file(), 0);
    } catch (Exception e) {
        unknown = Target.UNKNOWN;
    }
    // Remove all newlines and whitespaces.
    ListIterator<Token> it = stream.listIterator(0);
    while (it.hasNext()) {
        if (it.next().type.isWhitespace()) {
            it.remove();
        }
    }
    // Get the file options.
    final FileOptions fileOptions = stream.getFileOptions();
    ParseTree tree = new ParseTree(fileOptions);
    tree.setData(CNull.NULL);
    Stack<ParseTree> parents = new Stack<>();
    /**
     * constructCount is used to determine if we need to use autoconcat when reaching a FUNC_END. The previous
     * constructs, if the count is greater than 1, will be moved down into an autoconcat.
     */
    Stack<AtomicInteger> constructCount = new Stack<>();
    constructCount.push(new AtomicInteger(0));
    parents.push(tree);
    tree.addChild(new ParseTree(new CFunction("__autoconcat__", unknown), fileOptions));
    parents.push(tree.getChildAt(0));
    tree = tree.getChildAt(0);
    constructCount.push(new AtomicInteger(0));
    /**
     * The array stack is used to keep track of the number of square braces in use.
     */
    Stack<AtomicInteger> arrayStack = new Stack<>();
    arrayStack.add(new AtomicInteger(-1));
    Stack<AtomicInteger> minusArrayStack = new Stack<>();
    Stack<AtomicInteger> minusFuncStack = new Stack<>();
    int parens = 0;
    Token t = null;
    int bracketCount = 0;
    // Create a Token array to iterate over, rather than using the LinkedList's O(n) get() method.
    Token[] tokenArray = stream.toArray(new Token[stream.size()]);
    for (int i = 0; i < tokenArray.length; i++) {
        t = tokenArray[i];
        Token prev1 = i - 1 >= 0 ? tokenArray[i - 1] : new Token(TType.UNKNOWN, "", t.target);
        Token next1 = i + 1 < stream.size() ? tokenArray[i + 1] : new Token(TType.UNKNOWN, "", t.target);
        Token next2 = i + 2 < stream.size() ? tokenArray[i + 2] : new Token(TType.UNKNOWN, "", t.target);
        Token next3 = i + 3 < stream.size() ? tokenArray[i + 3] : new Token(TType.UNKNOWN, "", t.target);
        // Brace handling
        if (t.type == TType.LCURLY_BRACKET) {
            ParseTree b = new ParseTree(new CFunction("__cbrace__", t.getTarget()), fileOptions);
            tree.addChild(b);
            tree = b;
            parents.push(b);
            bracketCount++;
            constructCount.push(new AtomicInteger(0));
            continue;
        }
        if (t.type == TType.RCURLY_BRACKET) {
            bracketCount--;
            if (constructCount.peek().get() > 1) {
                // We need to autoconcat some stuff
                int stacks = constructCount.peek().get();
                int replaceAt = tree.getChildren().size() - stacks;
                ParseTree c = new ParseTree(new CFunction("__autoconcat__", tree.getTarget()), fileOptions);
                List<ParseTree> subChildren = new ArrayList<>();
                for (int b = replaceAt; b < tree.numberOfChildren(); b++) {
                    subChildren.add(tree.getChildAt(b));
                }
                c.setChildren(subChildren);
                if (replaceAt > 0) {
                    List<ParseTree> firstChildren = new ArrayList<>();
                    for (int d = 0; d < replaceAt; d++) {
                        firstChildren.add(tree.getChildAt(d));
                    }
                    tree.setChildren(firstChildren);
                } else {
                    tree.removeChildren();
                }
                tree.addChild(c);
            }
            parents.pop();
            tree = parents.peek();
            constructCount.pop();
            try {
                constructCount.peek().incrementAndGet();
            } catch (EmptyStackException e) {
                throw new ConfigCompileException("Unexpected end curly brace", t.target);
            }
            continue;
        }
        // Associative array/label handling
        if (t.type == TType.LABEL && tree.getChildren().size() > 0) {
            // If it's not an atomic identifier it's an error.
            if (!prev1.type.isAtomicLit() && prev1.type != TType.IVARIABLE && prev1.type != TType.KEYWORD) {
                ConfigCompileException error = new ConfigCompileException("Invalid label specified", t.getTarget());
                if (prev1.type == TType.FUNC_END) {
                    // and stop compilation at this point.
                    throw error;
                }
                compilerErrors.add(error);
            }
            // Wrap previous construct in a CLabel
            ParseTree cc = tree.getChildren().get(tree.getChildren().size() - 1);
            tree.removeChildAt(tree.getChildren().size() - 1);
            tree.addChild(new ParseTree(new CLabel(cc.getData()), fileOptions));
            continue;
        }
        // Array notation handling
        if (t.type.equals(TType.LSQUARE_BRACKET)) {
            arrayStack.push(new AtomicInteger(tree.getChildren().size() - 1));
            continue;
        } else if (t.type.equals(TType.RSQUARE_BRACKET)) {
            boolean emptyArray = false;
            if (prev1.type.equals(TType.LSQUARE_BRACKET)) {
                emptyArray = true;
            }
            if (arrayStack.size() == 1) {
                throw new ConfigCompileException("Mismatched square bracket", t.target);
            }
            // array is the location of the array
            int array = arrayStack.pop().get();
            // index is the location of the first node with the index
            int index = array + 1;
            if (!tree.hasChildren() || array == -1) {
                throw new ConfigCompileException("Brackets are illegal here", t.target);
            }
            ParseTree myArray = tree.getChildAt(array);
            ParseTree myIndex;
            if (!emptyArray) {
                myIndex = new ParseTree(new CFunction("__autoconcat__", myArray.getTarget()), fileOptions);
                for (int j = index; j < tree.numberOfChildren(); j++) {
                    myIndex.addChild(tree.getChildAt(j));
                }
            } else {
                myIndex = new ParseTree(new CSlice("0..-1", t.target), fileOptions);
            }
            tree.setChildren(tree.getChildren().subList(0, array));
            ParseTree arrayGet = new ParseTree(new CFunction("array_get", t.target), fileOptions);
            arrayGet.addChild(myArray);
            arrayGet.addChild(myIndex);
            // Check if the @var[...] had a negating "-" in front. If so, add a neg().
            if (!minusArrayStack.isEmpty() && arrayStack.size() + 1 == minusArrayStack.peek().get()) {
                if (!next1.type.equals(TType.LSQUARE_BRACKET)) {
                    // Wait if there are more array_get's comming.
                    ParseTree negTree = new ParseTree(new CFunction("neg", unknown), fileOptions);
                    negTree.addChild(arrayGet);
                    tree.addChild(negTree);
                    minusArrayStack.pop();
                } else {
                    // Negate the next array_get instead, so just add this one to the tree.
                    tree.addChild(arrayGet);
                }
            } else {
                tree.addChild(arrayGet);
            }
            constructCount.peek().set(constructCount.peek().get() - myIndex.numberOfChildren());
            continue;
        }
        // Smart strings
        if (t.type == TType.SMART_STRING) {
            if (t.val().contains("@")) {
                ParseTree function = new ParseTree(fileOptions);
                function.setData(new CFunction(new Compiler.smart_string().getName(), t.target));
                ParseTree string = new ParseTree(fileOptions);
                string.setData(new CString(t.value, t.target));
                function.addChild(string);
                tree.addChild(function);
            } else {
                tree.addChild(new ParseTree(new CString(t.val(), t.target), fileOptions));
            }
            constructCount.peek().incrementAndGet();
            continue;
        }
        if (t.type == TType.DEREFERENCE) {
            // Currently unimplemented, but going ahead and making it strict
            compilerErrors.add(new ConfigCompileException("The '" + t.val() + "' symbol is not currently allowed in raw strings. You must quote all" + " symbols.", t.target));
        }
        if (t.type.equals(TType.FUNC_NAME)) {
            CFunction func = new CFunction(t.val(), t.target);
            ParseTree f = new ParseTree(func, fileOptions);
            tree.addChild(f);
            constructCount.push(new AtomicInteger(0));
            tree = f;
            parents.push(f);
        } else if (t.type.equals(TType.FUNC_START)) {
            if (!prev1.type.equals(TType.FUNC_NAME)) {
                throw new ConfigCompileException("Unexpected parenthesis", t.target);
            }
            parens++;
        } else if (t.type.equals(TType.FUNC_END)) {
            if (parens <= 0) {
                throw new ConfigCompileException("Unexpected parenthesis", t.target);
            }
            parens--;
            // Pop function.
            parents.pop();
            if (constructCount.peek().get() > 1) {
                // We need to autoconcat some stuff
                int stacks = constructCount.peek().get();
                int replaceAt = tree.getChildren().size() - stacks;
                ParseTree c = new ParseTree(new CFunction("__autoconcat__", tree.getTarget()), fileOptions);
                List<ParseTree> subChildren = new ArrayList<>();
                for (int b = replaceAt; b < tree.numberOfChildren(); b++) {
                    subChildren.add(tree.getChildAt(b));
                }
                c.setChildren(subChildren);
                if (replaceAt > 0) {
                    List<ParseTree> firstChildren = new ArrayList<>();
                    for (int d = 0; d < replaceAt; d++) {
                        firstChildren.add(tree.getChildAt(d));
                    }
                    tree.setChildren(firstChildren);
                } else {
                    tree.removeChildren();
                }
                tree.addChild(c);
            }
            constructCount.pop();
            try {
                constructCount.peek().incrementAndGet();
            } catch (EmptyStackException e) {
                throw new ConfigCompileException("Unexpected end parenthesis", t.target);
            }
            try {
                tree = parents.peek();
            } catch (EmptyStackException e) {
                throw new ConfigCompileException("Unexpected end parenthesis", t.target);
            }
            // Handle "-func(args)" and "-func(args)[index]".
            if (!minusFuncStack.isEmpty() && minusFuncStack.peek().get() == parens + 1) {
                if (next1.type.equals(TType.LSQUARE_BRACKET)) {
                    // Move the negation to the array_get which contains this function.
                    // +1 because the bracket isn't counted yet.
                    minusArrayStack.push(new AtomicInteger(arrayStack.size() + 1));
                } else {
                    // Negate this function.
                    ParseTree negTree = new ParseTree(new CFunction("neg", unknown), fileOptions);
                    negTree.addChild(tree.getChildAt(tree.numberOfChildren() - 1));
                    tree.removeChildAt(tree.numberOfChildren() - 1);
                    tree.addChildAt(tree.numberOfChildren(), negTree);
                }
                minusFuncStack.pop();
            }
        } else if (t.type.equals(TType.COMMA)) {
            if (constructCount.peek().get() > 1) {
                int stacks = constructCount.peek().get();
                int replaceAt = tree.getChildren().size() - stacks;
                ParseTree c = new ParseTree(new CFunction("__autoconcat__", unknown), fileOptions);
                List<ParseTree> subChildren = new ArrayList<>();
                for (int b = replaceAt; b < tree.numberOfChildren(); b++) {
                    subChildren.add(tree.getChildAt(b));
                }
                c.setChildren(subChildren);
                if (replaceAt > 0) {
                    List<ParseTree> firstChildren = new ArrayList<>();
                    for (int d = 0; d < replaceAt; d++) {
                        firstChildren.add(tree.getChildAt(d));
                    }
                    tree.setChildren(firstChildren);
                } else {
                    tree.removeChildren();
                }
                tree.addChild(c);
            }
            constructCount.peek().set(0);
            continue;
        }
        if (t.type == TType.SLICE) {
            // "empty first" slice notation. Compare this to the code below.
            try {
                CSlice slice;
                String value = next1.val();
                if (next1.type == TType.MINUS || next1.type == TType.PLUS) {
                    value = next1.val() + next2.val();
                    i++;
                }
                slice = new CSlice(".." + value, t.getTarget());
                i++;
                tree.addChild(new ParseTree(slice, fileOptions));
                constructCount.peek().incrementAndGet();
                continue;
            } catch (ConfigRuntimeException ex) {
                // turn them into a CCE.
                throw new ConfigCompileException(ex);
            }
        }
        if (next1.type.equals(TType.SLICE)) {
            // Slice notation handling
            try {
                CSlice slice;
                if (t.type.isSeparator() || (t.type.isWhitespace() && prev1.type.isSeparator()) || t.type.isKeyword()) {
                    // empty first
                    String value = next2.val();
                    i++;
                    if (next2.type == TType.MINUS || next2.type == TType.PLUS) {
                        value = next2.val() + next3.val();
                        i++;
                    }
                    slice = new CSlice(".." + value, next1.getTarget());
                    if (t.type.isKeyword()) {
                        tree.addChild(new ParseTree(new CKeyword(t.val(), t.getTarget()), fileOptions));
                        constructCount.peek().incrementAndGet();
                    }
                } else if (next2.type.isSeparator() || next2.type.isKeyword()) {
                    // empty last
                    String modifier = "";
                    if (prev1.type == TType.MINUS || prev1.type == TType.PLUS) {
                        // The negative would have already been inserted into the tree
                        modifier = prev1.val();
                        tree.removeChildAt(tree.getChildren().size() - 1);
                    }
                    slice = new CSlice(modifier + t.value + "..", t.target);
                } else {
                    // both are provided
                    String modifier1 = "";
                    if (prev1.type == TType.MINUS || prev1.type == TType.PLUS) {
                        // It's a negative, incorporate that here, and remove the
                        // minus from the tree
                        modifier1 = prev1.val();
                        tree.removeChildAt(tree.getChildren().size() - 1);
                    }
                    Token first = t;
                    if (first.type.isWhitespace()) {
                        first = prev1;
                    }
                    Token second = next2;
                    i++;
                    String modifier2 = "";
                    if (next2.type == TType.MINUS || next2.type == TType.PLUS) {
                        modifier2 = next2.val();
                        second = next3;
                        i++;
                    }
                    slice = new CSlice(modifier1 + first.value + ".." + modifier2 + second.value, t.target);
                }
                i++;
                tree.addChild(new ParseTree(slice, fileOptions));
                constructCount.peek().incrementAndGet();
                continue;
            } catch (ConfigRuntimeException ex) {
                // turn them into a CCE.
                throw new ConfigCompileException(ex);
            }
        } else if (t.type == TType.LIT) {
            Construct c = Static.resolveConstruct(t.val(), t.target);
            if (c instanceof CString && fileOptions.isStrict()) {
                compilerErrors.add(new ConfigCompileException("Bare strings are not allowed in strict mode", t.target));
            } else if ((c instanceof CInt || c instanceof CDecimal) && next1.type == TType.DOT && next2.type == TType.LIT) {
                // minus zero before decimals and leading zeroes after decimals
                try {
                    if (t.value.startsWith("0m")) {
                        // CDecimal
                        String neg = "";
                        if (prev1.value.equals("-")) {
                            neg = "-";
                        }
                        c = new CDecimal(neg + t.value.substring(2) + '.' + next2.value, t.target);
                    } else {
                        // CDouble
                        c = new CDouble(Double.parseDouble(t.val() + '.' + next2.val()), t.target);
                    }
                    i += 2;
                } catch (NumberFormatException e) {
                // Not a double
                }
            }
            tree.addChild(new ParseTree(c, fileOptions));
            constructCount.peek().incrementAndGet();
        } else if (t.type.equals(TType.STRING) || t.type.equals(TType.COMMAND)) {
            tree.addChild(new ParseTree(new CString(t.val(), t.target), fileOptions));
            constructCount.peek().incrementAndGet();
        } else if (t.type.equals(TType.IDENTIFIER)) {
            tree.addChild(new ParseTree(new CPreIdentifier(t.val(), t.target), fileOptions));
            constructCount.peek().incrementAndGet();
        } else if (t.type.isKeyword()) {
            tree.addChild(new ParseTree(new CKeyword(t.val(), t.getTarget()), fileOptions));
            constructCount.peek().incrementAndGet();
        } else if (t.type.equals(TType.IVARIABLE)) {
            tree.addChild(new ParseTree(new IVariable(t.val(), t.target), fileOptions));
            constructCount.peek().incrementAndGet();
        } else if (t.type.equals(TType.UNKNOWN)) {
            tree.addChild(new ParseTree(Static.resolveConstruct(t.val(), t.target), fileOptions));
            constructCount.peek().incrementAndGet();
        } else if (t.type.isSymbol()) {
            // Also handles "-function()" and "-@var[index]".
            if (t.type.equals(TType.MINUS) && !prev1.type.isAtomicLit() && !prev1.type.equals(TType.IVARIABLE) && !prev1.type.equals(TType.VARIABLE) && !prev1.type.equals(TType.RCURLY_BRACKET) && !prev1.type.equals(TType.RSQUARE_BRACKET) && !prev1.type.equals(TType.FUNC_END) && (next1.type.equals(TType.IVARIABLE) || next1.type.equals(TType.VARIABLE) || next1.type.equals(TType.FUNC_NAME))) {
                // Check if we are negating a value from an array, function or variable.
                if (next2.type.equals(TType.LSQUARE_BRACKET)) {
                    // +1 because the bracket isn't counted yet.
                    minusArrayStack.push(new AtomicInteger(arrayStack.size() + 1));
                } else if (next1.type.equals(TType.FUNC_NAME)) {
                    // +1 because the function isn't counted yet.
                    minusFuncStack.push(new AtomicInteger(parens + 1));
                } else {
                    ParseTree negTree = new ParseTree(new CFunction("neg", unknown), fileOptions);
                    negTree.addChild(new ParseTree(new IVariable(next1.value, next1.target), fileOptions));
                    tree.addChild(negTree);
                    constructCount.peek().incrementAndGet();
                    // Skip the next variable as we've just handled it.
                    i++;
                }
            } else {
                tree.addChild(new ParseTree(new CSymbol(t.val(), t.type, t.target), fileOptions));
                constructCount.peek().incrementAndGet();
            }
        } else if (t.type == TType.DOT) {
            // Check for doubles that start with a decimal, otherwise concat
            Construct c = null;
            if (next1.type == TType.LIT && prev1.type != TType.STRING && prev1.type != TType.SMART_STRING) {
                try {
                    c = new CDouble(Double.parseDouble('.' + next1.val()), t.target);
                    i++;
                } catch (NumberFormatException e) {
                // Not a double
                }
            }
            if (c == null) {
                c = new CSymbol(".", TType.CONCAT, t.target);
            }
            tree.addChild(new ParseTree(c, fileOptions));
            constructCount.peek().incrementAndGet();
        } else if (t.type.equals(TType.VARIABLE) || t.type.equals(TType.FINAL_VAR)) {
            tree.addChild(new ParseTree(new Variable(t.val(), null, false, t.type.equals(TType.FINAL_VAR), t.target), fileOptions));
            constructCount.peek().incrementAndGet();
        // right_vars.add(new Variable(t.val(), null, t.line_num));
        }
    }
    assert t != null;
    if (arrayStack.size() != 1) {
        throw new ConfigCompileException("Mismatched square brackets", t.target);
    }
    if (parens != 0) {
        throw new ConfigCompileException("Mismatched parenthesis", t.target);
    }
    if (bracketCount != 0) {
        throw new ConfigCompileException("Mismatched curly braces", t.target);
    }
    Stack<List<Procedure>> procs = new Stack<>();
    procs.add(new ArrayList<Procedure>());
    processKeywords(tree);
    optimizeAutoconcats(tree, compilerErrors);
    optimize(tree, procs, compilerErrors);
    link(tree, compilerErrors);
    checkLabels(tree, compilerErrors);
    checkBreaks(tree, compilerErrors);
    if (!compilerErrors.isEmpty()) {
        if (compilerErrors.size() == 1) {
            // Just throw the one CCE
            for (ConfigCompileException e : compilerErrors) {
                throw e;
            }
        } else {
            throw new ConfigCompileGroupException(compilerErrors);
        }
    }
    parents.pop();
    tree = parents.pop();
    return tree;
}
Also used : CLabel(com.laytonsmith.core.constructs.CLabel) IVariable(com.laytonsmith.core.constructs.IVariable) Variable(com.laytonsmith.core.constructs.Variable) CSymbol(com.laytonsmith.core.constructs.CSymbol) CPreIdentifier(com.laytonsmith.core.constructs.CPreIdentifier) IVariable(com.laytonsmith.core.constructs.IVariable) ArrayList(java.util.ArrayList) Token(com.laytonsmith.core.constructs.Token) CString(com.laytonsmith.core.constructs.CString) ConfigRuntimeException(com.laytonsmith.core.exceptions.ConfigRuntimeException) ConfigCompileException(com.laytonsmith.core.exceptions.ConfigCompileException) CString(com.laytonsmith.core.constructs.CString) EmptyStackException(java.util.EmptyStackException) Target(com.laytonsmith.core.constructs.Target) CSlice(com.laytonsmith.core.constructs.CSlice) KeywordList(com.laytonsmith.core.compiler.KeywordList) FunctionList(com.laytonsmith.core.functions.FunctionList) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) CDecimal(com.laytonsmith.core.constructs.CDecimal) HashSet(java.util.HashSet) Compiler(com.laytonsmith.core.functions.Compiler) CFunction(com.laytonsmith.core.constructs.CFunction) CDouble(com.laytonsmith.core.constructs.CDouble) URISyntaxException(java.net.URISyntaxException) ProgramFlowManipulationException(com.laytonsmith.core.exceptions.ProgramFlowManipulationException) ConfigCompileException(com.laytonsmith.core.exceptions.ConfigCompileException) DataSourceException(com.laytonsmith.persistence.DataSourceException) NoSuchElementException(java.util.NoSuchElementException) EmptyStackException(java.util.EmptyStackException) IOException(java.io.IOException) ConfigRuntimeException(com.laytonsmith.core.exceptions.ConfigRuntimeException) ConfigCompileGroupException(com.laytonsmith.core.exceptions.ConfigCompileGroupException) Stack(java.util.Stack) CInt(com.laytonsmith.core.constructs.CInt) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Construct(com.laytonsmith.core.constructs.Construct) CKeyword(com.laytonsmith.core.constructs.CKeyword) ConfigCompileGroupException(com.laytonsmith.core.exceptions.ConfigCompileGroupException) FileOptions(com.laytonsmith.core.compiler.FileOptions)

Example 5 with Variable

use of com.laytonsmith.core.constructs.Variable 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)

Aggregations

Variable (com.laytonsmith.core.constructs.Variable)14 CString (com.laytonsmith.core.constructs.CString)12 IVariable (com.laytonsmith.core.constructs.IVariable)11 Construct (com.laytonsmith.core.constructs.Construct)8 ArrayList (java.util.ArrayList)7 ProfilePoint (com.laytonsmith.core.profiler.ProfilePoint)6 ConfigCompileException (com.laytonsmith.core.exceptions.ConfigCompileException)5 Token (com.laytonsmith.core.constructs.Token)4 ConfigRuntimeException (com.laytonsmith.core.exceptions.ConfigRuntimeException)4 ParseTree (com.laytonsmith.core.ParseTree)3 CArray (com.laytonsmith.core.constructs.CArray)3 CFunction (com.laytonsmith.core.constructs.CFunction)3 GlobalEnv (com.laytonsmith.core.environments.GlobalEnv)3 MethodScriptComplete (com.laytonsmith.core.MethodScriptComplete)2 FileOptions (com.laytonsmith.core.compiler.FileOptions)2 CDouble (com.laytonsmith.core.constructs.CDouble)2 CInt (com.laytonsmith.core.constructs.CInt)2 CNull (com.laytonsmith.core.constructs.CNull)2 CSlice (com.laytonsmith.core.constructs.CSlice)2 Command (com.laytonsmith.core.constructs.Command)2