visitClassStmt method

  1. @override
void visitClassStmt(
  1. ClassStmt stmt
)

Defines a class.

Implementation

@override
void visitClassStmt(ClassStmt stmt) {
  PyClass? superclassValue;
  Object? evaluatedSuper; // Temporary variable

  if (stmt.superclass != null) {
    evaluatedSuper = evaluate(stmt.superclass!);
    if (evaluatedSuper is! PyClass) {
      throw RuntimeError(
        stmt.superclass!.name,
        "Superclass must be a class.",
      );
    }
    superclassValue = evaluatedSuper;
  }

  // 1. Define class in current scope (for references within methods)
  _environment.define(stmt.name.lexeme, null); //temporary

  // 2. create environment for class body
  Environment classEnvironment =
      _environment; // default: enclosing environment
  if (superclassValue != null) {
    // If super class exists, create a new environment containing the current one
    // and defining "super".
    classEnvironment = Environment(enclosing: _environment);
    classEnvironment.define("super", superclassValue);
  }

  // 3. Process methods in class environment
  Map<String, PyFunction> methods = {};
  // Switch to class environment temporarily for setting up method closures correctly
  Environment previousEnv = _environment;
  _environment = classEnvironment;

  for (FunctionStmt methodStmt in stmt.methods) {
    // The closure of the methode *must* be the class environment, so that 'super' can be found
    PyFunction method = PyFunction.fromDef(
      methodStmt,
      classEnvironment, // Closure is the class environment
      isInitializer: methodStmt.name.lexeme == "__init__",
    );
    methods[methodStmt.name.lexeme] = method;
    // The method need not be defined in classEnvironment,
    // because it can be found via self.method or Class.method.
  }
  // Back to enclosing environment
  _environment = previousEnv;

  // 4. Create PyClass object
  PyClass thisClass = PyClass(stmt.name.lexeme, superclassValue, methods);

  // 5. Define class in original scope, overwriting temporary null
  _environment.assign(stmt.name, thisClass);
}