mirror of https://github.com/japl-lang/japl.git
Added methods, properties, setters and getters to class objects
This commit is contained in:
parent
8f4fe11813
commit
6becfb749e
|
@ -2,11 +2,12 @@ import operator
|
|||
from typing import List
|
||||
from .types.callable import Callable
|
||||
from .types.japlclass import JAPLClass
|
||||
from .types.instance import JAPLInstance
|
||||
from .meta.environment import Environment
|
||||
from .meta.tokentype import TokenType
|
||||
from .types.native import Clock, Type, JAPLFunction, Truthy, Stringify
|
||||
from .meta.exceptions import JAPLError, BreakException, ReturnException
|
||||
from .meta.expression import Expression, Variable, Literal, Logical, Binary, Unary, Grouping, Assignment, Call
|
||||
from .meta.expression import Expression, Variable, Literal, Logical, Binary, Unary, Grouping, Assignment, Call, Get, Set
|
||||
from .meta.statement import Statement, Print, StatementExpr, If, While, Del, Break, Return, Var, Block, Function, Class
|
||||
|
||||
|
||||
|
@ -31,7 +32,6 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
self.globals.define("type", Type())
|
||||
self.globals.define("truthy", Truthy())
|
||||
self.globals.define("stringify", Stringify())
|
||||
self.looping = False
|
||||
|
||||
def number_operand(self, op, operand):
|
||||
"""
|
||||
|
@ -161,7 +161,11 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
"""Visits a class declaration"""
|
||||
|
||||
self.environment.define(stmt.name.lexeme, None)
|
||||
klass = JAPLClass(stmt.name.lexeme)
|
||||
methods = {}
|
||||
for method in stmt.methods:
|
||||
func = JAPLFunction(method, self.environment)
|
||||
methods[method.name.lexeme] = func
|
||||
klass = JAPLClass(stmt.name.lexeme, methods)
|
||||
self.environment.assign(stmt.name, klass)
|
||||
|
||||
def visit_while(self, statement: While):
|
||||
|
@ -169,13 +173,11 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
Visits a while node and executes it
|
||||
"""
|
||||
|
||||
self.looping = True
|
||||
while self.eval(statement.condition):
|
||||
try:
|
||||
self.exec(statement.body)
|
||||
except BreakException:
|
||||
break
|
||||
self.looping = False
|
||||
|
||||
def visit_var_stmt(self, stmt: Var):
|
||||
"""
|
||||
|
@ -237,9 +239,7 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
Visits a break statement
|
||||
"""
|
||||
|
||||
if self.looping:
|
||||
raise BreakException()
|
||||
raise JAPLError(stmt.token, "'break' outside loop")
|
||||
raise BreakException()
|
||||
|
||||
def visit_call_expr(self, expr: Call):
|
||||
"""
|
||||
|
@ -288,6 +288,23 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
function = JAPLFunction(statement, self.environment)
|
||||
self.environment.define(statement.name.lexeme, function)
|
||||
|
||||
def visit_get(self, expr: Get):
|
||||
"""Visits property get expressions and evaluates them"""
|
||||
|
||||
obj = self.eval(expr.object)
|
||||
if isinstance(obj, JAPLInstance):
|
||||
return obj.get(expr.name)
|
||||
raise JAPLError(expr.name, "Only instances have properties")
|
||||
|
||||
def visit_set(self, expr: Set):
|
||||
"""Visits property set expressions and evaluates them"""
|
||||
|
||||
obj = self.eval(expr.object)
|
||||
if not isinstance(obj, JAPLInstance):
|
||||
raise JAPLError(expr, "Only instances have fields")
|
||||
value = self.eval(expr.value)
|
||||
obj.set(expr.name, value)
|
||||
|
||||
def exec(self, statement: Statement):
|
||||
"""
|
||||
Executes a statement
|
||||
|
|
|
@ -36,6 +36,14 @@ class Expression(object):
|
|||
def visit_unary(self, visitor):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def visit_get(self, visitor):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def visit_set(self, visitor):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@dataclass
|
||||
class Binary(Expression):
|
||||
|
@ -94,6 +102,7 @@ class Assignment(Expression):
|
|||
def __hash__(self):
|
||||
return super().__hash__()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Logical(Expression):
|
||||
left: Expression
|
||||
|
@ -113,6 +122,7 @@ class Call(Expression):
|
|||
def accept(self, visitor):
|
||||
return visitor.visit_call_expr(self)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Get(Expression):
|
||||
object: Expression
|
||||
|
@ -120,3 +130,13 @@ class Get(Expression):
|
|||
|
||||
def accept(self, visitor):
|
||||
return visitor.visit_get(self)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Set(Expression):
|
||||
object: Expression
|
||||
name: Token
|
||||
value: Expression
|
||||
|
||||
def accept(self, visitor):
|
||||
return visitor.visit_set(self)
|
||||
|
|
|
@ -4,3 +4,4 @@ from enum import Enum, auto
|
|||
class FunctionType(Enum):
|
||||
NONE = auto()
|
||||
FUNCTION = auto()
|
||||
METHOD = auto()
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
from enum import Enum, auto
|
||||
|
||||
|
||||
class LoopType(Enum):
|
||||
|
||||
NONE = auto()
|
||||
WHILE = auto()
|
|
@ -2,7 +2,7 @@ from .meta.exceptions import ParseError
|
|||
from .meta.tokentype import TokenType
|
||||
from .meta.tokenobject import Token
|
||||
from typing import List, Union
|
||||
from .meta.expression import Variable, Assignment, Logical, Call, Binary, Unary, Literal, Grouping, Expression, Get
|
||||
from .meta.expression import Variable, Assignment, Logical, Call, Binary, Unary, Literal, Grouping, Expression, Get, Set
|
||||
from .meta.statement import Print, StatementExpr, Var, Del, Block, If, While, Break, Function, Return, Class
|
||||
|
||||
|
||||
|
@ -238,6 +238,8 @@ class Parser(object):
|
|||
if isinstance(expr, Variable):
|
||||
name = expr.name
|
||||
return Assignment(name, value)
|
||||
elif isinstance(expr, Get):
|
||||
return Set(expr.object, expr.name, value)
|
||||
raise self.throw(eq, "Invalid syntax")
|
||||
return expr
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ from .meta.exceptions import JAPLError
|
|||
from .meta.expression import Expression
|
||||
from .meta.statement import Statement
|
||||
from .meta.functiontype import FunctionType
|
||||
from .meta.looptype import LoopType
|
||||
try:
|
||||
from functools import singledispatchmethod
|
||||
except ImportError:
|
||||
from singledispatchmethod import singledispatchmethod
|
||||
from singledispatchmethod import singledispatchmethod # Backport
|
||||
from typing import List, Union
|
||||
from collections import deque
|
||||
|
||||
|
@ -24,6 +25,7 @@ class Resolver(Expression.Visitor, Statement.Visitor):
|
|||
self.interpreter = interpreter
|
||||
self.scopes = deque()
|
||||
self.current_function = FunctionType.NONE
|
||||
self.current_loop = LoopType.NONE
|
||||
|
||||
@singledispatchmethod
|
||||
def resolve(self, stmt_or_expr: Union[Statement, Expression, List[Statement]]):
|
||||
|
@ -151,6 +153,9 @@ class Resolver(Expression.Visitor, Statement.Visitor):
|
|||
def visit_class(self, stmt):
|
||||
self.declare(stmt.name)
|
||||
self.define(stmt.name)
|
||||
for method in stmt.methods:
|
||||
ftype = FunctionType.METHOD
|
||||
self.resolve_function(method, ftype)
|
||||
|
||||
def visit_statement_expr(self, stmt):
|
||||
"""Visits a statement expression node"""
|
||||
|
@ -181,8 +186,11 @@ class Resolver(Expression.Visitor, Statement.Visitor):
|
|||
def visit_while(self, stmt):
|
||||
"""Visits a while statement node"""
|
||||
|
||||
loop = self.current_loop
|
||||
self.current_loop = LoopType.WHILE
|
||||
self.resolve(stmt.condition)
|
||||
self.resolve(stmt.body)
|
||||
self.current_loop = loop
|
||||
|
||||
def visit_binary(self, expr):
|
||||
"""Visits a binary expression node"""
|
||||
|
@ -225,4 +233,16 @@ class Resolver(Expression.Visitor, Statement.Visitor):
|
|||
def visit_break(self, stmt):
|
||||
"""Visits a break statement"""
|
||||
|
||||
return
|
||||
if self.current_loop == LoopType.NONE:
|
||||
raise JAPLError("'break' outside loop")
|
||||
|
||||
def visit_get(self, expr):
|
||||
"""Visits a property get expression"""
|
||||
|
||||
self.resolve(expr.object)
|
||||
|
||||
def visit_set(self, expr):
|
||||
"""Visits a property set expression"""
|
||||
|
||||
self.resolve(expr.value)
|
||||
self.resolve(expr.object)
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
from ..meta.exceptions import JAPLError
|
||||
from ..meta.tokenobject import Token
|
||||
|
||||
|
||||
class JAPLInstance:
|
||||
"""A class instance"""
|
||||
|
||||
def __init__(self, klass):
|
||||
self.klass = klass
|
||||
self.fields = {}
|
||||
|
||||
def __repr__(self):
|
||||
return f"<instance of '{self.klass.name}'>"
|
||||
|
||||
def get(self, name: Token):
|
||||
if name.lexeme in self.fields:
|
||||
return self.fields[name.lexeme]
|
||||
elif name.lexeme in self.klass.methods:
|
||||
return self.klass.methods[name.lexeme]
|
||||
raise JAPLError(name, f"Undefined property '{name.lexeme}'")
|
||||
|
||||
def set(self, name: Token, value: object):
|
||||
self.fields[name.lexeme] = value
|
||||
|
|
|
@ -5,8 +5,9 @@ from .instance import JAPLInstance
|
|||
class JAPLClass(Callable):
|
||||
"""A JAPL class"""
|
||||
|
||||
def __init__(self, name: str):
|
||||
def __init__(self, name: str, methods: dict):
|
||||
self.name = name
|
||||
self.methods = methods
|
||||
self.arity = 0
|
||||
|
||||
def __repr__(self):
|
||||
|
|
Loading…
Reference in New Issue