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);
}
}
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;
}
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;
}
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;
}
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;
}
Aggregations