Commit de639fb8 authored by Sebastian Pretscher's avatar Sebastian Pretscher

Merge branch 'master' of git@github.com:jroith/cup-eclipse.git

parents 5983cf5d c27875b2
......@@ -4,16 +4,32 @@
label="CupEclipsePluginFeature"
version="1.0.0.qualifier">
<description url="http://www.example.com/description">
[Enter Feature Description here.]
<description url="http://www2.in.tum.de/projects/cup/">
CUP stands for Construction of Useful Parsers and is an LALR parser generator for Java. It was developed by C. Scott Ananian, Frank Flannery, Dan Wang, Andrew W. Appel and Michael Petter. It implements standard LALR(1) parser generation.
</description>
<copyright url="http://www.example.com/copyright">
[Enter Copyright Description here.]
<copyright>
CUP Parser Generator Copyright Notice, License, and Disclaimer
Copyright 1996-1999 by Scott Hudson, Frank Flannery, C. Scott Ananian
Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the names of the authors or their employers not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
The authors and their employers disclaim all warranties with regard to this software, including all implied warranties of merchantability and fitness. In no event shall the authors or their employers be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software.
This is an open source license. It is also GPL-Compatible (see entry for &quot;Standard ML of New Jersey&quot;). The portions of CUP output which are hard-coded into the CUP source code are (naturally) covered by this same license, as is the CUP runtime code linked with the generated parser.
Java is a trademark of Sun Microsystems, Inc. References to the Java programming language in relation to JLex are not meant to imply that Sun endorses this product.
</copyright>
<license url="http://www.example.com/license">
[Enter License Description here.]
<license url="">
CUP Parser Generator Copyright Notice, License, and Disclaimer
Copyright 1996-1999 by Scott Hudson, Frank Flannery, C. Scott Ananian
Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the names of the authors or their employers not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
The authors and their employers disclaim all warranties with regard to this software, including all implied warranties of merchantability and fitness. In no event shall the authors or their employers be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software.
This is an open source license. It is also GPL-Compatible (see entry for &quot;Standard ML of New Jersey&quot;). The portions of CUP output which are hard-coded into the CUP source code are (naturally) covered by this same license, as is the CUP runtime code linked with the generated parser.
Java is a trademark of Sun Microsystems, Inc. References to the Java programming language in relation to JLex are not meant to imply that Sun endorses this product.
</license>
<requires>
......@@ -25,14 +41,14 @@
id="de.tum.in.www2.CupPlugin"
download-size="0"
install-size="0"
version="0.0.0"
version="1.0.0.qualifier"
unpack="false"/>
<plugin
id="CupReferencedLibraries"
download-size="0"
install-size="0"
version="0.0.0"
version="1.0.0.qualifier"
unpack="false"/>
</feature>
......@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: CupPlugin
Bundle-SymbolicName: de.tum.in.www2.CupPlugin;singleton:=true
Bundle-Version: 1.0
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: de.tum.in.www2.cupplugin.Activator
Bundle-Vendor: de.tum.in.www2
Require-Bundle: org.eclipse.ui,
......
package de.tum.in.www2.cupplugin;
public interface IDisposable {
public void dispose();
}
package de.tum.in.www2.cupplugin.controller;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.progress.UIJob;
import de.tum.in.www2.cupplugin.editors.CupTextEditor;
import de.tum.in.www2.cupplugin.model.Model;
class CallbackJob extends UIJob {
private CupTextEditor editor = null;
private List<JobStatus>jobStatusList;
private ControllerStatistics controllerStats;
public CallbackJob(CupTextEditor editor, List<JobStatus>jobStatusList, ControllerStatistics controllerStats) {
super("" + editor.hashCode());
this.editor = editor;
this.jobStatusList = jobStatusList;
this.controllerStats = controllerStats;
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
for(JobStatus status:jobStatusList)
Controller.getInstance(editor).notifyObserversOfJobStatus(status);
Model model = Model.getInstanceForDocument(editor.getDocument());
model.setControllerStatistics(controllerStats);
Controller.getInstance(editor).notifyChangeJobFinished();
return Status.OK_STATUS;
}
}
\ No newline at end of file
......@@ -26,11 +26,9 @@ public class Controller {
private static final int SET_CHANGED_AFTER_SECONDS = 1;
// TODO: we need to remove the editor from the list, when it gets disposed.
private static WeakHashMap<CupTextEditor, Controller> instances = new WeakHashMap<CupTextEditor, Controller>();
// The Job that's executed after a document change is detected (changeJob)
private DocumentDidChangeJob myJob = null;
private DocumentDidChangeJob job = null;
// if the changeJob takes some time, it's good to know if it has to be
// reexecuted after it finishes
......@@ -45,6 +43,11 @@ public class Controller {
private Set<IRegisterForControllerChanges> controllerObservers = Collections
.newSetFromMap(new WeakHashMap<IRegisterForControllerChanges, Boolean>());
public static void deleteForEditor(CupTextEditor editor) {
if (instances.containsKey(editor))
instances.remove(editor);
}
public static Controller getInstance(CupTextEditor edit) {
Controller instance = instances.get(edit);
if (instance != null) {
......@@ -57,8 +60,8 @@ public class Controller {
private Controller(CupTextEditor editor) {
this.editor = editor;
myJob = new DocumentDidChangeJob(editor);
myJob.setSystem(true);
job = new DocumentDidChangeJob(editor);
job.setSystem(true);
documentEvents = new LinkedList<DocumentEvent>();
Debugger.getInstance(editor.getDocument()); // TODO: why is this here?
}
......@@ -102,7 +105,7 @@ public class Controller {
public void initialRun() {
addJobToDo(JobsToDo.parseCode);
addJobToDo(JobsToDo.buildTable);
myJob.schedule(0);
job.schedule(0);
}
public void registerObserver(IRegisterForControllerChanges observer) {
......@@ -215,13 +218,13 @@ public class Controller {
}
private void runJobs(IDocument document) {
if (myJob.getState() == Job.NONE
|| (!myJob.running() && myJob.getState() == Job.SLEEPING)) {
myJob.cancel();
if (job.getState() == Job.NONE
|| (!job.running() && job.getState() == Job.SLEEPING)) {
job.cancel();
myJob.revNumber = RevisionManager.increment(document);
job.revNumber = RevisionManager.increment(document);
myJob.schedule(SET_CHANGED_AFTER_SECONDS * 1000);
job.schedule(SET_CHANGED_AFTER_SECONDS * 1000);
} else {
RevisionManager.increment(document);
changeJobReRunRequested = System.currentTimeMillis();
......@@ -279,10 +282,6 @@ public class Controller {
*/
public void notifyChangeJobFinished() {
synchronized (lock) {
/*
* System.out.println("job finished -> " + changeJobReRunRequested);
* TODO: REMOVE OUTPUT
*/
if (changeJobReRunRequested > 0) {
changeJobReRunRequested = 0;
......@@ -290,8 +289,8 @@ public class Controller {
// ???
long remainderOfDelay = (SET_CHANGED_AFTER_SECONDS * 100)
- (System.currentTimeMillis() - changeJobReRunRequested);
myJob.revNumber = RevisionManager.get(editor.getDocument());
myJob.schedule((remainderOfDelay >= 0) ? remainderOfDelay : 0);
job.revNumber = RevisionManager.get(editor.getDocument());
job.schedule((remainderOfDelay >= 0) ? remainderOfDelay : 0);
}
}
}
......
package de.tum.in.www2.cupplugin.controller;
/**
*
*/
public class ControllerStatistics {
private long parserRuns;
private long parserRunFailures;
private long lastParserRunNanos;
private long lastParserRunDate;
private long lalrRuns;
private long lalrRunFailures;
private long lastLALRRunNanos;
private long lastLALRRunDate;
public void addParserRun(long nanos) {
parserRuns++;
lastParserRunNanos = nanos;
}
public void addTableRun(long nanos) {
lalrRuns++;
lastParserRunNanos = nanos;
}
}
......@@ -2,6 +2,8 @@ package de.tum.in.www2.cupplugin.controller;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
......@@ -12,10 +14,7 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.IDocumentProvider;
import de.in.tum.www2.cup.CupContext;
import de.in.tum.www2.cup.CupParser;
......@@ -25,80 +24,63 @@ import de.in.tum.www2.cup.internal.internal_error;
import de.tum.in.www2.cupplugin.controller.Controller.JobsToDo;
import de.tum.in.www2.cupplugin.editors.CupEditorErrorReporter;
import de.tum.in.www2.cupplugin.editors.CupTextEditor;
import de.tum.in.www2.cupplugin.editors.RevisionManager;
import de.tum.in.www2.cupplugin.model.Model;
public class DocumentDidChangeJob extends Job {
boolean iAmCurrentlyRunning = false;
boolean currentlyRunning = false;
CupTextEditor editor = null;
private List<DocumentEvent> documentEvents;
private EnumSet<JobsToDo> jobs;
List<DocumentEvent> documentEvents;
EnumSet<JobsToDo> jobs;
String codeText;
private ParserResult result;
private CupContext context;
private LALRResult lalrResult;
private List<JobStatus> jobStatusList;
private ThreadMXBean tmxb;
private ControllerStatistics controllerStats;
private String codeText;
// Revision Number for checks
public long revNumber;
public DocumentDidChangeJob(CupTextEditor editor) {
super("" + editor.hashCode());
this.editor = editor;
jobStatusList = new ArrayList<JobStatus>();
this.jobStatusList = new ArrayList<JobStatus>();
this.tmxb = ManagementFactory.getThreadMXBean();
this.controllerStats = new ControllerStatistics();
}
public boolean running() {
return iAmCurrentlyRunning;
return currentlyRunning;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
iAmCurrentlyRunning = true;
currentlyRunning = true;
// TODO: this was moved here from the SetupJob.
// I think it's correct and safe here, but this TODO
// should only be removed, once we are sure.
jobStatusList.clear();
SetupJob setup = new SetupJob(this);
SetupJob setup = new SetupJob(editor, revNumber, this);
setup.setSystem(true);
setup.schedule();
try {
setup.join();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (setup.getResult() == Status.CANCEL_STATUS) {
return Status.CANCEL_STATUS;
}
// TODO: can we really work with the document just like that
// on this thread? What about changes that are happening
// to the document? We should probably copy the document
// data first or something !!!
// -> We should instead read the document into a buffer in the UI
// thread.
// and store it, along with the revision that we also get on the UI
// thread.
// -> The actual parsing and analysis can then be done on the background
// job.
// We need these null checks, because the document might have been
// closed already.
if (editor == null)
if (setup.getResult() == Status.CANCEL_STATUS)
return Status.CANCEL_STATUS;
IDocumentProvider provider = editor.getDocumentProvider();
if (provider == null)
if (editor == null || editor.getDocument() == null)
return Status.CANCEL_STATUS;
IDocument document = provider.getDocument(editor.getEditorInput());
if (document == null)
return Status.CANCEL_STATUS;
long parseTimeNano = -1;
long tableTimeNano = -1;
IFile file = ((FileEditorInput) editor.getEditorInput()).getFile();
CupEditorErrorReporter errorReporter = new CupEditorErrorReporter(file);
......@@ -112,7 +94,9 @@ public class DocumentDidChangeJob extends Job {
parser = new CupParser(errorReporter, in);
result = null;
try {
long before = tmxb.getCurrentThreadCpuTime();
result = parser.parse();
parseTimeNano = tmxb.getCurrentThreadCpuTime() - before;
} catch (Exception e1) {
e1.printStackTrace();
}
......@@ -133,33 +117,29 @@ public class DocumentDidChangeJob extends Job {
e.printStackTrace();
}
} else {
StatisticsIfFailedJob statisticsJob = new StatisticsIfFailedJob(
editor, context, revNumber);
statisticsJob.setSystem(true);
statisticsJob.schedule();
if (context != null) {
StatisticsIfFailedJob statisticsJob = new StatisticsIfFailedJob(
editor, context, revNumber);
statisticsJob.setSystem(true);
statisticsJob.schedule();
}
jobStatusList.add(new JobStatus(JobsToDo.parseCode, true));
//return Status.CANCEL_STATUS;
}
}
if (jobs.contains(JobsToDo.buildTable)) {
boolean hasParserErrors = false;
lalrResult = null;
try {
if (parser != null && !parser.hasParseErrors()) {
lalrResult = LALRResult.Compute(context,
context.start_production);
} else {
/*
* System.out.println("LALRResult.Compute was not called, "
* + "because the parser has reported errors."); TODO:
* REMOVE
*/
// TODO: we need to notify listeners THAT/WHY the call failed.
long before = tmxb.getCurrentThreadCpuTime();
lalrResult = LALRResult.Compute(context, context.start_production);
tableTimeNano = tmxb.getCurrentThreadCpuTime() - before;
}
else
hasParserErrors = true;
} catch (internal_error e) {
e.printStackTrace();
}
......@@ -173,99 +153,43 @@ public class DocumentDidChangeJob extends Job {
try {
resultLaLrModelJob.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
// TODO: we run the statistics job twice, here, even if we failed
// to build the parser. This should only run once.
StatisticsIfFailedJob statisticsJob = new StatisticsIfFailedJob(
if (context != null) {
StatisticsIfFailedJob statisticsJob = new StatisticsIfFailedJob(
editor, context, revNumber);
statisticsJob.setSystem(true);
statisticsJob.schedule();
statisticsJob.setSystem(true);
statisticsJob.schedule();
}
String message = null;
if (hasParserErrors)
message = "The parse tables were not created, because the " +
"grammar could not be parsed correctly.";
jobStatusList.add(new JobStatus(JobsToDo.buildTable, true));
jobStatusList.add(new JobStatus(JobsToDo.buildTable, true, message));
}
}
errorReporter.pushToUIThread();
/* System.out.println(jobs); TODO: REMOVE OUTPUT */
documentEvents.clear();
jobs.clear();
iAmCurrentlyRunning = false;
CallbackJob cb = new CallbackJob(editor, jobStatusList);
if (parseTimeNano != -1)
controllerStats.addParserRun(parseTimeNano);
if (tableTimeNano != -1)
controllerStats.addTableRun(tableTimeNano);
currentlyRunning = false;
CallbackJob cb = new CallbackJob(editor, jobStatusList, controllerStats);
cb.setSystem(true);
cb.schedule();
return Status.OK_STATUS;
}
static class CallbackJob extends UIJob {
private CupTextEditor editor = null;
private List<JobStatus>jobStatusList;
public CallbackJob(CupTextEditor editor, List<JobStatus>jobStatusList) {
super("" + editor.hashCode());
this.editor = editor;
this.jobStatusList = jobStatusList;
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
for(JobStatus status:jobStatusList) {
Controller.getInstance(editor).notifyObserversOfJobStatus(status);
}
Controller.getInstance(editor).notifyChangeJobFinished();
return Status.OK_STATUS;
}
}
class SetupJob extends UIJob {
private DocumentDidChangeJob documentDidChangeJob;
public SetupJob(DocumentDidChangeJob documentDidChangeJob) {
super("Parser Setup Job");
this.documentDidChangeJob = documentDidChangeJob;
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
Controller controller = Controller.getInstance(editor);
if (documentDidChangeJob.documentEvents == null
|| documentDidChangeJob.documentEvents.isEmpty()) {
documentDidChangeJob.documentEvents = controller
.popAllDocumentEvents();
} else {
documentDidChangeJob.documentEvents.addAll(controller
.popAllDocumentEvents());
}
if (documentDidChangeJob.jobs == null
|| documentDidChangeJob.jobs.isEmpty()) {
documentDidChangeJob.jobs = controller.popJobsToDo();
} else {
documentDidChangeJob.jobs.addAll(controller.popJobsToDo());
}
jobStatusList.clear();
if (editor == null)
return Status.CANCEL_STATUS;
IDocument document = editor.getDocument();
if (revNumber != RevisionManager.get(document))
return Status.CANCEL_STATUS;
documentDidChangeJob.codeText = document.get();
return Status.OK_STATUS;
}
}
}
......@@ -5,17 +5,27 @@ import de.tum.in.www2.cupplugin.controller.Controller.JobsToDo;
public class JobStatus {
private boolean failed;
private String message;
private JobsToDo affectedJob;
public JobStatus(JobsToDo affectedJob, boolean failed) {
this(affectedJob, failed, null);
}
public JobStatus(JobsToDo affectedJob, boolean failed, String message) {
this.affectedJob = affectedJob;
this.failed = failed;
this.message = message;
}
public boolean hasFailed() {
return failed;
}
public String getMessage() {
return message;
}
public JobStatus(boolean failed) {
this.failed = failed;
}
......
package de.tum.in.www2.cupplugin.controller;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.progress.UIJob;
import de.tum.in.www2.cupplugin.editors.CupTextEditor;
import de.tum.in.www2.cupplugin.editors.RevisionManager;
class SetupJob extends UIJob {
private DocumentDidChangeJob ddcJob;
private CupTextEditor editor;
private long revision;
public SetupJob(CupTextEditor editor, long revision,
DocumentDidChangeJob documentDidChangeJob) {
super("Parser Setup Job");
this.editor = editor;
this.ddcJob = documentDidChangeJob;
this.revision = revision;
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
Controller controller = Controller.getInstance(editor);
if (ddcJob.documentEvents == null || ddcJob.documentEvents.isEmpty())
ddcJob.documentEvents = controller.popAllDocumentEvents();
else
ddcJob.documentEvents.addAll(controller.popAllDocumentEvents());
if (ddcJob.jobs == null || ddcJob.jobs.isEmpty())
ddcJob.jobs = controller.popJobsToDo();
else
ddcJob.jobs.addAll(controller.popJobsToDo());
if (editor == null || editor.hasBeenDisposed())
return Status.CANCEL_STATUS;
IDocument document = editor.getDocument();
if (revision != RevisionManager.get(document))
return Status.CANCEL_STATUS;
ddcJob.codeText = document.get();
return Status.OK_STATUS;
}
}
\ No newline at end of file
......@@ -17,7 +17,7 @@ class StatisticsIfFailedJob extends UIJob {
private long revisionNumber;
public StatisticsIfFailedJob(CupTextEditor editor, CupContext context, long revisionNumber) {
super("Parser Result UI Job");
super("StatisticsIfFailed UI Job");
this.editor = editor;
this.context = context;
this.revisionNumber = revisionNumber;
......
......@@ -69,9 +69,15 @@ public class Debugger {
*/
return line;
}
FindNextCodeBlockLineVisitor v = new FindNextCodeBlockLineVisitor(line);
/*
* Eclipse's internal representation starts counting lines with 0, we do start with 1
*/
FindNextCodeBlockLineVisitor v = new FindNextCodeBlockLineVisitor(line+1);
astModel.accept(v, null);
return v.getResult();
/*
* convert from our to eclipse'S view on line-numbers
*/
return v.getResult()-1;
}
/**
......
......@@ -12,12 +12,15 @@ import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import de.in.tum.www2.cup.Declarations;
import de.in.tum.www2.cup.ast.ParserResult;
import de.tum.in.www2.cupplugin.IDisposable;
import de.tum.in.www2.cupplugin.controller.Controller;
import de.tum.in.www2.cupplugin.model.*;
public class CupContentAssistProcessor implements IContentAssistProcessor,
ICupParserASTChangeObserver {
public class CupContentAssistProcessor implements IContentAssistProcessor,
IDisposable, ICupParserASTChangeObserver {
private Declarations decls;
private IDocument doc;
......@@ -29,10 +32,10 @@ public class CupContentAssistProcessor implements IContentAssistProcessor,
Model model = Model.getInstanceForDocument(doc);
updateFromParserResult(model.getAstModel());
model.registerModelObserver(this);
// TODO: de-register on dispose!
}
public void dispose() {
Model.getInstanceForDocument(doc).unregisterModelObserver(this);
}
private void updateFromParserResult(ParserResult result) {
......
......@@ -6,7 +6,6 @@ import java.util.List;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IFontProvider;
......@@ -64,8 +63,8 @@ public class CupContentOutlinePage extends ContentOutlinePage implements
@Override
public void dispose() {
System.out.println("DISPOSING OUTLINE OBSERVER!");
Model.getInstanceForDocument(doc).deRegisterModelObserver(t