summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerationException.java12
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java8
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/Main.java6
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/Parser.java42
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/ParserException.java12
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/ASTNode.java8
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/Program.java5
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Expression.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/FunctionCall.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Identifier.java5
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/literal/StringLiteral.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Assignment.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/ExpressionStatement.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/IdentifierDeclaration.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Statement.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java39
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java12
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java34
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/tokenization/Position.java4
-rw-r--r--src/main/java/no/eliashaugsbakk/kompilator/tokenization/Token.java2
20 files changed, 146 insertions, 71 deletions
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerationException.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerationException.java
index ab881a0..b6a7062 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerationException.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerationException.java
@@ -1,7 +1,15 @@
package no.eliashaugsbakk.kompilator.IRGeneration;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
+
public class IRGenerationException extends RuntimeException {
- public IRGenerationException(String message) {
- super(message);
+ public IRGenerationException(Position position, String message) {
+ String pos;
+ if (position == null) {
+ pos = "unknown position";
+ } else {
+ pos = position.line() + ":" + position.column();
+ }
+ super(pos + ", " + message);
}
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java
index 657a817..c72db95 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/IRGeneration/IRGenerator.java
@@ -57,7 +57,7 @@ public class IRGenerator {
if (assignment.expression instanceof StringLiteral stringLiteral) {
value = stringLiteral.value;
} else {
- throw new IRGenerationException("Only strings are implemented");
+ throw new IRGenerationException(assignment.position, "Only strings are implemented");
}
ir.add(new Assign(assignment.identifier, value));
}
@@ -67,7 +67,7 @@ public class IRGenerator {
if (identifierDecl.initializer instanceof StringLiteral stringLiteral) {
value = stringLiteral.value;
} else {
- throw new IRGenerationException("Unknown function: " + identifierDecl.initializer);
+ throw new IRGenerationException(identifierDecl.position, "Unknown function: " + identifierDecl.initializer);
}
ir.add(new Alloc(identifierDecl.identifier, identifierDecl.type.name(), identifierDecl.mutable,
value));
@@ -77,7 +77,7 @@ public class IRGenerator {
if (expr instanceof FunctionCall call) {
generateFunctionCall(call);
} else {
- throw new IRGenerationException("Only expression which can stand alone are functions");
+ throw new IRGenerationException(expr.position, "Only expression which can stand alone are functions");
}
}
@@ -93,7 +93,7 @@ public class IRGenerator {
} else if (arg instanceof Identifier identifier) {
arguments.add(identifier.name);
} else {
- throw new IRGenerationException("Unknown function: " + arg);
+ throw new IRGenerationException(fnCall.position, "Unknown function: " + arg);
}
});
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/Main.java b/src/main/java/no/eliashaugsbakk/kompilator/Main.java
index bdd1b1e..512682c 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/Main.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/Main.java
@@ -43,7 +43,7 @@ public class Main {
try {
inputProgram = fileReaderWriter.readFile(inputFileName);
} catch (FileReaderWriterException e) {
- IO.println("Could not read input file: " + inputFileName + "\n\n" + e.getMessage());
+ IO.println("err: Could not read input file: " + inputFileName + "\n\n" + e.getMessage());
System.exit(1);
}
@@ -52,14 +52,14 @@ public class Main {
try {
ast = new Parser(tokens).parse();
} catch (ParserException e) {
- IO.println("Error while parsing: " + e.getMessage());
+ IO.println(e.getMessage());
System.exit(1);
}
try {
new Analyzer(ast).analyze();
} catch (SemanticException e) {
- IO.println("Semantic error: " + e.getMessage());
+ IO.println(e.getMessage());
System.exit(1);
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/Parser.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/Parser.java
index f959daf..45eca22 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/Parser.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/Parser.java
@@ -24,6 +24,7 @@ import no.eliashaugsbakk.kompilator.parsing.node.statement.Assignment;
import no.eliashaugsbakk.kompilator.parsing.node.statement.ExpressionStatement;
import no.eliashaugsbakk.kompilator.parsing.node.statement.IdentifierDeclaration;
import no.eliashaugsbakk.kompilator.parsing.node.statement.Statement;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
import no.eliashaugsbakk.kompilator.tokenization.Token;
/*
@@ -56,7 +57,7 @@ public class Parser {
public Parser(List<Token> tokens) {
this.tokens = tokens;
- this.rootNode = new Program();
+ this.rootNode = new Program(null);
}
public AST parse() throws ParserException {
@@ -101,7 +102,7 @@ public class Parser {
// must be some other token which is not a statement
else {
- throw new ParserException(token.line(), token.column(), "Unexpected token: " + token.value());
+ throw new ParserException(token.position(), "Unexpected token: " + token.value());
}
}
@@ -116,7 +117,7 @@ public class Parser {
} else if (token.value().contentEquals("skriv")) {
return parseExpressionStatement();
} else {
- throw new ParserException(token.line(), token.column(), "unknown keyword: " + token.value());
+ throw new ParserException(token.position(), "unknown keyword: " + token.value());
}
}
@@ -129,7 +130,7 @@ public class Parser {
Expression expression = parseExpression();
expectSemicolon();
- return new ExpressionStatement(expression);
+ return new ExpressionStatement(expression.position, expression);
}
private Statement parseIdentifierStatement() throws ParserException {
@@ -145,23 +146,23 @@ public class Parser {
current--; // rewind to identifier
Expression expr = parseFunctionCall();
expectSemicolon();
- return new ExpressionStatement(expr);
+ return new ExpressionStatement(expr.position, expr);
} else {
- throw new ParserException(next.line(), next.column(), "Unexpected token after identifier: " + next.value());
+ throw new ParserException(next.position(), "Unexpected token after identifier: " + next.value());
}
}
private Statement parseDeclaration(boolean isMutable) throws ParserException {
Token nameToken = tokens.get(current);
if (nameToken.type() != IDENTIFIER) {
- throw new ParserException(nameToken.line(), nameToken.column(),
+ throw new ParserException(nameToken.position(),
"Expected identifier after declaration keyword, got: " + nameToken.value());
}
String identifier = nameToken.value();
current++; // consume identifier
if (tokens.get(current).type() != COLON) {
- throw new ParserException(tokens.get(current).line(), tokens.get(current).column(),
+ throw new ParserException(tokens.get(current).position(),
"Expected : after identifier in declaration");
}
current++; // skip :
@@ -175,7 +176,7 @@ public class Parser {
}
expectSemicolon();
- return new IdentifierDeclaration(identifier, type, initializer, isMutable);
+ return new IdentifierDeclaration(nameToken.position(), identifier, type, initializer, isMutable);
}
private Statement parseAssignment(String identifier) throws ParserException {
@@ -185,13 +186,13 @@ public class Parser {
Expression value = parseExpression();
expectSemicolon();
- return new Assignment(identifier, value);
+ return new Assignment(value.position, identifier, value);
}
private Type parseType() throws ParserException {
Token token = tokens.get(current);
if (token.type() != TYPE) {
- throw new ParserException(token.line(), token.column(),
+ throw new ParserException(token.position(),
"Expected type, got: " + token.value());
}
current++;
@@ -217,32 +218,33 @@ public class Parser {
current++;
if (token.type() == STRING_LITERAL) {
- return new StringLiteral(token.value());
+ return new StringLiteral(token.position(), token.value());
} else if (token.type() == IDENTIFIER || token.type() == KEYWORD) {
// could be function call or just identifier reference
if (current < tokens.size() && tokens.get(current).type() == LPAREN) {
current--;
return parseFunctionCall();
}
- return new Identifier(token.value());
+ return new Identifier(token.position(), token.value());
} else {
- throw new ParserException(token.line(), token.column(),
+ throw new ParserException(token.position(),
"Unexpected token: " + token.value() + ". Expected an expression");
}
}
private FunctionCall parseFunctionCall() throws ParserException {
- String name = tokens.get(current).value();
+ Token token = tokens.get(current);
+ String name = token.value();
current++;
List<Expression> arguments = parseFunctionArguments();
- return new FunctionCall(name, arguments);
+ return new FunctionCall(token.position(), name, arguments);
}
private List<Expression> parseFunctionArguments() throws ParserException {
List<Expression> arguments = new ArrayList<>();
if (tokens.get(current).type() != LPAREN) {
- throw new ParserException(tokens.get(current).line(), tokens.get(current).column(),
+ throw new ParserException(tokens.get(current).position(),
"Expected (");
}
current++; // skip (
@@ -256,7 +258,7 @@ public class Parser {
}
if (current >= tokens.size() || tokens.get(current).type() != RPAREN) {
- throw new ParserException(tokens.get(current).line(), tokens.get(current).column(),
+ throw new ParserException(tokens.get(current).position(),
"Expected )");
}
current++; // skip )
@@ -266,9 +268,9 @@ public class Parser {
private void expectSemicolon() throws ParserException {
if (current >= tokens.size()) {
- throw new ParserException(-1, -1, "Expected ;");
+ throw new ParserException(null, "Expected ;");
} else if (tokens.get(current).type() != SEMICOLON) {
- throw new ParserException(tokens.get(current).line(), tokens.get(current).column(),
+ throw new ParserException(tokens.get(current).position(),
"Expected: ;");
}
current++;
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/ParserException.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/ParserException.java
index ac96a5c..892cd7b 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/ParserException.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/ParserException.java
@@ -1,7 +1,15 @@
package no.eliashaugsbakk.kompilator.parsing;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
+
public class ParserException extends Exception {
- public ParserException(int line, int column, String message) {
- super(line + ":" + column + ", " + message);
+ public ParserException(Position position, String message) {
+ String pos;
+ if (position == null) {
+ pos = "unknown position";
+ } else {
+ pos = position.line() + ":" + position.column();
+ }
+ super(pos + ", " + message);
}
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/ASTNode.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/ASTNode.java
index 1eb201c..c825ea9 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/ASTNode.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/ASTNode.java
@@ -1,7 +1,15 @@
package no.eliashaugsbakk.kompilator.parsing.node;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
+
/**
* Base class for all nodes in the Abstract Syntax Tree.
*/
public abstract class ASTNode {
+ public final Position position;
+
+
+ public ASTNode(Position position) {
+ this.position = position;
+ }
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/Program.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/Program.java
index d61654f..0fcf09e 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/Program.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/Program.java
@@ -3,6 +3,7 @@ package no.eliashaugsbakk.kompilator.parsing.node;
import java.util.ArrayList;
import java.util.List;
import no.eliashaugsbakk.kompilator.parsing.node.statement.Statement;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
/**
* Root node of the AST. Contains all top-level statements.
@@ -10,6 +11,10 @@ import no.eliashaugsbakk.kompilator.parsing.node.statement.Statement;
public class Program extends ASTNode {
public final List<Statement> statements = new ArrayList<>();
+ public Program(Position position) {
+ super(position);
+ }
+
public void addStatement(Statement statement) {
this.statements.add(statement);
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Expression.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Expression.java
index 18fc585..d50e632 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Expression.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Expression.java
@@ -1,6 +1,7 @@
package no.eliashaugsbakk.kompilator.parsing.node.expression;
import no.eliashaugsbakk.kompilator.parsing.node.ASTNode;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
/**
* Base class for all expression nodes.
@@ -14,4 +15,7 @@ import no.eliashaugsbakk.kompilator.parsing.node.ASTNode;
* - myFunction() (function call expression)
*/
public abstract class Expression extends ASTNode {
+ public Expression(Position position) {
+ super(position);
+ }
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/FunctionCall.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/FunctionCall.java
index e18063b..f9aea4f 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/FunctionCall.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/FunctionCall.java
@@ -2,6 +2,7 @@ package no.eliashaugsbakk.kompilator.parsing.node.expression;
import java.util.List;
import no.eliashaugsbakk.kompilator.parsing.Type;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
/**
* Represents a function call statement (e.g., print("Hello, world")).
@@ -10,7 +11,8 @@ public class FunctionCall extends Expression {
public final String functionName;
public final List<Expression> arguments;
- public FunctionCall(String functionName, List<Expression> arguments) {
+ public FunctionCall(Position position, String functionName, List<Expression> arguments) {
+ super(position);
this.functionName = functionName;
this.arguments = arguments;
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Identifier.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Identifier.java
index 91607f8..f899ef3 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Identifier.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/Identifier.java
@@ -1,12 +1,15 @@
package no.eliashaugsbakk.kompilator.parsing.node.expression;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
+
/**
* Resolves to a variable's value
*/
public class Identifier extends Expression {
public final String name;
- public Identifier(String name) {
+ public Identifier(Position position, String name) {
+ super(position);
this.name = name;
}
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/literal/StringLiteral.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/literal/StringLiteral.java
index 82402ed..8238880 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/literal/StringLiteral.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/expression/literal/StringLiteral.java
@@ -1,6 +1,7 @@
package no.eliashaugsbakk.kompilator.parsing.node.expression.literal;
import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
/**
* Represents a string literal expression (e.g., "Hello, World").
@@ -8,7 +9,8 @@ import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression;
public class StringLiteral extends Expression {
public final String value;
- public StringLiteral(String value) {
+ public StringLiteral(Position position, String value) {
+ super(position);
this.value = value;
}
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Assignment.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Assignment.java
index 01d207d..7b48c83 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Assignment.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Assignment.java
@@ -1,12 +1,14 @@
package no.eliashaugsbakk.kompilator.parsing.node.statement;
import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
public class Assignment extends Statement {
public final String identifier;
public final Expression expression;
- public Assignment(String identifier, Expression expression) {
+ public Assignment(Position position, String identifier, Expression expression) {
+ super(position);
this.identifier = identifier;
this.expression = expression;
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/ExpressionStatement.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/ExpressionStatement.java
index e46b691..ce900a7 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/ExpressionStatement.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/ExpressionStatement.java
@@ -1,6 +1,7 @@
package no.eliashaugsbakk.kompilator.parsing.node.statement;
import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
/**
* A statement which holds an expression.
@@ -9,7 +10,8 @@ import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression;
*/
public class ExpressionStatement extends Statement {
public final Expression expression;
- public ExpressionStatement(Expression expression) {
+ public ExpressionStatement(Position position, Expression expression) {
+ super(position);
this.expression = expression;
}
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/IdentifierDeclaration.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/IdentifierDeclaration.java
index 69b0f1b..0659bca 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/IdentifierDeclaration.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/IdentifierDeclaration.java
@@ -2,6 +2,7 @@ package no.eliashaugsbakk.kompilator.parsing.node.statement;
import no.eliashaugsbakk.kompilator.parsing.Type;
import no.eliashaugsbakk.kompilator.parsing.node.expression.Expression;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
public class IdentifierDeclaration extends Statement {
// identifier name: [my_var]: type = 4;
@@ -16,7 +17,8 @@ public class IdentifierDeclaration extends Statement {
public final boolean mutable;
- public IdentifierDeclaration(String identifier, Type type, Expression initializer, boolean mutable) {
+ public IdentifierDeclaration(Position position, String identifier, Type type, Expression initializer, boolean mutable) {
+ super(position);
this.identifier = identifier;
this.type = type;
this.initializer = initializer;
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Statement.java b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Statement.java
index aed21ad..5ab16a3 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Statement.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/parsing/node/statement/Statement.java
@@ -1,6 +1,7 @@
package no.eliashaugsbakk.kompilator.parsing.node.statement;
import no.eliashaugsbakk.kompilator.parsing.node.ASTNode;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
/**
* Base class for all statement nodes.
@@ -14,4 +15,7 @@ import no.eliashaugsbakk.kompilator.parsing.node.ASTNode;
* - if (x > 0) { } (conditional statement)
*/
public abstract class Statement extends ASTNode {
+ protected Statement(Position position) {
+ super(position);
+ }
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java
index 3c775d6..f981527 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java
@@ -35,7 +35,7 @@ public class Analyzer {
case ExpressionStatement exprStmt -> typeOf(exprStmt.expression);
case Assignment assignment -> analyzeAssignment(assignment);
case IdentifierDeclaration decl -> analyzeIdentifierDeclaration(decl);
- case null, default -> throw new SemanticException("Unrecognized statement");
+ default -> throw new SemanticException(stmt.position, "Unrecognized statement");
}
}
@@ -50,18 +50,19 @@ public class Analyzer {
// Ensure the type exists
// string is the only type implemented, but should look in a type table or something in the future
if (!declaredType.name().equals("string")) {
- throw new SemanticException("Unknown type declaration: " + declaredType.name());
+ throw new SemanticException(decl.position, "Unknown type declaration: " + declaredType.name());
}
// Immutable variables must always be initialized on declaration
if (!decl.mutable && !initialized) {
- throw new SemanticException(
+ throw new SemanticException(decl.position,
"Immutable variable " + decl.identifier + " must be initialized upon declaration.");
}
// If no initializer, ensure type is nullable
if (!initialized && !nullable) {
- throw new SemanticException("Non-nullable type requires initialization");
+ throw new SemanticException(decl.position,
+ "Non-nullable type requires initialization");
}
// If initializer exists, validate type matches declared type
@@ -83,12 +84,14 @@ public class Analyzer {
// Check existence
if (symbol == null) {
- throw new SemanticException("Variable not declared: " + assignment.identifier);
+ throw new SemanticException(assignment.position,
+ "Variable not declared: " + assignment.identifier);
}
// Check mutability
if (!symbol.mutable) {
- throw new SemanticException("Cannot assign to immutable value: " + assignment.identifier);
+ throw new SemanticException(assignment.position,
+ "Cannot assign to immutable value: " + assignment.identifier);
}
// Type and Nullability Check
@@ -107,12 +110,14 @@ public class Analyzer {
// skriv() is the only implemented function
// Should ref. function table in the future
if (!call.functionName.equals("skriv")) {
- throw new SemanticException("Function calls are not supported: " + call.functionName);
+ throw new SemanticException(call.position,
+ "Function call is not supported: " + call.functionName);
}
// Validate argument count
if (call.arguments.size() != 1) {
- throw new SemanticException("skriv() supports only one argument");
+ throw new SemanticException(call.position,
+ "skriv() supports only one argument");
}
// Validate argument types
@@ -120,22 +125,24 @@ public class Analyzer {
Type argType = typeOf(argument);
if (!argType.name().equals("string")) {
- throw new SemanticException("skriv() only supports string literals");
+ throw new SemanticException(call.position,
+ "skriv() only supports string literals");
}
if (argType.nullable()) {
- throw new SemanticException("Cannot print nullable string: " + argType.name() + "?.");
+ throw new SemanticException(call.position,
+ "Cannot print nullable string: " + argType.name() + "?.");
}
}
}
private void checkAssignable(Type target, Type value) throws SemanticException {
if (!target.name().equals(value.name())) {
- throw new SemanticException(
+ throw new SemanticException(null,
"Type mismatch: expected " + target.name() + ", found " + value.name());
}
if (value.nullable() && !target.nullable()) {
- throw new SemanticException(
+ throw new SemanticException(null,
"Cannot assign nullable " + value.name() + " to non-nullable " + target.name());
}
}
@@ -146,7 +153,7 @@ public class Analyzer {
*/
private Type typeOf(Expression expr) throws SemanticException {
switch (expr) {
- case null -> throw new SemanticException("Expression cannot be null");
+ case null -> throw new SemanticException(expr.position, "Expression cannot be null");
case StringLiteral _ -> {
return new Type("string", false);
}
@@ -154,10 +161,10 @@ public class Analyzer {
Symbol symbol = symbolTable.get(id.name);
if (symbol == null) {
- throw new SemanticException("Undeclared identifier: " + id.name);
+ throw new SemanticException(expr.position, "Undeclared identifier: " + id.name);
}
if (!symbol.initialized) {
- throw new SemanticException("Identifier is not initialized: " + id.name);
+ throw new SemanticException(expr.position, "Identifier is not initialized: " + id.name);
}
return symbol.type;
@@ -170,6 +177,6 @@ public class Analyzer {
}
}
- throw new SemanticException("Unknown expression type: " + expr.getClass().getSimpleName());
+ throw new SemanticException(expr.position, "Unknown expression type: " + expr.getClass().getSimpleName());
}
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java
index 402cfe7..d65a80d 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/SemanticException.java
@@ -1,7 +1,15 @@
package no.eliashaugsbakk.kompilator.semanticAnalysis;
+import no.eliashaugsbakk.kompilator.tokenization.Position;
+
public class SemanticException extends Exception {
- public SemanticException(String message) {
- super(message);
+ public SemanticException(Position position, String message) {
+ String pos;
+ if (position == null) {
+ pos = "unknown position";
+ } else {
+ pos = position.line() + ":" + position.column();
+ }
+ super(pos + ", " + message);
}
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java
index 149835d..517f0e5 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Lexer.java
@@ -72,7 +72,7 @@ public class Lexer {
position++;
}
- tokens.add(new Token(EOF, "End of File", line, column));
+ tokens.add(new Token(EOF, "End of File", new Position(line, column)));
if (Main.VERBOSE) {
IO.println("======= Lexer =======");
tokens.forEach(token -> IO.println(token.type().toString() + ": " + token.value()));
@@ -99,20 +99,20 @@ public class Lexer {
state = IN_WORD;
wordBuffer.append(current);
} else if (current == ':') {
- tokens.add(new Token(COLON, ":", line, column));
+ tokens.add(new Token(COLON, ":", new Position(line, column)));
state = IN_TYPE;
position++; // Skip whitespace
} else if (current == '=') {
- tokens.add(new Token(ASSIGN, "=", line, column));
+ tokens.add(new Token(ASSIGN, "=", new Position(line, column)));
} else if (current == '(') {
- tokens.add(new Token(LPAREN, "(", line, column));
+ tokens.add(new Token(LPAREN, "(", new Position(line, column)));
} else if (current == ')') {
- tokens.add(new Token(RPAREN, ")", line, column));
+ tokens.add(new Token(RPAREN, ")", new Position(line, column)));
} else if (current == ';') {
- tokens.add(new Token(SEMICOLON, ";", line, column));
+ tokens.add(new Token(SEMICOLON, ";", new Position(line, column)));
} else if (current == ',') {
- tokens.add(new Token(COMMA, ",", line, column));
+ tokens.add(new Token(COMMA, ",", new Position(line, column)));
}
}
@@ -124,12 +124,14 @@ public class Lexer {
if (current == '?') {
state = NORMAL;
- tokens.add(new Token(TYPE, wordBuffer.toString(), line, column - wordBuffer.length()));
- tokens.add(new Token(NULLABLE, "?", line, column));
+ tokens.add(
+ new Token(TYPE, wordBuffer.toString(), new Position(line, column - wordBuffer.length())));
+ tokens.add(new Token(NULLABLE, "?", new Position(line, column)));
clearWordBuffer();
} else if (Character.isWhitespace(current) || current == '=' || current == ';') {
state = NORMAL;
- tokens.add(new Token(TYPE, wordBuffer.toString(), line, column - wordBuffer.length()));
+ tokens.add(
+ new Token(TYPE, wordBuffer.toString(), new Position(line, column - wordBuffer.length())));
clearWordBuffer();
} else {
wordBuffer.append(current);
@@ -138,7 +140,7 @@ public class Lexer {
private void inString() {
if (current == '"') {
- tokens.add(new Token(STRING_LITERAL, wordBuffer.toString(), line, column));
+ tokens.add(new Token(STRING_LITERAL, wordBuffer.toString(), new Position(line, column)));
wordBuffer.delete(0, wordBuffer.length());
state = NORMAL;
position++; // skip closing "
@@ -172,15 +174,17 @@ public class Lexer {
private void characterizeWord() {
if (Keywords.KEYWORDS.contains(wordBuffer.toString())) {
if (wordBuffer.toString().contentEquals("set")) {
- tokens.add(new Token(KEYWORD, "set", line, column));
+ tokens.add(new Token(KEYWORD, "set", new Position(line, column)));
} else if (wordBuffer.toString().contentEquals("mut")) {
- tokens.add(new Token(KEYWORD, "mut", line, column));
+ tokens.add(new Token(KEYWORD, "mut", new Position(line, column)));
} else {
// this is a function call
- tokens.add(new Token(KEYWORD, wordBuffer.toString(), line, column - wordBuffer.length()));
+ tokens.add(new Token(KEYWORD, wordBuffer.toString(),
+ new Position(line, column - wordBuffer.length())));
}
} else {
- tokens.add(new Token(IDENTIFIER, wordBuffer.toString(), line, column - wordBuffer.length()));
+ tokens.add(new Token(IDENTIFIER, wordBuffer.toString(),
+ new Position(line, column - wordBuffer.length())));
}
clearWordBuffer();
}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Position.java b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Position.java
new file mode 100644
index 0000000..dea3050
--- /dev/null
+++ b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Position.java
@@ -0,0 +1,4 @@
+package no.eliashaugsbakk.kompilator.tokenization;
+
+public record Position(int line, int column) {
+}
diff --git a/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Token.java b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Token.java
index 1cab703..8dfb912 100644
--- a/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Token.java
+++ b/src/main/java/no/eliashaugsbakk/kompilator/tokenization/Token.java
@@ -1,4 +1,4 @@
package no.eliashaugsbakk.kompilator.tokenization;
-public record Token(TokenType type, String value, int line, int column) {
+public record Token(TokenType type, String value, Position position) {
}