1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
package no.eliashaugsbakk.kompilator.semanticAnalysis;
import java.util.HashMap;
import java.util.Map;
import no.eliashaugsbakk.kompilator.parsing.AST;
import no.eliashaugsbakk.kompilator.parsing.Type;
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.Identifier;
import no.eliashaugsbakk.kompilator.parsing.node.expression.literal.StringLiteral;
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;
public class Analyzer {
private final AST ast;
private Map<String, Symbol> symbolTable = new HashMap<>();
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) {
typeOf(exprStmt.expression);
} else if (stmt instanceof Assignment assignment) {
analyzeAssignment(assignment);
} else if (stmt instanceof IdentifierDeclaration decl) {
analyzeIdentifierDeclaration(decl);
} else {
throw new SemanticException("Unrecognized statement");
}
}
/**
* Analyzes identifier declaration: set/mut x: type [= value];
*/
private void analyzeIdentifierDeclaration(IdentifierDeclaration decl) throws SemanticException {
boolean initialized = decl.initializer != null;
boolean nullable = decl.type.nullable();
Type declaredType = decl.type;
// 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());
}
// Immutable variables must always be initialized on declaration
if (!decl.mutable && !initialized) {
throw new SemanticException(
"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");
}
// If initializer exists, validate type matches declared type
if (initialized) {
Type implementedType = typeOf(decl.initializer);
checkAssignable(declaredType, implementedType);
}
// Shadowing is allowed; do not check to see if the symbol already exists
symbolTable.put(decl.identifier,
new Symbol(decl.identifier, declaredType, decl.mutable, initialized));
}
/**
* Analyzes assignment: x = value;
*/
private void analyzeAssignment(Assignment assignment) throws SemanticException {
Symbol symbol = symbolTable.get(assignment.identifier);
// Check existence
if (symbol == null) {
throw new SemanticException("Variable not declared: " + assignment.identifier);
}
// Check mutability
if (!symbol.mutable) {
throw new SemanticException("Cannot assign to immutable value: " + assignment.identifier);
}
// Type and Nullability Check
Type assignedType = typeOf(assignment.expression);
checkAssignable(symbol.type, assignedType);
// Update symbol state
symbol.initialized = true;
}
/**
* Analyzes identifier usage (variable reference).
*/
private void analyzeIdentifier(Identifier identifier) throws SemanticException {
// Check if identifier is declared
if (!symbolTable.containsKey(identifier.name)) {
throw new SemanticException("Identifier does not exist: " + identifier.name);
}
// Check if identifier is initialized
if (!symbolTable.get(identifier.name).initialized) {
throw new SemanticException("Identifier is not initialized: " + identifier.name);
}
}
/**
* Analyzes function call.
*/
private void analyzeFunctionCall(FunctionCall call) throws SemanticException {
// Validate function exists
// 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);
}
// Validate argument count
if (call.arguments.size() != 1) {
throw new SemanticException("skriv() supports only one argument");
}
// Validate argument types
for (Expression argument : call.arguments) {
Type argType = typeOf(argument);
if (!argType.name().equals("string")) {
throw new SemanticException("skriv() only supports string literals");
}
if (argType.nullable()) {
throw new SemanticException("Cannot print nullable string: " + argType.name() + "?.");
}
}
}
private void checkAssignable(Type target, Type value) throws SemanticException {
if (!target.name().equals(value.name())) {
throw new SemanticException(
"Type mismatch: expected " + target.name() + ", found " + value.name());
}
if (value.nullable() && !target.nullable()) {
throw new SemanticException(
"Cannot assign nullable " + value.name() + " to non-nullable " + target.name());
}
}
/**
* Analyzes an expression, ensures all identifiers are declared and initialized, recursively
* validates sub-expressions, and returns the resulting Type.
*/
private Type typeOf(Expression expr) throws SemanticException {
switch (expr) {
case null -> throw new SemanticException("Expression cannot be null");
case StringLiteral stringLiteral -> {
return new Type("string", false);
}
case Identifier id -> {
Symbol symbol = symbolTable.get(id.name);
if (symbol == null) {
throw new SemanticException("Undeclared identifier: " + id.name);
}
if (!symbol.initialized) {
throw new SemanticException("Identifier is not initialized: " + id.name);
}
return symbol.type;
}
case FunctionCall call -> {
analyzeFunctionCall(call);
return call.getReturnType();
}
default -> {
}
}
throw new SemanticException("Unknown expression type: " + expr.getClass().getSimpleName());
}
}
|