use of com.laytonsmith.core.exceptions.ConfigCompileException in project CommandHelper by EngineHub.
the class LexerObject method lex.
public TokenStream lex() throws ConfigCompileException {
if (token_list != null) {
return new TokenStream(new ArrayList<Token>(token_list), "");
} else {
token_list = new ArrayList<Token>();
}
for (int i = 0; i < config.length(); i++) {
Character c = config.charAt(i);
Character c2 = null;
Character c3 = null;
if (i < config.length() - 1) {
c2 = config.charAt(i + 1);
}
if (i < config.length() - 2) {
c3 = config.charAt(i + 2);
}
column += i - lastColumn;
lastColumn = i;
if (c == '\n') {
line_num++;
column = 1;
}
target = new Target(line_num, file, column);
// File Options
if (state_in_fileopts) {
if (c == '\\' && c2 == '>') {
// literal >
fileopts.append('>');
i++;
continue;
} else if (c == '>') {
state_in_fileopts = false;
continue;
} else {
fileopts.append(c);
continue;
}
}
// Comments are only applicable if we are not inside a string
if (!state_in_double_quote && !state_in_single_quote) {
// If we aren't already in a comment, we might be starting one here
if (!state_in_block_comment && !state_in_line_comment) {
if (c == '/' && c2 == '*') {
// Start of block comment
parseBuffer();
state_in_block_comment = true;
start_block_comment = line_num;
if (c3 == '*') {
// It's also a smart block comment
state_in_smart_block_comment = true;
i++;
} else {
state_in_smart_block_comment = false;
}
i++;
continue;
}
if (c == '#') {
parseBuffer();
// Start of line comment
state_in_line_comment = true;
continue;
}
} else if (state_in_block_comment) {
// We might be ending the block comment
if (c == '*' && c2 == '/') {
state_in_block_comment = false;
i++;
if (state_in_smart_block_comment) {
// We need to process the block comment here
// TODO:
// We need to process the block comment here
// TODO:
}
clearBuffer();
continue;
}
} else if (state_in_line_comment) {
if (c == '\n') {
state_in_line_comment = false;
clearBuffer();
continue;
}
}
}
// Ok, now if we are in a comment, we should just continue
if (state_in_block_comment || state_in_line_comment) {
if (state_in_smart_block_comment) {
buffer(c);
}
}
// Now we need to check for strings
if (!state_in_double_quote) {
if (c == '"') {
parseBuffer();
// Start of smart string
state_in_double_quote = true;
start_double_quote = line_num;
continue;
}
}
if (!state_in_single_quote) {
if (c == '\'') {
// Start of string
parseBuffer();
state_in_single_quote = true;
start_single_quote = line_num;
continue;
}
}
if (state_in_double_quote || state_in_single_quote) {
if (c == '\\') {
// It's an escaped something or another
switch(c2) {
case 'n':
buffer("\n");
i++;
break;
case 't':
buffer("\t");
i++;
break;
case 'u':
StringBuilder unicode = new StringBuilder();
for (int m = 0; m < 4; m++) {
try {
unicode.append(config.charAt(i + 2 + m));
} catch (IndexOutOfBoundsException e) {
// If this fails, they didn't put enough characters in the stream
error("Incomplete unicode escape");
}
}
try {
Integer.parseInt(unicode.toString(), 16);
} catch (NumberFormatException e) {
error("Unrecognized unicode escape sequence");
}
buffer(Character.toChars(Integer.parseInt(unicode.toString(), 16)));
i += 4;
break;
case '\'':
if (state_in_double_quote) {
// It's an error if we're in double quotes to escape a single quote
error("Invalid escape found. It is an error to escape single quotes inside a double quote.");
} else {
buffer("'");
i++;
}
break;
case '"':
if (state_in_single_quote) {
// It's an error if we're in single quotes to escape a double quote
error("Invalid escape found. It is an error to escape double quotes inside a single quote.");
} else {
buffer('"');
i++;
}
break;
default:
// It's invalid, so throw an exception
error("The escape sequence \\" + c2 + " is not a recognized escape sequence");
break;
}
continue;
}
}
// Now deal with ending a quote
if (state_in_double_quote) {
if (c == '"') {
state_in_double_quote = false;
append(clearBuffer(), Token.TType.SMART_STRING);
// This is currently an error, but won't be forever
error("Double quotes are currently unsupported");
continue;
} else {
buffer(c);
continue;
}
}
if (state_in_single_quote) {
if (c == '\'') {
state_in_single_quote = false;
append(clearBuffer(), Token.TType.STRING);
continue;
} else {
buffer(c);
continue;
}
}
// Now deal with multiline states
if (c == '>' && c2 == '>' && c3 == '>') {
// Multiline start
if (state_in_multiline) {
error("Found multiline start symbol while already in multiline!");
}
state_in_multiline = true;
start_multiline = line_num;
i += 2;
continue;
}
if (c == '<' && c2 == '<' && c3 == '<') {
if (!state_in_multiline) {
error("Found multiline end symbol while not in multiline!");
}
state_in_multiline = false;
i += 2;
continue;
}
// Newlines don't count
if (Character.isWhitespace(c) && c != '\n') {
// We need to parse the buffer
parseBuffer();
continue;
}
if (c == '<' && c2 == '!') {
if (!token_list.isEmpty()) {
throw new ConfigCompileException("File options must come first in the file.", target);
}
state_in_fileopts = true;
i++;
continue;
}
// get special handling up here, as well as square brackets
if (!state_in_pure_mscript) {
if (c == '[') {
if (state_in_opt_var) {
error("Found [ symbol, but a previous optional variable had already been started");
}
state_in_opt_var = true;
parseBuffer();
append("[", Token.TType.LSQUARE_BRACKET);
continue;
}
if (c == ']') {
if (!state_in_opt_var) {
error("Found ] symbol, but no optional variable had been started");
}
state_in_opt_var = false;
parseBuffer();
append("]", Token.TType.RSQUARE_BRACKET);
continue;
}
if (state_in_opt_var) {
if (c == '=') {
// This is an optional variable declaration
parseBuffer();
append("=", Token.TType.OPT_VAR_ASSIGN);
continue;
}
}
if (c == '=') {
state_in_pure_mscript = true;
parseBuffer();
append("=", Token.TType.ALIAS_END);
continue;
}
if (c == ':') {
parseBuffer();
append(":", Token.TType.LABEL);
continue;
}
if (c == '\n') {
parseBuffer();
if (token_list.isEmpty() || token_list.get(token_list.size() - 1).type != TType.NEWLINE) {
append("\n", TType.NEWLINE);
}
continue;
}
// At this point, all other tokens are to be taken literally
buffer(c);
continue;
}
// kept (except duplicate ones)
if (c == '\n') {
if (state_in_multiline) {
continue;
} else {
if (!token_list.isEmpty() && token_list.get(token_list.size() - 1).type != Token.TType.NEWLINE) {
parseBuffer();
if (usingNonPure) {
if (token_list.get(token_list.size() - 1).type != TType.NEWLINE) {
// Don't add duplicates
append("\n", Token.TType.NEWLINE);
}
// This also signals the end of pure mscript
state_in_pure_mscript = false;
continue;
} else if (state_in_pure_mscript) {
continue;
}
} else {
continue;
}
}
}
// Handle decimal place vs concat
if (c == '.' && Character.isDigit(c2)) {
// It'll get identified automatically in a bit
buffer(c);
continue;
}
// We need to handle /cmd vs division
if (c == '/' && (c2 == '/' || Character.isLetter(c2))) {
// It'll be registered as a bare string later
buffer(c);
continue;
}
// Now we are in pure mscript mode
// Loop through our token
int skip;
if ((skip = identifySymbol(i)) != -1) {
// Cool, it found one. Jump ahead.
i += skip;
continue;
}
buffer(c);
}
parseBuffer();
return new TokenStream(new ArrayList<Token>(token_list), fileopts.toString());
}
use of com.laytonsmith.core.exceptions.ConfigCompileException 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;
}
use of com.laytonsmith.core.exceptions.ConfigCompileException 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;
}
use of com.laytonsmith.core.exceptions.ConfigCompileException 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;
}
use of com.laytonsmith.core.exceptions.ConfigCompileException 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;
}
Aggregations