/* Java code generated by "translate.java.pss" */ import java.io.*; import java.util.regex.*; import java.util.*; // contains stack public class jsoncheck { // using int instead of char so that all unicode code points are // available instead of just utf16. (emojis cant fit into utf16) private int accumulator; // counter for anything private int peep; // next char in input stream private int charsRead; // No. of chars read so far private int linesRead; // No. of lines read so far public StringBuffer workspace; // text accumulator private Stack stack; // parse token stack private int LENGTH; // tape initial length // use ArrayLists instead with .add() .get(n) and .set(n, E) // ArrayList al=new ArrayList(); private List tape; // array of token attributes private List marks; // tape marks private int tapePointer; // pointer to current cell private Reader input; // text input stream private boolean eof; // end of stream reached? private boolean flag; // not used here private StringBuffer escape; // char used to "escape" others "\" private StringBuffer delimiter; // push/pop delimiter (default is "*") private boolean markFound; // if the mark was found in tape /** make a new machine with a character stream reader */ public jsoncheck(Reader reader) { this.markFound = false; this.LENGTH = 100; this.input = reader; this.eof = false; this.flag = false; this.charsRead = 0; this.linesRead = 1; this.escape = new StringBuffer("\\"); this.delimiter = new StringBuffer("*"); this.accumulator = 0; this.workspace = new StringBuffer(""); this.stack = new Stack(); this.tapePointer = 0; this.tape = new ArrayList(); this.marks = new ArrayList(); for (int ii = 0; ii < this.LENGTH; ii++) { this.tape.add(new StringBuffer("")); this.marks.add(new StringBuffer("")); } try { this.peep = this.input.read(); } catch (java.io.IOException ex) { System.out.println("read error"); System.exit(-1); } } /** read one character from the input stream and update the machine. */ public void read() { int iChar; try { if (this.eof) { System.exit(0); } this.charsRead++; // increment lines if ((char)this.peep == '\n') { this.linesRead++; } this.workspace.append(Character.toChars(this.peep)); this.peep = this.input.read(); if (this.peep == -1) { this.eof = true; } } catch (IOException ex) { System.out.println("Error reading input stream" + ex); System.exit(-1); } } /** increment tape pointer by one */ public void increment() { this.tapePointer++; if (this.tapePointer >= this.LENGTH) { this.tape.add(new StringBuffer("")); this.marks.add(new StringBuffer("")); this.LENGTH++; } } /** remove escape character */ public void unescapeChar(char c) { if (workspace.length() > 0) { String s = this.workspace.toString().replace("\\"+c, c+""); this.workspace.setLength(0); workspace.append(s); } } /** add escape character */ public void escapeChar(char c) { if (workspace.length() > 0) { String s = this.workspace.toString().replace(c+"", "\\"+c); workspace.setLength(0); workspace.append(s); } } /** whether trailing escapes \\ are even or odd */ // untested code. check! eg try: add "x \\"; print; etc public boolean isEscaped(String ss, String sSuffix) { int count = 0; if (ss.length() < 2) return false; if (ss.length() <= sSuffix.length()) return false; if (ss.indexOf(this.escape.toString().charAt(0)) == -1) { return false; } int pos = ss.length()-sSuffix.length(); while ((pos > -1) && (ss.charAt(pos) == this.escape.toString().charAt(0))) { count++; pos--; } if (count % 2 == 0) return false; return true; } /* a helper to see how many trailing \\ escape chars */ private int countEscaped(String sSuffix) { String s = ""; int count = 0; int index = this.workspace.toString().lastIndexOf(sSuffix); // remove suffix if it exists if (index > 0) { s = this.workspace.toString().substring(0, index); } while (s.endsWith(this.escape.toString())) { count++; s = s.substring(0, s.lastIndexOf(this.escape.toString())); } return count; } /** reads the input stream until the workspace end with text */ // can test this with public void until(String sSuffix) { // read at least one character if (this.eof) return; this.read(); while (true) { if (this.eof) return; if (this.workspace.toString().endsWith(sSuffix)) { if (this.countEscaped(sSuffix) % 2 == 0) { return; } } this.read(); } } /** pop the first token from the stack into the workspace */ public Boolean pop() { if (this.stack.isEmpty()) return false; this.workspace.insert(0, this.stack.pop()); if (this.tapePointer > 0) this.tapePointer--; return true; } /** push the first token from the workspace to the stack */ public Boolean push() { String sItem; // dont increment the tape pointer on an empty push if (this.workspace.length() == 0) return false; // need to get this from this.delim not "*" int iFirstStar = this.workspace.indexOf(this.delimiter.toString()); if (iFirstStar != -1) { sItem = this.workspace.toString().substring(0, iFirstStar + 1); this.workspace.delete(0, iFirstStar + 1); } else { sItem = this.workspace.toString(); this.workspace.setLength(0); } this.stack.push(sItem); this.increment(); return true; } /** swap current tape cell with the workspace */ public void swap() { String s = new String(this.workspace); this.workspace.setLength(0); this.workspace.append(this.tape.get(this.tapePointer).toString()); this.tape.get(this.tapePointer).setLength(0); this.tape.get(this.tapePointer).append(s); } /** save the workspace to file "sav.pp" */ public void writeToFile() { try { File file = new File("sav.pp"); Writer out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file), "UTF8")); out.append(this.workspace.toString()); out.flush(); out.close(); } catch (Exception e) { System.out.println(e.getMessage()); } } public void goToMark(String mark) { this.markFound = false; for (var ii = 0; ii < this.marks.size(); ii++) { if (this.marks.get(ii).toString().equals(mark)) { this.tapePointer = ii; this.markFound = true; } } if (this.markFound == false) { System.out.print("badmark '" + mark + "'!"); System.exit(1); } } /** parse/check/compile the input */ public void parse(InputStreamReader input) { //this is where the actual parsing/compiling code should go //but this means that all generated code must use //"this." not "mm." } public static void main(String[] args) throws Exception { String temp = ""; jsoncheck mm = new jsoncheck(new InputStreamReader(System.in)); script: while (!mm.eof) { lex: { mm.read(); /* read */ // Unlike Crockfords grammar, I will just completely ignore whitespace, // but this may not be acceptable in a rigorous application. Also, I // am just using the ctype.h definition of whitespace, whatever that // may be. if (mm.workspace.toString().matches("^\\p{Space}+$")) { mm.workspace.setLength(0); /* clear */ break lex; } if (mm.workspace.toString().matches("^[0-9]+$")) { /* while */ while (Character.toString((char)mm.peep).matches("^[0-9]+$")) { if (mm.eof) { break; } mm.read(); } mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("integer*"); /* add */ mm.push(); break lex; } if (mm.workspace.toString().matches("^[a-z]+$") || mm.workspace.toString().matches("^[A-Z]+$")) { /* while */ while (Character.toString((char)mm.peep).matches("^[a-z]+$")) { if (mm.eof) { break; } mm.read(); } if (!mm.workspace.toString().equals("true") && !mm.workspace.toString().equals("false") && !mm.workspace.toString().equals("null") && !mm.workspace.toString().equals("e") && !mm.workspace.toString().equals("E")) { // handle error mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("Unknown value '"); /* add */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("' at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (character "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(").\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); if (mm.workspace.toString().equals("e") || mm.workspace.toString().equals("E")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("E*"); /* add */ mm.push(); break lex; } mm.workspace.setLength(0); /* clear */ mm.workspace.append("value*"); /* add */ mm.push(); break lex; } if (mm.workspace.toString().equals("\"")) { // save line number for error message mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.linesRead); /* lines */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.until("\""); if (mm.eof) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Unterminated quote (\") char, starting at line "); /* add */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } if (mm.workspace.length() > 0) { /* clip */ mm.workspace.delete(mm.workspace.length() - 1, mm.workspace.length()); } mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("string*"); /* add */ mm.push(); break lex; } // literal tokens if (mm.workspace.toString().equals(".") || mm.workspace.toString().equals(",") || mm.workspace.toString().equals(":") || mm.workspace.toString().equals("-") || mm.workspace.toString().equals("+") || mm.workspace.toString().equals("[") || mm.workspace.toString().equals("]") || mm.workspace.toString().equals("{") || mm.workspace.toString().equals("}")) { mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.append("*"); /* add */ mm.push(); break lex; } // here check if the workspace is empty. If not it is an error. if (!mm.workspace.toString().equals("")) { mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(", char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(": unquoted '"); /* add */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.workspace.append("' character.\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } } parse: while (true) { // This is for visualising stack reductions when debugging //unstack; add "\n"; print; clip; stack; // The parse/compile phase // -------------- // 2 tokens mm.pop(); mm.pop(); //----------- // Two token errors (not necessarily a complete list) // comma errors if (mm.workspace.toString().equals("{*,*") || mm.workspace.toString().equals(",*}*") || mm.workspace.toString().equals("[*,*") || mm.workspace.toString().equals(",*,*") || mm.workspace.toString().equals(",*]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(", char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(" (extra or misplaced ',' comma)\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // exponent errors (e/E must be followed by an int or signed int) if (!mm.workspace.toString().equals("E*") && mm.workspace.toString().startsWith("E*") && !mm.workspace.toString().endsWith("integer*") && !mm.workspace.toString().endsWith("-*") && !mm.workspace.toString().endsWith("+*") && !mm.workspace.toString().endsWith("number*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): misplaced exponent 'e' or 'E' \n"); /* add */ mm.workspace.append("In JSON syntax, e/E may only precede an int or signed int.\n"); /* add */ mm.workspace.append("for example: 33e+01 \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // exponent errors (e/E must be followed by an int or signed int) if (!mm.workspace.toString().equals("E*") && mm.workspace.toString().endsWith("E*") && !mm.workspace.toString().startsWith("integer*") && !mm.workspace.toString().startsWith("sign.integer*") && !mm.workspace.toString().startsWith("decimal*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): misplaced exponent 'e' or 'E' \n"); /* add */ mm.workspace.append("In JSON syntax, e/E may only be preceded by an int, signed int.\n"); /* add */ mm.workspace.append("or decimal eg: 33e+01 \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // sign errors (+/- must be followed by an integer if (!mm.workspace.toString().equals("-*") && mm.workspace.toString().startsWith("-*") && !mm.workspace.toString().endsWith("integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Json syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): misplaced negative '-' sign\n"); /* add */ mm.workspace.append("In JSON syntax, - may only precede a number \n"); /* add */ mm.workspace.append("for example: -33.01 \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // sign errors (+/- must be followed by an integer) if (!mm.workspace.toString().equals("+*") && mm.workspace.toString().startsWith("+*") && !mm.workspace.toString().endsWith("integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Json syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): misplaced positive '+' sign\n"); /* add */ mm.workspace.append("In JSON syntax, + may only precede a number \n"); /* add */ mm.workspace.append("for example: +33.01 \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // dot errors (. must be followed by an integer) if (!mm.workspace.toString().equals(".*") && mm.workspace.toString().startsWith(".*") && !mm.workspace.toString().endsWith("integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Json syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): misplaced dot '.' sign\n"); /* add */ mm.workspace.append("In JSON syntax, dots may only be used in decimal numbers \n"); /* add */ mm.workspace.append("for example: -33.01 \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // dot errors (. must be preceded by an integer or signed integer) if (!mm.workspace.toString().equals(".*") && mm.workspace.toString().endsWith(".*") && !mm.workspace.toString().startsWith("integer*") && !mm.workspace.toString().startsWith("sign.integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): misplaced dot '.' sign\n"); /* add */ mm.workspace.append("In JSON syntax, dots may only be used in decimal numbers \n"); /* add */ mm.workspace.append("for example: -33.01, but .44 is not a legal JSON number \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // eg errors "items*:*","members*:*",",*:*","[*:*","{*:*" // A colon must be preceded by a string. Using logic if (mm.workspace.toString().endsWith(":*") && !mm.workspace.toString().equals(":*") && !mm.workspace.toString().startsWith("string*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Json syntax error near line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(", char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(" (misplaced colon ':') \n"); /* add */ mm.workspace.append("A ':' can only occur after a string key in an object structure \n"); /* add */ mm.workspace.append("Example: {\"cancelled\":true} \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // more colon errors if (!mm.workspace.toString().equals(":*") && mm.workspace.toString().startsWith(":*")) { if (mm.workspace.toString().endsWith("}*") || mm.workspace.toString().endsWith(",*") || mm.workspace.toString().endsWith("]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error near line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(", char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(" (misplaced colon ':' or missing value?) \n"); /* add */ mm.workspace.append("A ':' only occur as part of an object member \n"); /* add */ mm.workspace.append("Example: {\"cancelled\":true} \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } } // catch object member errors // also need to check that not only 1 token in on the stack // hence the !"member*" construct if (mm.workspace.toString().startsWith("member*") || mm.workspace.toString().startsWith("members*")) { if (!mm.workspace.toString().equals("member*") && !mm.workspace.toString().equals("members*") && !mm.workspace.toString().endsWith(",*") && !mm.workspace.toString().endsWith("}*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error after object member near line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(")\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } } // catch array errors if (mm.workspace.toString().startsWith("items*") && !mm.workspace.toString().equals("items*") && !mm.workspace.toString().endsWith(",*") && !mm.workspace.toString().endsWith("]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Error after an array item near line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(")\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } if (mm.workspace.toString().startsWith("array*") || mm.workspace.toString().startsWith("object*")) { if (!mm.workspace.toString().equals("array*") && !mm.workspace.toString().equals("object*") && !mm.workspace.toString().endsWith(",*") && !mm.workspace.toString().endsWith("}*") && !mm.workspace.toString().endsWith("]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error near line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(")\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } } // invalid string sequence if (mm.workspace.toString().startsWith("string*")) { if (!mm.workspace.toString().equals("string*") && !mm.workspace.toString().endsWith(",*") && !mm.workspace.toString().endsWith("]*") && !mm.workspace.toString().endsWith("}*") && !mm.workspace.toString().endsWith(":*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error after a string near line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(")\n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } } // transmogrify into array item, start array if (mm.workspace.toString().equals("[*number*") || mm.workspace.toString().equals("[*string*") || mm.workspace.toString().equals("[*value*") || mm.workspace.toString().equals("[*array*") || mm.workspace.toString().equals("[*object*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("[*items*"); /* add */ mm.push(); mm.push(); continue parse; } // exponents (e-403, E+120, E04), this slightly simplifies number parsing if (mm.workspace.toString().equals("E*sign.integer*") || mm.workspace.toString().equals("E*integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("^"); /* add */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("exponent*"); /* add */ mm.push(); continue parse; } // JSON scientific format (23e-10, -201E+33) if (mm.workspace.toString().equals("integer*exponent*") || mm.workspace.toString().equals("sign.integer*exponent*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ // enforce multidigit zero rules // But is "0e44" legal JSON number syntax? That would seem odd // if it is. if (mm.workspace.toString().startsWith("+")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("JSON syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): \n"); /* add */ mm.workspace.append("In JSON syntax, the number part may not have a positive sign \n"); /* add */ mm.workspace.append("eg: +0.12e34 (error!) \n"); /* add */ mm.workspace.append("eg: 0.12e+34 (OK!) \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } if (mm.workspace.toString().startsWith("-")) { if (mm.workspace.length() > 0) { /* clop */ mm.workspace.delete(0, 1); } /* clop */ } if (!mm.workspace.toString().equals("0") && mm.workspace.toString().startsWith("0")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Json syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): \n"); /* add */ mm.workspace.append("In JSON syntax, multidigit numbers must begin with 1-9 \n"); /* add */ mm.workspace.append("eg: -0234.01E+9 (error) \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("number*"); /* add */ mm.push(); continue parse; } // JSON scientific format (-0.23e10, 10.2E+33) if (mm.workspace.toString().equals("decimal*exponent*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("number*"); /* add */ mm.push(); continue parse; } // where does a number terminate, this is the problem // It terminates at the tokens ,* }* ]* and maybe space but // this script doesnt have a space* token. if (mm.workspace.toString().equals("sign.integer*,*") || mm.workspace.toString().equals("integer*,*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("number*,*"); /* add */ mm.push(); mm.push(); continue parse; } // transmog if (mm.workspace.toString().equals("sign.integer*]*") || mm.workspace.toString().equals("integer*]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("items*]*"); /* add */ mm.push(); mm.push(); continue parse; } if (mm.workspace.toString().equals("sign.integer*}*") || mm.workspace.toString().equals("integer*}*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("number*}*"); /* add */ mm.push(); mm.push(); continue parse; } // convert decimals to numbers with token lookahead if (mm.workspace.toString().equals("decimal*}*") || mm.workspace.toString().equals("decimal*]*") || mm.workspace.toString().equals("decimal*,*")) { /* replace */ if (mm.workspace.length() > 0) { temp = mm.workspace.toString().replace("decimal*", "number*"); mm.workspace.setLength(0); mm.workspace.append(temp); } mm.push(); mm.push(); continue parse; } // signed numbers if (mm.workspace.toString().equals("-*integer*") || mm.workspace.toString().equals("+*integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("sign.integer*"); /* add */ mm.push(); continue parse; } // signed numbers if (mm.workspace.toString().equals("-*integer*") || mm.workspace.toString().equals("+*integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ mm.increment(); /* ++ */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.tapePointer > 0) mm.tapePointer--; /* -- */ mm.tape.get(mm.tapePointer).setLength(0); /* put */ mm.tape.get(mm.tapePointer).append(mm.workspace); mm.workspace.setLength(0); /* clear */ mm.workspace.append("sign.integer*"); /* add */ mm.push(); continue parse; } // empty arrays are legal json if (mm.workspace.toString().equals("[*]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("array*"); /* add */ mm.push(); continue parse; } // empty objects are legal json if (mm.workspace.toString().equals("{*}*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("object*"); /* add */ mm.push(); continue parse; } // -------------- // 3 tokens mm.pop(); //--------------- // Some three token errors // Object errors // A negative logic doesnt work because of the lookahead required for numbers if (mm.workspace.toString().equals("{*string*}*") || mm.workspace.toString().equals("{*integer*}*") || mm.workspace.toString().equals("{*sign.integer*}*") || mm.workspace.toString().equals("{*array*}*") || mm.workspace.toString().equals("{*object*}*") || mm.workspace.toString().equals("{*value*}*") || mm.workspace.toString().equals("{*decimal*}*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("Json syntax error near line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(", char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append(" (misplaced brace '}' or bad object) \n"); /* add */ mm.workspace.append("A '}' can only occur to terminate an object structure \n"); /* add */ mm.workspace.append("Example: {\"hour\":21.00, \"cancelled\":true} \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } // transmogrify number into array item if (mm.workspace.toString().equals("[*number*,*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("[*items*,*"); /* add */ mm.push(); mm.push(); mm.push(); continue parse; } // decimal numbers eg -4.334 or +4.3 or 0.1 if (mm.workspace.toString().equals("sign.integer*.*integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append(mm.tape.get(mm.tapePointer)); /* get */ if (mm.workspace.toString().startsWith("+")) { //error, no positive signed decimals in JSON mm.workspace.append("Json syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): misplaced positive '+' sign\n"); /* add */ mm.workspace.append("In JSON syntax, decimal numbers are not positively signed\n"); /* add */ mm.workspace.append("eg: +33.01 (error) \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } if (mm.workspace.toString().startsWith("-0") && !mm.workspace.toString().equals("-0")) { mm.workspace.append("Json syntax error at line "); /* add */ mm.workspace.append(mm.linesRead); /* lines */ mm.workspace.append(" (char "); /* add */ mm.workspace.append(mm.charsRead); /* chars */ mm.workspace.append("): \n"); /* add */ mm.workspace.append("In JSON syntax, multidigit numbers must begin with 1-9 \n"); /* add */ mm.workspace.append("eg: -0234.01E+9 (error) \n"); /* add */ System.out.print(mm.workspace); /* print */ break script; } mm.workspace.setLength(0); /* clear */ mm.workspace.append("decimal*"); /* add */ mm.push(); continue parse; } // decimal numbers eg -4.334 or +4.3 or 0.1 if (mm.workspace.toString().equals("integer*.*integer*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("decimal*"); /* add */ mm.push(); continue parse; } // arrays, if (mm.workspace.toString().equals("[*items*]*") || mm.workspace.toString().equals("[*number*]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("array*"); /* add */ mm.push(); continue parse; } // if (mm.workspace.toString().equals("items*,*string*") || mm.workspace.toString().equals("items*,*value*") || mm.workspace.toString().equals("items*,*array*") || mm.workspace.toString().equals("items*,*object*") || mm.workspace.toString().equals("items*,*number*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("items*"); /* add */ mm.push(); continue parse; } // object members //"string*:*integer*", if (mm.workspace.toString().equals("string*:*number*") || mm.workspace.toString().equals("string*:*string*") || mm.workspace.toString().equals("string*:*value*") || mm.workspace.toString().equals("string*:*object*") || mm.workspace.toString().equals("string*:*array*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("member*"); /* add */ mm.push(); continue parse; } // multiple elements of an object if (mm.workspace.toString().equals("member*,*member*") || mm.workspace.toString().equals("members*,*member*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("members*"); /* add */ mm.push(); continue parse; } // if (mm.workspace.toString().equals("{*members*}*") || mm.workspace.toString().equals("{*member*}*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("object*"); /* add */ mm.push(); continue parse; } mm.pop(); // -------------- // 4 tokens if (mm.workspace.toString().equals("items*,*items*,*") || mm.workspace.toString().equals("items*,*number*,*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("items*,*"); /* add */ mm.push(); mm.push(); continue parse; } // numbers require a lookahead token, unfortunately if (mm.workspace.toString().equals("string*:*number*,*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("member*,*"); /* add */ mm.push(); mm.push(); continue parse; } // numbers require a lookahead token, unfortunately if (mm.workspace.toString().equals("string*:*number*}*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("member*}*"); /* add */ mm.push(); mm.push(); continue parse; } // multiple elements of an object with lookahead if (mm.workspace.toString().equals("member*,*member*,*") || mm.workspace.toString().equals("members*,*member*,*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("members*,*"); /* add */ mm.push(); mm.push(); continue parse; } // multiple elements of an object with lookahead if (mm.workspace.toString().equals("member*,*member*}*") || mm.workspace.toString().equals("members*,*member*}*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("members*}*"); /* add */ mm.push(); mm.push(); continue parse; } mm.pop(); // -------------- // 5 tokens // need this clumsy rule for numbers which get resolved when // a ] is seen. This is the lookahead if (mm.workspace.toString().equals("[*items*,*items*]*") || mm.workspace.toString().equals("[*items*,*number*]*")) { mm.workspace.setLength(0); /* clear */ mm.workspace.append("array*"); /* add */ mm.push(); continue parse; } mm.push(); mm.push(); mm.push(); mm.push(); mm.push(); if (mm.eof) { while (mm.pop()); /* unstack */ if (mm.workspace.toString().equals("object*") || mm.workspace.toString().equals("array*") || mm.workspace.toString().equals("value*") || mm.workspace.toString().equals("string*") || mm.workspace.toString().equals("integer*") || mm.workspace.toString().equals("decimal*") || mm.workspace.toString().equals("number*")) { while(mm.push()); /* stack */ mm.workspace.append("(Appears to be) valid JSON syntax. Top level structure was '"); /* add */ System.out.print(mm.workspace); /* print */ mm.workspace.setLength(0); /* clear */ mm.pop(); if (mm.workspace.length() > 0) { /* clip */ mm.workspace.delete(mm.workspace.length() - 1, mm.workspace.length()); } mm.workspace.append("'\n"); /* add */ System.out.print(mm.workspace); /* print */ mm.workspace.setLength(0); /* clear */ break script; } while(mm.push()); /* stack */ mm.workspace.append("(maybe) Invalid JSON \n"); /* add */ mm.workspace.append("The parse stack was \n"); /* add */ System.out.print(mm.workspace); /* print */ mm.workspace.setLength(0); /* clear */ while (mm.pop()); /* unstack */ mm.workspace.append("\n"); /* add */ System.out.print(mm.workspace); /* print */ } break parse; } } } }