diff options
| author | Elias Haugsbakk <[email protected]> | 2026-09-21 14:36:59 +0200 |
|---|---|---|
| committer | Elias Haugsbakk <[email protected]> | 2026-09-21 14:36:59 +0200 |
| commit | e7720e2d9e1319712f85cb0527e17f1251bd7d2c (patch) | |
| tree | 19cb8b0f49a0fe234fc0c0bfb41dc5c520b1630b /src/main/java | |
| parent | 84215a9b8f79035ebec83fe38bc696b5ae78de5d (diff) | |
add position info to semantic and IRgen exceptions
Diffstat (limited to 'src/main/java')
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) { } |
