summaryrefslogtreecommitdiff
path: root/src/main/java/no/eliashaugsbakk/kompilator/semanticAnalysis/Analyzer.java
blob: 7b2adf02fd80ef250e49e140a3edad09e11fadde (plain)
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
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 final Map<String, Symbol> symbolTable = new HashMap<>();

  public Analyzer(AST ast) {
    this.ast = ast;
  }

  public void analyze() throws SemanticException {
    Program program = (Program) ast.root();

    for (Statement stmt : program.statements) {
      analyzeStatement(stmt);
    }
  }

  private void analyzeStatement(Statement stmt) throws SemanticException {
    switch (stmt) {
      case ExpressionStatement exprStmt -> typeOf(exprStmt.expression);
      case Assignment assignment -> analyzeAssignment(assignment);
      case IdentifierDeclaration decl -> analyzeIdentifierDeclaration(decl);
      default -> throw new SemanticException(stmt.position, "Ukjent programkonstruksjon");
    }
  }

  /**
   * 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("streng")) {
      throw new SemanticException(decl.position, "Ukjent type: " + declaredType.name());
    }

    // Immutable variables must always be initialized on declaration
    if (!decl.mutable && !initialized) {
      throw new SemanticException(decl.position,
          "Uforanderlig variabel " + decl.identifier + " må initialiseres ved deklarasjon.");
    }

    // If no initializer, ensure type is nullable
    if (!initialized && !nullable) {
      throw new SemanticException(decl.position,
          "En type som ikke kan være null, må initialiseres");
    }

    // 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(assignment.position,
          "Variabelen er ikke deklarert: " + assignment.identifier);
    }

    // Check mutability
    if (!symbol.mutable) {
      throw new SemanticException(assignment.position,
          "Kan ikke tilordne en uforanderlig verdi: " + assignment.identifier);
    }

    // Type and Nullability Check
    Type assignedType = typeOf(assignment.expression);
    checkAssignable(symbol.type, assignedType);

    // Update symbol state
    symbol.initialized = true;
  }

  /**
   * 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(call.position,
          "Funksjonskallet støttes ikke: " + call.functionName);
    }

    // Validate argument count
    if (call.arguments.size() != 1) {
      throw new SemanticException(call.position,
          "skriv() støtter bare ett argument");
    }

    // Validate argument types
    for (Expression argument : call.arguments) {
      Type argType = typeOf(argument);

      if (!argType.name().equals("streng")) {
        throw new SemanticException(call.position,
            "skriv() støtter bare strengliteraler");
      }

      if (argType.nullable()) {
        throw new SemanticException(call.position,
            "Kan ikke skrive ut en nullbar streng: " + argType.name() + "?.");
      }
    }
  }

  private void checkAssignable(Type target, Type value) throws SemanticException {
    if (!target.name().equals(value.name())) {
      throw new SemanticException(null,
          "Typekonflikt: forventet " + target.name() + ", fant " + value.name());
    }
    if (value.nullable() && !target.nullable()) {
      throw new SemanticException(null,
          "Kan ikke tilordne nullbar " + value.name() + " til ikke-nullbar " + 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(null, "Uttrykket kan ikke være null");
      case StringLiteral _ -> {
        return new Type("streng", false);
      }
      case Identifier id -> {
        Symbol symbol = symbolTable.get(id.name);

        if (symbol == null) {
          throw new SemanticException(expr.position,           "Udeklarert identifikator: " + id.name);
        }
        if (!symbol.initialized) {
          throw new SemanticException(expr.position, "Identifikatoren er ikke initialisert: " + id.name);
        }

        return symbol.type;
      }
      case FunctionCall call -> {
        analyzeFunctionCall(call);
        return call.getReturnType();
      }
      default -> {
      }
    }

    throw new SemanticException(expr.position, "Ukjent uttrykkstype: " + expr.getClass().getSimpleName());
  }
}