Initial commit - fix problems & refactor code

This commit is contained in:
2025-12-22 19:10:46 +03:00
commit ff36917cb5
39 changed files with 1499 additions and 0 deletions
@@ -0,0 +1,24 @@
from core.tokens import BinaryOperator
from core.exceptions import runtimeError
from core.environment import RepresentateAST
def evalBinaryOperator(op: BinaryOperator, a, b):
try:
match op:
case BinaryOperator.ADD:
return a + b
case BinaryOperator.SUB:
return a - b
case BinaryOperator.MUL:
return a * b
case BinaryOperator.DIV:
if type(a) is type(b) is int:
return a // b
return a / b
case BinaryOperator.MOD:
return a % b
case BinaryOperator.STRCONCAT:
return RepresentateAST(a) + RepresentateAST(b)
except Exception as e:
raise runtimeError(f'cannot apply {repr(op)} to {repr(a)} and {repr(b)}') from e
@@ -0,0 +1,14 @@
from core.tokens import BinaryPredicate
from core.exceptions import runtimeError
def evalBinaryPredicate(op: BinaryPredicate, a, b):
try:
match op:
case BinaryPredicate.LT:
return a < b
case BinaryPredicate.GT:
return a > b
case BinaryPredicate.EQ:
return a == b
except Exception as e:
raise runtimeError(f'cannot apply {repr(op)} to {repr(a)} and {repr(b)}') from e
+53
View File
@@ -0,0 +1,53 @@
from core.primitives.list import NIL, cons, car, cdr
from core.primitives.symbol import isSymbol, symbolName
from core.exceptions import syntaxError
from core.tokens import Lambda
from core.tail_recursion import tailRecursion
# from core.evaluator.naively_evaluate import naivelyEvaluate
from core.evaluator.utils.getFirstN import getFirstN
from core.environment import TCO
def evalCall(head, tail, environment, environmentParent, strict):
from core.evaluator.naively_evaluate import naivelyEvaluate
args = head.args
if args == NIL and tail != NIL:
raise syntaxError(f'more than zero args for {repr(head)}')
while args != NIL:
firstArg = car(args)
if not isSymbol(firstArg):
raise syntaxError(f'cannot take non-symbol as argument: {repr(firstArg)}')
if symbolName(firstArg) == '.':
firstArg = getFirstN(args, 2)[1]
arr = []
while tail != NIL:
arr.append(naivelyEvaluate(car(tail), environmentParent, True))
tail = cdr(tail)
arg = NIL
for elem in reversed(arr):
arg = cons(elem, arg)
args = NIL
else:
if tail == NIL:
break
arg = naivelyEvaluate(car(tail), environmentParent, True)
args = cdr(args)
tail = cdr(tail)
environment.add(firstArg, arg)
if tail != NIL:
raise syntaxError(f'extra args {repr(tail)}\nfor not variable lambda {repr(head)}')
if args != NIL:
return Lambda(args, head.body, environment)
if not TCO:
return naivelyEvaluate(head.body, environment, True)
ret = tailRecursion(naivelyEvaluate, head.body, environment, False)
return tailRecursion.eval(ret) if strict else ret
+49
View File
@@ -0,0 +1,49 @@
from core.primitives.list import cons, car, cdr, NIL
from core.primitives.symbol import symbolName, symbolKey
from core.tokens import Macro
from core.exceptions import syntaxError
from core.tail_recursion import tailRecursion
from core.macro_expand import macroExpand
# from core.evaluator.naively_evaluate import naivelyEvaluate
from core.evaluator.utils.getFirstN import getFirstN
from core.environment import TCO
def evalMacro(head, tail, environment, strict):
from core.evaluator.naively_evaluate import naivelyEvaluate
d = dict()
args = head.args
if args == NIL and tail != NIL:
raise syntaxError(f'more than zero args for {repr(head)}')
while args != NIL:
firstArg = car(args)
if symbolName(firstArg) == '.':
firstArg = getFirstN(args, 2)[1]
arg = tail
args = NIL
tail = NIL
else:
if tail == NIL:
break
arg = car(tail)
args = cdr(args)
tail = cdr(tail)
d[symbolKey(firstArg)] = arg
if tail != NIL:
raise syntaxError(f'extra args {repr(tail)}\nfor not variable macro {repr(head)}')
expanded = macroExpand(head.body, d)
if args != NIL:
return Macro(args, expanded)
if not TCO:
return naivelyEvaluate(expanded, environment, True)
ret = tailRecursion(naivelyEvaluate, expanded, environment, False)
return tailRecursion.eval(ret) if strict else ret
+137
View File
@@ -0,0 +1,137 @@
from core.tokens import (
SpecialForm,
Lambda,
Macro,
Pure
)
from core.primitives.list import (
cons,
car,
cdr,
isList
)
from core.primitives.symbol import symbol
from core.environment import NIL, RepresentateAST
from core.primitives.symbol import isSymbol
from core.exceptions import (
syntaxError,
runtimeError
)
# from core.evaluator.naively_evaluate import naivelyEvaluate
from core.evaluator.utils.getFirstN import getFirstN
from core.parser.parse_expression import parseExpression
def evalSpecialForm(head, tail, env, strict):
from core.evaluator.naively_evaluate import naivelyEvaluate
match head:
case SpecialForm.QUOTE:
return getFirstN(tail, 1)[0]
case SpecialForm.EVAL:
return naivelyEvaluate(naivelyEvaluate(getFirstN(tail, 1)[0], env, True), env, strict)
case SpecialForm.TYPEOF:
a = naivelyEvaluate(getFirstN(tail, 1)[0], env, True)
return 'List' if isList(a) else 'Symbol' if isSymbol(a) else str(a.__class__.__name__)
case SpecialForm.CONS:
a, b = getFirstN(tail, 2)
evaluated_b = naivelyEvaluate(b, env, True)
if not isList(evaluated_b):
raise syntaxError('cannot add element to non-list')
return cons(naivelyEvaluate(a, env, True), evaluated_b)
case SpecialForm.CAR:
a = naivelyEvaluate(getFirstN(tail, 1)[0], env, True)
if not isList(a):
return a # config moment
if a == NIL:
raise syntaxError("NIL's head???") # config moment
return car(a)
case SpecialForm.CDR:
a = naivelyEvaluate(getFirstN(tail, 1)[0], env, True)
if not isList(a):
return NIL # config moment
if a == NIL:
raise syntaxError("NIL's tail???") # config moment
return cdr(a)
case SpecialForm.IF:
a, b, c = getFirstN(tail, 3) # a ? b : c
evaled_a = naivelyEvaluate(a, env, True)
if type(evaled_a) is bool:
return naivelyEvaluate(b, env, strict) if evaled_a else naivelyEvaluate(c, env, strict)
raise syntaxError(f'not boolean {a} in (++ {repr(head)} {repr(tail)})') # TODO
case SpecialForm.DO:
if tail == NIL:
return NIL
ev = NIL
while cdr(tail) != NIL:
ev = naivelyEvaluate(car(tail), env, True)
tail = cdr(tail)
return naivelyEvaluate(car(tail), env, strict)
case SpecialForm.PRINT:
print(RepresentateAST(naivelyEvaluate(getFirstN(tail, 1)[0], env, True)), end='')
return NIL
case SpecialForm.FLUSH:
print(end='', flush=True)
return NIL
case SpecialForm.READ:
getFirstN(tail, 0)
s = input('\nreading: ')
return parseExpression(s)[0]
case SpecialForm.SYMBOL:
a = naivelyEvaluate(getFirstN(tail, 1)[0], env, True)
if not isinstance(a, str):
raise syntaxError('cannot make symbol from non-string')
return symbol(a)
case SpecialForm.DEF:
a, b = getFirstN(tail, 2)
if isSymbol(a):
env.add(a, naivelyEvaluate(b, env, True))
return NIL
raise syntaxError('cannot define non-symbol')
case SpecialForm.SET:
a, b = getFirstN(tail, 2)
if isSymbol(a):
env.set(a, naivelyEvaluate(b, env, True))
return NIL
raise syntaxError('cannot set non-Symbol')
case SpecialForm.LAMBDA:
a, b = getFirstN(tail, 2)
if isList(a):
return Lambda(a, b, env)
raise syntaxError(f'wrong args form for lambda: {repr(a)}')
# some errors may be here
case SpecialForm.PURE:
a, b = getFirstN(tail, 2)
if isList(a):
return Pure(a, b)
raise syntaxError(f'wrong args form for pure: {repr(a)}')
case SpecialForm.MACRO:
a, b = getFirstN(tail, 2)
if isList(a):
return Macro(a, b)
raise syntaxError(f'wrong args form for macro: {repr(a)}')
case SpecialForm.RAISE:
cause = naivelyEvaluate(getFirstN(tail, 1)[0], env, True)
raise runtimeError(RepresentateAST(cause))
case _:
raise syntaxError
+73
View File
@@ -0,0 +1,73 @@
from core.tokens import (
BinaryOperator,
BinaryPredicate,
SpecialForm,
Lambda,
Pure,
Macro,
)
# debug
import sys
from core.primitives.symbol import (
isSymbol
)
from core.primitives.list import (
cons, car, cdr,
isList,
NIL
)
from core.environment import EVAL_NIL, Environment, RepresentateAST
from core.evaluator.evaluate_binary_operator import evalBinaryOperator
from core.evaluator.evaluate_binary_predicate import evalBinaryPredicate
from core.evaluator.evaluate_special_form import evalSpecialForm
from core.evaluator.evaluate_call import evalCall
from core.evaluator.evaluate_macro import evalMacro
from core.evaluator.utils.getFirstN import getFirstN
from core.exceptions import error, syntaxError, typeError
def naivelyEvaluate(value, localEnvironment, strict):
if isSymbol(value):
return localEnvironment.get(value)
elif isList(value):
if value == NIL:
return EVAL_NIL()
head = naivelyEvaluate(car(value), localEnvironment, True)
tail = cdr(value)
try:
if isinstance(head, (BinaryOperator, BinaryPredicate)):
try:
a, b = getFirstN(tail, 2)
evaledA = naivelyEvaluate(a, localEnvironment, True)
evaledB = naivelyEvaluate(b, localEnvironment, True)
if isinstance(head, BinaryOperator):
return evalBinaryOperator(head, evaledA, evaledB)
return evalBinaryPredicate(head, evaledA, evaledB)
except syntaxError as e:
raise syntaxError(f'wrong args for: {repr(value)}') from e
elif isinstance(head, SpecialForm):
return evalSpecialForm(head, tail, localEnvironment, strict)
elif isinstance(head, (Lambda, Pure)):
if isinstance(head, Lambda):
environment = Environment(head.env)
else:
environment = Environment(parent=localEnvironment)
return evalCall(head, tail, environment, localEnvironment, strict)
elif isinstance(head, Macro):
return evalMacro(head, tail, localEnvironment, strict)
except error as e:
raise syntaxError(f'wrong args for {repr(head)}: {repr(cdr(value))}') from e
raise typeError(f'wrong head form: {RepresentateAST(head)}')
return value
+32
View File
@@ -0,0 +1,32 @@
from typing import Any
from core.primitives.list import cons, car, cdr, NIL
from core.exceptions import syntaxError
from core.printer import Representation
def getFirstN(_list: Any, n: int) -> list[Any]:
"""Get first N elements.
Args:
_list: A list.
n: The expected number of elements.
Returns:
A list of the extracted elements.
Raises:
syntaxError: If the list has wrong number of elements.
"""
result: list[Any] = []
current: Any = _list
while current != NIL and len(result) < n:
result.append(car(current))
current = cdr(current)
if current != NIL:
raise syntaxError(f"extra elems: {Representation(current)}")
if len(result) < n:
raise syntaxError("lack of elems")
return result