123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- 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 Code
- {
- protected final int realLine;
- protected final String function;
- protected final int pars;
- protected final byte layer;
- protected Object value;
-
- private Code(String function, int pars, Object value, int realLine, byte layer)
- {
- this.function = function;
- this.pars = pars;
- this.value = value;
- this.realLine = realLine;
- this.layer = layer;
- }
-
- public Code(String function, int pars, int realLine, byte layer)
- {
- this(function, pars, null, realLine, layer);
- }
-
- public Code(Object value, int realLine, byte layer)
- {
- this(null, 0, value, realLine, layer);
- }
-
- public void executeFunction(SnuviParser parser, Script sc, Stack<Object> stack)
- {
- if(function == null)
- {
- if(value != null)
- {
- if(value.getClass() == Variable.class)
- {
- stack.push(sc.getVar(((Variable) value).getName()));
- return;
- }
- stack.push(value);
- return;
- }
- stack.push(value);
- return;
- }
- Object[] input;
- if(pars > 0)
- {
- input = new Object[pars];
- for(int i = 0; i < pars; i++)
- {
- input[i] = stack.pop();
- }
- }
- else
- {
- input = new Object[0];
- }
- Object output = parser.parseFunction(sc, function, input);
- if(output != Void.TYPE)
- {
- stack.push(output);
- }
- }
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append(realLine);
- sb.append("| ");
-
- for(int i = -1; i < layer; i++)
- {
- sb.append('>');
- }
- sb.append(' ');
-
- if(function != null)
- {
- sb.append("call ");
- sb.append(function);
- sb.append(' ');
- sb.append(pars);
- if(value != null)
- {
- sb.append(" (");
- sb.append(value);
- sb.append(")");
- }
- }
- else
- {
- sb.append("push ");
- if(value != null)
- {
- sb.append(value.getClass().getSimpleName());
- }
- sb.append(" '");
- sb.append(value);
- sb.append("'");
- return sb.toString();
- }
- return sb.toString();
- }
-
- // -----------------------------------------------------------------------------------
- // code builder
- // -----------------------------------------------------------------------------------
-
- private static String scriptName = null;
-
- private static int findFirstNonLetterOrDigit(StringBuilder code, int pos)
- {
- int length = code.length();
- while(pos < length)
- {
- if(!Character.isLetterOrDigit(code.charAt(pos)) && code.charAt(pos) != '_')
- {
- return pos;
- }
- pos++;
- }
- return -1;
- }
-
- private static int findChar(char c, StringBuilder code, int pos)
- {
- int length = code.length();
- while(pos < length)
- {
- if(code.charAt(pos) == c)
- {
- return pos;
- }
- pos++;
- }
- return -1;
- }
-
- private static int findSyntaxIgnoreString(String find, StringBuilder code, int pos)
- {
- int length = code.length();
- int add = find.length();
- char c;
- boolean text = false;
- while(pos < length)
- {
- c = code.charAt(pos);
- if(text && c != '"')
- {
- pos++;
- continue;
- }
- else if(c == '"')
- {
- text = !text;
- pos++;
- continue;
- }
- else if(code.substring(pos, Math.min(pos + add, length)).equals(find))
- {
- return pos;
- }
- pos++;
- }
- return -1;
- }
- public static int countChar(char find, int end, StringBuilder code)
- {
- int counter = 0;
- int pos = 0;
- end = Math.min(end, code.length());
- while(pos < end)
- {
- if(code.charAt(pos) == find)
- {
- counter++;
- }
- pos++;
- }
- return counter;
- }
-
- @SuppressWarnings("")
- private static String removeNonSyntax(String s)
- {
- StringBuilder sb = new StringBuilder(s);
- int pos = 0;
- while(pos < sb.length())
- {
- switch(sb.charAt(pos))
- {
- case ' ':
- case '\t':
- case '\n':
- sb.deleteCharAt(pos);
- continue;
- }
- pos++;
- }
- return sb.toString();
- }
-
- private static void removeComments(StringBuilder sb)
- {
- int old = 0;
- int pos;
- while(true)
- {
- old = findSyntaxIgnoreString("/*", sb, old);
- if(old == -1)
- {
- break;
- }
- pos = findSyntaxIgnoreString("*/", sb, old);
- if(pos == -1)
- {
- throw new PreScriptException(scriptName, countChar('\n', old, sb), "/* without */");
- }
- sb.delete(old, pos + 2);
- }
- old = 0;
- while(true)
- {
- old = findSyntaxIgnoreString("//", sb, old);
- if(old == -1)
- {
- break;
- }
- pos = findSyntaxIgnoreString("\n", sb, old);
- if(pos == -1)
- {
- sb.delete(old, sb.length());
- break;
- }
- sb.delete(old, pos + 1);
- }
- }
-
- private static HashMap<String, String> replaceStrings(StringBuilder sb)
- {
- int old;
- int pos = 0;
- String replacement;
- String text;
- int stringIndex = 0;
- HashMap<String, String> strings = new HashMap<>();
- while(pos < sb.length())
- {
- if(sb.charAt(pos) == '"')
- {
- old = pos;
- pos++;
- while(sb.charAt(pos) != '"')
- {
- pos++;
- if(pos >= sb.length())
- {
- throw new PreScriptException(scriptName, countChar('\n', old, sb), "\" without another");
- }
- }
- pos++;
- text = sb.substring(old, pos);
- replacement = "#" + stringIndex;
- sb.replace(old, pos, replacement);
- strings.put(replacement, text);
- stringIndex++;
- pos -= (pos - old) - replacement.length();
- }
- pos++;
- }
- return strings;
- }
-
- private static void formatLabels(StringBuilder sb)
- {
- int pos;
- int old = 0;
- while(true)
- {
- pos = findChar('@', sb, old);
- if(pos == -1)
- {
- break;
- }
- pos++;
- old = pos;
- pos = findFirstNonLetterOrDigit(sb, pos);
- if(pos == -1)
- {
- break;
- }
- if(sb.charAt(pos) != ';')
- {
- sb.insert(pos, ';');
- }
- }
- }
-
- public static Code[] generate(SnuviParser parser, String scriptName, String code, HashMap<String, Integer> gotos)
- {
- System.out.print("Starting '" + scriptName + "' ... ");
- long startTime = System.currentTimeMillis();
-
- Code.scriptName = scriptName;
-
- StringBuilder sb = new StringBuilder(code);
-
- // ---------------------------------------------------------------------
- // comments
- // ---------------------------------------------------------------------
-
- removeComments(sb);
-
- // ---------------------------------------------------------------------
- // replacing Strings with #... to get rid of "
- // ---------------------------------------------------------------------
-
- HashMap<String, String> strings = replaceStrings(sb);
-
- // ---------------------------------------------------------------------
- // allowing labels without ;
- // ---------------------------------------------------------------------
-
- formatLabels(sb);
-
- code = sb.toString();
-
- // ---------------------------------------------------------------------
- // split code into lines
- // ---------------------------------------------------------------------
- ArrayList<String> lines = new ArrayList<>();
- int lineCounter = 1;
- ArrayList<Integer> realCodeLine = new ArrayList<>();
- int old = 0;
- int pos = 0;
- int length = code.length();
- int counter = 0;
- int bracketCounter = 0;
- boolean closed = true;
- Stack<Integer> brackets = new Stack<>();
- while(pos < length)
- {
- switch(code.charAt(pos))
- {
- case '(':
- closed = false;
- bracketCounter++;
- break;
- case ')':
- bracketCounter--;
- break;
- case ';':
- if(bracketCounter != 0)
- {
- throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
- }
- lines.add(removeNonSyntax(code.substring(old, pos)));
- realCodeLine.add(lineCounter);
- old = pos + 1;
- closed = true;
- break;
- case '{':
- if(bracketCounter != 0)
- {
- throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
- }
- brackets.push(lineCounter);
- counter++;
- lines.add(removeNonSyntax(code.substring(old, pos)));
- realCodeLine.add(lineCounter);
- old = pos + 1;
- lines.add("{");
- realCodeLine.add(lineCounter);
- break;
- case '}':
- if(!closed)
- {
- throw new PreScriptException(scriptName, lineCounter, "missing ;");
- }
- else if(bracketCounter != 0)
- {
- throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
- }
- counter--;
- if(counter < 0)
- {
- throw new PreScriptException(scriptName, lineCounter, "unexpected }");
- }
- old = pos + 1;
- lines.add("}");
- realCodeLine.add(lineCounter);
- brackets.pop();
- break;
- case '\n':
- lineCounter++;
- break;
- }
- pos++;
- }
- // well the second check should be useless
- if(counter != 0 && !brackets.isEmpty())
- {
- throw new PreScriptException(scriptName, brackets.pop(), "unbalanced {}");
- }
-
- // ---------------------------------------------------------------------
- // generating byte code
- // ---------------------------------------------------------------------
- LineCompiler compiler = new LineCompiler(parser, scriptName);
- ArrayList<Code> co = new ArrayList<>();
- int line;
- byte layer = 0;
- String s;
- for(int i = 0; i < lines.size(); i++)
- {
- s = lines.get(i);
- //System.out.println(s);
- if(s.isEmpty() || s.equals("{"))
- {
- continue;
- }
- else if(lines.get(Math.min(i + 1, lines.size() - 1)).equals("{"))
- {
- layer++;
- if(s.equals("try") || s.equals("catch") || s.equals("else"))
- {
- co.add(new Code(s, 0, realCodeLine.get(i), layer));
- continue;
- }
- }
- else if(s.equals("}"))
- {
- layer--;
- co.add(new Code("gotoline", 0, realCodeLine.get(i), layer));
- continue;
- }
- else if(s.charAt(0) == '@')
- {
- gotos.put(s.substring(1), co.size());
- continue;
- }
- line = realCodeLine.get(i);
- compiler.reset(line, layer, s);
- compiler.compile(co, strings);
- }
-
- // ---------------------------------------------------------------------
- // generating gotos of key words
- // ---------------------------------------------------------------------
-
- Code[] c = co.toArray(new Code[co.size()]);
- //java.util.Arrays.stream(c).forEach(cod -> System.out.println(cod.toString()));
-
- //System.exit(0);
- int oldLayer = 0;
- int newLayer;
- for(int i = 0; i < c.length; i++)
- {
- newLayer = c[i].layer;
- if(oldLayer < newLayer)
- {
- int j = findKeyWord(i, c);
- if(j == 0)
- {
- //java.util.Arrays.stream(c).forEach(cod -> System.out.println(cod.toString()));
- throw new PreScriptException(scriptName, c[i].realLine, "well, fuck this, this should never happen");
- }
- boolean jumpBack = j < 0;
- j = Math.abs(j);
- int k;
- for(k = j; k < c.length; k++)
- {
- if(c[k].layer < newLayer)
- {
- break;
- }
- }
- c[j].value = k - j;
- if(jumpBack)
- {
- c[k].value = i - k - 1;
- }
- else
- {
- c[k].value = 0;
- }
- }
- oldLayer = newLayer;
- }
- // end
- System.out.println((System.currentTimeMillis() - startTime) + " ms");
- //java.util.Arrays.stream(c).forEach(cod -> System.out.println(cod.toString()));
- //gotos.forEach((k, v) -> System.out.println("Lable " + k + " " + v));
- //System.exit(0);
- return c;
- }
-
- private static int findKeyWord(int start, Code[] c)
- {
- for(int j = start; j < c.length; j++)
- {
- if(c[j].function != null)
- {
- switch(c[j].function)
- {
- case "while":
- return -j;
- case "if":
- case "else":
- case "try":
- case "catch":
- return j;
- }
- }
- }
- return 0;
- }
-
- public static Object convertInput(HashMap<String, String> map, String s, boolean variable)
- {
- if(s == null)
- {
- return null;
- }
- s = s.trim();
- if(s.length() == 0)
- {
- return "";
- }
- if(s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"')
- {
- if(s.length() == 1)
- {
- System.out.println("Convert-Input - this should never happen");
- return "";
- }
- return s.substring(1, s.length() - 1);
- }
- else if(variable && s.startsWith("#"))
- {
- return convertInput(null, map.get(s), false);
- }
- else if(s.equals("true"))
- {
- return true;
- }
- else if(s.equals("false"))
- {
- return false;
- }
- else if(s.equals("null"))
- {
- return null;
- }
- try
- {
- if(s.contains("/"))
- {
- String[] parts = s.split("/");
- return new Fraction(Long.parseLong(parts[0].trim()), Long.parseLong(parts[1].trim()));
- }
- return new Fraction(Long.parseLong(s));
- }
- catch(NumberFormatException ex)
- {
- try
- {
- return Fraction.fromDouble(Double.parseDouble(s));
- }
- catch(NumberFormatException ex2)
- {
- if(variable)
- {
- return new Variable(s);
- }
- return s;
- }
- }
- }
-
- public static Object convertInput(String s)
- {
- return convertInput(null, s, false);
- }
- }
- // 916
|