package slangc.model; import slang.data.ComparisonOp; import slang.data.List; import slang.data.Map; import slangc.api.Reporter; import slangc.parser.Branch; import slangc.parser.Node; import slangc.parser.NodeType; import slangc.parser.Token; public class PackageModel { private SystemModel system; private String name; private Map types = new Map(); private Map sources = new Map(); public PackageModel(SystemModel system, String name) { this.system = system; this.name = name; if (name == null) { throw new Error("Package name is null"); } } public SystemModel getSystem() { return system; } public String getName() { return name; } public boolean hasType(String name) { return types.hasKey(name); } public TypeModel getType(String name) { if (hasType(name)) { return types.get(name); } else { throw new Error("Internal error/TODO: Type '" + name + "' doesn't exist in package '" + getName() + "'"); } } public String[] getTypeNames() { String[] result = new String[types.count()]; List list = new List(); list.appendList(types.keys()); list.sort(String.getDefaultComparisonOp()); return list.arrayCopy(); } public boolean hasSource(String name) { return sources.hasKey(name); } public SourceModel getSource(String name) { if (hasSource(name)) { return sources.get(name); } else { throw new Error("Internal error/TODO: Source '" + name + "' doesn't exist in package '" + getName() + "'"); } } public String[] getSourceNames() { String[] result = new String[sources.count()]; List list = new List(); list.appendList(sources.keys()); list.sort(String.getDefaultComparisonOp()); return list.arrayCopy(); } public int countErrors() { int total = 0; SourceModel[] srcs = sources.values().arrayCopy(); for(int i = 0; i < srcs.length; i++) { total += srcs[i].countErrors(); } return total; } public int deleteErrors() { int total = 0; SourceModel[] srcs = sources.values().arrayCopy(); for(int i = 0; i < srcs.length; i++) { total += srcs[i].deleteErrors(); } return total; } private void addInnerAndUntitledTypes(UserTypeModel model) { recursiveAddInnerAndUntitledTypes(model, model.getParsed().getSubnode(model.getParsed().countSubnodes() - 2), null); } private void recursiveAddInnerAndUntitledTypes(UserTypeModel model, Node subnode, InnerTypeScope.SourceScope withinScope) { if (subnode instanceof Branch) { Branch b = (Branch) subnode; if (b.getNodeType() == NodeType.CLASS_DECLARATION || b.getNodeType() == NodeType.INTERFACE_DECLARATION || b.getNodeType() == NodeType.ENUM_DECLARATION) { Token name = (Token)((Branch)b.getSubnode(2)).getSubnode(0); String shortname = name.getSnippet().getSource(); if (b.getNodeType() == NodeType.CLASS_DECLARATION || b.getNodeType() == NodeType.INTERFACE_DECLARATION) { shortname += genericTemplateString(b.getSubnode(3)); } TypeLevel tl = TypeLevel.INNER; if (withinScope != null) { shortname = shortname + "#" + model.nextUntitledNumber(); tl = TypeLevel.INNER_INNER; } String namestr = model.getOuterName() + ":" + shortname; if (types.hasKey(namestr)) { throw new Error("Internal error/TODO: Same type '" + namestr + "' (in package '" + getName() + "') defined twice"); } UserTypeModel inner = new UserTypeModel(this, shortname, model.getSource(), b, tl, withinScope == null ? ((InnerTypeScope.SourceScope)model) : withinScope); NodeData.of(b).setTypeDefinition(inner); types.set(namestr, inner); inner.setupTranslations(); addInnerAndUntitledTypes(inner); } else if (b.getNodeType() == NodeType.NEW_CLASS_EXPRESSION) { String shortname = "Untitled#" + model.nextUntitledNumber(); String namestr = model.getOuterName() + ":" + shortname; if (types.hasKey(namestr)) { throw new Error("Internal error/TODO: Same type '" + namestr + "' (in package '" + getName() + "') defined twice"); } UserTypeModel inner = new UserTypeModel(this, shortname, model.getSource(), b, TypeLevel.UNTITLED, withinScope == null ? ((InnerTypeScope.SourceScope) model) : withinScope); NodeData.of(b).setTypeDefinition(inner); types.set(namestr, inner); addInnerAndUntitledTypes(inner); } else { if (b.getNodeType() == NodeType.BLOCK_STATEMENT || b.getNodeType() == NodeType.SWITCH_MEMBERS) { if (withinScope == null) { withinScope = model.innerScopeForSource(b); } else { withinScope = withinScope.innerScopeForSource(b); } } //System.err.println("Within method? " + withinScope); for (int i = 0; i < b.countSubnodes(); i++) { recursiveAddInnerAndUntitledTypes(model, b.getSubnode(i), withinScope); } } } } private void recursiveInitialiseNodeData(NodeData outer, Node subnode) { assert(subnode.userdata == null); NodeData d = new NodeData(outer, subnode, system); subnode.userdata = d; if (subnode instanceof Branch) { Branch b = (Branch) subnode; for (int i = 0; i < b.countSubnodes(); i++) { recursiveInitialiseNodeData(d, b.getSubnode(i)); } } } public SyntheticTypeModel addSyntheticType(SyntheticTypeModel m) { if (types.hasKey(m.getOuterName())) { throw new Error("Internal error/TODO: Synthetic type '" + m.fullName() + "' collides with the name of an existing type"); } types.set(m.getOuterName(), m); return m; } public UserTypeModel addInstantiatedType(UserTypeModel m) { if (types.hasKey(m.getOuterName())) { throw new Error("Internal error/TODO: Instantiated type '" + m.fullName() + "' collides with the name of an existing type"); } types.set(m.getOuterName(), m); return m; } public SourceModel addSource(String filename, Branch parsed, boolean included) { if (sources.hasKey(filename)) { throw new Error("Internal error/TODO: Same file '" + filename + "' included twice"); } recursiveInitialiseNodeData(null, parsed); SourceModel s = new SourceModel(this, filename, parsed, included); Branch u = s.getTypesBranch(); int count = u.countSubnodes(); for (int i = 0; i < count; i++) { if (u.getSubnode(i).getNodeType() == NodeType.SEMICOLON) { continue; // Skip this } //System.err.println("Adding a " + u.getSubnode(i).getNodeType()); Token name; if (u.getSubnode(i).getNodeType() == NodeType.ATTRIBUTE_DECLARATION) { name = (Token)((Branch)((Branch)u.getSubnode(i)).getSubnode(3)).getSubnode(0); } else { name = (Token)((Branch)((Branch)u.getSubnode(i)).getSubnode(2)).getSubnode(0); } String namestr = name.getSnippet().getSource(); if (u.getSubnode(i).getNodeType() == NodeType.CLASS_DECLARATION || u.getSubnode(i).getNodeType() == NodeType.INTERFACE_DECLARATION) { String genstr = genericTemplateString(((Branch)((Branch)u.getSubnode(i)).getSubnode(3))); namestr += genstr; } if (types.hasKey(namestr)) { throw new Error("Internal error/TODO: Same type '" + namestr + "' (in package '" + getName() + "') defined twice"); } UserTypeModel model = new UserTypeModel(this, namestr, s, (Branch)u.getSubnode(i), TypeLevel.OUTER, null); NodeData.of(u.getSubnode(i)).setTypeDefinition(model); types.set(namestr, model); model.setupTranslations(); addInnerAndUntitledTypes(model); /* Branch b = (Branch)((Branch)u.getSubnode(i)).getSubnode(2); System.out.println("Got a '" + b.getNodeType() + "'"); int count2 = b.countSubnodes(); for (int i2 = 0; i2 < count2; i2++) { Node b2 = b.getSubnode(i2); System.out.println("> Got a '" + b2.getNodeType() + "': " + b2);//(b2 instanceof Token ? ((Token)b2).)); }*/ } sources.set(filename, s); return s; } public static int templateArgumentCount(Node templatepart) { if (templatepart.getNodeType() == NodeType.NO_GENERIC_DECLARATIONS) { return 0; } else { Branch argpart = (Branch) ((Branch)templatepart).getSubnode(1); int count = 0; for (int i = 0; i < argpart.countSubnodes(); i++) { if (argpart.getSubnode(i).getNodeType() != NodeType.COMMA) { count++; } } return count; } } public static String genericTemplateString(Node templatepart) { int c = templateArgumentCount(templatepart); if (c == 0) { return ""; } else { String result = ""; for (int i = 0; i < c; i++) { if (i != 0) { result += ","; } result += "?"; } return "<" + result + ">"; } } public int expand() { int total = 0; TypeModel[] typs = types.values().arrayCopy(); for(int i = 0; i < typs.length; i++) { total += typs[i].expand(); } return total; } public void dump(Reporter reporter, String indent, String incr) { reporter.note("DUMP", indent + "> Package '" + getName() + "'"); String[] typs = getTypeNames(); for (int i = 0; i < typs.length; i++) { getType(typs[i]).dump(reporter, indent + incr, incr); } } public int resolveTypes() { int total = 0; TypeModel[] typs = types.values().arrayCopy(); for(int i = 0; i < typs.length; i++) { total += typs[i].resolveTypes(); } return total; } public int resolveExpressions(boolean includingReferences) { int total = 0; TypeModel[] typs = types.values().arrayCopy(); for(int i = 0; i < typs.length; i++) { if (includingReferences || !typs[i].isReferenceOnly()) { total += typs[i].resolveExpressions(); } } return total; } }