Merge branch 'main' into Tests
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
This commit is contained in:
commit
b2e1745d51
15
.gitea/workflows/test.yml
Normal file
15
.gitea/workflows/test.yml
Normal file
@ -0,0 +1,15 @@
|
||||
name: Gitea Actions Demo
|
||||
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Explore-Gitea-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Run the Maven verify phase
|
||||
run: mvn --batch-mode --update-snapshots verify
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -40,7 +40,7 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
@ -1,13 +0,0 @@
|
||||
public class Example {
|
||||
|
||||
public int testVar;
|
||||
|
||||
public static int testMethod(char b){
|
||||
|
||||
int a;
|
||||
int a;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -19,52 +19,59 @@ import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0) {
|
||||
|
||||
} else {
|
||||
try {
|
||||
CharStream codeCharStream = CharStreams.fromPath(Paths.get("src/main/java/CompilerInput.txt"));
|
||||
parsefile(codeCharStream);
|
||||
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) {
|
||||
/* ------------------------- Scanner -> tokens ------------------------- */
|
||||
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
|
||||
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
|
||||
|
||||
// Printing the tokens
|
||||
tokenStream.fill();
|
||||
List<Token> tokens = tokenStream.getTokens();
|
||||
System.out.println("-------------------- Scanner -> tokens --------------------");
|
||||
for (Token token : tokens) {
|
||||
String tokenType = SimpleJavaLexer.VOCABULARY.getSymbolicName(token.getType());
|
||||
String tokenText = token.getText();
|
||||
// System.out.println("Token Type: " + tokenType + ", Token Text: " +
|
||||
// tokenText);
|
||||
System.out.println(tokenType + " " + tokenText);
|
||||
}
|
||||
System.out.println();
|
||||
// tokenStream.fill();
|
||||
// List<Token> tokens = tokenStream.getTokens();
|
||||
// System.out.println("-------------------- Scanner -> tokens
|
||||
// --------------------");
|
||||
// for (Token token : tokens) {
|
||||
// String tokenType =
|
||||
// SimpleJavaLexer.VOCABULARY.getSymbolicName(token.getType());
|
||||
// String tokenText = token.getText();
|
||||
// // System.out.println("Token Type: " + tokenType + ", Token Text: " +
|
||||
// // tokenText);
|
||||
// System.out.println(tokenType + " " + tokenText);
|
||||
// }
|
||||
// System.out.println();
|
||||
|
||||
/*------------------------- Parser -> Parsetree -------------------------*/
|
||||
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
|
||||
ParseTree parseTree = parser.program(); // parse the input
|
||||
|
||||
// Printing the parse tree
|
||||
System.out.println("-------------------- Parser -> Parsetree --------------------");
|
||||
System.out.println(parseTree.toStringTree(parser));
|
||||
printTree(parseTree, parser, 0);
|
||||
System.out.println();
|
||||
// System.out.println("-------------------- Parser -> Parsetree
|
||||
// --------------------");
|
||||
// System.out.println(parseTree.toStringTree(parser));
|
||||
// printTree(parseTree, parser, 0);
|
||||
// System.out.println();
|
||||
|
||||
/*------------------------- AST builder -> AST -------------------------*/
|
||||
ASTBuilder astBuilder = new ASTBuilder();
|
||||
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
|
||||
|
||||
// Printing the AST
|
||||
System.out.println("-------------------- AST builder -> AST --------------------");
|
||||
// System.out.println("AST: " + ast.toString());
|
||||
printAST(abstractSyntaxTree, 0);
|
||||
System.out.println();
|
||||
// System.out.println("-------------------- AST builder -> AST
|
||||
// --------------------");
|
||||
// // System.out.println("AST: " + ast.toString());
|
||||
// printAST(abstractSyntaxTree, 0);
|
||||
// System.out.println();
|
||||
|
||||
/*------------------------- Semantic Analyzer -> Tast -------------------------*/
|
||||
SemanticAnalyzer.generateTast(abstractSyntaxTree);
|
||||
@ -122,4 +129,5 @@ public class Main {
|
||||
// printAST(child, indent + 1);
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
@ -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<MethodNode> getMethods(){
|
||||
List<MethodNode> 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);
|
||||
|
@ -1,29 +0,0 @@
|
||||
package ast;
|
||||
|
||||
import semantic.SemanticVisitor;
|
||||
import typechecker.TypeCheckResult;
|
||||
import visitor.Visitable;
|
||||
|
||||
public class VarNode implements ASTNode, Visitable {
|
||||
|
||||
private String identifier;
|
||||
private String type;
|
||||
|
||||
public VarNode(String type, String identifier){
|
||||
this.type = type;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public String getType(){
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getIdentifier(){
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeCheckResult accept(SemanticVisitor visitor) {
|
||||
return visitor.analyze(this);
|
||||
}
|
||||
}
|
@ -7,9 +7,9 @@ import visitor.Visitable;
|
||||
public class BinaryExpressionNode implements ExpressionNode, Visitable {
|
||||
public ExpressionNode left;
|
||||
public ExpressionNode right;
|
||||
public String operator; // Stores the operator as a string (e.g., "+", "-", "&&")
|
||||
public ExpresssionOperator operator; // Stores the operator as a string (e.g., "+", "-", "&&")
|
||||
|
||||
public BinaryExpressionNode(ExpressionNode left, ExpressionNode right, String operator) {
|
||||
public BinaryExpressionNode(ExpressionNode left, ExpressionNode right, ExpresssionOperator operator) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.operator = operator;
|
||||
|
14
src/main/java/ast/expression/ExpresssionOperator.java
Normal file
14
src/main/java/ast/expression/ExpresssionOperator.java
Normal file
@ -0,0 +1,14 @@
|
||||
package ast.expression;
|
||||
|
||||
public enum ExpresssionOperator {
|
||||
DOT, // .
|
||||
PLUS, // +
|
||||
MINUS, // -
|
||||
MULTIPLY, // *
|
||||
DIVIDE, // /
|
||||
NOT, // !
|
||||
ASSIGNMENT, // =
|
||||
EQUALS, // ==
|
||||
UNEQUALS, // !=
|
||||
ERROR //TODO: Remove This
|
||||
}
|
@ -18,3 +18,5 @@ public class UnaryExpressionNode implements ExpressionNode, Visitable {
|
||||
return visitor.analyze(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -6,7 +6,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ParameterListNode implements ASTNode {
|
||||
List<ParameterNode> parameters = new ArrayList<>();
|
||||
public List<ParameterNode> parameters = new ArrayList<>();
|
||||
|
||||
public ParameterListNode(List<ParameterNode> parameters){
|
||||
this.parameters = parameters;
|
||||
|
@ -1,17 +1,14 @@
|
||||
package ast.statement;
|
||||
|
||||
import ast.VarNode;
|
||||
import ast.expression.ExpressionNode;
|
||||
import ast.expression.BinaryExpressionNode;
|
||||
import semantic.SemanticVisitor;
|
||||
import typechecker.TypeCheckResult;
|
||||
import visitor.Visitable;
|
||||
|
||||
public class AssignmentStatementNode extends StatementNode implements Visitable {
|
||||
public VarNode varNode;
|
||||
public ExpressionNode expression;
|
||||
public BinaryExpressionNode expression;
|
||||
|
||||
public AssignmentStatementNode(VarNode varNode, ExpressionNode expression) {
|
||||
this.varNode = varNode;
|
||||
public AssignmentStatementNode(BinaryExpressionNode expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
|
@ -13,14 +13,19 @@ import org.objectweb.asm.Opcodes;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class ClassCodeGen implements ClassVisitor {
|
||||
Mapper mapper = new Mapper();
|
||||
ClassWriter classWriter;
|
||||
private Mapper mapper;
|
||||
private ClassWriter classWriter;
|
||||
|
||||
public ClassCodeGen() {
|
||||
mapper = new Mapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ClassNode classNode) {
|
||||
classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
classWriter.visit(Opcodes.V1_8, mapper.mapAccesTypeToOpcode(classNode.accessType), classNode.identifier, null,
|
||||
classWriter.visit(Opcodes.V1_5, mapper.mapAccessTypeToOpcode(classNode.accessType), classNode.identifier, null,
|
||||
"java/lang/Object", null);
|
||||
|
||||
for (MemberNode memberNode : classNode.members) {
|
||||
@ -41,7 +46,7 @@ public class ClassCodeGen implements ClassVisitor {
|
||||
@Override
|
||||
public void visit(FieldNode fieldNode) {
|
||||
if(fieldNode.type instanceof BaseTypeNode baseTypeNode){
|
||||
classWriter.visitField(mapper.mapAccesTypeToOpcode(fieldNode.accessTypeNode), fieldNode.identifier, mapper.getTypeChar(baseTypeNode.enumType), null, null );
|
||||
classWriter.visitField(mapper.mapAccessTypeToOpcode(fieldNode.accessTypeNode), fieldNode.identifier, mapper.getTypeChar(baseTypeNode.enumType), null, null );
|
||||
}
|
||||
classWriter.visitEnd();
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
package bytecode;
|
||||
|
||||
import ast.member.FieldNode;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
|
||||
public class FieldCodeGen {
|
||||
|
||||
public void generateFieldCode(ClassWriter classWriter, FieldNode fieldNode) {
|
||||
Mapper mapper = new Mapper();
|
||||
FieldVisitor fieldVisitor = classWriter.visitField(mapper.mapAccesTypeToOpcode(fieldNode.accessTypeNode), fieldNode.identifier, "", null, null);
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
package bytecode;
|
||||
|
||||
import ast.parameter.ParameterListNode;
|
||||
import ast.parameter.ParameterNode;
|
||||
import ast.type.*;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import ast.type.BaseTypeNode;
|
||||
|
||||
public class Mapper {
|
||||
public int mapAccesTypeToOpcode(AccessTypeNode type) {
|
||||
public int mapAccessTypeToOpcode(AccessTypeNode type) {
|
||||
switch (type.enumAccessTypeNode) {
|
||||
case EnumAccessTypeNode.PUBLIC:
|
||||
return Opcodes.ACC_PUBLIC;
|
||||
@ -15,8 +17,12 @@ public class Mapper {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String generateMethodDescriptor(BaseTypeNode baseTypeNode) {
|
||||
String descriptor = "()";
|
||||
public String generateMethodDescriptor(BaseTypeNode baseTypeNode, ParameterListNode parameterListNode) {
|
||||
String descriptor = "(";
|
||||
for(ParameterNode parameterNode : parameterListNode.parameters) {
|
||||
descriptor += getTypeChar(EnumTypeNode.INT);
|
||||
}
|
||||
descriptor += ")";
|
||||
descriptor += getTypeChar(baseTypeNode.enumType);
|
||||
return descriptor;
|
||||
}
|
||||
|
@ -2,39 +2,99 @@ package bytecode;
|
||||
|
||||
import ast.member.ConstructorNode;
|
||||
import ast.member.MethodNode;
|
||||
import ast.parameter.ParameterListNode;
|
||||
import ast.parameter.ParameterNode;
|
||||
import ast.type.BaseTypeNode;
|
||||
import ast.type.EnumTypeNode;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
|
||||
public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
|
||||
|
||||
private ClassWriter classWriter;
|
||||
Mapper mapper = new Mapper();
|
||||
private Mapper mapper;
|
||||
private MethodVisitor methodVisitor;
|
||||
|
||||
private List<String> localVaribales;
|
||||
|
||||
public MethodCodeGen(ClassWriter classWriter) {
|
||||
this.classWriter = classWriter;
|
||||
mapper = new Mapper();
|
||||
localVaribales = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ConstructorNode constructorNode) {
|
||||
MethodVisitor constructor =
|
||||
classWriter.visitMethod(mapper.mapAccesTypeToOpcode(constructorNode.visibility),
|
||||
methodVisitor =
|
||||
classWriter.visitMethod(mapper.mapAccessTypeToOpcode(constructorNode.visibility),
|
||||
"<init>",
|
||||
"()V",
|
||||
null,
|
||||
null);
|
||||
constructor.visitEnd();
|
||||
methodVisitor.visitCode();
|
||||
methodVisitor.visitVarInsn(ALOAD, 0);
|
||||
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
methodVisitor.visitInsn(RETURN);
|
||||
methodVisitor.visitMaxs(1, 1);
|
||||
methodVisitor.visitEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode methodNode) {
|
||||
if (methodNode.type instanceof BaseTypeNode baseTypeNode) {
|
||||
MethodVisitor method = classWriter.visitMethod(mapper.mapAccesTypeToOpcode(methodNode.visibility),
|
||||
methodVisitor = classWriter.visitMethod(mapper.mapAccessTypeToOpcode(methodNode.visibility),
|
||||
methodNode.identifier,
|
||||
mapper.generateMethodDescriptor(baseTypeNode),
|
||||
mapper.generateMethodDescriptor(baseTypeNode, methodNode.parameters),
|
||||
null,
|
||||
null);
|
||||
method.visitEnd();
|
||||
|
||||
methodVisitor.visitCode();
|
||||
localVaribales.add("this");
|
||||
for (ParameterNode parameterNode : methodNode.parameters.parameters) {
|
||||
localVaribales.add(parameterNode.identifier);
|
||||
}
|
||||
|
||||
//test();
|
||||
methodVisitor.visitMaxs(1, localVaribales.size());
|
||||
methodVisitor.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public void test() {
|
||||
Label start = new Label();
|
||||
Label loop = new Label();
|
||||
Label end = new Label();
|
||||
methodVisitor.visitLabel(start);
|
||||
//methodVisitor.visitVarInsn(Opcodes.ICONST_M1, 99);
|
||||
//methodVisitor.visitInsn(Opcodes.ICONST_5);
|
||||
methodVisitor.visitLdcInsn(99);
|
||||
// methodVisitor.visitInsn(Opcodes.ICONST_0);
|
||||
//methodVisitor.visitVarInsn(Opcodes.ILOAD, 2);
|
||||
methodVisitor.visitVarInsn(Opcodes.ISTORE, 1);
|
||||
methodVisitor.visitLabel(loop);
|
||||
methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
methodVisitor.visitInsn(Opcodes.ICONST_5);
|
||||
methodVisitor.visitJumpInsn(Opcodes.IF_ICMPGE, end);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC,
|
||||
"java/lang/System", "out",
|
||||
"Ljava/io/PrintStream;");
|
||||
methodVisitor.visitLdcInsn("Bytecode");
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
||||
"java/io/PrintStream", "println",
|
||||
"(Ljava/lang/String;)V", false);
|
||||
methodVisitor.visitIincInsn(1, 1);
|
||||
methodVisitor.visitJumpInsn(Opcodes.GOTO, loop);
|
||||
methodVisitor.visitLabel(end);
|
||||
methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
methodVisitor.visitInsn(Opcodes.IRETURN);
|
||||
methodVisitor.visitEnd();
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -3,6 +3,7 @@ package parser;
|
||||
import ast.*;
|
||||
import ast.expression.BinaryExpressionNode;
|
||||
import ast.expression.ExpressionNode;
|
||||
import ast.expression.ExpresssionOperator;
|
||||
import ast.expression.IdentifierExpressionNode;
|
||||
import ast.expression.UnaryExpressionNode;
|
||||
import ast.member.FieldNode;
|
||||
@ -54,7 +55,11 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
|
||||
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<StatementNode> statements = new ArrayList<>();
|
||||
for (SimpleJavaParser.StatementContext stmtCtx : ctx.statement()) {
|
||||
statements.add((StatementNode) visit(stmtCtx));
|
||||
@ -133,14 +138,14 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
|
||||
|
||||
@Override
|
||||
public ASTNode visitAssignmentStatement(SimpleJavaParser.AssignmentStatementContext ctx) {
|
||||
VarNode varNode = (VarNode) visit(ctx.var());
|
||||
ExpressionNode expression = (ExpressionNode) visit(ctx.expression());
|
||||
return new AssignmentStatementNode(varNode, expression);
|
||||
|
||||
BinaryExpressionNode expression = (BinaryExpressionNode) visit(ctx.expression());
|
||||
return new AssignmentStatementNode(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTNode visitVar(SimpleJavaParser.VarContext ctx) {
|
||||
return new VarNode("int", ctx.getText());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -176,8 +181,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
|
||||
if (ctx.getChildCount() == 3 && ctx.getChild(1) instanceof TerminalNode) {
|
||||
ExpressionNode left = (ExpressionNode) visit(ctx.expression(0));
|
||||
ExpressionNode right = (ExpressionNode) visit(ctx.expression(1));
|
||||
String operator = ctx.getChild(1).getText();
|
||||
return new BinaryExpressionNode(left, right, operator);
|
||||
return new BinaryExpressionNode(left, right, ExpresssionOperator.ERROR);
|
||||
}
|
||||
// Handle unary operations
|
||||
else if (ctx.getChildCount() == 2) {
|
||||
|
@ -9,6 +9,10 @@ public class Scope {
|
||||
|
||||
private Stack<HashMap<String, TypeNode>> localVars;
|
||||
|
||||
public Scope() {
|
||||
localVars = new Stack<HashMap<String, TypeNode>>();
|
||||
}
|
||||
|
||||
public void addLocalVar(String name, TypeNode type) {
|
||||
if (this.contains(name)) {
|
||||
throw new RuntimeException("Variable " + name + " already exists in this scope");
|
||||
|
@ -5,39 +5,58 @@ 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<String> currentFields = new ArrayList<>();
|
||||
private static ArrayList<String> currentFields = new ArrayList<>();
|
||||
|
||||
private Scope currentScope;
|
||||
public static ArrayList<Exception> errors = new ArrayList<>();
|
||||
|
||||
public static ASTNode generateTast(ASTNode node) throws RuntimeException {
|
||||
private static Scope currentScope;
|
||||
private static 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;
|
||||
}
|
||||
|
||||
public static void clearAnalyzier(){
|
||||
currentFields.clear();
|
||||
errors.clear();
|
||||
currentScope = null;
|
||||
currentClass = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeCheckResult analyze(ProgramNode node) {
|
||||
|
||||
var valid = true;
|
||||
|
||||
currentScope = new Scope();
|
||||
|
||||
List<ClassNode> classes = node.classes;
|
||||
for (ClassNode classNode : classes) {
|
||||
var result = classNode.accept(this);
|
||||
@ -49,12 +68,25 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
||||
@Override
|
||||
public TypeCheckResult analyze(ClassNode classNode) {
|
||||
var valid = true;
|
||||
|
||||
currentClass = classNode;
|
||||
|
||||
List<MemberNode> 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 +100,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<ParameterNode> 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<StatementNode> statements = methodNode.statements;
|
||||
for (StatementNode statement : statements) {
|
||||
if (statement instanceof AssignmentStatementNode assignmentStatementNode) {
|
||||
@ -80,13 +127,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);
|
||||
}
|
||||
@ -95,21 +145,17 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
||||
|
||||
@Override
|
||||
public TypeCheckResult analyze(AssignmentStatementNode assignmentStatementNode) {
|
||||
if (assignmentStatementNode.expression instanceof LiteralNode literalNode) {
|
||||
TypeCheckResult varResult = assignmentStatementNode.varNode.accept(this);
|
||||
TypeCheckResult expressionResult = assignmentStatementNode.expression.accept(this);
|
||||
}
|
||||
return new TypeCheckResult(true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeCheckResult analyze(VarNode toCheck) {
|
||||
return new TypeCheckResult(true, null);
|
||||
boolean valid = true;
|
||||
BinaryExpressionNode binaryExpressionNode = assignmentStatementNode.expression;
|
||||
var result = binaryExpressionNode.accept(this);
|
||||
valid = valid && result.isValid();
|
||||
return new TypeCheckResult(valid, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeCheckResult analyze(BinaryExpressionNode toCheck) {
|
||||
return null;
|
||||
boolean valid = true;
|
||||
return new TypeCheckResult(valid, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -124,7 +170,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);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package semantic;
|
||||
|
||||
import ast.ClassNode;
|
||||
import ast.ProgramNode;
|
||||
import ast.VarNode;
|
||||
import ast.expression.BinaryExpressionNode;
|
||||
import ast.expression.IdentifierExpressionNode;
|
||||
import ast.expression.UnaryExpressionNode;
|
||||
@ -24,8 +23,6 @@ public interface SemanticVisitor {
|
||||
|
||||
TypeCheckResult analyze(AssignmentStatementNode toCheck);
|
||||
|
||||
TypeCheckResult analyze(VarNode toCheck);
|
||||
|
||||
TypeCheckResult analyze(BinaryExpressionNode toCheck);
|
||||
|
||||
TypeCheckResult analyze(IdentifierExpressionNode toCheck);
|
||||
|
@ -0,0 +1,9 @@
|
||||
package semantic.exeptions;
|
||||
|
||||
public class AlreadyDeclearedException extends RuntimeException {
|
||||
|
||||
public AlreadyDeclearedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
18
src/main/resources/CompilerInput.java
Normal file
18
src/main/resources/CompilerInput.java
Normal file
@ -0,0 +1,18 @@
|
||||
public class Example {
|
||||
|
||||
public int a;
|
||||
|
||||
public static int testMethod(char x){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Test {
|
||||
|
||||
public static int testMethod(char x, int a){
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
107
src/test/java/semantic/SemanticTest.java
Normal file
107
src/test/java/semantic/SemanticTest.java
Normal file
@ -0,0 +1,107 @@
|
||||
package semantic;
|
||||
|
||||
|
||||
import ast.*;
|
||||
import ast.expression.BinaryExpressionNode;
|
||||
import ast.expression.ExpressionNode;
|
||||
import ast.expression.ExpresssionOperator;
|
||||
import ast.expression.IdentifierExpressionNode;
|
||||
import ast.member.FieldNode;
|
||||
import ast.member.MemberNode;
|
||||
import ast.member.MethodNode;
|
||||
import ast.parameter.ParameterListNode;
|
||||
import ast.parameter.ParameterNode;
|
||||
import ast.statement.AssignmentStatementNode;
|
||||
import ast.statement.StatementNode;
|
||||
import ast.type.AccessTypeNode;
|
||||
import ast.type.BaseTypeNode;
|
||||
import ast.type.EnumAccessTypeNode;
|
||||
import ast.type.EnumTypeNode;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import semantic.exeptions.AlreadyDeclearedException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class SemanticTest {
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
SemanticAnalyzer.clearAnalyzier();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alreadyDeclaredLocalFieldVar(){
|
||||
|
||||
ProgramNode programNode = new ProgramNode();
|
||||
List<ClassNode> classList = new ArrayList<ClassNode>();
|
||||
AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC);
|
||||
ClassNode classNode = new ClassNode(accessTypeNode, "testClass");
|
||||
|
||||
MemberNode memberNode1 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar");
|
||||
classNode.members.add(memberNode1);
|
||||
|
||||
MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar");
|
||||
classNode.members.add(memberNode2);
|
||||
|
||||
classList.add(classNode);
|
||||
programNode.classes = classList;
|
||||
|
||||
ASTNode typedAst = SemanticAnalyzer.generateTast(programNode);
|
||||
|
||||
assertEquals(1, SemanticAnalyzer.errors.size());
|
||||
assertEquals(true, SemanticAnalyzer.errors.get(0) instanceof AlreadyDeclearedException);
|
||||
assertEquals(null, typedAst);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldWorkWithNoError(){
|
||||
|
||||
ProgramNode programNode = new ProgramNode();
|
||||
List<ClassNode> classList = new ArrayList<ClassNode>();
|
||||
AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC);
|
||||
ClassNode classNode = new ClassNode(accessTypeNode, "testClass");
|
||||
|
||||
MemberNode memberNode1 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar1");
|
||||
classNode.members.add(memberNode1);
|
||||
|
||||
MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2");
|
||||
classNode.members.add(memberNode2);
|
||||
|
||||
List<ParameterNode> parameterNodeList = new ArrayList<ParameterNode>();
|
||||
ParameterNode parameterNode1 = new ParameterNode(new BaseTypeNode(EnumTypeNode.INT), "param1");
|
||||
parameterNodeList.add(parameterNode1);
|
||||
ParameterListNode parameterListNode = new ParameterListNode(parameterNodeList);
|
||||
|
||||
List<StatementNode> statementNodeList = new ArrayList<StatementNode>();
|
||||
|
||||
ExpressionNode expressionNodeObjectVariableLeft = new IdentifierExpressionNode("this");
|
||||
ExpressionNode expressionNodeObjectVariableRight = new IdentifierExpressionNode("objectVar");
|
||||
|
||||
ExpressionNode expressionNodeLeft = new BinaryExpressionNode(expressionNodeObjectVariableLeft, expressionNodeObjectVariableRight, ExpresssionOperator.DOT);
|
||||
|
||||
ExpressionNode expressionNodeRight = new LiteralNode(1);
|
||||
|
||||
BinaryExpressionNode expressionNode = new BinaryExpressionNode(expressionNodeLeft, expressionNodeRight, ExpresssionOperator.ASSIGNMENT);
|
||||
|
||||
StatementNode statementNode1 = new AssignmentStatementNode(expressionNode);
|
||||
statementNodeList.add(statementNode1);
|
||||
|
||||
MemberNode memberNode3 = new MethodNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2",parameterListNode, statementNodeList );
|
||||
classNode.members.add(memberNode3);
|
||||
|
||||
classList.add(classNode);
|
||||
programNode.classes = classList;
|
||||
|
||||
ASTNode typedAst = SemanticAnalyzer.generateTast(programNode);
|
||||
|
||||
assertEquals(0, SemanticAnalyzer.errors.size());
|
||||
assertEquals(programNode, typedAst);
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user