package slangc.api; import slang.data.Mappable; import slang.streams.SyncOutput; import slang.streams.SyncInput; import slangc.parser.Annotation; import slangc.parser.AnnotationType; import slangc.parser.Branch; import slangc.parser.LocationAnnotation; import slangc.parser.ErrorAnnotation; import slangc.parser.NoteAnnotation; import slangc.parser.LocationType; import slangc.parser.Node; import slangc.parser.Parse; import slangc.parser.Scan; import slangc.parser.Source; import slangc.parser.Token; import slangc.parser.WarningType; /** * Holds all of the information for a unit (sourceFile file) and abstracts away the semantics of scanning/parsing/checking them. * @author Zak * */ public class Unit { private CompilerWorld world; private Source source; private Scan scan = null; private Branch parse = null; public Unit(CompilerWorld world, Source source) { this.world = world; this.source = source; } public CompilerWorld getWorld() { return world; } public Source getSource() { return source; } public String getFilename() { return getSource().getFilename(); } int hashCache = -1; public int getCacheHash() { if (hashCache < 0) { hashCache = Mappable.nonNegativeHashOf(getSource().getString(0, getSource().getIndexLength())); } return hashCache; } public boolean updateCache() { try { Branch b = parse(); // TODO: Check if needs updating here SyncOutput o = world.getCacheManager().getOutput(getCacheName()); if (o == null) { return false; } b.dumpBinary(o); o.close(); return true; } catch (duck d) { Log.line("WARNING: Updating cache failed with error"); return false; } } public String getCacheName() { return "." + getCacheHash() + "." + getSource().getIndexLength() + ".cache"; } public String getPackageName() { Branch pkg = (Branch) parse().getSubnode(0); if (pkg == null) { return "defpkg"; //throw new Error("Invalid package format"); } pkg = (Branch) pkg.getSubnode(1); if (pkg == null) { throw new Error("Invalid package format"); } String name = ""; for (int i = 0; i < pkg.countSubnodes(); i++) { Branch sub = (Branch) pkg.getSubnode(i); if (sub == null) { throw new Error("Invalid package format"); } Token token = (Token) sub.getSubnode(0); if (token == null) { throw new Error("Invalid package format"); } name += token.getSnippet().getSource(); } return name; } public Scan scan() { if (scan == null) { scan = new Scan(world.getLanguage(), source); //scan.scanPedantic(); scan.scanQuick(); } return scan; } public Branch loadCache(SyncInput inp) { scan = new Scan(world.getLanguage(), source); parse = (Branch) Node.loadBinary(scan, inp); inp.close(); _wasCached = true; return parse; } private boolean _wasCached = false; public boolean wasCached() { return _wasCached; } public Branch parse() { if (parse == null) { SyncInput cacheIn = world.getCacheManager().getInput(getCacheName()); if (cacheIn != null) { //Log.line("Loading from cache..."); return loadCache(cacheIn); } //Log.line("Not cached..."); Scan s = scan(); Parse p = new Parse(); parse = (Branch) p.parseUnit(s); } return parse; } private Token checkRecursively(Branch b, Token p) { if (!Node.mightHaveErrors && !Node.mightHaveWarnings) { return p; } if (p != null && b.countAnnotations(AnnotationType.LOCATION) == 0) { b.annotate(new LocationAnnotation(LocationType.AFTER, p)); } for (int i = 0; i < b.countSubnodes(); i++) { Node n = b.getSubnode(i); if (n instanceof Token) { p = (Token) n; p.annotate(new LocationAnnotation(LocationType.AROUND, p)); } else if (n == null) { b.annotate(WarningType.INTERNAL_WARNING, "Had a null member"); } else { p = checkRecursively((Branch) n, p); } } if (p != null && b.countAnnotations(AnnotationType.LOCATION) == 0) { b.annotate(new LocationAnnotation(LocationType.AROUND, p)); } return p; } public int checkErrors(int verbosity) { int nerrors = 0; Branch p = parse(); checkRecursively(p, null); //getWorld().getReporter().note("CHECKING", getFilename()); //getWorld().getReporter().note("CHECKING", "File '" + getFilename() + "' reports package as '" + getPackageName() + "'"); nerrors += p.countErrorsRecursively(); if (nerrors != 0) { getWorld().getReporter().note("ERRORS", "Found " + nerrors + " errors in '" + getFilename() + "', dumping AST:"); dump(getWorld().getReporter(), parse(), verbosity); } return nerrors; } private static void dump(Reporter out, Node n, int verbosity) { dump(out, n, "", verbosity < 5 ? "" : " ", verbosity); } private static void dump(Reporter out, Node n, String curindent, String subindent, int verbosity) { if (n instanceof Branch) { dumpBranch(out, (Branch) n, curindent, subindent, verbosity); } else if (n instanceof Token) { dumpToken(out, (Token) n, curindent, subindent, verbosity); } else if (n instanceof Annotation) { dumpAnnotation(out, (Annotation) n, curindent, subindent, verbosity); } else if (n == null) { out.note("DUMP", curindent + "-! UH-OH !- null"); } else { out.note("DUMP", curindent + "-! TO-DO !- code to dump " + n.toString()); } } private static void dumpAnnotations(Reporter out, Node n, String curindent, String subindent, int verbosity) { for (int i = 0; i < n.countAnnotations(); i++) { dump(out, n.getAnnotation(i), curindent + subindent, subindent, verbosity); } } private static void dumpBranch(Reporter out, Branch b, String curindent, String subindent, int verbosity) { if (verbosity < 10 && b.countErrorsRecursively() == 0) { if (verbosity > 2) out.note("DUMP", curindent + "-> " + b.getNodeType().name() + " (...)"); return; } if (verbosity >= 5 || b.countErrorsHere() > 0) { if (verbosity > 2) out.note("DUMP", curindent + "-> " + b.getNodeType()); dumpAnnotations(out, b, curindent, subindent, verbosity); } for (int i = 0; i < b.countSubnodes(); i++) { dump(out, b.getSubnode(i), curindent + subindent, subindent, verbosity); } } private static void dumpToken(Reporter out, Token t, String curindent, String subindent, int verbosity) { if (verbosity >= 5) { out.note("DUMP", curindent + "-. " + t); dumpAnnotations(out, t, curindent, subindent, verbosity); } } private static void dumpAnnotation(Reporter out, Annotation a, String curindent, String subindent, int verbosity) { if (a instanceof ErrorAnnotation) { out.note("DUMP", curindent + "-! " + ((ErrorAnnotation) a).getErrorType().name()); } else if (a instanceof NoteAnnotation) { out.note("DUMP", curindent + "-+ " + ((NoteAnnotation) a).getText()); } else if (a instanceof LocationAnnotation) { out.note("DUMP", curindent + "-- " + ((LocationAnnotation) a).niceString()); } else { out.note("DUMP", curindent + "-+ " + a.toString()); } dumpAnnotations(out, a, curindent, subindent, verbosity); } }