diff options
author | Santo Cariotti <santo@dcariotti.me> | 2024-06-13 11:00:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-13 11:00:06 +0200 |
commit | 8e7089a5d6ba1f4f50a90133bb50bc5c6c554aff (patch) | |
tree | d4a627e56a199720f7d811af5756402e76628864 | |
parent | 1c8761901b26c0be4d61f3aed5ec0495a558a0e7 (diff) |
Set up visitor (#3)
Co-authored-by: geno <gabriele.genovese2@studio.unibo.it>
43 files changed, 2188 insertions, 41 deletions
@@ -6,7 +6,7 @@ PARSER_DIR = src/parser BIN_DIR = out MAIN_CLASS = com.clp.project.Main PARSEALL_CLASS = com.clp.project.ParseAll -SOURCES = $(wildcard $(SRC_DIR)/*.java) +SOURCES = $(wildcard $(SRC_DIR)/*.java $(SRC_DIR)/*/*.java $(SRC_DIR)/*/*/*.java) GRAMMARS = $(PARSER_DIR)/Python3Lexer.g4 $(PARSER_DIR)/Python3Parser.g4 ANTLR_OUTPUT = $(PARSER_DIR)/*.java DATE = $(shell date +%Y%m%d-%H%M%S) @@ -18,6 +18,9 @@ all: $(SOURCES) $(ANTLR_OUTPUT) $(ANTLR_OUTPUT): $(GRAMMARS) java -jar lib/antlr-4.13.1-complete.jar $^ +build: + $(JAVAC) $(JAVAC_FLAGS) $(SOURCES) + run: java -cp $(ANTLR_COMPLETE):$(BIN_DIR) $(MAIN_CLASS) $(ARGS) @@ -30,4 +33,4 @@ clean: release: clean zip -r ../python3-miniparser-$(DATE).zip . -.PHONY: all run clean release runall +.PHONY: all build run clean release runall @@ -6,5 +6,8 @@ Project for the AA 2023/2024 **Complementi di programmazione**'s course of the M ```shell make +make run make runall ``` + +For archlinux users, add the following env variable to visualize the window: `export _JAVA_AWT_WM_NONREPARENTING=1` diff --git a/progs/test.py b/progs/test.py index f168c9b..718885d 100644 --- a/progs/test.py +++ b/progs/test.py @@ -1,12 +1,17 @@ -def find_first_duplicate(nums): +import math +from re import f4, r3 + +from abs import * + + +def find_first_duplicate(nums, x): num_set = set() no_duplicate = -1 for i in range(len(nums)): - if nums[i] in num_set: return nums[i] else: num_set.add(nums[i]) - return no_duplicate
\ No newline at end of file + return no_duplicate diff --git a/progs/test2.py b/progs/test2.py new file mode 100644 index 0000000..c62d44a --- /dev/null +++ b/progs/test2.py @@ -0,0 +1,3 @@ +x = 1 +if y == 1: + print("a") diff --git a/src/Main.java b/src/Main.java index e773b07..c672e19 100644 --- a/src/Main.java +++ b/src/Main.java @@ -4,6 +4,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; import javax.swing.*; @@ -11,41 +12,71 @@ import org.antlr.v4.gui.TreeViewer; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; +import com.clp.project.ast.*; +import com.clp.project.ast.nodes.*; import com.clp.project.parser.*; +import com.clp.project.semanticanalysis.*; public class Main { - public static void main(String[] args) { - for (File file : Objects.requireNonNull(new File("./progs/").listFiles())) { - try { - String fileStr = file.getPath(); - // FIXME: use the fileStr above - fileStr = "./progs/test.py"; - System.out.println(fileStr); - System.out.println(readFile(fileStr)); + public static void main(String[] args) { - CharStream cs = CharStreams.fromFileName(fileStr); - Python3Lexer lexer = new Python3Lexer(cs); - CommonTokenStream tokenStream = new CommonTokenStream(lexer); - Python3Parser parser = new Python3Parser(tokenStream); - Python3Parser.RootContext tree = parser.root(); - // String treeStr = tree.toString(); - // System.out.println(treeStr); - // Visualize the parse tree - JFrame frame = new JFrame("Parse Tree"); - JPanel panel = new JPanel(); - TreeViewer viewer = new TreeViewer(Arrays.asList(parser.getRuleNames()), tree); - viewer.setScale(1.5); // Zoom factor - panel.add(viewer); - frame.add(panel); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(800, 600); - frame.setVisible(true); - break; - } catch (Exception e) { - e.printStackTrace(); + // for (File file : Objects.requireNonNull(new File("./progs/").listFiles())) { + try { + // String fileStr = file.getPath(); + // FIXME: use the fileStr above + String fileStr = "./progs/test.py"; + System.out.println(fileStr); + System.out.println(readFile(fileStr)); + CharStream cs = CharStreams.fromFileName(fileStr); + Python3Lexer lexer = new Python3Lexer(cs); + CommonTokenStream tokens = new CommonTokenStream(lexer); + Python3Parser parser = new Python3Parser(tokens); + Python3Parser.RootContext tree = parser.root(); + // DEBUG + // { + // tokens.fill(); + // for (Token token : tokens.getTokens()) { + // System.out.println(token.toString()); + // } + // + // System.out.println("Tree: " + tree); + // } + JFrame frame = new JFrame("Parse Tree"); + JPanel panel = new JPanel(); + TreeViewer viewer = new TreeViewer(Arrays.asList(parser.getRuleNames()), + tree); + viewer.setScale(1.5); // Zoom factor + panel.add(viewer); + frame.add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(800, 600); + frame.setVisible(true); + if (tree == null) { + System.err.println("The tree is null."); + return; + } + if (parser.getNumberOfSyntaxErrors() > 0) { + System.err.println("Error on program parsing."); + return; + } + Python3VisitorImpl visitor = new Python3VisitorImpl(); + SymbolTable ST = new SymbolTable(); + Node ast = visitor.visit(tree); + ArrayList<SemanticError> errors = ast.checkSemantics(ST, 0); + if (errors.size() > 0) { + System.out.println("You had: " + errors.size() + " errors:"); + for (SemanticError e : errors) { + System.out.println("\t" + e); + } + } else { + System.out.println("Visualizing AST..."); + System.out.println(ast.toPrint("")); } + } catch (Exception e) { + e.printStackTrace(); } + // } } private static String readFile(String filePath) throws IOException { diff --git a/src/ast/Python3VisitorImpl.java b/src/ast/Python3VisitorImpl.java new file mode 100644 index 0000000..0783409 --- /dev/null +++ b/src/ast/Python3VisitorImpl.java @@ -0,0 +1,530 @@ +package com.clp.project.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.clp.project.ast.*; +import com.clp.project.ast.nodes.*; +import com.clp.project.ast.types.*; +import com.clp.project.parser.Python3ParserBaseVisitor; +import com.clp.project.parser.Python3Parser.*; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * Implementation for the `Python3ParserBaseVisitor` class for the `Node` type. + * Overrides each `visitNODE` method from the base class. + */ +public class Python3VisitorImpl extends Python3ParserBaseVisitor<Node> { + /** + * Since a root can be a simple_stmts or a compound_stmt, this method + * returns a new `RootNode` with a list of them. + * + * ``` + * root : NEWLINE* (simple_stmts | compound_stmt)* EOF; + * ``` + */ + public Node visitRoot(RootContext ctx) { + ArrayList<Node> stmts = new ArrayList<Node>(); + ArrayList<Node> compStmts = new ArrayList<Node>(); + + for (Simple_stmtsContext stm : ctx.simple_stmts()) { + stmts.add(visit(stm)); + } + for (Compound_stmtContext stm : ctx.compound_stmt()) { + compStmts.add(visit(stm)); + } + + return new RootNode(stmts, compStmts); + } + + /** + * Returns a `SimpleStmtsNode`, made by an array of SimpleStmtNode + * + * ``` + * simple_stmts : simple_stmt (';' simple_stmt)* ';'? NEWLINE ; + * ``` + */ + public Node visitSimple_stmts(Simple_stmtsContext ctx) { + ArrayList<Node> stmts = new ArrayList<Node>(); + + for (Simple_stmtContext stm : ctx.simple_stmt()) { + stmts.add(visit(stm)); + } + + return new SimpleStmtsNode(stmts); + } + + /** + * Returns a `CompoundNode`. It can be built by a different kind of + * statements, so only one of theme won't be null. + * + * ``` + * compound_stmt : if_stmt | while_stmt | for_stmt | funcdef ; + * ``` + */ + public Node visitCompound_stmt(Compound_stmtContext ctx) { + Node ifStmt = null; + Node funcDef = null; + Node forStmt = null; + Node whileStmt = null; + + if (ctx.if_stmt() != null) { + ifStmt = visit(ctx.if_stmt()); + } + + if (ctx.funcdef() != null) { + funcDef = visit(ctx.funcdef()); + } + + if (ctx.for_stmt() != null) { + forStmt = visit(ctx.for_stmt()); + } + + if (ctx.while_stmt() != null) { + whileStmt = visit(ctx.while_stmt()); + } + + return new CompoundNode(ifStmt, funcDef, forStmt, whileStmt); + } + + /** + * Returns a `SimpleStmtNode`. It can be built by a different kind of + * statements, so only one of theme won't be null. + * + * ``` + * simple_stmt : assignment | expr | return_stmt | import_stm ; + * ``` + */ + public Node visitSimple_stmt(Simple_stmtContext ctx) { + Node assignment = null; + Node expr = null; + Node returnStmt = null; + Node importStmt = null; + + if (ctx.assignment() != null) { + assignment = visit(ctx.assignment()); + } + + if (ctx.expr() != null) { + expr = visit(ctx.expr()); + } + + if (ctx.return_stmt() != null) { + returnStmt = visit(ctx.return_stmt()); + } + + if (ctx.import_stm() != null) { + importStmt = visit(ctx.import_stm()); + } + + return new SimpleStmtNode(assignment, expr, returnStmt, importStmt); + } + + /** + * Returns an `AssignmentNode`. It's made by left side, an assignment and a + * right side. + * + * ``` + * assignment : exprlist augassign exprlist ; + * ``` + */ + public Node visitAssignment(AssignmentContext ctx) { + Node lhr = visit(ctx.exprlist(0)); + Node assign = visit(ctx.augassign()); + Node rhr = visit(ctx.exprlist(1)); + + return new AssignmentNode(lhr, assign, rhr); + } + + /** + * Returns a `ReturnStmtNode`. The returned exprlist can be null. + * + * ``` + * return_stmt : 'return' exprlist? ; + * ``` + */ + public Node visitReturn_stmt(Return_stmtContext ctx) { + Node exprList = null; + if (ctx.exprlist() != null) { + exprList = visit(ctx.exprlist()); + } + + return new ReturnStmtNode(exprList); + } + + /** + * Returns a `ImportNode`. An import can be made in different ways so we + * check the way in a module is imported (by from, by alias or by star). + * + * ``` + * import_stm : 'import' dotted_name ('as' NAME)? + * | 'from' dotted_name 'import' (NAME (',' NAME)* | '*') ; + * ``` + */ + public Node visitImport_stm(Import_stmContext ctx) { + boolean isFrom = ctx.FROM() != null; + boolean importAs = ctx.AS() != null; + boolean importAll = ctx.STAR() != null; + + Node dottedName = visit(ctx.dotted_name()); + + ArrayList<String> names = new ArrayList<String>(); + + for (var s : ctx.NAME()) { + names.add(s.toString()); + } + + return new ImportNode(dottedName, isFrom, importAs, importAll, names); + } + + /** + * Returns a `DottedNameNode` used in `import_stm`. + * + * ``` + * dotted_name : NAME ('.' NAME)* ; + * ``` + */ + public Node visitDotted_name(Dotted_nameContext ctx) { + ArrayList<TerminalNode> names = new ArrayList<TerminalNode>(); + + for (var name : ctx.NAME()) { + names.add(name); + } + + return new DottedNameNode(names); + } + + /** + * Returns a `FuncdefNode`. A paramlist can be null. + * + * ``` + * funcdef : 'def' NAME '(' paramlist? ')' ':' block ; + * ``` + */ + public Node visitFuncdef(FuncdefContext ctx) { + Node paramlist = null; + + if (ctx.paramlist() != null) { + paramlist = visit(ctx.paramlist()); + } + + Node block = visit(ctx.block()); + + return new FuncdefNode(ctx.NAME(), paramlist, block); + } + + /** + * Returns a `ParamlistNode`. We ignore the paramdef with default values + * (eg: is_used=False) because there is no test which uses this feature. + * + * ``` + * paramlist : paramdef ('=' expr)? (',' paramdef ('=' expr)?)* ; + * + * ``` + */ + public Node visitParamlist(ParamlistContext ctx) { + ArrayList<Node> params = new ArrayList<Node>(); + + for (ParamdefContext s : ctx.paramdef()) { + params.add(visit(s)); + } + + return new ParamlistNode(params); + } + + /** + * Returns a `ParamdefNode`. We ignore the paramdef with type annotation + * (eg: x : int) because there is no test which uses this feature. + * + * ``` + * paramdef : NAME (':' expr)? ; + * ``` + */ + public Node visitParamdef(ParamdefContext ctx) { + return new ParamdefNode(ctx.NAME().toString()); + } + + /** + * Returns an `AugassignNode`. We don't provide all kinds of assignment + * below. + * + * ``` + * augassign : '=' | '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | + * '^=' | '<<=' | '>>=' | '**=' | '//=' + * ; + * ``` + */ + public Node visitAugassign(AugassignContext ctx) { + Node x = null; + + if (ctx.ASSIGN() != null) { + x = new AugassignNode(ctx.ASSIGN()); + } else if (ctx.ADD_ASSIGN() != null) { + x = new AugassignNode(ctx.ADD_ASSIGN()); + } else if (ctx.SUB_ASSIGN() != null) { + x = new AugassignNode(ctx.SUB_ASSIGN()); + } else if (ctx.MULT_ASSIGN() != null) { + x = new AugassignNode(ctx.MULT_ASSIGN()); + } else if (ctx.DIV_ASSIGN() != null) { + x = new AugassignNode(ctx.DIV_ASSIGN()); + } + + return x; + } + + /** + * Returns a `IfNode`. + * FIXME: add support for elif statement. + * + * ``` + * if_stmt : 'if' expr ':' block ('elif' expr ':' block)* ('else' ':' block)? ; + * ``` + */ + public Node visitIf_stmt(If_stmtContext ctx) { + var blocks = ctx.block(); + Node condExp = visit(ctx.expr(0)); + Node thenExp = visit(blocks.get(0)); + Node elseExp = null; + if (blocks.size() > 1) { + elseExp = visit(blocks.get(1)); + } + + return new IfNode(condExp, thenExp, elseExp); + } + + /** + * Returns a `WhileStmtNode`. We do not provide 'else' branch. + * + * ``` + * while_stmt : 'while' expr ':' block ('else' ':' block)? ; + * ``` + */ + public Node visitWhile_stmt(While_stmtContext ctx) { + Node expr = visit(ctx.expr()); + + // Block 1 is for the while-else statement + Node block = visit(ctx.block(0)); + + return new WhileStmtNode(expr, block); + } + + /** + * Returns a `ForSmtNode`. We do not provide 'else' branch. + * + * ``` + * for_stmt : 'for' exprlist ':' block ('else' ':' block)? ; + * ``` + */ + public Node visitFor_stmt(For_stmtContext ctx) { + Node exprList = visit(ctx.exprlist()); + + // Block 1 is for the for-else statement + Node block = visit(ctx.block(0)); + + return new ForStmtNode(exprList, block); + } + + /** + * Returns a `BlockNode`. A block can be be a simple_stmts or a list of + * simple_stms and/or compound_stmt, so we just use a list for each kind. + * + * ``` + * block : simple_stmts + * | NEWLINE INDENT (simple_stmts | compound_stmt)+ DEDENT ; + * ``` + */ + public Node visitBlock(BlockContext ctx) { + ArrayList<Node> stmts = new ArrayList<Node>(); + ArrayList<Node> compStmts = new ArrayList<Node>(); + + for (Simple_stmtsContext s : ctx.simple_stmts()) { + stmts.add(visit(s)); + } + for (Compound_stmtContext s : ctx.compound_stmt()) { + compStmts.add(visit(s)); + } + + return new BlockNode(stmts, compStmts); + } + + /** + * Returns a `CompNode`. It should never be null. + * + * ``` + * comp_op : '<' | '>' | '==' | '>=' | '<=' | '<>' | '!=' | 'in' | 'not' 'in' | + * 'is' | 'is' 'not' ; + * ``` + */ + public Node visitComp_op(Comp_opContext ctx) { + Node comp = null; + if (ctx.LESS_THAN() != null) { + comp = new CompNode(ctx.LESS_THAN()); + } else if (ctx.GREATER_THAN() != null) { + comp = new CompNode(ctx.GREATER_THAN()); + } else if (ctx.EQUALS() != null) { + comp = new CompNode(ctx.EQUALS()); + } else if (ctx.GT_EQ() != null) { + comp = new CompNode(ctx.GT_EQ()); + } else if (ctx.LT_EQ() != null) { + comp = new CompNode(ctx.LT_EQ()); + } else if (ctx.NOT_EQ_2() != null) { + // We're ignoring NOT_EQ_1() because no one uses `<>` + comp = new CompNode(ctx.NOT_EQ_2()); + } else if (ctx.IN() != null) { + comp = new CompNode(ctx.IN()); + } else if (ctx.NOT() != null) { + comp = new CompNode(ctx.NOT()); + } else if (ctx.IS() != null) { + comp = new CompNode(ctx.IS()); + } + + return comp; + } + + /** + * Returns an `ExprNode`. An expession can be a different kind of + * sub-expression. We do not provide all kinds of expr(s). + * + * ``` + * expr : atom trailer* | expr '**' expr | ('+' | '-' | '~')+ expr | expr ('*' | + * '@' | '/' | '%' | '//') expr | expr ('+' | '-') expr | expr ('<<' | '>>') + * expr | expr '&' expr | expr '^' expr | expr '|' expr | 'not' expr | expr + * comp_op expr | expr 'and' expr | expr 'or' expr | expr 'if' expr 'else' expr + * ; + * ``` + */ + public Node visitExpr(ExprContext ctx) { + Node atom = null; + Node compOp = null; + ArrayList<Node> exprs = new ArrayList<Node>(); + ArrayList<Node> trailers = new ArrayList<Node>(); + String op = null; + + if (ctx.ADD(0) != null) { + op = ctx.ADD(0).toString(); + } + + if (ctx.MINUS(0) != null) { + op = ctx.MINUS(0).toString(); + } + + if (ctx.NOT() != null) { + op = ctx.NOT().toString(); + } + + if (ctx.STAR() != null) { + op = ctx.STAR().toString(); + } + + if (ctx.DIV() != null) { + op = ctx.DIV().toString(); + } + + if (ctx.atom() != null) { + atom = visit(ctx.atom()); + } + + if (ctx.comp_op() != null) { + compOp = visit(ctx.comp_op()); + } + + for (ExprContext s : ctx.expr()) { + exprs.add(visit(s)); + } + + for (TrailerContext s : ctx.trailer()) { + trailers.add(visit(s)); + } + + return new ExprNode(atom, compOp, exprs, op, trailers); + } + + /** + * Returns an `AtomNode`. + * FIXME: add support for testlist_comp + * + * ``` + * atom : '(' testlist_comp? ')' | '[' testlist_comp? ']' | '{' testlist_comp? + * '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' ; + * ``` + */ + public Node visitAtom(AtomContext ctx) { + if (ctx.NUMBER() != null) { + return new AtomNode(ctx.NUMBER().toString()); + } else if (ctx.TRUE() != null) { + return new AtomNode(ctx.TRUE().toString()); + } else if (ctx.FALSE() != null) { + return new AtomNode(ctx.FALSE().toString()); + } else if (ctx.NAME() != null) { + return new AtomNode(ctx.NAME().toString()); + } else if (ctx.STRING() != null) { + + var varName = ""; + for (var x : ctx.STRING()) { + varName += x; + } + + return new AtomNode(varName); + } + return new AtomNode(ctx.NONE().toString()); + } + + /** + * Returns an `TrailerNode`. + * + * ``` + * trailer : '(' arglist? ')' | '[' expr (',' expr)* ','? ']' | '.' NAME | '[' + * expr? ':' expr? (':' expr? )? ']' ; + * ``` + */ + public Node visitTrailer(TrailerContext ctx) { + Node arglist = null; + if (ctx.arglist() != null) { + arglist = visit(ctx.arglist()); + } + + ArrayList<Node> exprs = new ArrayList<Node>(); + for (ExprContext expr : ctx.expr()) { + exprs.add(visit(expr)); + } + + // A trailer could be `.methodName()`. + TerminalNode methodCall = null; + if (ctx.DOT() != null) { + methodCall = ctx.NAME(); + } + + return new TrailerNode(arglist, exprs, methodCall); + } + + /** + * Returns a `Node`. + * FIXME: what to do in case of list?? + * + * ``` + * exprlist : expr (',' expr )* ','? ; + * ``` + */ + public Node visitExprlist(ExprlistContext ctx) { + Node exp = visit(ctx.expr(0)); + + return exp; + } + + /** + * Returns a `ArglistNode`. + * + * ``` + * arglist : argument (',' argument)* ','? ; + * ``` + */ + public Node visitArglist(ArglistContext ctx) { + ArrayList<Node> arguments = new ArrayList<Node>(); + + for (ArgumentContext c : ctx.argument()) { + arguments.add(visit(c)); + } + + return new ArglistNode(arguments); + } +} diff --git a/src/ast/nodes/ArglistNode.java b/src/ast/nodes/ArglistNode.java new file mode 100644 index 0000000..8a5b4b9 --- /dev/null +++ b/src/ast/nodes/ArglistNode.java @@ -0,0 +1,53 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `arglist` statement of the grammar. + */ +public class ArglistNode implements Node { + protected ArrayList<Node> arguments; + + public ArglistNode(ArrayList<Node> arguments) { + this.arguments = arguments; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + for (var arg : arguments) { + errors.addAll(arg.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for arglist node + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "ArglistNode\n"; + + prefix += " "; + for (Node arg : arguments) { + str += arg.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/AssignmentNode.java b/src/ast/nodes/AssignmentNode.java new file mode 100644 index 0000000..d722f41 --- /dev/null +++ b/src/ast/nodes/AssignmentNode.java @@ -0,0 +1,52 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `assignment` statement of the grammar. + */ +public class AssignmentNode implements Node { + private Node lhr; + private Node assign; + private Node rhr; + + public AssignmentNode(Node lhr, Node assign, Node rhr) { + this.lhr = lhr; + this.assign = assign; + this.rhr = rhr; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + errors.addAll(lhr.checkSemantics(ST, _nesting)); + errors.addAll(assign.checkSemantics(ST, _nesting)); + errors.addAll(rhr.checkSemantics(ST, _nesting)); + + return errors; + } + + // TODO: check it out for this type + @Override + public Type typeCheck() { + return rhr.typeCheck(); + } + + // TODO: add code generation for assignment + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + return prefix + "Assignment\n" + lhr.toPrint(prefix + " ") + assign.toPrint(prefix + " ") + + rhr.toPrint(prefix + " "); + } + +} diff --git a/src/ast/nodes/AtomNode.java b/src/ast/nodes/AtomNode.java new file mode 100644 index 0000000..fa81f92 --- /dev/null +++ b/src/ast/nodes/AtomNode.java @@ -0,0 +1,45 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `atom` statement of the grammar. + */ +public class AtomNode implements Node { + protected String val; + + public AtomNode(String val) { + this.val = val; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + return new ArrayList<SemanticError>(); + } + + // FIXME: this type for atom + @Override + public Type typeCheck() { + return new AtomType(); + } + + // TODO: add code generation for atom node + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + if (val != null) { + return prefix + "Atom(" + val + ")\n"; + } + + return prefix + "Atom(null)\n"; + + } +} diff --git a/src/ast/nodes/AugassignNode.java b/src/ast/nodes/AugassignNode.java new file mode 100644 index 0000000..3519b50 --- /dev/null +++ b/src/ast/nodes/AugassignNode.java @@ -0,0 +1,42 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; + +import com.clp.project.ast.types.*; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * Node for the `augassign` statement of the grammar. + */ +public class AugassignNode implements Node { + private TerminalNode val; + + public AugassignNode(TerminalNode val) { + this.val = val; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + return new ArrayList<SemanticError>(); + } + + // FIXME: use the right type + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for augassign node + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + return prefix + "Augassign(" + val + ")\n"; + } +} diff --git a/src/ast/nodes/BlockNode.java b/src/ast/nodes/BlockNode.java new file mode 100644 index 0000000..1fdfcc3 --- /dev/null +++ b/src/ast/nodes/BlockNode.java @@ -0,0 +1,38 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for `block` statement of the grammar. + * It extends the `RootNode`. + */ +public class BlockNode extends RootNode implements Node { + public BlockNode(ArrayList<Node> stmts, ArrayList<Node> compoundStmts) { + super(stmts, compoundStmts); + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "Block\n"; + + prefix += " "; + for (Node stmt : stmts) { + str += stmt.toPrint(prefix); + } + for (Node stmt : compoundStmts) { + str += stmt.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/CompNode.java b/src/ast/nodes/CompNode.java new file mode 100644 index 0000000..6deb51c --- /dev/null +++ b/src/ast/nodes/CompNode.java @@ -0,0 +1,41 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * Node for the `comp_op` statement of the grammar. + */ +public class CompNode implements Node { + private TerminalNode op; + + public CompNode(TerminalNode op) { + this.op = op; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + return new ArrayList<SemanticError>(); + } + + // TODO: it should be boolean, right? + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for CompNode + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + return prefix + "CompNode(" + op + ")\n"; + } +} diff --git a/src/ast/nodes/CompoundNode.java b/src/ast/nodes/CompoundNode.java new file mode 100644 index 0000000..bfa0523 --- /dev/null +++ b/src/ast/nodes/CompoundNode.java @@ -0,0 +1,84 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `compound_node` statement of the grammar. + */ +public class CompoundNode implements Node { + private Node ifNode; + private Node funcDef; + private Node forStmt; + private Node whileStmt; + + public CompoundNode(Node ifNode, Node funcDef, Node forStmt, Node whileStmt) { + this.ifNode = ifNode; + this.funcDef = funcDef; + this.forStmt = forStmt; + this.whileStmt = whileStmt; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + if (ifNode != null) { + errors.addAll(ifNode.checkSemantics(ST, _nesting)); + } + + if (funcDef != null) { + errors.addAll(funcDef.checkSemantics(ST, _nesting)); + } + + if (forStmt != null) { + errors.addAll(forStmt.checkSemantics(ST, _nesting)); + } + + if (whileStmt != null) { + errors.addAll(whileStmt.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for CompoundNode + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "CompoundNode\n"; + + prefix += " "; + + if (ifNode != null) { + str += ifNode.toPrint(prefix); + } + + if (funcDef != null) { + str += funcDef.toPrint(prefix); + } + + if (forStmt != null) { + str += forStmt.toPrint(prefix); + } + + if (whileStmt != null) { + str += whileStmt.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/DottedNameNode.java b/src/ast/nodes/DottedNameNode.java new file mode 100644 index 0000000..342950a --- /dev/null +++ b/src/ast/nodes/DottedNameNode.java @@ -0,0 +1,51 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * Node for the `dooted_name` statement of the grammar. + */ +public class DottedNameNode implements Node { + protected ArrayList<TerminalNode> names; + + public DottedNameNode(ArrayList<TerminalNode> names) { + this.names = names; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // NOTE: we do not provide code generation for this node in the same way + // we do not want to do this for the import stm. + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "DottedName\n"; + + prefix += " "; + for (var name : names) { + str += prefix + name.toString() + "\n"; + } + + return str; + } + +} diff --git a/src/ast/nodes/ExprNode.java b/src/ast/nodes/ExprNode.java new file mode 100644 index 0000000..3aa112f --- /dev/null +++ b/src/ast/nodes/ExprNode.java @@ -0,0 +1,90 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `expr` statement of the grammar. + */ +public class ExprNode implements Node { + private Node atom; + private Node compOp; + private String op; + private ArrayList<Node> exprs; + private ArrayList<Node> trailers; + + public ExprNode(Node atom, Node compOp, ArrayList<Node> exprs, String op, ArrayList<Node> trailers) { + this.atom = atom; + this.compOp = compOp; + this.exprs = exprs; + this.op = op; + this.trailers = trailers; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + if (atom != null) { + errors.addAll(atom.checkSemantics(ST, _nesting)); + } + + if (compOp != null) { + errors.addAll(compOp.checkSemantics(ST, _nesting)); + } + + for (var expr : exprs) { + errors.addAll(expr.checkSemantics(ST, _nesting)); + } + + for (var trailer : trailers) { + errors.addAll(trailer.checkSemantics(ST, _nesting)); + } + + return errors; + } + + // FIXME: type for the expr + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for expr + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "Expr\n"; + + prefix += " "; + if (atom != null) { + str += atom.toPrint(prefix); + } + + if (compOp != null) { + str += compOp.toPrint(prefix); + } + + for (var expr : exprs) { + str += expr.toPrint(prefix); + } + + for (var trailer : trailers) { + str += trailer.toPrint(prefix); + } + + if (op != null) { + str += prefix + "Op(" + op + ")\n"; + } + + return str; + } + +} diff --git a/src/ast/nodes/ForStmtNode.java b/src/ast/nodes/ForStmtNode.java new file mode 100644 index 0000000..69dee74 --- /dev/null +++ b/src/ast/nodes/ForStmtNode.java @@ -0,0 +1,47 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `for_stmt` statement of the grammar. + */ +public class ForStmtNode implements Node { + private Node exprList; + private Node block; + + public ForStmtNode(Node exprList, Node block) { + this.exprList = exprList; + this.block = block; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + errors.addAll(exprList.checkSemantics(ST, _nesting)); + errors.addAll(block.checkSemantics(ST, _nesting)); + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for while + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + return prefix + "For\n" + exprList.toPrint(prefix + " ") + block.toPrint(prefix + " "); + } + +} diff --git a/src/ast/nodes/FuncdefNode.java b/src/ast/nodes/FuncdefNode.java new file mode 100644 index 0000000..c48eb1c --- /dev/null +++ b/src/ast/nodes/FuncdefNode.java @@ -0,0 +1,63 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * Node for the `funcdef` statement of the grammar. + */ +public class FuncdefNode implements Node { + private TerminalNode name; + private Node paramlist; + private Node block; + + public FuncdefNode(TerminalNode name, Node paramlist, Node block) { + this.name = name; + this.paramlist = paramlist; + this.block = block; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + if (paramlist != null) { + errors.addAll(paramlist.checkSemantics(ST, _nesting)); + } + + errors.addAll(block.checkSemantics(ST, _nesting)); + + return errors; + } + + // FIXME: this type must be the same of the return stmt variable + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: code generation for funcdef + @Override + public String codeGeneration() { + return ""; + } + + public String toPrint(String prefix) { + String str = prefix + "Funcdef(" + name + ")\n"; + + prefix += " "; + + if (paramlist != null) { + str += paramlist.toPrint(prefix); + } + + str += block.toPrint(prefix); + + return str; + } + +} diff --git a/src/ast/nodes/IfNode.java b/src/ast/nodes/IfNode.java new file mode 100644 index 0000000..a4f160d --- /dev/null +++ b/src/ast/nodes/IfNode.java @@ -0,0 +1,71 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `if` statement of the grammar. + */ +public class IfNode implements Node { + private Node guard; + private Node thenbranch; + private Node elsebranch; + + public IfNode(Node guard, Node thenbranch, Node elsebranch) { + this.guard = guard; + this.thenbranch = thenbranch; + this.elsebranch = elsebranch; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + errors.addAll(guard.checkSemantics(ST, _nesting)); + errors.addAll(thenbranch.checkSemantics(ST, _nesting)); + if (elsebranch != null) { + errors.addAll(elsebranch.checkSemantics(ST, _nesting)); + } + + return errors; + } + + // FIXME: fix the if statement + @Override + public Type typeCheck() { + if (guard.typeCheck() instanceof BoolType) { + Type thenexp = thenbranch.typeCheck(); + Type elseexp = elsebranch.typeCheck(); + if (thenexp.getClass().equals(elseexp.getClass())) + return thenexp; + else { + System.out.println("Type Error: incompatible types in then and else branches"); + return new ErrorType(); + } + } else { + System.out.println("Type Error: non boolean condition in if"); + return new ErrorType(); + } + } + + // TODO: add code generation for if + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "If\n" + guard.toPrint(prefix + " ") + thenbranch.toPrint(prefix + " "); + + if (elsebranch != null) { + str += elsebranch.toPrint(prefix + " "); + } + + return str; + } + +} diff --git a/src/ast/nodes/ImportNode.java b/src/ast/nodes/ImportNode.java new file mode 100644 index 0000000..e6ac8c7 --- /dev/null +++ b/src/ast/nodes/ImportNode.java @@ -0,0 +1,76 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `import_stmt` statement of the grammar. + */ +public class ImportNode implements Node { + private Node dottedName; + private boolean isFrom; + private boolean importAs; + private boolean importAll; + private ArrayList<String> names; + + public ImportNode(Node dottedName, boolean isFrom, boolean importAs, boolean importAll, + ArrayList<String> names) { + this.dottedName = dottedName; + this.isFrom = isFrom; + this.importAs = importAs; + this.importAll = importAll; + this.names = names; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // NOTE: we do not want to provide a code generation for this statement + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "Import\n"; + + prefix += " "; + if (isFrom) { + str += prefix + " From\n" + dottedName.toPrint(prefix + " "); + } else { + str += dottedName.toPrint(prefix); + } + + if (importAs) { + str += prefix + " As " + names.get(0) + "\n"; + } + + if (importAll) { + str += prefix + " All\n"; + } + + for (int i = 0; i < names.size(); ++i) { + if (i == 0 && importAs) + continue; + + str += prefix + names.get(i) + "\n"; + } + + str += "\n"; + return str; + } + +} diff --git a/src/ast/nodes/Node.java b/src/ast/nodes/Node.java new file mode 100644 index 0000000..f807745 --- /dev/null +++ b/src/ast/nodes/Node.java @@ -0,0 +1,36 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Base interface for a Node. + */ +public interface Node { + /** + * Checks semantics for a given node for a SymbolTable ST and a level of + * nesting. + * Returns a list of `SemanticError`. + */ + ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting); + + /** + * Checks the type for a given node. If there's any error, returns an + * `ErrorType`. + */ + Type typeCheck(); + + /** + * Returns a string for the Python Virtual Machine. + */ + String codeGeneration(); + + /** + * Returns a string for a given node with a prefix. + * It used when an AST wants to be visualized on screen. + */ + String toPrint(String prefix); +} diff --git a/src/ast/nodes/ParamdefNode.java b/src/ast/nodes/ParamdefNode.java new file mode 100644 index 0000000..d8e04f7 --- /dev/null +++ b/src/ast/nodes/ParamdefNode.java @@ -0,0 +1,29 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; + +import com.clp.project.ast.types.*; + +/** + * Node for the `paramdef` statement of the grammar. Extends the `AtomNode` + * class. + */ +public class ParamdefNode extends AtomNode implements Node { + public ParamdefNode(String val) { + super(val); + } + + // FIXME: it should returns the param' type + @Override + public Type typeCheck() { + return new VoidType(); + } + + @Override + public String toPrint(String prefix) { + return prefix + "Paramdef(" + val + ")\n"; + } +} diff --git a/src/ast/nodes/ParamlistNode.java b/src/ast/nodes/ParamlistNode.java new file mode 100644 index 0000000..d4f40b8 --- /dev/null +++ b/src/ast/nodes/ParamlistNode.java @@ -0,0 +1,53 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `param_list` statement of the grammar. + */ +public class ParamlistNode implements Node { + private ArrayList<Node> params; + + public ParamlistNode(ArrayList<Node> _params) { + params = _params; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + for (var param : params) { + errors.addAll(param.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: code generation for param list + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "Paramlist\n"; + + prefix += " "; + for (var param : params) { + str += param.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/ReturnStmtNode.java b/src/ast/nodes/ReturnStmtNode.java new file mode 100644 index 0000000..0cfd2b4 --- /dev/null +++ b/src/ast/nodes/ReturnStmtNode.java @@ -0,0 +1,57 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `return_stmt` statement of the grammar. + */ +public class ReturnStmtNode implements Node { + private Node exprList; + + public ReturnStmtNode(Node exprList) { + this.exprList = exprList; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + if (this.exprList != null) { + errors.addAll(this.exprList.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + if (this.exprList != null) { + return this.exprList.typeCheck(); + } + + return new VoidType(); + } + + // TODO: add code generation for return stmt + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "ReturnStmt\n"; + + prefix += " "; + if (this.exprList != null) { + str += this.exprList.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/RootNode.java b/src/ast/nodes/RootNode.java new file mode 100644 index 0000000..12d2f22 --- /dev/null +++ b/src/ast/nodes/RootNode.java @@ -0,0 +1,64 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `root` statement of the grammar. + */ +public class RootNode implements Node { + // stms and compundStmts are protected because they are reused for a + // BlockNode + protected ArrayList<Node> stmts; + protected ArrayList<Node> compoundStmts; + + public RootNode(ArrayList<Node> stmts, ArrayList<Node> compoundStmts) { + this.stmts = stmts; + this.compoundStmts = compoundStmts; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + for (Node stmt : stmts) { + errors.addAll(stmt.checkSemantics(ST, _nesting)); + } + for (Node stmt : compoundStmts) { + errors.addAll(stmt.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: Code generation for RootNode + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = "Root\n"; + + prefix += " "; + + for (Node stmt : stmts) { + str += stmt.toPrint(prefix); + } + for (Node stmt : compoundStmts) { + str += stmt.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/SimpleStmtNode.java b/src/ast/nodes/SimpleStmtNode.java new file mode 100644 index 0000000..3f4dec3 --- /dev/null +++ b/src/ast/nodes/SimpleStmtNode.java @@ -0,0 +1,84 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `simple_stmt` statement of the grammar. + */ +public class SimpleStmtNode implements Node { + private Node assignment; + private Node expr; + private Node returnStmt; + private Node importStmt; + + public SimpleStmtNode(Node assignment, Node expr, Node returnStmt, Node importStmt) { + this.assignment = assignment; + this.expr = expr; + this.returnStmt = returnStmt; + this.importStmt = importStmt; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + if (assignment != null) { + errors.addAll(assignment.checkSemantics(ST, _nesting)); + } + + if (expr != null) { + errors.addAll(expr.checkSemantics(ST, _nesting)); + } + + if (returnStmt != null) { + errors.addAll(returnStmt.checkSemantics(ST, _nesting)); + } + + if (importStmt != null) { + errors.addAll(importStmt.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for SimpleStmtNode + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "SimpleStmt\n"; + + prefix += " "; + + if (assignment != null) { + str += assignment.toPrint(prefix); + } + + if (expr != null) { + str += expr.toPrint(prefix); + } + + if (returnStmt != null) { + str += returnStmt.toPrint(prefix); + } + + if (importStmt != null) { + str += importStmt.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/SimpleStmtsNode.java b/src/ast/nodes/SimpleStmtsNode.java new file mode 100644 index 0000000..c8013a3 --- /dev/null +++ b/src/ast/nodes/SimpleStmtsNode.java @@ -0,0 +1,54 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `simple_stmts` statement of the grammar. + */ +public class SimpleStmtsNode implements Node { + private ArrayList<Node> stmts; + + public SimpleStmtsNode(ArrayList<Node> stmts) { + this.stmts = stmts; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + for (Node stmt : stmts) { + errors.addAll(stmt.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: Code generation for SimpleStmtsNode + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "SimpleStmts\n"; + + prefix += " "; + + for (Node stmt : stmts) { + str += stmt.toPrint(prefix); + } + + return str; + } + +} diff --git a/src/ast/nodes/TrailerNode.java b/src/ast/nodes/TrailerNode.java new file mode 100644 index 0000000..fa7e1ed --- /dev/null +++ b/src/ast/nodes/TrailerNode.java @@ -0,0 +1,78 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * Node for the `trailer` statement of the grammar. + */ +public class TrailerNode implements Node { + private Node arglist; + private ArrayList<Node> exprs; + private TerminalNode methodCall; + private boolean isEmpty; + + public TrailerNode(Node arglist, ArrayList<Node> exprs, TerminalNode methodCall) { + this.arglist = arglist; + this.exprs = exprs; + this.methodCall = methodCall; + + this.isEmpty = (this.arglist == null && this.exprs.size() == 0 && this.methodCall == null); + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + if (arglist != null) { + errors.addAll(arglist.checkSemantics(ST, _nesting)); + } + + for (var expr : exprs) { + errors.addAll(expr.checkSemantics(ST, _nesting)); + } + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for trailer node + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + String str = prefix + "TrailerNode\n"; + + prefix += " "; + + if (arglist != null) { + str += arglist.toPrint(prefix); + } + + for (var expr : exprs) { + str += expr.toPrint(prefix); + } + + if (methodCall != null) { + str += prefix + "Method(" + methodCall + ")\n"; + } + + if (isEmpty) { + str += prefix + "()\n"; + } + + return str; + } + +} diff --git a/src/ast/nodes/WhileStmtNode.java b/src/ast/nodes/WhileStmtNode.java new file mode 100644 index 0000000..a84d60e --- /dev/null +++ b/src/ast/nodes/WhileStmtNode.java @@ -0,0 +1,46 @@ +package com.clp.project.ast.nodes; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.types.*; + +/** + * Node for the `while_stmt` statement of the grammar. + */ +public class WhileStmtNode implements Node { + private Node expr; + private Node block; + + public WhileStmtNode(Node expr, Node block) { + this.expr = expr; + this.block = block; + } + + @Override + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + ArrayList<SemanticError> errors = new ArrayList<SemanticError>(); + + errors.addAll(expr.checkSemantics(ST, _nesting)); + errors.addAll(block.checkSemantics(ST, _nesting)); + + return errors; + } + + @Override + public Type typeCheck() { + return new VoidType(); + } + + // TODO: add code generation for while + @Override + public String codeGeneration() { + return ""; + } + + @Override + public String toPrint(String prefix) { + return prefix + "While\n" + expr.toPrint(prefix + " ") + block.toPrint(prefix + " "); + } +} diff --git a/src/ast/types/AtomType.java b/src/ast/types/AtomType.java new file mode 100644 index 0000000..a44a06c --- /dev/null +++ b/src/ast/types/AtomType.java @@ -0,0 +1,11 @@ +package com.clp.project.ast.types; + +/** + * An tom type. + * TODO: do I need to use this one? + */ +public class AtomType extends Type { + public String toPrint(String prefix) { + return prefix + "Atom\n"; + } +} diff --git a/src/ast/types/BoolType.java b/src/ast/types/BoolType.java new file mode 100644 index 0000000..7fc28ee --- /dev/null +++ b/src/ast/types/BoolType.java @@ -0,0 +1,10 @@ +package com.clp.project.ast.types; + +/** + * A boolean type. A bool is True or False. + */ +public class BoolType extends Type { + public String toPrint(String prefix) { + return prefix + "Bool\n"; + } +} diff --git a/src/ast/types/ErrorType.java b/src/ast/types/ErrorType.java new file mode 100644 index 0000000..30cb70e --- /dev/null +++ b/src/ast/types/ErrorType.java @@ -0,0 +1,10 @@ +package com.clp.project.ast.types; + +/** + * Error type. + */ +public class ErrorType extends Type { + public String toPrint(String prefix) { + return prefix + "Error\n"; + } +} diff --git a/src/ast/types/IntType.java b/src/ast/types/IntType.java new file mode 100644 index 0000000..9f4b250 --- /dev/null +++ b/src/ast/types/IntType.java @@ -0,0 +1,10 @@ +package com.clp.project.ast.types; + +/** + * An integer type. + */ +public class IntType extends Type { + public String toPrint(String prefix) { + return prefix + "Int\n"; + } +} diff --git a/src/ast/types/Type.java b/src/ast/types/Type.java new file mode 100644 index 0000000..4756255 --- /dev/null +++ b/src/ast/types/Type.java @@ -0,0 +1,41 @@ +package com.clp.project.ast.types; + +import java.util.ArrayList; + +import com.clp.project.semanticanalysis.SemanticError; +import com.clp.project.semanticanalysis.SymbolTable; +import com.clp.project.ast.nodes.*; + +/** + * A node which represents a type class. + */ +public class Type implements Node { + public boolean isEqual(Type A, Type B) { + if (A.getClass().equals(B.getClass())) + return true; + else + return false; + } + + public String toPrint(String s) { + return s; + } + + public ArrayList<SemanticError> checkSemantics(SymbolTable ST, int _nesting) { + // It is never invoked + return null; + } + + @Override + public Type typeCheck() { + // It is never invoked + return null; + } + + @Override + public String codeGeneration() { + // It is never invoked + return ""; + } + +} diff --git a/src/ast/types/VoidType.java b/src/ast/types/VoidType.java new file mode 100644 index 0000000..766aee2 --- /dev/null +++ b/src/ast/types/VoidType.java @@ -0,0 +1,10 @@ +package com.clp.project.ast.types; + +/** + * A void type. Voids return nothing. + */ +public class VoidType extends Type { + public String toPrint(String prefix) { + return prefix + "Void\n"; + } +} diff --git a/src/parser/Python3Lexer.java b/src/parser/Python3Lexer.java index 9a48207..ae33e3d 100644 --- a/src/parser/Python3Lexer.java +++ b/src/parser/Python3Lexer.java @@ -1,8 +1,10 @@ package com.clp.project.parser; -// import com.clp.project.parser.Python3LexerBase; - -// Generated from src/Python3Lexer.g4 by ANTLR 4.13.1 +// Generated from src/parser/Python3Lexer.g4 by ANTLR 4.13.1 +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA; diff --git a/src/parser/Python3Parser.java b/src/parser/Python3Parser.java index 4965501..a407482 100644 --- a/src/parser/Python3Parser.java +++ b/src/parser/Python3Parser.java @@ -1,6 +1,6 @@ package com.clp.project.parser; -// Generated from src/Python3Parser.g4 by ANTLR 4.13.1 +// Generated from src/parser/Python3Parser.g4 by ANTLR 4.13.1 import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.*; diff --git a/src/parser/Python3ParserBaseListener.java b/src/parser/Python3ParserBaseListener.java index 6fa323f..7191565 100644 --- a/src/parser/Python3ParserBaseListener.java +++ b/src/parser/Python3ParserBaseListener.java @@ -1,5 +1,5 @@ package com.clp.project.parser; -// Generated from src/Python3Parser.g4 by ANTLR 4.13.1 +// Generated from src/parser/Python3Parser.g4 by ANTLR 4.13.1 import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ErrorNode; diff --git a/src/parser/Python3ParserBaseVisitor.java b/src/parser/Python3ParserBaseVisitor.java index 4ad92a0..4fc355f 100644 --- a/src/parser/Python3ParserBaseVisitor.java +++ b/src/parser/Python3ParserBaseVisitor.java @@ -1,6 +1,6 @@ package com.clp.project.parser; -// Generated from src/Python3Parser.g4 by ANTLR 4.13.1 +// Generated from src/parser/Python3Parser.g4 by ANTLR 4.13.1 import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; /** diff --git a/src/parser/Python3ParserListener.java b/src/parser/Python3ParserListener.java index 248105d..f71a32d 100644 --- a/src/parser/Python3ParserListener.java +++ b/src/parser/Python3ParserListener.java @@ -1,6 +1,6 @@ package com.clp.project.parser; -// Generated from src/Python3Parser.g4 by ANTLR 4.13.1 +// Generated from src/parser/Python3Parser.g4 by ANTLR 4.13.1 import org.antlr.v4.runtime.tree.ParseTreeListener; /** diff --git a/src/parser/Python3ParserVisitor.java b/src/parser/Python3ParserVisitor.java index b286b61..88607ed 100644 --- a/src/parser/Python3ParserVisitor.java +++ b/src/parser/Python3ParserVisitor.java @@ -1,6 +1,6 @@ package com.clp.project.parser; -// Generated from src/Python3Parser.g4 by ANTLR 4.13.1 +// Generated from src/parser/Python3Parser.g4 by ANTLR 4.13.1 import org.antlr.v4.runtime.tree.ParseTreeVisitor; /** diff --git a/src/semanticanalysis/STentry.java b/src/semanticanalysis/STentry.java new file mode 100644 index 0000000..42990fa --- /dev/null +++ b/src/semanticanalysis/STentry.java @@ -0,0 +1,55 @@ +package com.clp.project.semanticanalysis; + +import com.clp.project.ast.types.Type; + +/** + * Entry class for the symbol table. + */ +public class STentry { + private Type type; + private int offset; + private int nesting; + private String label; + + public STentry(Type type, int offset, int nesting) { + this.type = type; + this.offset = offset; + this.nesting = nesting; + } + + public STentry(Type type, int offset, int nesting, String label) { + this.type = type; + this.offset = offset; + this.nesting = nesting; + this.label = label; + } + + /** + * Getter for `type` + */ + public Type getType() { + return type; + } + + /** + * Getter for `offset` + */ + public int getOffset() { + return offset; + } + + /** + * Getter for `nesting` + */ + public int getNesting() { + return nesting; + } + + /** + * Getter for `label` + */ + public String getLabel() { + return label; + } + +} diff --git a/src/semanticanalysis/SemanticError.java b/src/semanticanalysis/SemanticError.java new file mode 100644 index 0000000..1df1ce9 --- /dev/null +++ b/src/semanticanalysis/SemanticError.java @@ -0,0 +1,16 @@ +package com.clp.project.semanticanalysis; + +/** + * Class respresents a semantic error. + */ +public class SemanticError { + private String msg; + + public SemanticError(String msg) { + this.msg = msg; + } + + public String toString() { + return msg; + } +} diff --git a/src/semanticanalysis/SymbolTable.java b/src/semanticanalysis/SymbolTable.java new file mode 100644 index 0000000..d4fda37 --- /dev/null +++ b/src/semanticanalysis/SymbolTable.java @@ -0,0 +1,153 @@ +package com.clp.project.semanticanalysis; + +import java.util.ArrayList; +import java.util.HashMap; +import com.clp.project.ast.*; +import com.clp.project.ast.types.*; + +/** + * Class representing a symbol table. It's a list of hash table symbol table. We + * keep track of a ArrayList of HashMap called `symbolTable` and an array of + * integer called `offset`. + */ +public class SymbolTable { + + private ArrayList<HashMap<String, STentry>> symbolTable; + private ArrayList<Integer> offset; + + public SymbolTable() { + this.symbolTable = new ArrayList<HashMap<String, STentry>>(); + this.offset = new ArrayList<Integer>(); + } + + /** + * Returns the nesting level. + */ + public Integer nesting() { + return this.symbolTable.size() - 1; + } + + /** + * Check out if an `id` is into the symbol table. Returns an STentry object + * or null if the `id` is not found. + * + * @param id is the identifier of the STentry to find. + */ + public STentry lookup(String id) { + int n = this.symbolTable.size() - 1; + boolean found = false; + STentry T = null; + while ((n >= 0) && !found) { + HashMap<String, STentry> H = this.symbolTable.get(n); + T = H.get(id); + if (T != null) { + found = true; + }else { + n = n - 1; + } + } + return T; + } + + /** + * Return the position of a STentry given the `id`, if it exists. Otherwise + * return `-1`. We start the search from the last inserted hashmap. + * + * @param id is the identifier of the STentry to find. + */ + public Integer nslookup(String id) { + int n = this.symbolTable.size() - 1; + boolean found = false; + while ((n >= 0) && !found) { + HashMap<String, STentry> H = this.symbolTable.get(n); + if (H.get(id) != null) { + found = true; + }else { + n = n - 1; + } + } + return n; + } + + /** + * Add an hashmap to the given symbol table and increase the offset level. + * We start from 2 because we have FP and AL before all. + * + * @param H is an hashmap that is must be added into the symbol table + */ + public void add(HashMap<String, STentry> H) { + this.symbolTable.add(H); + this.offset.add(1); + } + + /** + * Remove the last level for the symbol table. + */ + public void remove() { + int x = this.symbolTable.size(); + this.symbolTable.remove(x - 1); + this.offset.remove(x - 1); + } + + /** + * Return `true` if the `id` is present in the last inseted hashmap. + * Otherwise return `false`. + * + * @param id is the identifier of the STentry to find. + */ + public boolean top_lookup(String id) { + int n = symbolTable.size() - 1; + STentry T = null; + HashMap<String, STentry> H = symbolTable.get(n); + T = H.get(id); + return (T != null); + } + + /** + * Insert a new entry into the symbol table. + * + * @param id + * @param type + * @param _nesting + * @param _label + */ + public void insert(String id, Type type, int _nesting, String _label) { + int n = symbolTable.size() - 1; + HashMap<String, STentry> H = this.symbolTable.get(n); + this.symbolTable.remove(n); + + int offs = this.offset.get(n); + this.offset.remove(n); + + STentry idtype = new STentry(type, offs, _nesting, _label); + H.put(id, idtype); + + this.symbolTable.add(H); + + // We always increment the offset by 1 otherwise we need ad-hoc bytecode + // operations + if (type.getClass().equals((new BoolType()).getClass())) { + offs = offs + 1; + } else if (type.getClass().equals((new IntType()).getClass())) { + offs = offs + 1; + } else { + offs = offs + 1; + } + + this.offset.add(offs); + } + + /** + * Increase the offset level. + */ + public void increaseoffset() { + int n = this.offset.size() - 1; + int offs = this.offset.get(n); + this.offset.remove(n); + + offs = offs + 1; + + this.offset.add(offs); + } + +} |