/******************************************************************************* * Copyright (c) 2009-2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI * * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl * * Mark Hills - Mark.Hills@cwi.nl (CWI) * * Arnold Lankamp - Arnold.Lankamp@cwi.nl *******************************************************************************/ package org.rascalmpl.semantics.dynamic; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.rascalmpl.ast.ImportedModule; import org.rascalmpl.ast.LocationLiteral; import org.rascalmpl.ast.Module; import org.rascalmpl.ast.Name; import org.rascalmpl.ast.QualifiedName; import org.rascalmpl.ast.SyntaxDefinition; import org.rascalmpl.ast.Tag; import org.rascalmpl.ast.TagString.Lexical; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.asserts.Ambiguous; import org.rascalmpl.interpreter.asserts.ImplementationError; import org.rascalmpl.interpreter.control_exceptions.Throw; import org.rascalmpl.interpreter.env.Environment; import org.rascalmpl.interpreter.env.GlobalEnvironment; import org.rascalmpl.interpreter.env.ModuleEnvironment; import org.rascalmpl.interpreter.result.ICallableValue; import org.rascalmpl.interpreter.result.Result; import org.rascalmpl.interpreter.result.ResultFactory; import org.rascalmpl.interpreter.result.SourceLocationResult; import org.rascalmpl.interpreter.staticErrors.ModuleImport; import org.rascalmpl.interpreter.staticErrors.ModuleNameMismatch; import org.rascalmpl.interpreter.staticErrors.StaticError; import org.rascalmpl.interpreter.staticErrors.SyntaxError; import org.rascalmpl.interpreter.staticErrors.UndeclaredModule; import org.rascalmpl.interpreter.staticErrors.UndeclaredModuleProvider; import org.rascalmpl.interpreter.utils.Modules; import org.rascalmpl.interpreter.utils.Names; import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory; import org.rascalmpl.library.lang.rascal.syntax.RascalParser; import org.rascalmpl.parser.ASTBuilder; import org.rascalmpl.parser.Parser; import org.rascalmpl.parser.ParserGenerator; import org.rascalmpl.parser.gtd.IGTD; import org.rascalmpl.parser.gtd.exception.ParseError; import org.rascalmpl.parser.gtd.exception.UndeclaredNonTerminalException; import org.rascalmpl.parser.gtd.result.action.IActionExecutor; import org.rascalmpl.parser.gtd.result.out.DefaultNodeFlattener; import org.rascalmpl.parser.uptr.UPTRNodeFactory; import org.rascalmpl.parser.uptr.action.NoActionExecutor; import org.rascalmpl.uri.URIResolverRegistry; import org.rascalmpl.uri.URIUtil; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.IList; import org.rascalmpl.value.IListWriter; import org.rascalmpl.value.IMap; import org.rascalmpl.value.INode; import org.rascalmpl.value.ISet; import org.rascalmpl.value.ISetWriter; import org.rascalmpl.value.ISourceLocation; import org.rascalmpl.value.IString; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.type.Type; import org.rascalmpl.values.uptr.ITree; import org.rascalmpl.values.uptr.ProductionAdapter; import org.rascalmpl.values.uptr.RascalValueFactory; import org.rascalmpl.values.uptr.SymbolAdapter; import org.rascalmpl.values.uptr.TreeAdapter; import org.rascalmpl.values.uptr.visitors.IdentityTreeVisitor; public abstract class Import { static public class External extends org.rascalmpl.ast.Import.External { public External(ISourceLocation src, IConstructor node, QualifiedName name, LocationLiteral at) { super(src, node, name, at); } @Override public Result<IValue> interpret(IEvaluator<Result<IValue>> eval) { // Compute the URI location, which contains the scheme (and other info we need later) ISourceLocation sl = (ISourceLocation)getAt().interpret(eval).getValue(); // If we have a resource scheme, given as resource scheme + standard scheme, we // extract that out, e.g., jdbctable+mysql would give a resource scheme of // jdbctable and a standard URI scheme of mysql. If we do not have a separate // resource scheme, we just use the specified scheme, e.g., sdf would give // a resource scheme of sdf and a URI scheme of sdf. String resourceScheme = sl.getScheme(); if (resourceScheme.contains("+")) { String uriScheme = resourceScheme.substring(resourceScheme.indexOf("+")+1); resourceScheme = resourceScheme.substring(0,resourceScheme.indexOf("+")); try { sl = URIUtil.changeScheme(sl, uriScheme); } catch (URISyntaxException e) { throw RuntimeExceptionFactory.malformedURI(sl.toString().substring(sl.toString().indexOf("+")+1), null, null); } } String moduleName = Names.fullName(this.getName()); IString mn = VF.string(moduleName); // Using the scheme, get back the correct importer ICallableValue importer = getImporter(resourceScheme, eval.getCurrentEnvt()); if (importer != null) { Type[] argTypes = new org.rascalmpl.value.type.Type[] {TF.stringType(), TF.sourceLocationType()}; IValue[] argValues = new IValue[] { mn, sl }; // Invoke the importer, which should generate the text of the module that we need // to actually import. IValue module = importer.call(argTypes, argValues, null).getValue(); String moduleText = module.getType().isString() ? ((IString) module).getValue() : TreeAdapter.yield((IConstructor) module); moduleText = "@generated\n" + moduleText; try { URIResolverRegistry reg = URIResolverRegistry.getInstance(); String moduleEnvName = eval.getCurrentModuleEnvironment().getName(); ISourceLocation ur = null; if (moduleEnvName.equals(ModuleEnvironment.SHELL_MODULE)) { ur = URIUtil.rootLocation("cwd"); } else { ur = eval.getRascalResolver().getRootForModule(moduleEnvName); } Result<?> loc = new SourceLocationResult(TF.sourceLocationType(), ur, eval); String modulePath = moduleName.replaceAll("::", "/"); loc = loc.add(ResultFactory.makeResult(TF.stringType(), VF.string(modulePath), eval)); loc = loc.fieldUpdate("extension", ResultFactory.makeResult(TF.stringType(), VF.string(".rsc"), eval), eval.getCurrentEnvt().getStore()); OutputStream outputStream; try { outputStream = reg.getOutputStream(((ISourceLocation) loc.getValue()), false); } catch (IOException e) { outputStream = reg.getOutputStream(URIUtil.rootLocation("cwd"), false); } if (outputStream == null) { outputStream = reg.getOutputStream(URIUtil.rootLocation("cwd"), false); } BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream)); writer.write(moduleText); writer.close(); } catch (IOException e) { throw RuntimeExceptionFactory.moduleNotFound(mn, eval.getCurrentAST(), eval.getStackTrace()); } importModule(Names.fullName(this.getName()), getLocation(), eval); return ResultFactory.nothing(); } else { throw new UndeclaredModuleProvider(resourceScheme, eval.getCurrentAST()); } } private ICallableValue getImporter(String s, Environment currentEnvt) { return currentEnvt.getHeap().getResourceImporter(s); } } static public class Extend extends org.rascalmpl.ast.Import.Extend { public Extend(ISourceLocation src, IConstructor node, ImportedModule module) { super(src, node, module); } @Override public Result<IValue> interpret(IEvaluator<Result<IValue>> eval) { String name = Names.fullName(this.getModule().getName()); extendCurrentModule(this.getLocation(), name, eval); return org.rascalmpl.interpreter.result.ResultFactory.nothing(); } } static public class Default extends org.rascalmpl.ast.Import.Default { public Default(ISourceLocation __param1, IConstructor tree, ImportedModule __param2) { super(__param1, tree, __param2); } @Override public Result<IValue> interpret(IEvaluator<Result<IValue>> eval) { try { importModule(Names.fullName(getModule().getName()), getLocation(), eval); } finally { eval.setCurrentAST(this); } return ResultFactory.nothing(); } } static public class Syntax extends org.rascalmpl.ast.Import.Syntax { public Syntax(ISourceLocation __param1, IConstructor tree, SyntaxDefinition __param2) { super(__param1, tree, __param2); } @Override public Result<IValue> interpret(IEvaluator<Result<IValue>> eval) { String parseTreeModName = "ParseTree"; if (!eval.__getHeap().existsModule(parseTreeModName)) { loadModule(getLocation(), parseTreeModName, eval); } addImportToCurrentModule(getLocation(), parseTreeModName, eval); getSyntax().interpret(eval); return nothing(); } } public static void importModule(String name, ISourceLocation src, IEvaluator<Result<IValue>> eval) { GlobalEnvironment heap = eval.__getHeap(); if (!heap.existsModule(name)) { // deal with a fresh module that needs initialization heap.addModule(new ModuleEnvironment(name, heap)); loadModule(src, name, eval); } else if (eval.getCurrentEnvt() == eval.__getRootScope()) { // in the root scope we treat an import as a "reload" heap.resetModule(name); loadModule(src, name, eval); } addImportToCurrentModule(src, name, eval); if (heap.getModule(name).isDeprecated()) { eval.getStdErr().println(src + ":" + name + " is deprecated, " + heap.getModule(name).getDeprecatedMessage()); } return; } public static void extendCurrentModule(ISourceLocation x, String name, IEvaluator<Result<IValue>> eval) { GlobalEnvironment heap = eval.__getHeap(); ModuleEnvironment other = heap.getModule(name); try { if (other == null) { // deal with a fresh module that needs initialization heap.addModule(new ModuleEnvironment(name, heap)); other = loadModule(x, name, eval); } else if (eval.getCurrentEnvt() == eval.__getRootScope()) { // in the root scope we treat an extend as a "reload" heap.resetModule(name); other = loadModule(x, name, eval); } // now simply extend the current module eval.getCurrentModuleEnvironment().extend(other); //heap.getModule(name)); } catch (Throwable e) { // extending a module is robust against broken modules if (eval.isInterrupted()) { throw e; } } } public static ModuleEnvironment loadModule(ISourceLocation x, String name, IEvaluator<Result<IValue>> eval) { GlobalEnvironment heap = eval.getHeap(); ModuleEnvironment env = heap.getModule(name); if (env == null) { env = new ModuleEnvironment(name, heap); heap.addModule(env); } try { ISourceLocation uri = eval.getRascalResolver().resolveModule(name); if (uri == null) { throw new ModuleImport(name, "can not find in search path", x); } Module module = buildModule(uri, env, eval); if (isDeprecated(module)) { eval.getStdErr().println("WARNING: deprecated module " + name + ":" + getDeprecatedMessage(module)); } if (module != null) { String internalName = org.rascalmpl.semantics.dynamic.Module.getModuleName(module); if (!internalName.equals(name)) { throw new ModuleNameMismatch(internalName, name, x); } heap.setModuleURI(name, module.getLocation().getURI()); module.interpret(eval); return env; } } catch (SyntaxError e) { heap.removeModule(env); eval.getEvaluator().warning("Could not load " + name, x); throw e; } catch (StaticError | Throw e) { heap.removeModule(env); eval.getEvaluator().warning("Could not load " + name, x); throw e; } catch (Throwable e) { heap.removeModule(env); eval.getEvaluator().warning("Could not load " + name, x); throw new ModuleImport(name, e.getMessage(), x); } heap.removeModule(env); throw new ImplementationError("Unexpected error while parsing module " + name + " and building an AST for it ", x); } private static boolean isDeprecated(Module preModule){ for (Tag tag : preModule.getHeader().getTags().getTags()) { if (((Name.Lexical) tag.getName()).getString().equals("deprecated")) { return true; } } return false; } private static String getDeprecatedMessage(Module preModule){ for (Tag tag : preModule.getHeader().getTags().getTags()) { if (((Name.Lexical) tag.getName()).getString().equals("deprecated")) { String contents = ((Lexical) tag.getContents()).getString(); return contents.substring(1, contents.length() -1); } } return ""; } private static Module buildModule(ISourceLocation uri, ModuleEnvironment env, IEvaluator<Result<IValue>> eval) throws IOException { ITree tree = eval.parseModuleAndFragments(eval, uri); return getBuilder().buildModule(tree); } private static ASTBuilder getBuilder() { return new ASTBuilder(); } private static void addImportToCurrentModule(ISourceLocation src, String name, IEvaluator<Result<IValue>> eval) { ModuleEnvironment module = eval.getHeap().getModule(name); if (module == null) { throw new UndeclaredModule(name, src); } ModuleEnvironment current = eval.getCurrentModuleEnvironment(); current.addImport(name, module); current.setSyntaxDefined(current.definesSyntax() || module.definesSyntax()); } public static ITree parseModuleAndFragments(char[] data, ISourceLocation location, IEvaluator<Result<IValue>> eval){ eval.__setInterrupt(false); IActionExecutor<ITree> actions = new NoActionExecutor(); try { eval.startJob("Parsing " + location, 10); eval.event("initial parse"); ITree tree = new RascalParser().parse(Parser.START_MODULE, location.getURI(), data, actions, new DefaultNodeFlattener<IConstructor, ITree, ISourceLocation>(), new UPTRNodeFactory(true)); if (TreeAdapter.isAmb(tree)) { // Ambiguity is dealt with elsewhere return tree; } ITree top = TreeAdapter.getStartTop(tree); String name = Modules.getName(top); // create the current module if it does not exist yet GlobalEnvironment heap = eval.getHeap(); ModuleEnvironment env = heap.getModule(name); if(env == null){ env = new ModuleEnvironment(name, heap); // do not add the module to the heap here. } env.setBootstrap(needBootstrapParser(data)); // make sure all the imported and extended modules are loaded // since they may provide additional syntax definitions\ Environment old = eval.getCurrentEnvt(); try { eval.setCurrentEnvt(env); env.setInitialized(true); eval.event("defining syntax"); eval.getCurrentModuleEnvironment().clearProductions(); ISet rules = Modules.getSyntax(top); for (IValue rule : rules) { evalImport(eval, (IConstructor) rule); } eval.event("importing modules"); ISet imports = Modules.getImports(top); for (IValue mod : imports) { evalImport(eval, (IConstructor) mod); } eval.event("extending modules"); ISet extend = Modules.getExtends(top); for (IValue mod : extend) { evalImport(eval, (IConstructor) mod); } eval.event("generating modules"); ISet externals = Modules.getExternals(top); for (IValue mod : externals) { evalImport(eval, (IConstructor) mod); } } finally { eval.setCurrentEnvt(old); } // parse the embedded concrete syntax fragments of the current module ITree result = tree; if (!eval.getHeap().isBootstrapper() && (needBootstrapParser(data) || (env.definesSyntax() && containsBackTick(data, 0)))) { eval.event("parsing concrete syntax"); result = parseFragments(eval, tree, location, env); } return result; } finally { eval.endJob(true); } } public static void evalImport(IEvaluator<Result<IValue>> eval, IConstructor mod) { org.rascalmpl.ast.Import imp = (org.rascalmpl.ast.Import) getBuilder().buildValue(mod); try { imp.interpret(eval); } catch (Throwable e) { eval.getEvaluator().warning(e.getMessage(), imp.getLocation()); // parsing the current module should be robust wrt errors in modules it depends on. if (eval.isInterrupted()) { throw e; } } } /** * This function will reconstruct a parse tree of a module, where all nested concrete syntax fragments * have been parsed and their original flat literal strings replaced by fully structured parse trees. * * @param module is a parse tree of a Rascal module containing flat concrete literals * @param parser is the parser to use for the concrete literals * @return parse tree of a module with structured concrete literals, or parse errors */ public static ITree parseFragments(final IEvaluator<Result<IValue>> eval, IConstructor module, final ISourceLocation location, final ModuleEnvironment env) { return (ITree) module.accept(new IdentityTreeVisitor<ImplementationError>() { final IValueFactory vf = eval.getValueFactory(); @Override public ITree visitTreeAppl(ITree tree) { IConstructor pattern = getConcretePattern(tree); if (pattern != null) { ITree parsedFragment = parseFragment(eval, env, (ITree) TreeAdapter.getArgs(tree).get(0), location); return TreeAdapter.setArgs(tree, vf.list(parsedFragment)); } else { IListWriter w = vf.listWriter(); IList args = TreeAdapter.getArgs(tree); for (IValue arg : args) { w.append(arg.accept(this)); } args = w.done(); return TreeAdapter.setArgs(tree, args); } } private IConstructor getConcretePattern(ITree tree) { String sort = TreeAdapter.getSortName(tree); if (sort.equals("Expression") || sort.equals("Pattern")) { String cons = TreeAdapter.getConstructorName(tree); if (cons.equals("concrete")) { return (IConstructor) TreeAdapter.getArgs(tree).get(0); } } return null; } @Override public ITree visitTreeAmb(ITree arg) { throw new Ambiguous(arg); } }); } @SuppressWarnings("unchecked") public static IGTD<IConstructor, ITree, ISourceLocation> getParser(IEvaluator<Result<IValue>> eval, ModuleEnvironment currentModule, ISourceLocation loc, boolean force) { if (currentModule.getBootstrap()) { return new RascalParser(); } if (currentModule.hasCachedParser()) { String className = currentModule.getCachedParser(); Class<?> clazz; for (ClassLoader cl: eval.getClassLoaders()) { try { clazz = cl.loadClass(className); return (IGTD<IConstructor, ITree, ISourceLocation>) clazz.newInstance(); } catch (ClassNotFoundException e) { continue; } catch (InstantiationException e) { throw new ImplementationError("could not instantiate " + className + " to valid IGTD parser", e); } catch (IllegalAccessException e) { throw new ImplementationError("not allowed to instantiate " + className + " to valid IGTD parser", e); } } throw new ImplementationError("class for cached parser " + className + " could not be found"); } ParserGenerator pg = eval.getParserGenerator(); IMap definitions = currentModule.getSyntaxDefinition(); Class<IGTD<IConstructor, ITree, ISourceLocation>> parser = eval.getHeap().getObjectParser(currentModule.getName(), definitions); if (parser == null || force) { String parserName = currentModule.getName(); parser = pg.getNewParser(eval, loc, parserName, definitions); eval.getHeap().storeObjectParser(parserName, definitions, parser); } try { return parser.newInstance(); } catch (InstantiationException e) { throw new ImplementationError(e.getMessage(), e); } catch (IllegalAccessException e) { throw new ImplementationError(e.getMessage(), e); } catch (ExceptionInInitializerError e) { throw new ImplementationError(e.getMessage(), e); } } private static ITree parseFragment(IEvaluator<Result<IValue>> eval, ModuleEnvironment env, ITree tree, ISourceLocation uri) { IConstructor symTree = TreeAdapter.getArg(tree, "symbol"); ITree lit = TreeAdapter.getArg(tree, "parts"); Map<String, ITree> antiquotes = new HashMap<>(); IGTD<IConstructor, ITree, ISourceLocation> parser = env.getBootstrap() ? new RascalParser() : getParser(eval, env, TreeAdapter.getLocation(tree), false); try { String parserMethodName = eval.getParserGenerator().getParserMethodName(symTree); DefaultNodeFlattener<IConstructor, ITree, ISourceLocation> converter = new DefaultNodeFlattener<IConstructor, ITree, ISourceLocation>(); UPTRNodeFactory nodeFactory = new UPTRNodeFactory(false); SortedMap<Integer,Integer> corrections = new TreeMap<>(); char[] input = replaceAntiQuotesByHoles(eval, lit, antiquotes, corrections); ITree fragment = (ITree) parser.parse(parserMethodName, uri.getURI(), input, converter, nodeFactory); // Adjust locations before replacing the holes back to the original anti-quotes, // since these anti-quotes already have the right location (!). fragment = (ITree) fragment.accept(new AdjustLocations(corrections, eval.getValueFactory())); fragment = replaceHolesByAntiQuotes(eval, fragment, antiquotes, corrections); IConstructor prod = TreeAdapter.getProduction(tree); IConstructor sym = ProductionAdapter.getDefined(prod); sym = SymbolAdapter.delabel(sym); IValueFactory vf = eval.getValueFactory(); prod = ProductionAdapter.setDefined(prod, vf.constructor(RascalValueFactory.Symbol_Label, vf.string("$parsed"), sym)); return TreeAdapter.setProduction(TreeAdapter.setArg(tree, "parts", fragment), prod); } catch (ParseError e) { ISourceLocation loc = TreeAdapter.getLocation(tree); ISourceLocation src = eval.getValueFactory().sourceLocation(loc.top(), loc.getOffset() + e.getOffset(), loc.getLength(), loc.getBeginLine() + e.getBeginLine() - 1, loc.getEndLine() + e.getEndLine() - 1, loc.getBeginColumn() + e.getBeginColumn(), loc.getBeginColumn() + e.getEndColumn()); eval.getMonitor().warning("parse error in concrete syntax", src); return (ITree) tree.asAnnotatable().setAnnotation("parseError", src); } catch (Ambiguous e) { ISourceLocation ambLocation = e.getLocation(); ISourceLocation loc = TreeAdapter.getLocation(tree); ISourceLocation src = ambLocation.hasOffsetLength() ? eval.getValueFactory().sourceLocation(loc.top(), loc.getOffset() + ambLocation.getOffset() , loc.getLength(), loc.getBeginLine() + ambLocation.getBeginLine() - 1, loc.getEndLine() + ambLocation.getEndLine() - 1, loc.getBeginColumn() + ambLocation.getBeginColumn(), loc.getBeginColumn() + ambLocation.getEndColumn()) : loc; eval.getMonitor().warning("ambiguity in concrete syntax", src); return (ITree) tree.asAnnotatable().setAnnotation("parseError", src); } catch (StaticError e) { ISourceLocation loc = TreeAdapter.getLocation(tree); ISourceLocation src = eval.getValueFactory().sourceLocation(loc.top(), loc.getOffset(), loc.getLength(), loc.getBeginLine(), loc.getEndLine(), loc.getBeginColumn(), loc.getBeginColumn()); eval.getMonitor().warning(e.getMessage(), e.getLocation()); return (ITree) tree.asAnnotatable().setAnnotation("can not parse fragment due to " + e.getMessage(), src); } catch (UndeclaredNonTerminalException e) { ISourceLocation loc = TreeAdapter.getLocation(tree); ISourceLocation src = eval.getValueFactory().sourceLocation(loc.top(), loc.getOffset(), loc.getLength(), loc.getBeginLine(), loc.getEndLine(), loc.getBeginColumn(), loc.getBeginColumn()); eval.getMonitor().warning(e.getMessage(), src); return (ITree) tree.asAnnotatable().setAnnotation("can not parse fragment due to " + e.getMessage(), src); } } private static class AdjustLocations extends IdentityTreeVisitor<ImplementationError> { private SortedMap<Integer, Integer> corrections; private IValueFactory vf; AdjustLocations(SortedMap<Integer, Integer> corrections, IValueFactory vf) { this.corrections = corrections; this.vf = vf; } private int offsetFor(int locOffset) { // find the entry k, v in corrections, // where k is the largest that is smaller or equal to locOffset. if (corrections.isEmpty()) { return 0; } int key = -1; SortedMap<Integer, Integer> rest = corrections.tailMap(locOffset); if (rest.isEmpty()) { key = corrections.lastKey(); assert key < locOffset; } else if (rest.firstKey() == locOffset) { key = locOffset; } else { assert rest.firstKey() > locOffset; SortedMap<Integer, Integer> front = corrections.headMap(rest.firstKey()); if (front.isEmpty()) { return 0; } key = front.lastKey(); assert key < locOffset; } int off = corrections.get(key); return off; } @Override public ITree visitTreeAppl(ITree tree) { ISourceLocation loc = TreeAdapter.getLocation(tree); if (loc == null) { return tree; } int off = offsetFor(loc.getOffset()); loc = vf.sourceLocation(loc, loc.getOffset() + off, loc.getLength()); IListWriter w = vf.listWriter(); IList args = TreeAdapter.getArgs(tree); for (IValue arg : args) { w.append(arg.accept(this)); } args = w.done(); return TreeAdapter.setLocation(TreeAdapter.setArgs(tree, args), loc); } @Override public ITree visitTreeAmb(ITree arg) throws ImplementationError { TreeAdapter.getAlternatives(arg).iterator().next().accept(this); return arg; } } private static char[] replaceAntiQuotesByHoles(IEvaluator<Result<IValue>> eval, ITree lit, Map<String, ITree> antiquotes, SortedMap<Integer, Integer> corrections ) { IList parts = TreeAdapter.getArgs(lit); StringBuilder b = new StringBuilder(); ISourceLocation loc = TreeAdapter.getLocation(lit); int offset = 0; // where we are in the parse tree // 012345 // (Exp)`a \> b` parses as "a > b" // this means the loc of > must be shifted right (e.g. + 1) // (so we *add* to shift when something bigger becomes smaller) int shift = loc.getOffset(); // where we need to be in the location corrections.put(offset, shift); for (IValue elem : parts) { ITree part = (ITree) elem; String cons = TreeAdapter.getConstructorName(part); int partLen = TreeAdapter.getLocation(part).getLength(); if (cons.equals("text")) { offset += partLen; b.append(TreeAdapter.yield(part)); } else if (cons.equals("newline")) { shift += partLen - 1; corrections.put(++offset, shift); b.append('\n'); } else if (cons.equals("lt")) { corrections.put(++offset, ++shift); b.append('<'); } else if (cons.equals("gt")) { corrections.put(++offset, ++shift); b.append('>'); } else if (cons.equals("bq")) { corrections.put(++offset, ++shift); b.append('`'); } else if (cons.equals("bs")) { corrections.put(++offset, ++shift); b.append('\\'); } else if (cons.equals("hole")) { String hole = createHole(eval, part, antiquotes); shift += partLen - hole.length(); offset += hole.length(); corrections.put(offset, shift); b.append(hole); } } return b.toString().toCharArray(); } private static String createHole(IEvaluator<Result<IValue>> ctx, ITree part, Map<String, ITree> antiquotes) { String ph = ctx.getParserGenerator().createHole(part, antiquotes.size()); antiquotes.put(ph, part); return ph; } private static ITree replaceHolesByAntiQuotes(final IEvaluator<Result<IValue>> eval, ITree fragment, final Map<String, ITree> antiquotes, final SortedMap<Integer,Integer> corrections) { return (ITree) fragment.accept(new IdentityTreeVisitor<ImplementationError>() { private final IValueFactory vf = eval.getValueFactory(); @Override public ITree visitTreeAppl(ITree tree) { String cons = TreeAdapter.getConstructorName(tree); if (cons == null || !cons.equals("$MetaHole") ) { IListWriter w = eval.getValueFactory().listWriter(); IList args = TreeAdapter.getArgs(tree); for (IValue elem : args) { w.append(elem.accept(this)); } args = w.done(); return TreeAdapter.setArgs(tree, args); } IConstructor type = retrieveHoleType(tree); return (ITree) antiquotes.get(TreeAdapter.yield(tree)).asAnnotatable().setAnnotation("holeType", type) .asAnnotatable().setAnnotation("category", vf.string("MetaVariable")); } private IConstructor retrieveHoleType(ITree tree) { IConstructor prod = TreeAdapter.getProduction(tree); ISet attrs = ProductionAdapter.getAttributes(prod); for (IValue attr : attrs) { if (((IConstructor) attr).getConstructorType() == RascalValueFactory.Attr_Tag) { IValue arg = ((IConstructor) attr).get(0); if (arg.getType().isNode() && ((INode) arg).getName().equals("holeType")) { return (IConstructor) ((INode) arg).get(0); } } } throw new ImplementationError("expected to find a holeType, but did not: " + tree); } @Override public ITree visitTreeAmb(ITree arg) { ISetWriter w = vf.setWriter(); for (IValue elem : TreeAdapter.getAlternatives(arg)) { w.insert(elem.accept(this)); } return (ITree) arg.set("alternatives", w.done()); } }); } private static boolean containsBackTick(char[] data, int offset) { for (int i = data.length - 1; i >= offset; --i) { if (data[i] == '`') return true; } return false; } private static boolean needBootstrapParser(char[] input) { return new String(input).contains("@bootstrapParser"); } }