diff --git a/Q1/Artefacts/RobotLang.g4 b/Q1/Artefacts/RobotLang.g4 index 42cf48b..d42986c 100644 --- a/Q1/Artefacts/RobotLang.g4 +++ b/Q1/Artefacts/RobotLang.g4 @@ -20,4 +20,8 @@ repeatStat: 'REPEAT' NUMBER '{' stat+ '}' ; ID_PREFIX: '// STUDENT ID:' ; NUMBER: [0-9]+ ; STRING: '"' .*? '"' ; -WS: [ \t\r\n]+ -> skip ; \ No newline at end of file +WS: [ \t\r\n]+ -> skip ; + + + + diff --git a/Q1/Artefacts/RobotLangVisitorUser.py b/Q1/Artefacts/RobotLangVisitorUser.py index f965080..5b52867 100644 --- a/Q1/Artefacts/RobotLangVisitorUser.py +++ b/Q1/Artefacts/RobotLangVisitorUser.py @@ -36,4 +36,8 @@ class RobotLangVisitorUser(RobotLangVisitor): for _ in range(times): for stat_ctx in ctx.stat(): self.visit(stat_ctx) - return None \ No newline at end of file + return None + + + + diff --git a/Q1/Q1.docx b/Q1/Q1.docx index ac13580..ff43081 100644 Binary files a/Q1/Q1.docx and b/Q1/Q1.docx differ diff --git a/Q2/Artefacts/fspow.g4 b/Q2/Artefacts/fspow.g4 index 04d8936..f533bad 100644 --- a/Q2/Artefacts/fspow.g4 +++ b/Q2/Artefacts/fspow.g4 @@ -1,62 +1,62 @@ grammar fspow; -// workshop 4 version prog: stat+ ; // labelling alternatives creates extra visitor methods -// the labels migh clash with rule names, so I make them unique -stat: - assignment # statAssignment +// the labels might clash with rule names, so I make them unique +stat: assignment # statAssignment | fcList # statFcList | fcApplySelector # statFcApplySelector | message # statMessage ; assignment : - ID '=' expression ; - + ID '=' expression; + // when alternatives are introduced, try using labels to distinguish -expression : - ID # exprID - | - selCreation # exprSelCreation - | - fcCreation # exprFcCreation +expression : ID # exprID + | selCreation # exprSelCreation + | fcCreation # exprFcCreation ; // ------- -// filecollection statements +// filecollection statements // ------- fcCreation: fcCreationName '(' rootSpecifier ')' ; fcCreationName: 'FileCollection' ; rootSpecifier: STRING ; // ------- -// selector statements +// selector statements // ------- -selCreation : selCreationName '(' filterSpecifier ')' ; +selCreation : selCreationName '(' testexpression ')' ; +selCreationName: 'Selector' ; -selCreationName: 'Selector' ; -filterSpecifier: selfilter '(' STRING ')' ; -selfilter: 'name' ; +testexpression : testunit (testoperator testunit)* ; + +testoperator: 'intersect' #testoperatorIntersect ; + +testunit: 'name' '(' STRING ')' #testunitname + | 'size' '(' STRING ')' #testunitsize + | 'type' '(' STRING ')' #testunittype + | 'date' '(' STRING ')' #testunitdate + | '(' testexpression ')' #testunittestexpression + | 'not' '(' testexpression ')' #testunitnottestexpression ; // ------- -// apply statement +// apply statement // ------- fcApplySelector: ID '.' applyName '(' expression ')' ; - applyName: 'apply' ; // ------- -// print statement +// print statement // ------- -fcList: ID '.' printName ; - +fcList: ID '.' printName ; // fc.print() printName: 'print()' ; // ------- -// message statement +// message statement // ------- message: 'message' STRING ; ID: [a-zA-Z][a-zA-Z0-9]* ; -STRING: '"'.*?'"' - {self.text = self.text[1:-1]} ; +STRING: '"'.*?'"' {self.text = self.text[1:-1]} ; WS : [ \t\r\n]+ -> skip ; -COMMENT : '//' ~[\r\n]* -> channel(HIDDEN) ; +COMMENT : '//' ~[\r\n]* -> channel(HIDDEN) ; \ No newline at end of file diff --git a/Q2/Artefacts/fspow.py b/Q2/Artefacts/fspow.py index 6e77c62..baef670 100644 --- a/Q2/Artefacts/fspow.py +++ b/Q2/Artefacts/fspow.py @@ -4,6 +4,7 @@ Parse and execute statements either in stdin or in file """ import sys +import os from antlr4 import * from fspowLexer import fspowLexer @@ -11,7 +12,7 @@ from fspowParser import fspowParser # from fspowListener import fspowListener # from antlr4.tree.Trees import Trees from TreesUser import TreesUser -from Q2.Artefacts.fspowVisitorUser import fspowVisitorUser +from fspowVisitorUser import fspowVisitorUser def main(argv): diff --git a/Q2/Artefacts/fspowVisitorUser.py b/Q2/Artefacts/fspowVisitorUser.py index d53345d..bf6e5a6 100644 --- a/Q2/Artefacts/fspowVisitorUser.py +++ b/Q2/Artefacts/fspowVisitorUser.py @@ -1,36 +1,88 @@ -# Generated from fspow.g4 by ANTLR from antlr4 import * -#from idlelib.idle_test.test_configdialog import root -if __name__ is not None and "." in __name__: - from .fspowParser import fspowParser -else: - from fspowParser import fspowParser - -# superclass +from fspowParser import fspowParser from fspowVisitor import fspowVisitor -# our own imports: -from repertoire.FileCollection import * -from repertoire.Selector import * -from repertoire.FSObject import * +from repertoire.FileCollection import FileCollection +from repertoire.Selector import Selector -# for symbol table -from collections import defaultdict - -# This class defines a complete specific visitor for a parse tree produced by fspowParser. class fspowVisitorUser(fspowVisitor): - - # where all the variables reside - variablesTable = defaultdict() + def __init__(self): + # Dictionary to track assigned variables during the AST walk + self.variablesTable = {} - - # just write methods to be overridden - # in future, may want to implement multiple subclasses - # but for now, I'd copy the ones I need to override here to implement them - + def visitMessage(self, ctx): + # Quotes are already stripped by the inline lexer action + print(ctx.STRING().getText()) + return self.visitChildren(ctx) + def visitFcCreation(self, ctx): + root_name = self.visit(ctx.rootSpecifier()) + return FileCollection(root_name) -del fspowParser + def visitRootSpecifier(self, ctx): + return ctx.STRING().getText() + def visitAssignment(self, ctx): + # Maps string identifiers to their evaluated backend objects + var_id = ctx.ID().getText() + self.variablesTable[var_id] = self.visit(ctx.expression()) + return True + def visitFcList(self, ctx): + var_id = ctx.getChild(0).getText() + if var_id in self.variablesTable: + print(self.variablesTable[var_id].list()) + else: + print(f"File collection {var_id} not found") + + def visitFcApplySelector(self, ctx): + fc_id = ctx.ID().getText() + if fc_id in self.variablesTable: + sel_id = ctx.expression().getText() + if sel_id in self.variablesTable: + self.variablesTable[fc_id].apply(self.variablesTable[sel_id]) + else: + print(f"Selector {sel_id} not found") + else: + print(f"File collection {fc_id} not found") + + def visitSelCreation(self, ctx): + # Triggers the AST walk to process the complex filter expression + return self.visit(ctx.testexpression()) + + def visitExprID(self, ctx): + var_id = ctx.ID().getText() + return self.variablesTable.get(var_id) + + # --- Filter List --- + + def visitTestexpression(self, ctx): + # Dynamically builds the recursive list, encoding 'intersect' as 1 + expr_list = [] + for i in range(ctx.getChildCount()): + child = ctx.getChild(i) + if child.getText() == 'intersect': + expr_list.append(1) + else: + expr_list.append(self.visit(child)) + return expr_list + + def visitTestunitnottestexpression(self, ctx): + # Appends -1 to mathematically encode the 'not' inversion + return [-1, self.visit(ctx.testexpression())] + + def visitTestunittestexpression(self, ctx): + return self.visit(ctx.testexpression()) + + def visitTestunitname(self, ctx): + return {"name": ctx.STRING().getText()} + + def visitTestunitsize(self, ctx): + return {"size": ctx.STRING().getText()} + + def visitTestunittype(self, ctx): + return {"type": ctx.STRING().getText()} + + def visitTestunitdate(self, ctx): + return {"date": ctx.STRING().getText()} \ No newline at end of file