mirror of https://github.com/japl-lang/japl.git
Began to add classes
This commit is contained in:
parent
0c05b24c3d
commit
1d6a9f30da
|
@ -6,7 +6,7 @@ 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.statement import Statement, Print, StatementExpr, If, While, Del, Break, Return, Var, Block, Function
|
||||
from .meta.statement import Statement, Print, StatementExpr, If, While, Del, Break, Return, Var, Block, Function, Class
|
||||
|
||||
|
||||
class Interpreter(Expression.Visitor, Statement.Visitor):
|
||||
|
@ -31,7 +31,6 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
self.globals.define("truthy", Truthy())
|
||||
self.globals.define("stringify", Stringify())
|
||||
self.looping = False
|
||||
self.in_function = False
|
||||
|
||||
def number_operand(self, op, operand):
|
||||
"""
|
||||
|
@ -157,6 +156,10 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
elif statement.else_branch:
|
||||
self.exec(statement.else_branch)
|
||||
|
||||
def visit_class(self, stmt: Class):
|
||||
"""Visits a class declaration"""
|
||||
|
||||
|
||||
def visit_while(self, statement: While):
|
||||
"""
|
||||
Visits a while node and executes it
|
||||
|
@ -268,13 +271,10 @@ class Interpreter(Expression.Visitor, Statement.Visitor):
|
|||
Visits a return statement
|
||||
"""
|
||||
|
||||
if self.in_function:
|
||||
value = None
|
||||
if statement.value:
|
||||
value = self.eval(statement.value)
|
||||
raise ReturnException(value)
|
||||
else:
|
||||
raise JAPLError(statement.keyword, "'return' outside function")
|
||||
value = None
|
||||
if statement.value:
|
||||
value = self.eval(statement.value)
|
||||
raise ReturnException(value)
|
||||
|
||||
def visit_function(self, statement: Function):
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
from enum import Enum, auto
|
||||
|
||||
|
||||
class FunctionType(Enum):
|
||||
NONE = auto()
|
||||
FUNCTION = auto()
|
|
@ -56,6 +56,9 @@ class Statement(object):
|
|||
def visit_return(self, visitor):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def visit_class(self, visitor):
|
||||
raise NotImplementedError
|
||||
|
||||
@dataclass
|
||||
class Print(Statement):
|
||||
|
@ -170,3 +173,14 @@ class Return(Statement, BaseException):
|
|||
|
||||
def accept(self, visitor):
|
||||
visitor.visit_return(self)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Class(Statement):
|
||||
"""A class statement"""
|
||||
|
||||
name: Token
|
||||
methods: list
|
||||
|
||||
def accept(self, visitor):
|
||||
visitor.visit_class(self)
|
||||
|
|
|
@ -3,8 +3,8 @@ 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, Expression, Binary, Unary, Literal, Grouping, Expression
|
||||
from .meta.statement import Print, StatementExpr, Var, Del, Block, If, While, Break, Function, Return, Statement
|
||||
from .meta.expression import Variable, Assignment, Logical, Call, Binary, Unary, Literal, Grouping, Expression
|
||||
from .meta.statement import Print, StatementExpr, Var, Del, Block, If, While, Break, Function, Return, Class
|
||||
|
||||
|
||||
class Parser(object):
|
||||
|
@ -398,11 +398,24 @@ class Parser(object):
|
|||
body = self.block()
|
||||
return Function(name, parameters, body)
|
||||
|
||||
def class_declaration(self):
|
||||
"""Parses a class declaration"""
|
||||
|
||||
name = self.consume(TokenType.ID, "Expecting class name")
|
||||
self.consume(TokenType.LB, "Expecting '{' before class body")
|
||||
methods = []
|
||||
while not self.check(TokenType.RB) and not self.done():
|
||||
methods.append(self.function("method"))
|
||||
self.consume(TokenType.RB, "Expecting '}' after class body")
|
||||
return Class(name, methods)
|
||||
|
||||
def declaration(self):
|
||||
"""Parses a declaration"""
|
||||
|
||||
try:
|
||||
if self.match(TokenType.FUN):
|
||||
if self.match(TokenType.CLASS):
|
||||
return self.class_declaration()
|
||||
elif self.match(TokenType.FUN):
|
||||
return self.function("function")
|
||||
elif self.match(TokenType.VAR):
|
||||
return self.var_declaration()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from .meta.exceptions import JAPLError
|
||||
from .meta.expression import Expression
|
||||
from .meta.statement import Statement
|
||||
from .meta.functiontype import FunctionType
|
||||
try:
|
||||
from functools import singledispatchmethod
|
||||
except ImportError:
|
||||
|
@ -9,7 +10,7 @@ from typing import List, Union
|
|||
from collections import deque
|
||||
|
||||
|
||||
class Resolver(object):
|
||||
class Resolver(Expression.Visitor, Statement.Visitor):
|
||||
"""
|
||||
This class serves the purpose of correctly resolving
|
||||
name bindings (even with closures) efficiently
|
||||
|
@ -22,6 +23,7 @@ class Resolver(object):
|
|||
|
||||
self.interpreter = interpreter
|
||||
self.scopes = deque()
|
||||
self.current_function = FunctionType.NONE
|
||||
|
||||
@singledispatchmethod
|
||||
def resolve(self, stmt_or_expr: Union[Statement, Expression, List[Statement]]):
|
||||
|
@ -75,6 +77,8 @@ class Resolver(object):
|
|||
if not self.scopes:
|
||||
return
|
||||
scope = self.scopes[-1]
|
||||
if name.lexeme in scope:
|
||||
raise JAPLError(name, "Cannot re-declare the same variable in local scope, use assignment instead")
|
||||
scope[name.lexeme] = False
|
||||
|
||||
def define(self, name):
|
||||
|
@ -118,15 +122,18 @@ class Resolver(object):
|
|||
self.interpreter.resolve(expr, i)
|
||||
i += 1
|
||||
|
||||
def resolve_function(self, function):
|
||||
def resolve_function(self, function, function_type: FunctionType):
|
||||
"""Resolves function objects"""
|
||||
|
||||
enclosing = self.current_function
|
||||
self.current_function = function_type
|
||||
self.begin_scope()
|
||||
for param in function.params:
|
||||
self.declare(param)
|
||||
self.define(param)
|
||||
self.resolve(function.body)
|
||||
self.end_scope()
|
||||
self.current_function = enclosing
|
||||
|
||||
def visit_assign(self, expr):
|
||||
"""Visits an assignment expression"""
|
||||
|
@ -139,7 +146,11 @@ class Resolver(object):
|
|||
|
||||
self.declare(stmt.name)
|
||||
self.define(stmt.name)
|
||||
self.resolve_function(stmt)
|
||||
self.resolve_function(stmt, FunctionType.FUNCTION)
|
||||
|
||||
def visit_class(self, stmt):
|
||||
self.declare(stmt.name)
|
||||
self.define(stmt.name)
|
||||
|
||||
def visit_statement_expr(self, stmt):
|
||||
"""Visits a statement expression node"""
|
||||
|
@ -162,6 +173,8 @@ class Resolver(object):
|
|||
def visit_return(self, stmt):
|
||||
"""Visits a return statement node"""
|
||||
|
||||
if self.current_function == FunctionType.NONE:
|
||||
raise JAPLError(stmt.keyword, "'return' outside function")
|
||||
if stmt.value is not None:
|
||||
self.resolve(stmt.value)
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
class JAPLClass(object):
|
||||
"""A JAPL class"""
|
||||
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
|
@ -35,7 +35,7 @@ class JAPL(object):
|
|||
token, message = err.args
|
||||
print(f"An exception occurred at line {token.line}, file '{file}' at '{token.lexeme}': {message}")
|
||||
else:
|
||||
print(f"An exception occurred, details below\n\n{err}")
|
||||
print(f"An exception occurred, details below\n\n{type(err).__name__}: {err}")
|
||||
|
||||
def repl(self):
|
||||
"""Starts an interactive REPL"""
|
||||
|
@ -67,8 +67,12 @@ class JAPL(object):
|
|||
self.resolver.resolve(ast)
|
||||
result = self.interpreter.interpret(ast)
|
||||
except JAPLError as error:
|
||||
token, message = error.args
|
||||
print(f"A runtime exception occurred at line {token.line} at '{token.lexeme}': {message}")
|
||||
if len(error.args) == 2:
|
||||
token, message = error.args
|
||||
print(
|
||||
f"An exception occurred at line {token.line}, file 'stdin' at '{token.lexeme}': {message}")
|
||||
else:
|
||||
print(f"An exception occurred, details below\n\n{type(error).__name__}: {error}")
|
||||
else:
|
||||
if result is not None:
|
||||
print(repr(result))
|
||||
|
|
Loading…
Reference in New Issue