Exemplo n.º 1
0
        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);
        }
Exemplo n.º 2
0
        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);
        }