evaluate method

  1. @override
LangVal<LangType> evaluate(
  1. SymbolTable table
)
override

Evaluates this AST node and generates corresponding LLVM IR code.

This is the main method that each AST node must implement to:

  1. Perform semantic analysis (type checking, symbol resolution)
  2. Generate LLVM IR instructions for the node's operation
  3. Return the result value (if this is an expression)

Parameters:

  • table: The current symbol table for variable and function lookups

Returns: The result of evaluating this node (type depends on node type)

Throws:

  • Exception for semantic errors (undefined variables, type mismatches, etc.)
  • Various specific exceptions for different error conditions

Note: The base implementation throws "Not implemented" - concrete subclasses must override this method with their specific evaluation logic.

Implementation

@override
LangVal evaluate(SymbolTable table) {
  var leftResult = left.evaluate(table);
  var rightResult = right.evaluate(table);

  //cant be array
  if (leftResult.type is ArrayType || rightResult.type is ArrayType) {
    throw Exception("Cannot apply binary operator to array");
  }
  if (leftResult.type != rightResult.type) {
    if (leftResult.type.primitiveType == PrimitiveTypes.float) {
      Node.addIrLine(
          "%conv.$id = sitofp i64 ${rightResult.regName} to double");
      rightResult =
          LangVal("%conv.$id", const PrimitiveType(PrimitiveTypes.float));
    } else {
      Node.addIrLine(
          "%conv.$id = sitofp i64 ${leftResult.regName} to double");
      leftResult =
          LangVal("%conv.$id", const PrimitiveType(PrimitiveTypes.float));
    }
  }
  if (leftResult.type.primitiveType == PrimitiveTypes.float) {
    switch (nodeValue) {
      case RelOperator.eq:
        Node.addIrLine(
            "%temp.$id = fcmp oeq double ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.ne:
        Node.addIrLine(
            "%temp.$id = fcmp one double ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.lt:
        Node.addIrLine(
            "%temp.$id = fcmp olt double ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.gt:
        Node.addIrLine(
            "%temp.$id = fcmp ogt double ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.le:
        Node.addIrLine(
            "%temp.$id = fcmp ole double ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.ge:
        Node.addIrLine(
            "%temp.$id = fcmp oge double ${leftResult.regName}, ${rightResult.regName}");
        break;
      default:
        throw Exception("Unknown operator: $nodeValue");
    }
  } else {
    switch (nodeValue) {
      case RelOperator.eq:
        Node.addIrLine(
            "%temp.$id = icmp eq ${leftResult.type.irType} ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.ne:
        Node.addIrLine(
            "%temp.$id = icmp ne ${leftResult.type.irType} ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.lt:
        Node.addIrLine(
            "%temp.$id = icmp slt ${leftResult.type.irType} ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.gt:
        Node.addIrLine(
            "%temp.$id = icmp sgt ${leftResult.type.irType} ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.le:
        Node.addIrLine(
            "%temp.$id = icmp sle ${leftResult.type.irType} ${leftResult.regName}, ${rightResult.regName}");
        break;
      case RelOperator.ge:
        Node.addIrLine(
            "%temp.$id = icmp sge ${leftResult.type.irType} ${leftResult.regName}, ${rightResult.regName}");
        break;
      default:
        throw Exception("Unknown operator: $nodeValue");
    }
  }
  Node.addIrLine("%relOp.$id = zext i1 %temp.$id to i64");
  return LangVal("%relOp.$id", const PrimitiveType(PrimitiveTypes.int));
}