/*******************************************************************************
* Copyright (c) 2009-2015 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
* * Emilie Balland - (CWI)
* * Anya Helene Bagge - A.H.S.Bagge@cwi.nl (Univ. Bergen)
* * Mark Hills - Mark.Hills@cwi.nl (CWI)
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
* * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.eclipse.pages;
import java.util.Collections;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.rascalmpl.eclipse.Activator;
import org.rascalmpl.eclipse.nature.ProjectEvaluatorFactory;
import org.rascalmpl.eclipse.nature.RascalMonitor;
import org.rascalmpl.eclipse.nature.WarningsToMessageHandler;
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.asserts.Ambiguous;
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.staticErrors.StaticError;
import org.rascalmpl.library.lang.rascal.syntax.RascalParser;
import org.rascalmpl.parser.Parser;
import org.rascalmpl.parser.gtd.exception.ParseError;
import org.rascalmpl.parser.gtd.result.out.DefaultNodeFlattener;
import org.rascalmpl.parser.uptr.UPTRNodeFactory;
import org.rascalmpl.parser.uptr.action.NoActionExecutor;
import org.rascalmpl.semantics.dynamic.Import;
import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.ISet;
import io.usethesource.vallang.ISetWriter;
import io.usethesource.vallang.ISourceLocation;
import io.usethesource.vallang.IValue;
import io.usethesource.vallang.IValueFactory;
import io.usethesource.vallang.exceptions.FactTypeUseException;
import org.rascalmpl.values.uptr.ITree;
import org.rascalmpl.values.uptr.TreeAdapter;
import io.usethesource.impulse.parser.IMessageHandler;
public class ParseController extends org.rascalmpl.eclipse.editor.ParseController {
Evaluator getEvaluator() {
return parser;
}
private class ParseJob extends org.rascalmpl.eclipse.editor.ParseController.ParseJob {
public ParseJob(String name, ISourceLocation uri, IMessageHandler handler) {
super(name, uri, handler);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
RascalMonitor rm = new RascalMonitor(monitor, warnings);
clearMarkers();
rm.startJob("parsing", 500);
parseTree = null;
if (input == null || path == null || (path != null && !path.isAbsolute() && project == null)) {
// may happen when project is deleted before Eclipse was started
return null;
}
try {
ModuleEnvironment env;
synchronized (parser) {
if (project != null) {
// if this is a source file in a Rascal project then
// reload other modules to find out about new syntax definitions
ProjectEvaluatorFactory.getInstance().reloadProject(project.getRawProject(), new WarningsToMessageHandler(uri, getMessageHandler()), Collections.emptySet());
}
parseTree = new RascalParser().parse(Parser.START_COMMANDS, uri.getURI(), input.toCharArray(), new NoActionExecutor() , new DefaultNodeFlattener<IConstructor, ITree, ISourceLocation>(), new UPTRNodeFactory(false));
//parseTree = parser.parseCommands(rm, input, uri);
GlobalEnvironment heap = parser.getHeap();
env = new ModuleEnvironment("Scrapbook", heap);
Environment oldEnv = parser.getCurrentEnvt();
try {
parser.setCurrentEnvt(env);
// parser.event("defining syntax");
IValueFactory vf = parser.getValueFactory();
ISetWriter rulesWriter = vf.setWriter();
ISetWriter importsWriter = vf.setWriter();
ISetWriter extendsWriter = vf.setWriter();
ISetWriter externalsWriter = vf.setWriter();
for (IValue v: TreeAdapter.getASTArgs(TreeAdapter.getArg(TreeAdapter.getArg(parseTree, "top"), "commands"))) {
ITree cmd = (ITree) v;
if (TreeAdapter.getConstructorName(cmd).equals("import")) {
ITree imp = TreeAdapter.getArg(cmd, "imported");
if (TreeAdapter.getConstructorName(imp).equals("syntax")) {
rulesWriter.insert(imp);
}
else if (TreeAdapter.getConstructorName(imp).equals("default")) {
importsWriter.insert(imp);
}
else if (TreeAdapter.getConstructorName(imp).equals("extend")) {
extendsWriter.insert(imp);
}
else if (TreeAdapter.getConstructorName(imp).equals("external")) {
externalsWriter.insert(imp);
}
}
}
ISet rules = rulesWriter.done();
for (IValue rule : rules) {
Import.evalImport(parser, (IConstructor) rule);
}
// parser.event("importing modules");
ISet imports = importsWriter.done();
for (IValue mod : imports) {
Import.evalImport(parser, (IConstructor) mod);
}
// parser.event("extending modules");
ISet extend = extendsWriter.done();
for (IValue mod : extend) {
Import.evalImport(parser, (IConstructor) mod);
}
// parser.event("generating modules");
ISet externals = externalsWriter.done();
for (IValue mod : externals) {
Import.evalImport(parser, (IConstructor) mod);
}
if (env.definesSyntax() && input.indexOf('`') > -1) {
parseTree = Import.parseFragments(parser, parseTree, uri, env);
}
}
finally {
parser.setCurrentEnvt(oldEnv);
}
}
}
catch (FactTypeUseException ftue) {
Activator.getInstance().logException("parsing rascal failed", ftue);
}
catch (ParseError pe){
int offset = pe.getOffset();
if(offset > 0 && offset == input.length()) {
--offset;
}
int k = Math.min(offset + 20, input.length());
String follow = input.substring(offset, k);
StringBuffer msg = new StringBuffer();
boolean hasUni = false;
msg.append(pe.toString()).append(" FOLLOWED BY: ");
for(int i = 0; i < follow.length();i ++){
int c = follow.codePointAt(i);
if((Character.isSpaceChar(c) &&
c != (int)' ' && c != (int)'\t' && c != (int)'\r' && c != (int)'\n')){
if(Character.charCount(c) == 1){
msg.append(String.format("\\u%04x", c));
} else {
msg.append(String.format("\\U%06x", c));
}
hasUni = true;
} else
msg.appendCodePoint(c);
}
if(hasUni)
msg.append(" NOTE: unrecognized characters occur at \\u followed by a hexadecimal number");
setParseError(offset, pe.getLength(), pe.getBeginLine() + 1, pe.getBeginColumn(), pe.getEndLine() + 1, pe.getEndColumn(), msg.toString());
}
catch (StaticError e) {
ISourceLocation loc = e.getLocation();
if (loc.hasOffsetLength()) {
setParseError(loc.getOffset(), loc.getLength(), loc.getBeginLine(), loc.getBeginColumn(), loc.getEndLine(), loc.getEndColumn(), e.getMessage());
}
else {
Activator.log("weird error during parsing", e);
}
}
catch (Throw t) {
ISourceLocation loc = t.getLocation();
setParseError(loc.getOffset(), loc.getLength(), loc.getBeginLine(), loc.getBeginColumn(), loc.getEndLine(), loc.getEndColumn(), t.getMessage());
}
catch (Ambiguous e) {
ISourceLocation loc = e.getLocation();
setParseError(loc.getOffset(), loc.getLength(), loc.getBeginLine(), loc.getBeginColumn(), loc.getEndLine(), loc.getEndColumn(), e.getMessage());
// reparse with raw rascal parser to get the full forest
Activator.log("unexpected ambiguity during parsing of Rascal module", e);
}
finally {
rm.endJob(true);
}
return Status.OK_STATUS;
}
}
@Override
protected void initParseJob(IMessageHandler handler, ISourceLocation location) {
this.job = new ParseJob("Rascal parser", location, handler);
}
@Override
public Object parse(String input, IProgressMonitor monitor) {
parseTree = null;
try {
job.initialize(input);
job.schedule();
job.join();
parseTree = job.parseTree;
return parseTree;
} catch (InterruptedException e) {
Activator.getInstance().logException("parser interrupted", e);
}
return null;
}
}