diff --git a/src/main/java/CompilerInput.txt b/src/main/java/CompilerInput.txt deleted file mode 100644 index 918c686..0000000 --- a/src/main/java/CompilerInput.txt +++ /dev/null @@ -1,13 +0,0 @@ -public class Example { - - public int testVar; - - public static int testMethod(char b){ - - int a; - int a; - - - } - -} \ No newline at end of file diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 97ea0ef..a4c274d 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -16,16 +16,20 @@ import java.nio.file.Paths; public class Main { public static void main(String[] args) throws Exception { - try { - CharStream codeCharStream = CharStreams.fromPath(Paths.get("src/main/java/CompilerInput.txt")); - parseFile(codeCharStream); - } catch (IOException e) { - System.err.println("Error reading the file: " + e.getMessage()); + if(args.length > 0) { + + } else { + try { + CharStream codeCharStream = CharStreams.fromPath(Paths.get("src/main/resources/CompilerInput.java")); + parseFile(codeCharStream); + } catch (IOException e) { + System.err.println("Error reading the file: " + e.getMessage()); + } } } - static void parseFile(CharStream codeCharStream){ + static void parseFile(CharStream codeCharStream) { SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream); CommonTokenStream tokens = new CommonTokenStream(lexer); SimpleJavaParser parser = new SimpleJavaParser(tokens); @@ -38,6 +42,8 @@ public class Main { ProgramNode typedAst = (ProgramNode) SemanticAnalyzer.generateTast(ast); ByteCodeGenerator byteCodeGenerator = new ByteCodeGenerator(); - byteCodeGenerator.visit(typedAst); + if (typedAst != null) + byteCodeGenerator.visit(typedAst); } + } \ No newline at end of file diff --git a/src/main/java/ast/ClassNode.java b/src/main/java/ast/ClassNode.java index 947a977..f1f5a4c 100644 --- a/src/main/java/ast/ClassNode.java +++ b/src/main/java/ast/ClassNode.java @@ -2,6 +2,7 @@ package ast; import ast.member.ConstructorNode; import ast.member.MemberNode; +import ast.member.MethodNode; import ast.type.AccessTypeNode; import ast.type.EnumAccessTypeNode; @@ -38,6 +39,16 @@ public class ClassNode implements ASTNode, Visitable { } } + public List getMethods(){ + List methods = new ArrayList<>(); + for (MemberNode member : members) { + if (member instanceof MethodNode methodNode) { + methods.add(methodNode); + } + } + return methods; + } + @Override public TypeCheckResult accept(SemanticVisitor visitor) { return visitor.analyze(this); diff --git a/src/main/java/ast/member/MethodNode.java b/src/main/java/ast/member/MethodNode.java index c50f6af..bd67bc4 100644 --- a/src/main/java/ast/member/MethodNode.java +++ b/src/main/java/ast/member/MethodNode.java @@ -36,6 +36,22 @@ public class MethodNode implements MemberNode, Visitable { this.identifier = identifier; } + public boolean isSame(MethodNode methodNode){ + boolean isSame = false; + if(methodNode.identifier.equals(identifier)){ + if(parameters != null && methodNode.parameters != null){ + if(parameters.parameters.size() == methodNode.parameters.parameters.size()){ + for(int i = 0; i < parameters.parameters.size(); i++){ + if(parameters.parameters.get(i).identifier.equals(methodNode.parameters.parameters.get(i).identifier)){ + isSame = true; + } + } + } + } + } + return isSame; + } + @Override public TypeCheckResult accept(SemanticVisitor visitor) { return visitor.analyze(this); diff --git a/src/main/java/ast/parameter/ParameterListNode.java b/src/main/java/ast/parameter/ParameterListNode.java index 69d12f3..ff1c58d 100644 --- a/src/main/java/ast/parameter/ParameterListNode.java +++ b/src/main/java/ast/parameter/ParameterListNode.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; public class ParameterListNode implements ASTNode { - List parameters = new ArrayList<>(); + public List parameters = new ArrayList<>(); public ParameterListNode(List parameters){ this.parameters = parameters; diff --git a/src/main/java/classFileOutput/Example.class b/src/main/java/classFileOutput/Example.class index c99f3e0..925c770 100644 Binary files a/src/main/java/classFileOutput/Example.class and b/src/main/java/classFileOutput/Example.class differ diff --git a/src/main/java/parser/ASTBuilder.java b/src/main/java/parser/ASTBuilder.java index ee61b9b..ff41738 100644 --- a/src/main/java/parser/ASTBuilder.java +++ b/src/main/java/parser/ASTBuilder.java @@ -54,7 +54,11 @@ public class ASTBuilder extends SimpleJavaBaseVisitor { AccessTypeNode accessType = (AccessTypeNode) visit(ctx.accessType()); TypeNode returnType = (TypeNode) visit(ctx.type()); String methodName = ctx.IDENTIFIER().getText(); - ParameterListNode parameterListNode = (ParameterListNode) visit(ctx.parameterList()); + + ParameterListNode parameterListNode = null; + if(ctx.parameterList() != null) { + parameterListNode = (ParameterListNode) visit(ctx.parameterList()); + } List statements = new ArrayList<>(); for (SimpleJavaParser.StatementContext stmtCtx : ctx.statement()) { statements.add((StatementNode) visit(stmtCtx)); diff --git a/src/main/java/semantic/Scope.java b/src/main/java/semantic/Scope.java index ab5c0cb..7cf4cc9 100644 --- a/src/main/java/semantic/Scope.java +++ b/src/main/java/semantic/Scope.java @@ -9,6 +9,10 @@ public class Scope { private Stack> localVars; + public Scope() { + localVars = new Stack>(); + } + public void addLocalVar(String name, TypeNode type) { if (this.contains(name)) { throw new RuntimeException("Variable " + name + " already exists in this scope"); diff --git a/src/main/java/semantic/SemanticAnalyzer.java b/src/main/java/semantic/SemanticAnalyzer.java index 7a6002b..e6cdc99 100644 --- a/src/main/java/semantic/SemanticAnalyzer.java +++ b/src/main/java/semantic/SemanticAnalyzer.java @@ -5,32 +5,42 @@ import ast.*; import ast.expression.BinaryExpressionNode; import ast.expression.IdentifierExpressionNode; import ast.expression.UnaryExpressionNode; +import ast.member.ConstructorNode; import ast.member.FieldNode; import ast.member.MemberNode; import ast.member.MethodNode; +import ast.parameter.ParameterListNode; +import ast.parameter.ParameterNode; import ast.statement.*; import java.util.ArrayList; import java.util.List; +import semantic.exeptions.AlreadyDeclearedException; import typechecker.TypeCheckResult; public class SemanticAnalyzer implements SemanticVisitor { private ArrayList currentFields = new ArrayList<>(); - private Scope currentScope; + public static ArrayList errors = new ArrayList<>(); - public static ASTNode generateTast(ASTNode node) throws RuntimeException { + private Scope currentScope; + private ClassNode currentClass; + + public static ASTNode generateTast(ASTNode node) { SemanticAnalyzer semanticCheck = new SemanticAnalyzer(); ProgramNode programNode = (ProgramNode) node; var result = programNode.accept(semanticCheck); if (result.isValid()) { return node; } else { - throw new RuntimeException("Not Valid"); + for (Exception e : errors) { + e.printStackTrace(System.out); + } } + return null; } @Override @@ -38,6 +48,8 @@ public class SemanticAnalyzer implements SemanticVisitor { var valid = true; + currentScope = new Scope(); + List classes = node.classes; for (ClassNode classNode : classes) { var result = classNode.accept(this); @@ -49,12 +61,25 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(ClassNode classNode) { var valid = true; + + currentClass = classNode; + List members = classNode.members; for (MemberNode memberNode : members) { if (memberNode instanceof FieldNode fieldNode) { + //LocalFields var result = fieldNode.accept(this); valid = valid && result.isValid(); } else if (memberNode instanceof MethodNode methodNode) { + //Methods + for(MethodNode methode : currentClass.getMethods()){ + if(methode.equals(methodNode)) + break; + if(methode.isSame(methodNode)){ + errors.add(new AlreadyDeclearedException("This method has already been declared")); + valid = false; + } + } var result = methodNode.accept(this); valid = valid && result.isValid(); } @@ -68,8 +93,23 @@ public class SemanticAnalyzer implements SemanticVisitor { public TypeCheckResult analyze(MethodNode methodNode) { var valid = true; - currentLocalScope.pushScope(); + currentScope.pushScope(); + //Parameter + ParameterListNode parameterListNode = methodNode.parameters; + if (parameterListNode != null) { + List parameters = parameterListNode.parameters; + for (ParameterNode parameter : parameters) { + if (currentScope.contains(parameter.identifier)) { + errors.add(new AlreadyDeclearedException("Duplicated Parameter " + parameter.identifier)); + return new TypeCheckResult(false, null); + } else { + currentScope.addLocalVar(parameter.identifier, parameter.type); + } + } + } + + //Statements List statements = methodNode.statements; for (StatementNode statement : statements) { if (statement instanceof AssignmentStatementNode assignmentStatementNode) { @@ -80,13 +120,16 @@ public class SemanticAnalyzer implements SemanticVisitor { valid = valid && result.isValid(); } } + + currentScope.popScope(); return new TypeCheckResult(valid, null); } @Override public TypeCheckResult analyze(FieldNode toCheck) { if (currentFields.contains(toCheck.identifier)) { - throw new RuntimeException(toCheck.identifier + " Is Already Declared"); + errors.add(new AlreadyDeclearedException("Already declared " + toCheck.identifier)); + return new TypeCheckResult(false, null); } else { currentFields.add(toCheck.identifier); } @@ -124,7 +167,12 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(VariableDeclarationStatementNode toCheck) { - + if (currentScope.contains(toCheck.identifier)) { + errors.add(new AlreadyDeclearedException("Already declared " + toCheck.identifier + " in this scope")); + return new TypeCheckResult(false, null); + } else { + currentScope.addLocalVar(toCheck.identifier, toCheck.type); + } return new TypeCheckResult(true, null); } diff --git a/src/main/java/semantic/exeptions/AlreadyDeclearedException.java b/src/main/java/semantic/exeptions/AlreadyDeclearedException.java new file mode 100644 index 0000000..d00fd36 --- /dev/null +++ b/src/main/java/semantic/exeptions/AlreadyDeclearedException.java @@ -0,0 +1,9 @@ +package semantic.exeptions; + +public class AlreadyDeclearedException extends RuntimeException { + + public AlreadyDeclearedException(String message) { + super(message); + } + +} diff --git a/src/main/resources/CompilerInput.java b/src/main/resources/CompilerInput.java new file mode 100644 index 0000000..2efd50e --- /dev/null +++ b/src/main/resources/CompilerInput.java @@ -0,0 +1,10 @@ +public class Example { + + public int a; + + public static int testMethod(char x, int a){ + + + } + +} \ No newline at end of file