123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- package me.hammerle.code;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Stack;
- import me.hammerle.exceptions.PreScriptException;
- import me.hammerle.math.Fraction;
- public class LineCompiler
- {
- private int realLine;
- // comma counter, for functions
- private final Stack<Integer> commaCounter;
-
- private String line;
- private byte layer;
-
- private String scriptName;
-
- // helper to support things like print(-3);
- private boolean minus;
-
- public LineCompiler(SnuviParser parser, String scriptName)
- {
- realLine = 0;
- commaCounter = new Stack<>();
- line = "";
- layer = 0;
- minus = false;
- this.scriptName = scriptName;
- }
- public void reset(int realLine, byte layer, String line)
- {
- this.realLine = realLine;
- commaCounter.clear();
- minus = false;
- this.line = line;
- this.layer = layer;
- }
-
- private boolean isAllowedChar(char c)
- {
- return Character.isLetterOrDigit(c) || c == '.' || c == '_' || c == '#' || c == '$';
- }
-
- public void compile(ArrayList<Code> co, HashMap<String, String> strings)
- {
- Stack<Stack<Syntax>> syntaxStack = new Stack<>();
- syntaxStack.push(new Stack<>());
-
- char[] chars = line.toCharArray();
- char c;
- int old = chars.length;
- Syntax syntax;
- String s;
- for(int i = old - 1; i >= 0; i--)
- {
- c = chars[i];
- if(!isAllowedChar(c))
- {
- syntax = getSyntax(line, i);
- if(syntax == Syntax.UNKNOWN)
- {
- throw new PreScriptException(scriptName, realLine, "unexpected character '" + c + "'");
- }
- else if(syntax.isIncOrDec())
- {
- Code change = co.get(co.size() - 1);
- if(change.function.equals("array.get"))
- {
- change.function = "array." + syntax.getFunction();
- }
- }
- s = line.substring(i + 1, old).toLowerCase();
- if(!s.isEmpty())
- {
- Stack<Syntax> stack = syntaxStack.peek();
- if(!stack.isEmpty())
- {
- Syntax sy = stack.peek();
- if(sy == Syntax.INC)
- {
- stack.pop();
- stack.push(Syntax.POST_INC);
- }
- else if(sy == Syntax.DEC)
- {
- stack.pop();
- stack.push(Syntax.POST_DEC);
- }
- }
- co.add(new Code(Code.convertInput(strings, s, true), realLine, layer));
- minus = false;
- }
- i += 1 - syntax.getFunction().length();
- old = i;
- if(syntax.shouldStartLayer())
- {
- syntaxStack.push(new Stack<>());
- commaCounter.push(0);
- continue;
- }
- else if(syntax.shouldEndLayer())
- {
- Stack<Syntax> currentStack = syntaxStack.pop();
- while(!currentStack.isEmpty())
- {
- doStackEmptying(co, currentStack.pop());
- }
- old--;
- int pos = old;
- while(old >= 0 && isAllowedChar(chars[old]))
- {
- old--;
- }
- old++;
- s = line.substring(old, pos + 1).toLowerCase();
- if(syntax.isArray())
- {
- minus = false;
- co.add(new Code(s, realLine, layer));
- if(!syntaxStack.isEmpty())
- {
- Stack<Syntax> arrayFunction = syntaxStack.pop();
- if(!arrayFunction.isEmpty())
- {
- Syntax sy = arrayFunction.pop();
- if(sy.isIncOrDec())
- {
- if(sy == Syntax.INC)
- {
- sy = Syntax.POST_INC;
- }
- else if(sy == Syntax.DEC)
- {
- sy = Syntax.POST_DEC;
- }
- }
- co.add(new Code("array." + sy.getFunction(), sy.isIncOrDec() ? 2 : 3, realLine, layer));
- continue;
- }
- }
- co.add(new Code("array.get", 2, realLine, layer));
- }
- else
- {
- minus = false;
- if(line.startsWith("()", pos + 1))
- {
- co.add(new Code(s, 0, realLine, layer));
- }
- else
- {
- co.add(new Code(s, commaCounter.pop() + 1, realLine, layer));
- }
- }
- continue;
- }
- else if(!syntaxStack.isEmpty())
- {
- Stack<Syntax> currentStack = syntaxStack.peek();
- while(!currentStack.isEmpty() && currentStack.peek().getWeight() <= syntax.getWeight())
- {
- doStackEmptying(co, currentStack.pop());
- }
- currentStack.push(syntax);
- }
-
- if(syntax == Syntax.COMMA)
- {
- commaCounter.push(commaCounter.pop() + 1);
- }
- else if(syntax == Syntax.SUB)
- {
- minus = true;
- }
- }
- }
-
- s = line.substring(0, old).toLowerCase();
- if(!s.isEmpty())
- {
- co.add(new Code(s, realLine, layer));
- }
-
- if(!syntaxStack.isEmpty())
- {
- Stack<Syntax> currentStack = syntaxStack.peek();
- while(!currentStack.isEmpty())
- {
- doStackEmptying(co, currentStack.pop());
- }
- }
- }
-
- private void doStackEmptying(ArrayList<Code> co, Syntax sy)
- {
- if(sy.isFunction())
- {
- if(sy.isIncOrDec())
- {
- Code change = co.get(co.size() - 1);
- if(change.value instanceof Variable)
- {
- change.value = ((Variable) change.value).getName();
- }
- co.add(new Code(sy.getFunction(), sy.getParameters(), realLine, layer));
- }
- else
- {
- if(minus)
- {
- minus = false;
- co.add(new Code(new Fraction(0), realLine, layer));
- }
- co.add(new Code(sy.getFunction(), sy.getParameters(), realLine, layer));
- }
- }
- }
-
- private static Syntax getSyntax(String code, int pos)
- {
- // priorities from C specification
- // http://en.cppreference.com/w/c/language/operator_precedence
- switch(code.charAt(pos))
- {
- case '(': return Syntax.OPEN_BRACKET;
- case ')': return Syntax.CLOSE_BRACKET;
- case '[': return Syntax.OPEN_SBRACKET;
- case ']': return Syntax.CLOSE_SBRACKET;
- case '!': return Syntax.NOT;
- case '~': return Syntax.BIT_NOT;
- case '*': return Syntax.MUL;
- case '/': return Syntax.DIV;
- case '%': return Syntax.MOD;
- case '^': return Syntax.BIT_XOR;
- case ',': return Syntax.COMMA;
- case '+':
- if(pos >= 1 && code.charAt(pos - 1) == '+')
- {
- return Syntax.INC;
- }
- return Syntax.ADD;
- case '-':
- if(pos >= 1 && code.charAt(pos - 1) == '-')
- {
- return Syntax.DEC;
- }
- return Syntax.SUB;
- case '<':
- if(pos >= 1 && code.charAt(pos - 1) == '<')
- {
- return Syntax.LEFT_SHIFT;
- }
- return Syntax.SMALLER;
- case '>':
- if(pos >= 1 && code.charAt(pos - 1) == '>')
- {
- return Syntax.RIGHT_SHIFT;
- }
- return Syntax.GREATER;
- case '&':
- if(pos >= 1 && code.charAt(pos - 1) == '&')
- {
- return Syntax.AND;
- }
- return Syntax.BIT_AND;
- case '|':
- if(pos >= 1 && code.charAt(pos - 1) == '|')
- {
- return Syntax.OR;
- }
- return Syntax.BIT_OR;
- case '=':
- if(pos >= 1)
- {
- switch(code.charAt(pos - 1))
- {
- case '<':
- if(pos >= 2 && code.charAt(pos - 2) == '<')
- {
- return Syntax.SET_SHIFT_LEFT;
- }
- return Syntax.SMALLER_EQUAL;
- case '>':
- if(pos >= 2 && code.charAt(pos - 2) == '>')
- {
- return Syntax.SET_SHIFT_RIGHT;
- }
- return Syntax.GREATER_EQUAL;
- case '=': return Syntax.EQUAL;
- case '!': return Syntax.NOT_EQUAL;
- case '+': return Syntax.SET_ADD;
- case '-': return Syntax.SET_SUB;
- case '*': return Syntax.SET_MUL;
- case '/': return Syntax.SET_DIV;
- case '%': return Syntax.SET_MOD;
- case '&': return Syntax.SET_BIT_AND;
- case '^': return Syntax.SET_BIT_XOR;
- case '|': return Syntax.SET_BIT_OR;
- }
- }
- return Syntax.SET;
- }
- return Syntax.UNKNOWN;
- }
- }
|