package edu.ucsd.arcum.interpreter.query; import static edu.ucsd.arcum.interpreter.ast.ASTUtil.IDENTITY_ACCESSOR; import static edu.ucsd.arcum.interpreter.ast.ASTUtil.checkNames; import static edu.ucsd.arcum.interpreter.ast.ASTUtil.extractNames; import static edu.ucsd.arcum.util.Subset.subset; import static java.util.Collections.synchronizedMap; import java.io.PrintStream; import java.util.*; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import edu.ucsd.arcum.ArcumPlugin; import edu.ucsd.arcum.builders.ParseArcumCodeOperation; import edu.ucsd.arcum.exceptions.ArcumError; import edu.ucsd.arcum.interpreter.ast.*; import edu.ucsd.arcum.interpreter.ast.ASTUtil.NameAccessor; import edu.ucsd.arcum.interpreter.parser.FragmentParser; import edu.ucsd.arcum.interpreter.transformation.ResolvedConceptMapEntry; import edu.ucsd.arcum.util.Subset; // A symbol table for all Arcum code constructs: Options, Option Interfaces, // and Maps. A map declaration (that is, one specific instantiation of an Option // with parameters) can be looked up to find its equivalent OptionMatchTable. public class ArcumDeclarationTable { private static Map<IProject, ArcumDeclarationTable> arcumDeclarationTables; static { arcumDeclarationTables = synchronizedMap(new WeakHashMap<IProject, ArcumDeclarationTable>()); } public static ArcumDeclarationTable lookupSymbolTable(final IProject project) { ArcumDeclarationTable declarationTable = arcumDeclarationTables.get(project); if (declarationTable == null) { ParseArcumCodeOperation.parseArcumCodeWithDialog(project); } // this counts specifically on the ArcumBuilder calling newSymbolTable declarationTable = arcumDeclarationTables.get(project); return declarationTable; } // Creates a new symbol table and associates it with the given project public static ArcumDeclarationTable newSymbolTable(IProject project) { ArcumDeclarationTable symbTab = new ArcumDeclarationTable(project); arcumDeclarationTables.put(project, symbTab); return symbTab; } // The "_" is a special variable that matches anything, a "don't care"; this // can only be used in patterns for pure matching: cannot be used in patterns // for code generation public static final String SPECIAL_ANY_VARIABLE = "_"; private Map<String, TopLevelConstruct> allDeclarations; private IProject project; // valid after makeEntityTables is called private Map<String, OptionMatchTable> entityTableLookup; private EntityDataBase entityDataBase; private ArcumDeclarationTable(IProject project) { this.project = project; this.allDeclarations = new LinkedHashMap<String, TopLevelConstruct>(); this.entityTableLookup = new HashMap<String, OptionMatchTable>(); this.entityDataBase = null; // parseBuiltinConcepts(); } public <T extends TopLevelConstruct> T conditionalCreate(String name, ConstructorThunk<T> ctor) { if (allDeclarations.containsKey(name)) { ArcumError.fatalError("A declaration named " + name + " already exists%n"); } T result = ctor.create(); allDeclarations.put(name, result); return result; } public void typeCheck() { Collection<TopLevelConstruct> decls; decls = allDeclarations.values(); List<String> allGlobalNames = new ArrayList<String>(); extractNames(allGlobalNames, decls, new NameAccessor<TopLevelConstruct>() { public String getName(TopLevelConstruct decl) { return decl.getName(); } }); checkNames(allGlobalNames, IDENTITY_ACCESSOR); for (OptionInterface optionInterface : subset(decls, OptionInterface.class)) { optionInterface.doTypeCheck(this); } for (Option option : subset(decls, Option.class)) { option.doTypeCheck(this); } Subset<RequireMap> requireMaps = subset(decls, RequireMap.class); for (RequireMap requireMap : requireMaps) { requireMap.doTypeCheck(this); } if (false && ArcumPlugin.DEBUG) { printDeclarations(System.out); } } public void printDeclarations(PrintStream out) { for (TopLevelConstruct decl : allDeclarations.values()) { out.printf("%s%n", decl); } } public <T> T lookup(String name, Class<T> clazz) { TopLevelConstruct decl = allDeclarations.get(name); if (decl != null && clazz.isAssignableFrom(decl.getClass())) { return clazz.cast(decl); } else { return null; } } public List<OptionMatchTable> makeEntityTables() throws CoreException { this.entityTableLookup.clear(); List<OptionMatchTable> result = new ArrayList<OptionMatchTable>(); for (ResolvedConceptMapEntry binding : getAllResolvedBindings()) { OptionMatchTable entities = makeEntityTable(binding); result.add(entities); } return result; } // parses built-in concepts, done once per project // private void parseBuiltinConcepts() { // if (ArcumPlugin.DEBUG) { // System.out.printf("Reading Arcum built-in files...%n"); // } // for (String filePath : BUILT_IN_FILES) { // String source = FileUtil.readBundledFile(filePath); // String[] strings = filePath.split("/"); // String name = strings[strings.length - 1]; // ArcumSourceFileParser parser; // parser = new ArcumSourceFileParser(source, null, name, project); // parser.parseArcumSource(this); // } // } public List<Option> getImplementingOptions(TopLevelConstruct optionInterface) { List<Option> result = new ArrayList<Option>(); for (TopLevelConstruct decl : allDeclarations.values()) { if (decl instanceof Option) { Option option = (Option)decl; if (option.getOptionInterface() == optionInterface) { result.add(option); } } } return result; } public List<RequireMap> getAllMaps() { List<RequireMap> result = new ArrayList<RequireMap>(); for (TopLevelConstruct decl : allDeclarations.values()) { if (decl instanceof RequireMap) { result.add((RequireMap)decl); } } return result; } public OptionMatchTable constructEntityTable(String optionArgs) throws CoreException { OptionMatchTable result = entityTableLookup.get(optionArgs); if (result == null) { for (ResolvedConceptMapEntry binding : getAllResolvedBindings()) { String argumentSourceText = binding.getArgumentSourceText(); if (optionArgs.equals(argumentSourceText)) { OptionMatchTable entities = makeEntityTable(binding); return entities; } } } return result; } private List<ResolvedConceptMapEntry> getAllResolvedBindings() { List<ResolvedConceptMapEntry> result = new ArrayList<ResolvedConceptMapEntry>(); for (RequireMap map : subset(allDeclarations.values(), RequireMap.class)) { for (ResolvedConceptMapEntry binding : map.getResolvedBindings()) { result.add(binding); } } return result; } private OptionMatchTable makeEntityTable(ResolvedConceptMapEntry binding) throws CoreException { if (entityDataBase == null) { entityDataBase = new EntityDataBase(project); entityDataBase.populate(); } OptionMatchTable entities = new OptionMatchTable(this, binding); entities.matchAllEntities(entityDataBase); boolean passed = entities.checkExtraDefinitionConditions(entityDataBase); if (passed) { String argumentSourceText = binding.getArgumentSourceText(); entityTableLookup.put(argumentSourceText, entities); } else { ArcumError.stop(); } return entities; } public void disposeEntityTable(String optionArgs) { entityTableLookup.remove(optionArgs); } public FragmentParser newParser(TopLevelConstruct context) { return new FragmentParser(context.getImports(), project); } public void rematchAllCode() { ParseArcumCodeOperation.rematchAllCodeWithDialog(project); } public EntityDataBase getEntityDataBase() { return entityDataBase; } }