
astra.compiler.ASTRAClassHierarchy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of astra-compiler Show documentation
Show all versions of astra-compiler Show documentation
Core compiler artifact for the ASTRA Language
The newest version!
package astra.compiler;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import astra.ast.core.IJavaHelper;
import astra.ast.core.ParseException;
import astra.ast.visitor.CodeGeneratorVisitor;
import astra.ast.visitor.ComponentStore;
import astra.ast.visitor.ComponentVisitor;
import astra.ast.visitor.GoalCheckVisitor;
import astra.ast.visitor.TypeCheckVisitor;
import graph.core.DirectedGraph;
import graph.core.Edge;
import graph.core.Vertex;
import graph.impl.DirectedAdjacencyListGraph;
public class ASTRAClassHierarchy {
private IJavaHelper helper;
Map> classes = new HashMap>();
DirectedGraph graph = new DirectedAdjacencyListGraph();
public ASTRAClassHierarchy(IJavaHelper helper) {
this.helper = helper;
// add the default Agent type
// we assume that there are no problems here...
Vertex vertex = addClass("astra.lang.Agent");
// System.out.println("Loading 'Agent' class...");
vertex.element().load(helper);
}
private Vertex addClass(String clazz) {
ASTRAClass node = new ASTRAClass(clazz);
Vertex vertex = graph.insertVertex(node);
classes.put(clazz, vertex);
return vertex;
}
/**
* This method returns true if the class is already loaded, false otherwise...
*
* @param cls the name of the ASTRA class
* @return true if the class is loaded, false otherwise
*/
public synchronized boolean contains(String cls) {
return classes.containsKey(cls);
}
public synchronized void compile(String cls, Map> errors) {
List> dependencies = new LinkedList>();
load(cls, errors, dependencies);
// System.out.println("Dependencies: " + dependencies);
// NOTE: this method can return with an empty dependencies list - this happens if
// all the code has already been compiled AND is up to date...
if (dependencies.isEmpty()) return;
// Add all dependencies to the error map - this is to ensure that all existing
// problem markers are deleted...
for (Vertex vertex : dependencies) {
if (!errors.containsKey(vertex.element().name())) {
errors.put(vertex.element().name(), new LinkedList());
}
}
// Check if the class being recompiled has errors
if (errors.get(cls) == null || errors.get(cls).isEmpty()) {
// No errors, so start building it...
// Compile the main class
compile(dependencies.remove(0), errors);
// Now refresh the dependencies (needs reload of AST + recompilation)
refreshDependencies(dependencies, errors);
} else {
// Errors - add an error to each dependent indicating that it could
// not be compiled do to the error in this class...
dependencies.remove(0);
for (Vertex vertex : dependencies) {
if (vertex.element().element() != null) {
addError(
vertex,
errors,
new ParseException("Cannot compile: " + vertex.element().name() + " due to error in " + cls, vertex.element().element()));
}
}
}
}
private void compile(Vertex vertex, Map> errors) {
try {
compile(vertex);
} catch (ParseException e) {
addError(vertex, errors, e);
}
}
/**
* This method actually loads the specified class and updates the
* hierarchy to reflect it. This can include loading of parents
* (where the class is new) or invalidating of classes (forcing
* them to be recompiled).
*
* Upon completion, a list of invalid classes will have been
* created. ALL of these classes will need to be recompiled...
*
* @param cls
*/
private void load(String cls, Map> errors, List> list) {
// System.out.println("[ASTRAClassHierarchy.load("+cls+")] Starting...");
Vertex vertex = classes.get(cls);
if (vertex != null) {
// System.out.println("[ASTRACompiler] Class: " + cls + " has already been loaded.");
// Check if we need to recompile...
if (!vertex.element().sourceChanged(helper)) {
// System.out.println("[ASTRACompiler] Class: " + cls + " has already been compiled.");
// class already loaded and source not changed, so
// do nothing...
return ;
} else {
System.out.println("[ASTRACompiler] Class: " + cls + " is not compiled.");
}
// Source is newer than compiled code - need to remove upward dependencies and reload AST.
// Here we also need to invalidate any children as they will need to be recompiled once the
// parent is recompiled...
removeParentDependencies(vertex);
} else {
// System.out.println("[ASTRACompiler] New Class: " + cls + " is ADDED.");
// Class has not been seen before, so add it...
vertex = addClass(cls);
}
invalidate(vertex, list);
// Okay, so the vertex representing the class
// has been added / existing parent dependencies
// have been removed.
//
// Lets reload the AST and re-add the parent dependencies...
if (!vertex.element().load(helper)) {
// Okay, we did not manage to load the class so there should be
// some errors, lets return them...
errors.put(cls, vertex.element().errorList());
} else {
ASTRAClass clazz = vertex.element();
String[] parents = clazz.element().getClassDeclaration().parents();
for (int i=0; i < parents.length; i++) {
parents[i] = helper.getQualifiedName(parents[i], clazz.element().packageElement().packageName(), clazz.element.imports());
// System.out.println("Loading Parent: " + parents[i]);
// Recursive call to load(...) method fills error map
load(parents[i], errors, list);
graph.insertEdge(classes.get(parents[i]), vertex, "");
}
}
}
private void invalidate(Vertex vertex, List> list) {
list.add(vertex);
for(Edge edge : graph.outEdges(vertex)) {
invalidate(graph.target(edge), list);
}
}
public synchronized void refreshDependencies(List> list, Map> errors) {
while (!list.isEmpty()) {
// Only try to compile the class if there are no syntax
// errors...
Vertex vertex = list.remove(0);
vertex.element().load(helper);
if (vertex.element().errorList().isEmpty()) {
vertex.element().load(helper);
compile(vertex, errors);
}
}
}
private void addError(Vertex vertex, Map> errors, ParseException e) {
List l = errors.get(vertex.element().name());
if (l == null) {
l = new LinkedList();
errors.put(vertex.element().name(), l);
}
l.add(e);
}
private void compile(Vertex vertex) throws ParseException {
if (vertex.element().element() == null) {
throw new ParseException("Cannot compile: " + vertex.element().name() + " source is not local", vertex.element().element());
}
// Only try to generate code if the file is local...
if (vertex.element().element().local()) {
// The class is loaded, but only syntactic checks have been carried out. Now do
// the semantic checks...
LinkedList linearisation = getLinearisation(vertex);
// Step 1: Iterate through the linearised list of classes (general-specific order)
// building a representation of the ontologies & triggering events.
ComponentStore store = new ComponentStore();
ComponentVisitor visitor = new ComponentVisitor(helper, store);
for (int i=linearisation.size()-1; i >= 0; i--) {
ASTRAClass node = linearisation.get(i);
if (!node.isLoaded()) {
throw new ParseException("Could not compile: " + vertex.element().name() + " due to error in: " + node.name(), 0, 0, 0);
}
// Construct the component store for the class: contains a set of resolved modules,
// ontologies and rule trigger events.
node.element().accept(visitor, store);
}
// Now get a reference to the class you are actually compiling...
ASTRAClass node = linearisation.get(0);
// Step 2: Check that the formulae and goals have corresponding entries in
// the component store.
node.element().accept(new TypeCheckVisitor(), store);
node.element().accept(new GoalCheckVisitor(), store);
// Step 3: Generate the source code and save it to disk
CodeGeneratorVisitor cgv = new CodeGeneratorVisitor(helper,store);
node.element().accept(cgv, null);
helper.createTarget(node.element(), cgv.toString());
}
}
private void removeParentDependencies(Vertex vertex) {
for(Edge edge : graph.inEdges(vertex)) {
graph.removeEdge(edge);
}
}
public synchronized boolean checkClass(String cls) {
Vertex vertex = classes.get(cls);
if (vertex != null) {
// check if the class has been modified...
return vertex.element().sourceChanged(helper);
}
return false;
}
/**
* Linearisation of Class Hierarchy using Breadth First Traversal of
* the Directed Graph.
*
* @param vertex the vertex containing the class to be linearized
* @return a list of classes ordered from child to root
* @throws ParseException thrown by methods called in this method
*/
public synchronized LinkedList getLinearisation(Vertex vertex) throws ParseException {
LinkedList linearization = new LinkedList();
Queue> queue = new LinkedList>();
queue.add(vertex);
while (!queue.isEmpty()) {
vertex = queue.poll();
if (!linearization.contains(vertex.element())) {
for (Edge edge: graph.inEdges(vertex)) {
Vertex source = graph.source(edge);
if (!queue.contains(source)) queue.add(source);
}
}
linearization.add(vertex.element());
}
return linearization;
}
public synchronized void deleteClass(String cls, Map> errors) {
System.out.println("[ASTRACompiler] Deleting: " + cls);
Vertex vertex = classes.remove(cls);
if (vertex == null) {
System.out.println("VERTEX is null");
return;
}
List> list = new LinkedList>();
if (vertex.element() != null && vertex.element().isLoaded()) invalidate(vertex, list);
graph.removeVertex(vertex);
//remove vertex from the list
list.remove(0);
refreshDependencies(list, errors);
}
public synchronized LinkedList getLinearisation(String cls) throws ParseException {
return getLinearisation(classes.get(cls));
}
public synchronized ASTRAClass getClass(String cls) {
return classes.get(cls).element();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy