/******************************************************************************* * LastCalc - The last calculator you'll ever need * Copyright (C) 2011, 2012 Uprizer Labs LLC * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Affero General Public License for more * details. ******************************************************************************/ package com.lastcalc.parsers; import java.io.PrintStream; import java.io.Serializable; import java.util.*; import com.google.common.collect.*; import com.lastcalc.TokenList; import com.lastcalc.parsers.Parser.ParseResult; public class PreParser extends Parser { public static PreParser singleton = new PreParser(); private static final long serialVersionUID = 3705611710430408505L; private static TokenList template; public static Set<String> open = Sets.newHashSet("(", "{", "["); public static Set<String> close = Sets.newHashSet(")", "}", "]"); public static Set<String> reserved = Sets.newHashSet(",", ":", "...", "ans"); static { reserved.addAll(open); reserved.addAll(close); final ArrayList<Object> tpl = Lists.newArrayList(); tpl.add(Lists.<Object> newArrayList(close)); template = new TokenList.SimpleTokenList(tpl); } public static TokenList flatten(final TokenList input) { boolean willFlatten = false; for (final Object o : input) { if (o instanceof TokenList) { willFlatten = true; break; } if (o instanceof List) { willFlatten = true; break; } if (o instanceof Map) { willFlatten = true; break; } if (o instanceof ListWithTail || o instanceof MapWithTail) { willFlatten = true; break; } } if (!willFlatten) return input; final List<Object> ret = Lists.newArrayListWithCapacity(input.size() * 4); flattenTo(input, ret); return TokenList.create(ret); } private static void flattenTo(final Object obj, final List<Object> output) { if (obj instanceof TokenList) { final TokenList sts = (TokenList) obj; if (sts.size() == 1) { flattenTo(sts.get(0), output); } else { for (final Object o : sts) { flattenTo(o, output); } } } else if (obj instanceof List) { final List<Object> list = (List<Object>) obj; output.add("["); for (final Object lo : list) { flattenTo(lo, output); output.add(","); } if (list.isEmpty()) { output.add("]"); } else { // Overwrite last "," output.set(output.size() - 1, "]"); } } else if (obj instanceof Map) { final Map<Object, Object> map = (Map<Object, Object>) obj; output.add("{"); for (final Map.Entry<Object, Object> e : map.entrySet()) { flattenTo(e.getKey(), output); output.add(":"); flattenTo(e.getValue(), output); output.add(","); } if (map.isEmpty()) { output.add("}"); } else { output.set(output.size() - 1, "}"); } } else if (obj instanceof ListWithTail) { final ListWithTail lwt = (ListWithTail) obj; output.add("["); for (final Object lo : lwt.list) { flattenTo(lo, output); output.add(","); } output.set(output.size() - 1, "..."); flattenTo(lwt.tail, output); output.add("]"); } else if (obj instanceof MapWithTail) { final MapWithTail mwt = (MapWithTail) obj; output.add("{"); for (final Map.Entry<Object, Object> e : mwt.map.entrySet()) { flattenTo(e.getKey(), output); output.add(":"); flattenTo(e.getValue(), output); output.add(","); } output.set(output.size() - 1, "..."); flattenTo(mwt.tail, output); output.add("}"); } else { output.add(obj); } } public static int findEdgeOrObjectBackwards(final TokenList orig, final int startPos, final Object obj) { int depth = 0; for (int x = startPos-1;; x--) { if (x == -1) return 0; final Object tx = orig.get(x); if (depth == 0 && tx.equals(obj)) return x; else if (close.contains(tx)) { depth++; } else if (open.contains(tx)) { if (depth == 0) return x+1; else { depth--; } } else if (reserved.contains(tx) && depth == 0) return x+1; } } public static int findEdgeOrObjectForwards(final TokenList orig, final int startPos, final Object obj) { int depth = 0; for (int x = startPos + 1;; x++) { if (x >= orig.size()) return orig.size() - 1; final Object tx = orig.get(x); if (depth == 0 && tx.equals(obj)) return x; else if (open.contains(tx)) { depth++; } else if (close.contains(tx)) { if (depth == 0) return x - 1; else { depth--; } } else if (reserved.contains(tx) && depth == 0) return x - 1; } } public static TokenList enclosedByStructure(final TokenList orig, final int pos) { int startPos; for (startPos = pos; startPos > -1 && !reserved.contains(orig.get(startPos)); startPos--) { } startPos++; int endPos; for (endPos = pos; endPos < orig.size() && !reserved.contains(orig.get(endPos)); endPos++) { } return orig.subList(startPos, endPos); } @Override public TokenList getTemplate() { return template; } @Override public ParseResult parse(final TokenList tokens, final int templatePos, final ParserContext context) { if (tokens.get(templatePos).equals(")")) { if (tokens.get(templatePos - 2).equals("(") && !reserved.contains(tokens.get(templatePos - 1))) return ParseResult.success(tokens.replaceWithTokens(templatePos - 2, templatePos + 1, tokens.get(templatePos - 1))); else return ParseResult.fail(); } else if (tokens.get(templatePos).equals("]")) { // Is there a tail int pos = templatePos; Object tail = null; if (tokens.get(templatePos - 1).equals("[")) { pos--; } else if (tokens.get(templatePos - 2).equals("...")) { tail = tokens.get(templatePos - 1); pos -= 2; } final LinkedList<Object> list = Lists.newLinkedList(); while (true) { final Object posToken = tokens.get(pos); if (posToken.equals("[")) { break; } if (!posToken.equals(",") && !posToken.equals("...") && !posToken.equals("]")) return ParseResult.fail(); if (reserved.contains(tokens.get(pos - 1))) return ParseResult.fail(); list.addFirst(tokens.get(pos - 1)); pos -= 2; } if (tail == null) return ParseResult.success(tokens.replaceWithTokens(pos, templatePos + 1, list)); else if (tail instanceof Collection) { list.addAll((Collection<Object>) tail); return ParseResult.success(tokens.replaceWithTokens(pos, templatePos + 1, list)); } else return ParseResult .success(tokens.replaceWithTokens(pos, templatePos + 1, new ListWithTail(list, tail))); } else if (tokens.get(templatePos).equals("}")) { // find start of map declaration int startBracket = templatePos - 1; while (true) { final Object tokenPos = tokens.get(startBracket); if (tokenPos.equals("{")) { break; } if (startBracket == 0) return ParseResult.fail(); if (tokenPos.equals("{") || tokenPos.equals("[") || tokenPos.equals("]")) return ParseResult.fail(); startBracket--; } int pos = startBracket; final Map<Object, Object> map = Maps.newLinkedHashMap(); Object tail = null; if (startBracket < templatePos - 1) { // Handle empty map ie. {} while (pos < templatePos) { final Object posToken = tokens.get(pos); if (posToken.equals("{") || posToken.equals(",")) { final Object key = tokens.get(pos + 1); if (key instanceof String && reserved.contains(key)) return ParseResult.fail(); if (!tokens.get(pos + 2).equals(":")) return ParseResult.fail(); final Object value = tokens.get(pos + 3); if (value instanceof String && reserved.contains(value)) return ParseResult.fail(); map.put(key, value); } else if (posToken.equals("...") && pos == templatePos - 2) { tail = tokens.get(pos + 1); } else return ParseResult.fail(); pos += 4; } } if (tail == null) return ParseResult.success(tokens.replaceWithTokens(startBracket, templatePos + 1, map)); else if (tail instanceof Map) { map.putAll((Map<Object, Object>) tail); return ParseResult.success(tokens.replaceWithTokens(startBracket, templatePos + 1, map)); } else return ParseResult.success(tokens.replaceWithTokens(startBracket, templatePos + 1, new MapWithTail(map, tail))); } else if(tokens.get(templatePos).equals("ans")) { return ParseResult.success(tokens.replaceWithTokens(templatePos, templatePos, new Integer(1))); } return ParseResult.fail(); } @Override public String toString() { return ""; } @Override public int hashCode() { return "BracketsParser".hashCode(); } @Override public boolean equals(final Object obj) { return obj instanceof PreParser; } public static class ListWithTail implements Serializable { private static final long serialVersionUID = -529889868305161556L; public final List<Object> list; public final Object tail; public ListWithTail(final List<Object> list, final Object tail) { this.list = list; this.tail = tail; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ListWithTail [list="); builder.append(list); builder.append(", tail="); builder.append(tail); builder.append("]"); return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((list == null) ? 0 : list.hashCode()); result = prime * result + ((tail == null) ? 0 : tail.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof ListWithTail)) return false; final ListWithTail other = (ListWithTail) obj; if (list == null) { if (other.list != null) return false; } else if (!list.equals(other.list)) return false; if (tail == null) { if (other.tail != null) return false; } else if (!tail.equals(other.tail)) return false; return true; } } public static class MapWithTail implements Serializable { private static final long serialVersionUID = -3556614307132116724L; public final Map<Object, Object> map; public final Object tail; public MapWithTail(final Map<Object, Object> map, final Object tail) { this.map = map; this.tail = tail; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("MapWithTail [map="); builder.append(map); builder.append(", tail="); builder.append(tail); builder.append("]"); return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((map == null) ? 0 : map.hashCode()); result = prime * result + ((tail == null) ? 0 : tail.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof MapWithTail)) return false; final MapWithTail other = (MapWithTail) obj; if (map == null) { if (other.map != null) return false; } else if (!map.equals(other.map)) return false; if (tail == null) { if (other.tail != null) return false; } else if (!tail.equals(other.tail)) return false; return true; } } }