astra.ast.visitor.ComponentVisitor 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.ast.visitor;
import astra.ast.core.ASTRAClassElement;
import astra.ast.core.IAction;
import astra.ast.core.IJavaHelper;
import astra.ast.core.ILanguageDefinition;
import astra.ast.core.IStatement;
import astra.ast.core.ITerm;
import astra.ast.core.IType;
import astra.ast.core.ParseException;
import astra.ast.core.Token;
import astra.ast.element.ConstantElement;
import astra.ast.element.FunctionElement;
import astra.ast.element.GRuleElement;
import astra.ast.element.InferenceElement;
import astra.ast.element.ModuleElement;
import astra.ast.element.RuleElement;
import astra.ast.element.TypesElement;
import astra.ast.event.MessageEvent;
import astra.ast.event.ModuleEvent;
import astra.ast.event.UpdateEvent;
import astra.ast.formula.AndFormula;
import astra.ast.formula.BindFormula;
import astra.ast.formula.BooleanTermElement;
import astra.ast.formula.BracketFormula;
import astra.ast.formula.ComparisonFormula;
import astra.ast.formula.FormulaVariable;
import astra.ast.formula.GoalFormula;
import astra.ast.formula.MethodSignature;
import astra.ast.formula.ModuleFormula;
import astra.ast.formula.NOTFormula;
import astra.ast.formula.OrFormula;
import astra.ast.formula.PredicateFormula;
import astra.ast.formula.ScopedGoalFormula;
import astra.ast.formula.TestGoalFormula;
import astra.ast.statement.AssignmentStatement;
import astra.ast.statement.BlockStatement;
import astra.ast.statement.DeclarationStatement;
import astra.ast.statement.ForAllStatement;
import astra.ast.statement.ForEachStatement;
import astra.ast.statement.IfStatement;
import astra.ast.statement.MaintainBlockStatement;
import astra.ast.statement.MinusMinusStatement;
import astra.ast.statement.ModuleCallStatement;
import astra.ast.statement.PlanCallStatement;
import astra.ast.statement.PlusPlusStatement;
import astra.ast.statement.QueryStatement;
import astra.ast.statement.ScopedStatement;
import astra.ast.statement.SendStatement;
import astra.ast.statement.SpawnGoalStatement;
import astra.ast.statement.SubGoalStatement;
import astra.ast.statement.SynchronizedBlockStatement;
import astra.ast.statement.TRStatement;
import astra.ast.statement.TestGoalStatement;
import astra.ast.statement.TryRecoverStatement;
import astra.ast.statement.UpdateStatement;
import astra.ast.statement.WaitStatement;
import astra.ast.statement.WhileStatement;
import astra.ast.term.AtIndexTerm;
import astra.ast.term.Brackets;
import astra.ast.term.CountFormulaeTerm;
import astra.ast.term.CountTerm;
import astra.ast.term.Function;
import astra.ast.term.HeadTerm;
import astra.ast.term.InlineVariableDeclaration;
import astra.ast.term.ListSplitterTerm;
import astra.ast.term.ListTerm;
import astra.ast.term.ModuleTerm;
import astra.ast.term.NotTerm;
import astra.ast.term.Operator;
import astra.ast.term.QueryTerm;
import astra.ast.term.TailTerm;
import astra.ast.term.VariableElement;
import astra.ast.tr.BlockAction;
import astra.ast.tr.FunctionCallAction;
import astra.ast.tr.TRModuleCallAction;
import astra.ast.tr.TRRuleElement;
import astra.ast.tr.UpdateAction;
import astra.ast.type.ObjectType;
/**
* This class iterates through the parse tree adding types where necessary.
*
* @author Rem Collier
*
*/
public class ComponentVisitor extends AbstractVisitor {
private IJavaHelper helper;
private ComponentStore store;
public ComponentVisitor(IJavaHelper helper, ComponentStore store) {
this.helper = helper;
this.store = store;
}
@Override
public Object visit(ASTRAClassElement element, Object data) throws ParseException {
helper.setup(element.packageElement(), element.imports());
// Check each module exists and store a reference to the module in the component store
for (ModuleElement module : element.getModules()) {
String qualifiedName = helper.resolveModule(module.className());
if (qualifiedName == null)
throw new ParseException("Unknown module declaration: " + module.className(), module);
module.setQualifiedName(qualifiedName);
store.modules.put(module.name(), module);
}
// Record any types that you find
for (TypesElement type : element.getOntologies()) {
if (store.types.contains(type.name()))
throw new ParseException("Duplicate Ontology: " + type.name(),
type.start, type.end);
for (ILanguageDefinition definition : type.definitions()) {
// System.out.println("definition: " + definition.toSignature());
if (store.signatures.contains(definition.toSignature()))
throw new ParseException("Conflict in ontology: " + type.name() + " for term: " + definition,
type.start, type.end);
store.signatures.add(definition.toSignature());
}
// Ontology was loaded without conflict so we are okay...
store.types.add(type.name());
}
// Record the event part of any events that you find.
for (RuleElement rule : element.getRules()) {
try {
// rule.accept(this, store);
String signature = rule.event().toSignature();
// System.out.println("signature: " + signature);
if (!store.events.contains(signature)) store.events.add(signature);
} catch (NullPointerException npe) {
npe.printStackTrace();
throw new ParseException("Illegal variable use in: " + rule.event(), rule.event());
}
}
for (GRuleElement rule : element.getGRules()) {
try {
// rule.accept(this, store);
String signature = rule.event().toSignature();
// System.out.println("grule signature: " + signature);
if (!store.events.contains(signature)) store.events.add(signature);
} catch (NullPointerException npe) {
npe.printStackTrace();
throw new ParseException("Illegal variable use in: " + rule.event(), rule.event());
}
}
// Now do the type checking...
for (InferenceElement inference : element.getInferences()) {
inference.accept(this, newVariableTypeStack(element));
}
for (RuleElement rule : element.getRules()) {
rule.accept(this, newVariableTypeStack(element));
}
for (GRuleElement rule : element.getGRules()) {
rule.accept(this, newVariableTypeStack(element));
}
for (FunctionElement function: element.getFunctions()) {
function.accept(this, newVariableTypeStack(element));
}
return null;
}
private VariableTypeStack newVariableTypeStack(ASTRAClassElement element) {
VariableTypeStack stack = new VariableTypeStack();
// Record Any Constants
for (ConstantElement constant : element.getConstants()) {
stack.addVariable(constant.constant(), constant.type());
// store.constants.put(constant.constant(), constant.term());
}
return stack;
}
@Override
public Object visit(InferenceElement element, Object data) throws ParseException {
element.head().accept(this, data);
element.body().accept(this, data);
return null;
}
@Override
public Object visit(RuleElement element, Object data) throws ParseException {
element.event().accept(this, data);
element.context().accept(this, data);
element.statement().accept(this, data);
return null;
}
@Override
public Object visit(GRuleElement element, Object data) throws ParseException {
element.event().accept(this, data);
element.context().accept(this, data);
element.dropCondition().accept(this, data);
element.statement().accept(this, data);
// NOT SURE WHAT THIS WILL DO - WILL IT WORK?
VariableTypeStack stk = (VariableTypeStack) data;
for(RuleElement rule : element.rules()) {
stk.addScope();
rule.accept(this, data);
stk.removeScope();
}
return null;
}
@Override
public Object visit(FunctionElement element, Object data) throws ParseException {
element.signature().accept(this, data);
for (TRRuleElement rule : element.rules()) {
VariableTypeStack stk = (VariableTypeStack) data;
stk.addScope();
rule.accept(this, stk);
stk.removeScope();
}
return null;
}
@Override
public Object visit(TRRuleElement rule, Object data) throws ParseException {
rule.formula().accept(this, data);
rule.action().accept(this, data);
return null;
}
@Override
public Object visit(FunctionCallAction action, Object data) throws ParseException {
((VariableTypeStack) data).addScope();
action.call().accept(this, data);
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(UpdateAction action, Object data) throws ParseException {
((VariableTypeStack) data).addScope();
action.call().accept(this, data);
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(BlockAction action, Object data) throws ParseException {
for (IAction act : action.actions()) {
act.accept(this, data);
}
return null;
}
@Override
public Object visit(TRModuleCallAction action, Object data) throws ParseException {
((VariableTypeStack) data).addScope();
action.method().accept(this, data);
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(UpdateEvent event, Object data) throws ParseException {
event.content().accept(this, data);
return null;
}
@Override
public Object visit(ModuleEvent event, Object data) throws ParseException {
event.event().accept(this, data);
return null;
}
@Override
public Object visit(MessageEvent event, Object data) throws ParseException {
event.speechact().accept(this, data);
event.sender().accept(this, data);
event.content().accept(this, data);
if (event.params() != null) event.params().accept(this, data);
return null;
}
@Override
public Object visit(GoalFormula formula, Object data) throws ParseException {
formula.predicate().accept(this, data);
return null;
}
@Override
public Object visit(TestGoalFormula formula, Object data) throws ParseException {
formula.predicate().accept(this, data);
return null;
}
@Override
public Object visit(BracketFormula formula, Object data)
throws ParseException {
formula.formula().accept(this, data);
return null;
}
@Override
public Object visit(ScopedGoalFormula formula, Object data) throws ParseException {
formula.goal().accept(this, data);
return null;
}
@Override
public Object visit(PredicateFormula formula, Object data) throws ParseException {
for (ITerm term : formula.terms()) {
term.accept(this, data);
}
return null;
}
@Override
public Object visit(Function function, Object data) throws ParseException {
for (ITerm term : function.terms()) {
term.accept(this, data);
}
return null;
}
@Override
public Object visit(NOTFormula formula, Object data) throws ParseException {
((VariableTypeStack) data).addScope();
formula.formula().accept(this, data);
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(ListTerm term, Object data) throws ParseException {
for (ITerm t : term.terms()) {
t.accept(this, data);
}
return null;
}
@Override
public Object visit(DeclarationStatement statement, Object data)
throws ParseException {
if (((VariableTypeStack) data).exists(statement.variable())) {
throw new ParseException(
"Duplicate variable declaration: " + statement.variable(),
statement);
}
if (statement.term() != null) {
statement.term().accept(this, data);
try {
if (!typesMatch(statement.type(), statement.term().type())) {
throw new ParseException(
"Type mismatch: expected an expression of type '" + Token.toTypeString(statement.type().type()) +
"' but got an expression of type '" + Token.toTypeString(statement.term().type().type()) + "'",
statement);
}
} catch (RuntimeException rte) {
throw new ParseException(rte.getMessage(), rte, statement);
}
}
((VariableTypeStack) data).addVariable(statement.variable(), statement.type());
return null;
}
private boolean typesMatch(IType left, IType right) {
if (left instanceof ObjectType) {
left = new ObjectType(Token.OBJECT_TYPE, helper.getFullClassName(((ObjectType) left).getClazz()));
}
if (right instanceof ObjectType) {
right = new ObjectType(Token.OBJECT_TYPE, helper.getFullClassName(((ObjectType) right).getClazz()));
}
return left.equals(right);
}
@Override
public Object visit(PlusPlusStatement statement, Object data)
throws ParseException {
IType type = ((VariableTypeStack) data).getType(statement.variable());
if (type == null) {
throw new ParseException(
"Undeclared variable: " + statement.variable(),
statement);
}
if (type.type() != Token.INTEGER) {
throw new ParseException(
"Type mismatch: variable must be integer type but got: '" + Token.toTypeString(statement.type().type()) + "'",
statement);
}
statement.setType(type);
return null;
}
@Override
public Object visit(MinusMinusStatement statement, Object data)
throws ParseException {
IType type = ((VariableTypeStack) data).getType(statement.variable());
if (type == null) {
throw new ParseException(
"Undeclared variable: " + statement.variable(),
statement);
}
if (type.type() != Token.INTEGER) {
throw new ParseException(
"Type mismatch: variable must be integer type but got: '" + Token.toTypeString(statement.type().type()) + "'",
statement);
}
statement.setType(type);
return null;
}
@Override
public Object visit(AssignmentStatement statement, Object data) throws ParseException {
IType type = ((VariableTypeStack) data).getType(statement.variable());
if (type == null) {
throw new ParseException("Undeclared variable: " + statement.variable(), statement);
}
statement.term().accept(this, data);
// Do a type check to see if the type of the term matches the type of the variable...
try {
if (!typesMatch(type, statement.term().type())) {
throw new ParseException(
"Type mismatch: expected an expression of type '" + Token.toTypeString(type.type()) +
"' but got an expression of type '" + Token.toTypeString(statement.term().type().type()) + "'",
statement);
}
} catch (RuntimeException rte) {
rte.printStackTrace();
throw new ParseException(rte.getMessage(), rte, statement);
}
statement.setType(type);
return null;
}
@Override
public Object visit(SynchronizedBlockStatement statement, Object data) throws ParseException {
((VariableTypeStack) data).addScope();
for (IStatement s: statement.statements()) {
s.accept(this, data);
}
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(MaintainBlockStatement statement, Object data) throws ParseException {
statement.formula().accept(this, data);
((VariableTypeStack) data).addScope();
for (IStatement s: statement.statements()) {
s.accept(this, data);
}
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(BlockStatement statement, Object data) throws ParseException {
((VariableTypeStack) data).addScope();
for (IStatement s: statement.statements()) {
s.accept(this, data);
}
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(ModuleCallStatement statement, Object data) throws ParseException {
statement.method().accept(this, data);
ModuleElement element = store.modules.get(statement.module());
if (element == null) {
throw new ParseException("Could not locate declaration for module: " + statement.module(), statement);
}
MethodSignature signature = new MethodSignature(statement.method(), IJavaHelper.ACTION);
boolean hasMethod = false;
try {
hasMethod = helper.validate(element.className(), signature);
} catch (Exception th) {
th.printStackTrace();
throw new ParseException("Could not find matching module call method: " + statement.toString() + " for class: " + element.className(), statement);
}
if (!hasMethod && !helper.hasAutoAction(element.className())) {
throw new ParseException("Could not find matching module call method: " + statement.toString(), statement);
}
return null;
}
@Override
public Object visit(ModuleFormula formula, Object data) throws ParseException {
formula.method().accept(this, data);
ModuleElement element = store.modules.get(formula.module());
if (element == null) {
throw new ParseException("Could not locate declaration for module: " + formula.module(), formula);
}
MethodSignature signature = new MethodSignature(formula.method(), IJavaHelper.FORMULA);
if (!helper.validate(element.className(), signature) && !helper.hasAutoFormula(element.className())) {
throw new ParseException("Could not match formula to a method: " + formula.method() + " for module: "+formula.module(), formula);
}
return null;
}
@Override
public Object visit(PlanCallStatement statement, Object data) throws ParseException {
statement.call().accept(this, data);
if (!store.plans.contains(statement.call().toSignature())) {
throw new ParseException("[TypeVisitor3] Could not find plan to match plan call: " + statement.toString(), statement);
}
return null;
}
@Override
public Object visit(QueryStatement statement, Object data) throws ParseException {
statement.formula().accept(this, data);
return null;
}
@Override
public Object visit(WhileStatement statement, Object data)
throws ParseException {
statement.guard().accept(this, data);
statement.statement().accept(this, data);
return null;
}
@Override
public Object visit(ForEachStatement statement, Object data)
throws ParseException {
((VariableTypeStack) data).addScope();
statement.guard().accept(this, data);
statement.statement().accept(this, data);
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(ForAllStatement statement, Object data)
throws ParseException {
((VariableTypeStack) data).addScope();
statement.variable().accept(this, data);
statement.list().accept(this, data);
statement.statement().accept(this, data);
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(WaitStatement statement, Object data)
throws ParseException {
statement.guard().accept(this, data);
if (statement.timeout() != null) statement.timeout().accept(this, data);
return null;
}
@Override
public Object visit(TRStatement statement, Object data)
throws ParseException {
if (statement.function() != null) {
statement.function().accept(this, data);
}
return null;
}
@Override
public Object visit(TryRecoverStatement statement, Object data)
throws ParseException {
statement.tryStatement().accept(this, data);
statement.recoverStatement().accept(this, data);
return null;
}
@Override
public Object visit(InlineVariableDeclaration term, Object data) throws ParseException {
if (((VariableTypeStack) data).exists(term.identifier())) {
throw new ParseException("Duplicate variable declaration: " + term.identifier(), term);
}
((VariableTypeStack) data).addVariable(term.identifier(), term.type());
return null;
}
@Override
public Object visit(Operator term, Object data) throws ParseException {
term.left().accept(this, data);
term.right().accept(this, data);
term.updateType();
return null;
}
@Override
public Object visit(Brackets brackets, Object data) throws ParseException {
brackets.contents().accept(this, data);
brackets.updateType();
return null;
}
@Override
public Object visit(VariableElement term, Object data) throws ParseException {
IType type = ((VariableTypeStack) data).getType(term.identifier());
if (type == null) {
throw new ParseException("Undeclared variable: " + term.identifier(), term);
}
term.setType(type);
return null;
}
@Override
public Object visit(ModuleTerm term, Object data) throws ParseException {
term.method().accept(this, data);
ModuleElement element = store.modules.get(term.module());
if (element == null) {
throw new ParseException("Could not locate declaration for module: " + term.module(), term);
}
MethodSignature signature = new MethodSignature(term.method(), IJavaHelper.TERM);
// System.out.println("\n\nSignature: " + signature);
// System.out.println("term: " + term.toString());
if (!helper.validate(element.className(), signature)) {
// System.out.println("Issue...");
// System.exit(0);
throw new ParseException("Could not match term to a method: " + term.toString() + " for module: "+term.module(), term);
}
IType type = helper.getType(element.className(), signature);
if (type == null) {
throw new ParseException("Could not find matching module method: " + term.toString(), term);
}
term.setType(type);
return null;
}
@Override
public Object visit(IfStatement statement, Object data)
throws ParseException {
((VariableTypeStack) data).addScope();
statement.guard().accept(this, data);
statement.ifStatement().accept(this, data);
if (statement.elseStatement() != null) statement.elseStatement().accept(this, data);
((VariableTypeStack) data).removeScope();
return null;
}
@Override
public Object visit(ComparisonFormula formula, Object data)
throws ParseException {
formula.left().accept(this, data);
formula.right().accept(this, data);
return null;
}
@Override
public Object visit(FormulaVariable formula, Object data)
throws ParseException {
if (!((VariableTypeStack) data).exists(formula.identifier())) {
((VariableTypeStack) data).addVariable(formula.identifier(), formula.type());
}
return null;
}
@Override
public Object visit(AndFormula formula, Object data) throws ParseException {
formula.left().accept(this, data);
formula.right().accept(this, data);
return null;
}
@Override
public Object visit(OrFormula formula, Object data) throws ParseException {
formula.left().accept(this, data);
formula.right().accept(this, data);
return null;
}
@Override
public Object visit(UpdateStatement statement, Object data)
throws ParseException {
statement.formula().accept(this, data);
return null;
}
@Override
public Object visit(SpawnGoalStatement statement, Object data)
throws ParseException {
statement.goal().accept(this, data);
return null;
}
@Override
public Object visit(SubGoalStatement statement, Object data)
throws ParseException {
statement.goal().accept(this, data);
return null;
}
@Override
public Object visit(TestGoalStatement statement, Object data)
throws ParseException {
statement.goal().accept(this, data);
return null;
}
@Override
public Object visit(SendStatement statement, Object data)
throws ParseException {
statement.performative().accept(this, data);
statement.sender().accept(this, data);
statement.content().accept(this, data);
if (statement.params() != null) statement.params().accept(this, data);
return null;
}
@Override
public Object visit(ScopedStatement statement, Object data)
throws ParseException {
statement.statement().accept(this, data);
return null;
}
@Override
public Object visit(QueryTerm term, Object data) throws ParseException {
term.formula().accept(this, data);
return null;
}
@Override
public Object visit(ListSplitterTerm term, Object data) throws ParseException {
term.head().accept(this, data);
term.tail().accept(this, data);
return null;
}
@Override
public Object visit(BindFormula formula, Object data) throws ParseException {
formula.variable().accept(this, data);
formula.term().accept(this, data);
return null;
}
@Override
public Object visit(CountTerm term, Object data) throws ParseException {
term.term().accept(this, data);
return null;
}
@Override
public Object visit(CountFormulaeTerm term, Object data) throws ParseException {
term.formula().accept(this, data);
return null;
}
@Override
public Object visit(HeadTerm term, Object data) throws ParseException {
term.term().accept(this, data);
return null;
}
@Override
public Object visit(TailTerm term, Object data) throws ParseException {
term.term().accept(this, data);
return null;
}
@Override
public Object visit(AtIndexTerm term, Object data) throws ParseException {
term.term().accept(this, data);
term.index().accept(this, data);
return null;
}
@Override
public Object visit(NotTerm term, Object data) throws ParseException {
term.term().accept(this, data);
return null;
}
@Override
public Object visit(BooleanTermElement element, Object data) throws ParseException {
element.term().accept(this, data);
if (element.term().type().type() != Token.BOOLEAN) {
throw new ParseException("Invalid formula: " + element, element);
}
return null;
}
}