summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/Main.java11
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/AST.java8
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java52
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java7
-rw-r--r--src/test/java/no/eliashaugsbakk/kompilator/parsing/ParserTest.java6
-rw-r--r--src/test/java/no/eliashaugsbakk/kompilator/semanticAnalysis/AnalyzerTest.java48
6 files changed, 126 insertions, 6 deletions
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/Main.java b/src/main/java/no/eliashaugsbakk/kompilator/Main.java
index fa7f79c..b1ad70a 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/Main.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/Main.java
@@ -9,6 +9,8 @@ import no.eliashaugsbakk.kompilator.assembleAndLink.AssemblerAndLinker;
import no.eliashaugsbakk.kompilator.parsing.AST;
import no.eliashaugsbakk.kompilator.parsing.Parser;
import no.eliashaugsbakk.kompilator.parsing.ParserException;
+import no.eliashaugsbakk.kompilator.semanticAnalysis.Analyzer;
+import no.eliashaugsbakk.kompilator.semanticAnalysis.SemanticException;
import no.eliashaugsbakk.kompilator.tokenization.Lexer;
import no.eliashaugsbakk.kompilator.tokenization.Token;
@@ -44,10 +46,17 @@ public class Main {
ast = new Parser(tokens).parse();
} catch (ParserException e) {
IO.println("Error while parsing: " + e.getMessage());
+ System.exit(1);
+ }
+
+ try {
+ new Analyzer(ast).analyze();
+ } catch (SemanticException e) {
+ IO.println("Semantic error: " + e.getMessage());
+ System.exit(1);
}
// TODO:
- // new Analyzer(ast).analyze();
// List<String> IR = new IRGenerator(ast).generate();
List<String> IR = List.of();
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/AST.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/AST.java
index 95ec77c..ad8a3a2 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/AST.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/AST.java
@@ -3,9 +3,13 @@ package no.eliashaugsbakk.kompilator.parsing;
import no.eliashaugsbakk.kompilator.parsing.node.ASTNode;
public class AST {
- ASTNode root;
+ private final ASTNode root;
- AST(ASTNode root) {
+ public AST(ASTNode root) {
this.root = root;
}
+
+ public ASTNode getRoot() {
+ return root;
+ }
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java
index 891018c..0b01b14 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java
@@ -1,4 +1,56 @@
package no.eliashaugsbakk.kompilator.semanticAnalysis;
+import no.eliashaugsbakk.kompilator.parsing.AST;
+import no.eliashaugsbakk.kompilator.parsing.node.Program;
+import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression;
+import no.eliashaugsbakk.kompilator.parsing.node.expression.FunctionCall;
+import no.eliashaugsbakk.kompilator.parsing.node.expression.StringLiteral;
+import no.eliashaugsbakk.kompilator.parsing.node.statement.ExpressionStatement;
+import no.eliashaugsbakk.kompilator.parsing.node.statement.Statement;
+
public class Analyzer {
+ private final AST ast;
+
+ public Analyzer(AST ast) {
+ this.ast = ast;
+ }
+
+ public void analyze() throws SemanticException {
+ Program program = (Program) ast.getRoot();
+
+ for (Statement stmt : program.statements) {
+ analyzeStatement(stmt);
+ }
+ }
+
+ private void analyzeStatement(Statement stmt) throws SemanticException {
+ if (stmt instanceof ExpressionStatement exprStmt) {
+ analyzeExpression(exprStmt.expression);
+ } else {
+ // Analyze the statement
+ // NO other statements implemented
+ }
+ }
+
+ private void analyzeExpression(Expression expr) throws SemanticException {
+ if (expr instanceof FunctionCall call) {
+ checkFunctionCall(call);
+ } else {
+ // analyze the expression
+ }
+ }
+
+ private void checkFunctionCall(FunctionCall call) throws SemanticException {
+ if (!call.functionName.equals("print")) {
+ throw new SemanticException("unknown function: " + call.functionName);
+ }
+
+ if (call.arguments.size() != 1) {
+ throw new SemanticException("print expects 1 argument, got " + call.arguments.size());
+ }
+
+ if (!(call.arguments.getFirst() instanceof StringLiteral)) {
+ throw new SemanticException("print expects String argument only");
+ }
+ }
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java
new file mode 100644
index 0000000..402cfe7
--- /dev/null
+++ b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java
@@ -0,0 +1,7 @@
+package no.eliashaugsbakk.kompilator.semanticAnalysis;
+
+public class SemanticException extends Exception {
+ public SemanticException(String message) {
+ super(message);
+ }
+}
diff --git a/src/test/java/no/eliashaugsbakk/kompilator/parsing/ParserTest.java b/src/test/java/no/eliashaugsbakk/kompilator/parsing/ParserTest.java
index 133e9a6..ac31dfc 100644
--- a/src/test/java/no/eliashaugsbakk/kompilator/parsing/ParserTest.java
+++ b/src/test/java/no/eliashaugsbakk/kompilator/parsing/ParserTest.java
@@ -30,9 +30,9 @@ class ParserTest {
AST ast = new Parser(tokens).parse();
// Verify tree structure
- assertNotNull(ast.root);
- assertInstanceOf(Program.class, ast.root);
- Program program = (Program) ast.root;
+ assertNotNull(ast.getRoot());
+ assertInstanceOf(Program.class, ast.getRoot());
+ Program program = (Program) ast.getRoot();
assertEquals(1, program.statements.size());
ExpressionStatement stmt = (ExpressionStatement) program.statements.getFirst();
diff --git a/src/test/java/no/eliashaugsbakk/kompilator/semanticAnalysis/AnalyzerTest.java b/src/test/java/no/eliashaugsbakk/kompilator/semanticAnalysis/AnalyzerTest.java
new file mode 100644
index 0000000..531673d
--- /dev/null
+++ b/src/test/java/no/eliashaugsbakk/kompilator/semanticAnalysis/AnalyzerTest.java
@@ -0,0 +1,48 @@
+package no.eliashaugsbakk.kompilator.semanticAnalysis;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.List;
+import no.eliashaugsbakk.kompilator.parsing.AST;
+import no.eliashaugsbakk.kompilator.parsing.node.Program;
+import no.eliashaugsbakk.kompilator.parsing.node.expression.FunctionCall;
+import no.eliashaugsbakk.kompilator.parsing.node.expression.StringLiteral;
+import no.eliashaugsbakk.kompilator.parsing.node.statement.ExpressionStatement;
+import org.junit.jupiter.api.Test;
+
+class AnalyzerTest {
+
+ @Test
+ void validPrintStatementDoesNotThrow() {
+ AST ast = buildAST("print", "hello");
+ assertDoesNotThrow(() -> new Analyzer(ast).analyze());
+ }
+
+ @Test
+ void unknownFunctionThrows() {
+ AST ast = buildAST("unknown", "hello");
+ assertThrows(SemanticException.class, () -> new Analyzer(ast).analyze());
+ }
+
+ @Test
+ void missingArgumentThrows() {
+ AST ast = buildASTNoArgs("print");
+ assertThrows(SemanticException.class, () -> new Analyzer(ast).analyze());
+ }
+
+ private AST buildAST(String functionName, String argument) {
+ FunctionCall call = new FunctionCall(functionName, List.of(new StringLiteral(argument)));
+ ExpressionStatement stmt = new ExpressionStatement(call);
+ Program program = new Program();
+ program.addStatement(stmt);
+ return new AST(program);
+ }
+
+ private AST buildASTNoArgs(String functionName) {
+ FunctionCall call = new FunctionCall(functionName, List.of());
+ ExpressionStatement stmt = new ExpressionStatement(call);
+ Program program = new Program();
+ program.addStatement(stmt);
+ return new AST(program);
+ }
+}