public object visitClassStatement(Statement.Class classStatement) { ClassType enclosingClass = currentClass; currentClass = ClassType.CLASS; declare(classStatement.name); define(classStatement.name); Dictionary <string, bool> superclassDuplicates = new Dictionary <string, bool>(); if (classStatement.superclass.Count != 0) { currentClass = ClassType.SUBCLASS; foreach (Expr.Variable superClass in classStatement.superclass) { superclassDuplicates.TryGetValue(superClass.name.lexeme, out bool value); if (value) { Lox.error(superClass.name, "A class can't inherit from the same class twice."); } else { superclassDuplicates[superClass.name.lexeme] = true; } if (classStatement.name.lexeme.Equals(superClass.name.lexeme)) { Lox.error(superClass.name, "A class can't inherit from itself."); } resolve(superClass); } beginScope(); Variable sup = new Variable(classStatement.name); //Class name is a variable, but the user likely won't want to be warned about it being unused. sup.initialized = true; sup.used = true; scopes.Peek().Add("super", sup); } beginScope(); Token thisVar = new Token(TokenType.THIS_OBJECT, "this", null, -1, -1); Variable varHacky = new Variable(thisVar); varHacky.initialized = true; scopes.Peek().Add("this", varHacky); foreach (Statement.function method in classStatement.methods) { FunctionType declaration = FunctionType.METHOD; if (method.name.lexeme.Equals("init")) { declaration = FunctionType.INITIALIZER; } if (method._params.Count == 1 && method._params.ElementAt(0).type == TokenType.SEMICOLON && method._params.ElementAt(0).lexeme.Equals("getter")) { FunctionType previous = currentFunction; currentFunction = FunctionType.METHOD; Variable getter = new Variable(method.name); getter.initialized = (method.body.Count != 0); getter.used = true; //We don't want warnings for member functions/getters. resolve(method.body); scopes.Peek().Add(method.name.lexeme, getter); currentFunction = previous; } else { resolveFunction(method, declaration); } } endScope(); if (classStatement.superclass.Count > 0) { endScope(); } currentClass = enclosingClass; return(null); }
public object visitClassStatement(Statement.Class classStatement) { List <LoxClass> superClasses = new List <LoxClass>(); if (classStatement.superclass.Count != 0) { foreach (Object superClass in classStatement.superclass) { Object eval = evaluate((Expr.Variable)superClass); if (!(eval is LoxClass)) { throw new Exceptions.RuntimeError(new HelperFunctions.GetToken().evaluate((Expr)superClass), "Superclass must be a class."); } else { superClasses.Add((LoxClass)eval); } } } environment.define(classStatement.name.lexeme, null, classStatement.name); if (classStatement.superclass.Count != 0) { environment = new Environment(environment); environment.define("super", superClasses); } Dictionary <string, LoxFunction> methods = new Dictionary <string, LoxFunction>(); Dictionary <LoxFunction, Token> getters = new Dictionary <LoxFunction, Token>(); List <Statement.function> methodsList = classStatement.methods; foreach (Statement.function method in methodsList) { if (method._params.Count == 1 && method._params.ElementAt(0).type == TokenType.SEMICOLON && method._params.ElementAt(0).lexeme.Equals("getter")) { Statement.function noParams = new Statement.function(method.name, new List <Token>(), method.body); LoxFunction getter = new LoxFunction(noParams, environment, false, true); methods.Add(method.name.lexeme, getter); getters[getter] = method.name; } else { LoxFunction function = new LoxFunction(method, environment, method.name.lexeme.Equals("init")); if (!methods.ContainsKey(method.name.lexeme)) { methods.Add(method.name.lexeme, function); } else { throw new Exceptions.RuntimeError(method.name, "Cannot have two functions with the same name in the same class."); } } } LoxClass _class = new LoxClass(classStatement.name.lexeme, superClasses, methods); foreach (Statement.function staticFunction in classStatement.staticFunctions) { _class.set(staticFunction.name, staticFunction); } foreach (LoxFunction getter in getters.Keys) { _class.set(getters[getter], getter); } if (superClasses.Count != 0) { environment = environment.enclosing; } environment.assign(classStatement.name, _class); return(null); }