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
+3
View File
@@ -0,0 +1,3 @@
__pycache__
.venv
.idea
View File
View File
+54
View File
@@ -0,0 +1,54 @@
from __future__ import annotations
from typing import Any, Callable, Optional
from string import whitespace
from core.primitives.symbol import symbolKey, isSymbol
from core.printer import Representation
from core.primitives.list import NIL
class Environment:
"""Environment for variable bindings with lexical scoping."""
frame: dict[str, Any]
parent: Environment | None
def __init__(self, parent: Environment | None) -> None:
self.frame: dict[str, Any] = {}
self.parent: Environment | None = parent
def add(self, symbol_key: Any, value: Any) -> None:
"""Add a new binding to the current environment frame."""
self.frame[symbolKey(symbol_key)] = value
def set(self, symbol_key: Any, value: Any) -> None:
"""Set a binding in the environment chain."""
key: str = symbolKey(symbol_key)
currentEnvironment: Environment = self
while currentEnvironment is not None:
if key in currentEnvironment.frame:
currentEnvironment.frame[key] = value
return
assert currentEnvironment is Environment
currentEnvironment = currentEnvironment.parent
raise SyntaxError(f"no such symbol: {key}")
def get(self, symbol_key: Any) -> Any:
"""Get a binding from the environment chain."""
key: str = symbolKey(symbol_key)
currentEnvironment: Environment | None = self
while currentEnvironment is not None:
if key in currentEnvironment.frame:
return currentEnvironment.frame[key]
currentEnvironment = currentEnvironment.parent
return symbol_key
EVAL_NIL: Callable[[], Any] = lambda: NIL
ENV: Environment = Environment(None)
MEANINGFUL: str = "\"();'"
IGNORE: str = whitespace + ","
RepresentateAST: Callable[[Any], str] = lambda value: (
value if type(value) is str and not isSymbol(value) else Representation(value)
)
TCO: bool = True
@@ -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
+31
View File
@@ -0,0 +1,31 @@
"""Exception classes for Lisp interpreter."""
class error(Exception):
"""Base exception for all Lisp-related errors."""
pass
class syntaxError(error):
"""Raised when there is a syntax error in the Lisp code."""
pass
class runtimeError(error):
"""Raised when there is a runtime error during evaluation."""
pass
class valueError(error):
"""Raised when there is an invalid value."""
pass
class typeError(error):
"""Raised when there is a type error during evaluation."""
pass
+28
View File
@@ -0,0 +1,28 @@
from typing import Any
from core.primitives.symbol import symbolKey, isSymbol
from core.primitives.list import NIL, isList, cons, car, cdr
def macroExpand(body: Any, bindings: dict[str, Any]) -> Any:
"""Expand a macro by substituting bindings into the body.
Args:
body: The macro body to expand.
bindings: Dictionary of symbol-to-value bindings.
Returns:
The expanded body.
"""
if isSymbol(body):
if symbolKey(body) in bindings:
return bindings[symbolKey(body)] # auto quote
return body
if isList(body):
if body == NIL:
return NIL
return cons(
macroExpand(car(body), bindings), macroExpand(cdr(body), bindings)
)
return body
+29
View File
@@ -0,0 +1,29 @@
from core.parser.parse_expression import parseExpression
from core.evaluator.naively_evaluate import naivelyEvaluate
from core.exceptions import error
from core.environment import ENV
from traceback import print_exception
def parseEvaluate(source: str, *, load: bool = False) -> str:
results: list[str] = []
s = source
while s:
try:
expr, s = parseExpression(s)
value = naivelyEvaluate(expr, ENV, True)
results.append(repr(value))
except error as e:
# Unwrap nested causes so that the original error message is shown
cause = e
while cause.__cause__ and isinstance(cause.__cause__, error):
cause = cause.__cause__
print(cause)
break
except KeyboardInterrupt:
return "interrupted\n"
except Exception as exc:
print_exception(exc)
break
return ' '.join(results)
View File
+10
View File
@@ -0,0 +1,10 @@
from core.environment import IGNORE
def cleanString(s: str, ignore: str = IGNORE) -> str:
s = s.lstrip(ignore)
while s and s[0] == ';':
line_end: int = s.find('\n')
if line_end < 0:
return ''
s = s[line_end:].lstrip(ignore)
return s
+9
View File
@@ -0,0 +1,9 @@
from core.environment import IGNORE, MEANINGFUL
import re
def findPrefix(s: str, separators: str = IGNORE + MEANINGFUL) -> str:
pattern = rf'^[^{separators}]*'
match = re.match(pattern, s)
if match:
return match.group(0)
return ''
+52
View File
@@ -0,0 +1,52 @@
from core.parser.clean_string import cleanString
from core.parser.parse_token import parseToken
from core.parser.find_prefix import findPrefix
from core.primitives.list import (
cons,
NIL
)
from core.exceptions import (
syntaxError,
runtimeError
)
from core.tokens import SpecialForm
def parseExpression(s: str) -> tuple:
s = cleanString(s)
if not s:
return NIL, s
match s[0]:
case '"': # string
matching = s.find('"', 1)
if matching < 0:
raise syntaxError(f'the closing " is missing: {s}')
return s[1:matching], s[matching + 1:]
case '(': # list
cache = s # saving initial view for Error illustration
s = s[1:]
arr: list = []
while s and s[0] != ')':
elem, s = parseExpression(s)
arr.append(elem)
s = cleanString(s)
if not s:
raise syntaxError(f'the closing ) is missing: {cache}')
result = NIL
for elem in reversed(arr):
result = cons(elem, result)
return result, s[1:]
case ')':
raise syntaxError(f'extra closing bracket: {s}') # TODO debug
case "'":
try:
v, rem = parseExpression(s[1:])
except Exception as e:
raise runtimeError(f'invalid quote: {s}') from e
if v is None:
raise syntaxError(f'quote of nothing: {s}')
return cons(SpecialForm.QUOTE, cons(v, NIL)), rem
case _: # maybe we can find token
first = findPrefix(s)
return parseToken(first) if first else NIL, s[len(first):]
+22
View File
@@ -0,0 +1,22 @@
from core.tokens import (
SpecialForm,
BinaryOperator,
BinaryPredicate
)
from core.primitives.symbol import symbol
def parseToken(token: str):
for en in [SpecialForm, BinaryOperator, BinaryPredicate]:
if token in en:
return en(token)
if token in ['true', 'false']:
return token == 'true'
try:
return int(token)
except ValueError:
if token == 'inf':
return symbol(token)
try:
return float(token)
except ValueError:
return symbol(token)
View File
+26
View File
@@ -0,0 +1,26 @@
from typing import Any
from core.exceptions import syntaxError
# from core.printer import Representation
class LinkedList:
value = None
nxt = None
def __init__(self, value_, nxt_):
self.value = value_
self.nxt = nxt_
def car(lst):
return lst.value
def cdr(lst):
return lst.nxt
def cons(val, lst):
return LinkedList(val, lst)
def isList(val):
return isinstance(val, LinkedList)
NIL = cons(None, None)
+25
View File
@@ -0,0 +1,25 @@
from functools import lru_cache
class Symbol:
_name: str
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@lru_cache(maxsize=None)
def symbol(name: str) -> Symbol:
return Symbol(name)
def symbolName(sym: Symbol) -> str:
# return sym.name[1:]
return sym.name
def symbolKey(sym: Symbol) -> str:
return sym.name
def isSymbol(obj) -> bool:
return isinstance(obj, Symbol)
+55
View File
@@ -0,0 +1,55 @@
from typing import Any
from core.primitives.list import cons, car, cdr, isList
from core.primitives.symbol import isSymbol, symbolName
from core.tokens import BaseToken, Lambda, Dambda, Macro, SpecialForm
from core.primitives.list import NIL
def Representation(obj: Any) -> str:
"""Return a showable string representation of an object in Lisp notation.
Args:
obj: The object to represent.
Returns:
A string that could be parsed back by the Lisp parser.
"""
if obj is None:
obj = NIL
if isList(obj):
result_str: str = ""
is_first: bool = True
current_item: Any = obj
while current_item != NIL:
if is_first:
is_first = False
else:
result_str += " "
result_str += Representation(car(current_item))
current_item = cdr(current_item)
return f"({result_str})"
elif isSymbol(obj):
return symbolName(obj) # type: ignore
elif isinstance(obj, str):
return f'"{obj}"'
elif isinstance(obj, BaseToken):
return obj.value
elif isinstance(obj, bool):
return "true" if obj else "false"
elif isinstance(obj, Lambda):
return f"({Representation(SpecialForm.LAMBDA)} {Representation(obj.args)} {Representation(obj.body)})"
elif isinstance(obj, Dambda):
return f"({Representation(SpecialForm.DAMBDA)} {Representation(obj.args)} {Representation(obj.body)})"
elif isinstance(obj, Macro):
return f"({Representation(SpecialForm.MACRO)} {Representation(obj.args)} {Representation(obj.body)})"
return str(obj)
+38
View File
@@ -0,0 +1,38 @@
from core.environment import IGNORE, MEANINGFUL
import re
def processString(source: str) -> str:
"""Remove blank characters and comments from source code.
Args:
source: The source code string.
Returns:
The cleaned source code.
"""
source = source.lstrip(IGNORE)
while source and source[0] == ";":
line_end: int = source.find("\n")
if line_end < 0:
return ""
source = source[line_end:].lstrip(IGNORE)
return source
def findPrefix(source: str, separators: str = IGNORE + MEANINGFUL) -> str:
"""Find a meaningful prefix (token) in the source string.
Args:
source: The source code string.
separators: Characters that mark token boundaries.
Returns:
The prefix token found, or empty string if none.
"""
pattern: str = rf"^[^{separators}]*"
match = re.match(pattern, source)
return match.group(0) if match else ""
+23
View File
@@ -0,0 +1,23 @@
from typing import Callable, Any
class tailRecursion:
"""Tail-call optimization wrapper."""
f: Callable[..., Any]
args: tuple[Any, ...]
kwargs: dict[str, Any]
def __init__(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> None:
self.f: Callable[..., Any] = func
self.args: tuple[Any, ...] = args
self.kwargs: dict[str, Any] = kwargs
def __call__(self) -> Any:
return self.f(*self.args, **self.kwargs)
@staticmethod
def eval(val: Any) -> Any:
"""Evaluate tail calls recursively until completion."""
while isinstance(val, tailRecursion):
val = val()
return val
+87
View File
@@ -0,0 +1,87 @@
from __future__ import annotations
from enum import Enum
from typing import Any, TYPE_CHECKING
# from core.environment import Environment
if TYPE_CHECKING:
from core.environment import Environment
class Lambda:
"""Lambda function with arguments, body, and closure environment"""
args: Any
body: Any
env: Environment
def __init__(self, args: Any, body: Any, env: Environment) -> None:
self.args: Any = args
self.body: Any = body
self.env: Environment = env
class Dambda:
"""Dambda function"""
args: Any
body: Any
def __init__(self, args: Any, body: Any) -> None:
self.args: Any = args
self.body: Any = body
class Macro:
"""Macro definition"""
args: Any
body: Any
def __init__(self, args: Any, body: Any) -> None:
self.args: Any = args
self.body: Any = body
class Pure():
"""Pure definition"""
args: Any
body: Any
def __init__(self, args: Any, body: Any) -> None:
self.args: Any = args
self.body: Any = body
class BaseToken(Enum):
"""Base class for enumerations"""
class SpecialForm(BaseToken):
EVAL = "eval"
IF = "if"
DO = "do"
PRINT = "print"
READ = "read"
SYMBOL = "symbol"
DEF = "def"
SET = "set!"
FLUSH = "flush"
PURE = "pure"
LAMBDA = "lambda"
DAMBDA = "dambda"
MACRO = "macro"
RAISE = "raise"
QUOTE = "quote"
CONS = "cons"
CAR = "car"
CDR = "cdr"
TYPEOF = "typeof"
class BinaryOperator(BaseToken):
ADD = "_+_"
SUB = "_-_"
MUL = "_*_"
DIV = "_/_"
MOD = "_%_"
STRCONCAT = "_++_"
class BinaryPredicate(BaseToken):
LT = "_<_"
GT = "_>_"
EQ = "_=_"
+27
View File
@@ -0,0 +1,27 @@
from typing import Callable
class TR:
def __init__(self, f, *args, **kwargs):
self.f = f
self.args = args
self.kwargs = kwargs
def __call__(self):
return self.f(*self.args, **self.kwargs)
def evalTR(value):
while isinstance(value, TR):
value = value()
return value
def suman(n, acc):
return acc if n <= 0 else TR(suman, n - 1, acc + n)
def isOdd(n):
return False if n == 0 else TR(isEven, n - 1)
def isEven(n):
return True if n == 0 else TR(isOdd, n - 1)
while n := int(input()):
print(evalTR(suman(n, 0)))
# print(evalTR(isEven(n)), evalTR(isOdd(n)))
+20
View File
@@ -0,0 +1,20 @@
; (def endl "
; ")
(def acc-class
(lambda ()
(do
(def get '(lambda () val))
(def add
'(lambda (delta)
(do
(set! val (+ val delta))
val)))
(def sub
'(lambda (delta)
(do
(def new (- val delta))
(if (< new 0) (print (++ "аяяй" endl)) (set val new))
val)))
(def acc-constructor (lambda (val) (lambda (method) (eval (eval method)))))
acc-constructor)))
+32
View File
@@ -0,0 +1,32 @@
(def comment (macro (some) '()))
(comment
(print "input a number")
(def n (read))
(def res 1)
; (print (_+_ n 1))
(def f '(do
(set! res (_*_ res n))
(set! n (_-_ n 1))
(if (_<_ 0 n) (eval f) 0)
))
(eval f)
(print res)
(def t (macro (x) (do (def z 42) (print x) x)))
(def m (dambda (a) (do (print z) (_+_ a a) (print z))))
(print (m (t 3)))
)
(def dact (dambda (n self) (
if (_<_ n 2) 1
(_*_ n (self (_-_ n 1) self))
)))
(def lact (lambda (n self) (
if (_<_ n 2) 1
(_*_ n (lact (_-_ n 1) self))
)))
(def foo (lambda (f, n) (do (def lact 43) (def dact 42) (f n f))))
+5
View File
@@ -0,0 +1,5 @@
(def fact (dambda (n) (
if (_<_ n 2) 1
(_*_ n (fact (_-_ n 1)))
)))
+9
View File
@@ -0,0 +1,9 @@
(defmacro fact (n)
; (prints 'n \n)
(if (< n 3) n
(* n (fact (- n 1)))))
(defmacro facc (n acc)
; (prints 'acc \n)
(if (< n 2) acc
(facc (- n 1) (* n acc))))
+6
View File
@@ -0,0 +1,6 @@
(def fact (lambda (n) (
if (_<_ n 2) 1
(_*_ n (fact (_-_ n 1)))
)))
; (def foo fact)
; (set! fact (lambda (n) (_+_ n 1)))
+116
View File
@@ -0,0 +1,116 @@
; path: list of points ((1 3) (3 5) (5 7))
; ^
; stack - список ещё не проверенных направлений
;
; (((1 2) (1 -2) (-1 2) (-1 -2)) ((1 -2) (-1 2) (-1 -2)) ((-1 2) (-1 -2)))
; (( ... ) ((1 -2) (-1 2) (-1 -2)) ((1 -2) (-1 2) (-1 -2)) ((-1 2) (-1 -2)))
; (() ((1 -2) (-1 2) (-1 -2)) ((-1 2) (-1 -2)))
; ((2 5) (1 3) (3 5) (5 7))
;; Сделать так, чтобы код работал для доски 8x8
;; Необходимо сделать Tail Call Optimization
(def m 6)
(def n 6)
(def nm (* n m))
(def start-position '(1 1))
;;
;; (defn check-step? (step path) ; '(to from)
;; (cond (or (nil? path) (nil? (cdr path))) false
;; (= (take 2 path) step) true
;; (check-step? step (cdr path))))
;; (a b c d e a b f g)
(defn in-borders? (p) (and (<= 1 (car p) n) (<= 1 (car (cdr p)) m)))
;; (defn duplicated-step? (path) (check-step? (take 2 path) (cdr path)))
;; (def all-dirs '((2 1) (2 -1) (-2 1) (-2 -1) (1 2) (1 -2) (-1 2) (-1 -2)))
;; (def all-dirs '((1 2) (2 1) (2 -1) (1 -2) (-1 -2) (-2 -1) (-2 1) (-1 2)))
(def all-dirs '((2 1) (1 2) (-1 2) (-2 1) (-2 -1) (-1 -2) (1 -2) (2 -1)))
;; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(def all-cells
(concat (map (lambda (r)
(map (lambda (c)
(list r c))
(range 1 (+ 1 m))))
(range 1 (+ 1 n)))))
(def all-moves (concat (map (lambda (cell)
(filter (lambda (step) (in-borders? (car step)))
(map (lambda (delta-move) (list (zip-with + cell delta-move) cell))
all-dirs)))
all-cells)))
(defn steps-set-gen ()
(eval (cons do (map (lambda (step) (list def (symbol (++ step)) false)) all-moves)))
(defn add (x) (eval (list set! (symbol (++ x)) true)))
(defn has? (x) (eval (symbol (++ x))))
(defn remove (x) (eval (list set! (symbol (++ x)) false)))
(lambda (cmd) (eval cmd))
)
(def steps-set (steps-set-gen))
(defn step-was? (step) ((steps-set 'has?) step))
;; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(defmacro -= (x v) (set! x (- x v)))
(defmacro += (x v) (set! x (+ x v)))
(defn cells-multiset-gen ()
(eval (cons do (map (lambda (cell) (list def (symbol (++ cell)) 0)) all-cells)))
(def distinct 0)
(defn add (x) (do
(def name (symbol (++ x)))
(when (= (eval name) 0) (+= distinct 1))
(eval (list += name 1))))
(defn remove (x) (do
(def name (symbol (++ x)))
(when (= (eval name) 1) (-= distinct 1))
(eval (list -= name 1))))
;; (defn distinct () xxx)
(lambda (cmd) (eval cmd))
)
(def cells-multiset (cells-multiset-gen))
(defn path-isgood? (path) (and (= (car path) start-position) (= (cells-multiset 'distinct) nm)))
;; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(defn dfs-tail (path stack)
;; (print path)
(if
; success
(path-isgood? path) path (if
; если мы выше начала - нет решения
(nil? path) false (if
; если некуда ходить из текущей позиции
(nil? (car stack)) (do
(if (not (nil? (cdr path))) ((steps-set 'remove) (take 2 path)) nil)
((cells-multiset 'remove) (car path))
(dfs-tail (cdr path) (cdr stack)))
; иначе нам есть что делать
(do (def next-pos (zip-with + (car path) (car (car stack))))
(def step (list next-pos (car path)))
(def new-stack (cons (cdr (car stack)) (cdr stack)))
(if
; если мы резко оказались вне доски
(not (in-borders? next-pos)) (dfs-tail path new-stack) (if
; если этот ход мы уже делали
(step-was? step) (dfs-tail path new-stack)
(do
((steps-set 'add) step)
((cells-multiset 'add) next-pos)
(dfs-tail (cons next-pos path) (cons all-dirs new-stack))))))))))
(print (dfs-tail (list start-position) (list all-dirs)))
+79
View File
@@ -0,0 +1,79 @@
(def n 5)
(def m 4)
(def start-position '(1 1))
;; path - '((1 1) (4 2) (0 0))
(def knight-moves '((2 1) (2 -1) (-2 1) (-2 -1) (1 2) (1 -2) (-1 2) (-1 -2)))
(defn check-pos? (p) (and (<= 1 (car p) n) (<= 1 (car (cdr p)) m)))
(def all-cells
(concat (map (lambda (r)
(map (lambda (c)
(list r c))
(range 1 (+ 1 m))))
(range 1 (+ 1 n)))))
(def all-moves (concat (map (lambda (cell)
(filter (lambda (step) (check-pos? (car step)))
(map (lambda (delta-move) (list (zip-with + cell delta-move) cell))
knight-moves)))
all-cells)))
(defn steps-set-gen ()
(eval (cons do (map (lambda (step) (list def (symbol (++ step)) false)) all-moves)))
(defn add (x) (eval (list set! (symbol (++ x)) true)))
(defn has? (x) (eval (symbol (++ x))))
(defn remove (x) (eval (list set! (symbol (++ x)) false)))
(lambda (cmd) (eval cmd))
)
(def steps-set (steps-set-gen))
(defn check-step? (step path) (not ((steps-set 'has?) step)))
(defmacro -= (x v) (set! x (- x v)))
(defmacro += (x v) (set! x (+ x v)))
(defn cells-multiset-gen ()
(eval (cons do (map (lambda (cell) (list def (symbol (++ cell)) 0)) all-cells)))
(def distinct 0)
(defn add (x) (do
(def name (symbol (++ x)))
(when (= (eval name) 0) (+= distinct 1))
(eval (list += name 1))))
(defn remove (x) (do
(def name (symbol (++ x)))
(when (= (eval name) 1) (-= distinct 1))
(eval (list -= name 1))))
;; (defn distinct () xxx)
(lambda (cmd) (eval cmd))
)
(def cells-multiset (cells-multiset-gen))
(defn dfs (path)
;; (def current-position (car path))
;; (print path)
(if (and (= (car path) start-position) (= (cells-multiset 'distinct) (* n m)))
path
(reduce (lambda (acc, x)
(if (!= acc false) acc
(do
(def new-pos (zip-with + x (car path)))
(def step (list new-pos (car path)))
(if (and (check-pos? new-pos) (check-step? step path))
(do ((steps-set 'add) step)
((cells-multiset 'add) new-pos)
(def res (dfs (cons new-pos path)))
((steps-set 'remove) step)
((cells-multiset 'remove) new-pos)
res
)
false))))
false knight-moves))
)
+4
View File
@@ -0,0 +1,4 @@
(defn maybeprint (n) (when (= (% n 50) 0) (do (prints n " ") (flush))))
(defn odd? (n) (if (= n 0) false (do (maybeprint n) (even? (- n 1)))))
(defn even? (n) (if (= n 0) true (do (maybeprint n) (odd? (- n 1)))))
; (print (odd? (read)))
+50
View File
@@ -0,0 +1,50 @@
"(def \n "
")
(def acc-class (lambda ()
(do
(def get (dambda () val))
(def add (dambda (delta)
(do
(set! val (_+_ val delta))
val
)))
(def sub (dambda (delta)
(do
(def new (_-_ val delta))
(if (_<_ new 0) (print (_++_ "аяяй" \n)) (set! val new))
val
)))
(def acc-constructor (lambda (val) (lambda (method-name) (eval (eval method-name) ))))
acc-constructor
)))"
(def nil '())
(def acc-class (lambda (initial-val)
(do
(def val initial-val)
(lambda (all-args)
(do
(def method (car all-args))
(def args (cdr all-args))
(def get (dambda () val))
(def add (dambda (delta)
(do
(set! val (_+_ val delta))
val
)))
(def sub (dambda (delta)
(do
(def new (_-_ val delta))
(if (_<_ new 0) (print (_++_ "аяяй" "\n")) (set! val new))
val
)))
(if (_==_ method (symbol "get"))
(get)
(if (_==_ method (symbol "add"))
(add (car args))
(if (_==_ method (symbol "sub"))
(sub (car args))
nil))))))))
+2
View File
@@ -0,0 +1,2 @@
(defun selfactorial (self, n) (* n (if (< n 3) 1 (f f (- n 1)))))
(defun factorial (n) (selfactorial selfactorial n))
+59
View File
@@ -0,0 +1,59 @@
import os
import sys
from core.parse_evaluate import parseEvaluate
def repl() -> None:
stdlib_path = 'stdlib.lsp'
if os.path.isfile(stdlib_path):
with open(stdlib_path, 'r', encoding='utf8') as f:
parseEvaluate(f.read(), load=True)
else:
print('>> Warning: standard library not found.')
if not sys.stdin.isatty():
parseEvaluate(sys.stdin.read(), load=True)
print('\ninput exhausted, exiting REPL...')
return
prompt = '>>> '
exit_cmds = {':q', ':quit', ':exit', 'exit', 'quit'}
load_cmds = {':l', ':load'}
while True:
try:
line = input(prompt).strip()
except (KeyboardInterrupt, EOFError):
print("\nexiting REPL...")
break
if not line:
continue
parts = line.split(None, 1)
if not parts:
continue
token = parts[0]
if token in exit_cmds:
print("\nexiting REPL...")
break
if token in load_cmds:
filename = parts[1].strip() if len(parts) > 1 else ''
if not filename:
print(prompt + "Error: no filename provided.")
continue
if not os.path.isfile(filename):
print(prompt + f'Error: no such file "{filename}"')
continue
with open(filename, 'r', encoding='utf-8') as f:
parseEvaluate(f.read(), load=True)
print()
continue
print(parseEvaluate(line))
if __name__ == '__main__':
repl()
+216
View File
@@ -0,0 +1,216 @@
;;;; stdlib
;;; useful stuff
(def \n "
")
(def nil '())
(def fn lambda)
(def nil? (macro (l) (_=_ l nil)))
(def list (lambda (. args) args))
;;; easier functions
(def defmacro-defun-codegen
(lambda (head name args body)
(list def name
(list head args
(if (nil? body) nil
(if (nil? (cdr body)) (car body)
(cons do body)))))))
(def defmacro (macro (name args . body) (eval (defmacro-defun-codegen macro 'name 'args 'body))))
(defmacro defun (name args . body) (eval (defmacro-defun-codegen lambda 'name 'args 'body)))
(def defn defun)
;;; useful functions
(defmacro comment (. a) nil)
(defun reduce (f a l) (if (nil? l) a (reduce f (f a (car l)) (cdr l))))
(defun foldr (f a l) (if (nil? l) a (f (foldr f a (cdr l)) (car l))))
;;; boolean functions
(defun not (boo) (if boo false true))
(defun or-codegen (args) (if (nil? args) false (list if (car args) true (or-codegen (cdr args)))))
(defun and-codegen (args) (if (nil? args) true (list if (car args) (and-codegen (cdr args)) false)))
(defmacro or (. args) (eval ( or-codegen 'args)))
(defmacro and (. args) (eval (and-codegen 'args))) ; (and 1 2 3) = (if 1 (if 2 3 false) false)
(defun cond-codegen (args)
(if (nil? args) nil ; (raise "wrong arity of cond")
(if (nil? (cdr args)) (car args)
(list if (car args) (car (cdr args)) (cond-codegen (cdr (cdr args)))))))
(defmacro cond (. args) (eval (cond-codegen 'args))) ; (cond 1 2 3 4 5) = (if 1 2 (if 3 4 5))
;;; basic math operations
; (defun + (. args) (if (nil? args) 0 (reduce _+_ (car args) (cdr args)))) ; ability to interop +
(defun + (. args) (reduce _+_ 0 args)) ; (+ 1 2 3 4) = (_+_ 0 (_+_ 1 (_+_ 2 (_+_ 3 4))))
(defun * (. args) (reduce _*_ 1 args))
(defun ++ (. args) (reduce _++_ "" args))
(defun - (. args)
(cond (nil? args) (raise "zero args for -")
(nil? (cdr args)) (_-_ 0 (car args))
(reduce _-_ (car args) (cdr args))))
(defun / (. args)
(cond (nil? args) (raise "zero args for /")
(nil? (cdr args)) (_/_ 1 (car args))
(reduce _/_ (car args) (cdr args))))
(defun % (. args)
(cond (nil? args) (raise "zero args for %")
(nil? (cdr args)) (raise "one arg for %")
(reduce _%_ (car args) (cdr args))))
;;; basic binary predicates
(defun bp-core (bp a l) (if (nil? l) true (if (bp a (car l)) (bp-core bp (car l) (cdr l)) false )))
(defun bp-pre-core (bp args) (if (nil? args) true (bp-core bp (car args) (cdr args))))
(defun < (. args) (bp-pre-core _<_ args))
(defun > (. args) (bp-pre-core _>_ args))
(defun _>=_ (a b) (not (_<_ a b)))
(defun _<=_ (a b) (not (_>_ a b)))
(defun <= (. args) (bp-pre-core _<=_ args))
(defun >= (. args) (bp-pre-core _>=_ args))
(defun three-op-eq (a b c) (if (_=_ a b) (_=_ b c) false))
(defun bineq (a b)
(def type-a (typeof a))
(def type-b (typeof b))
(cond (three-op-eq type-a type-b "Symbol") (_=_ (++ a) (++ b))
(three-op-eq type-a type-b "List") (_=_ (++ a) (++ b))
(_=_ a b)))
(defun = (. args) (bp-pre-core bineq args))
(defun != (. args) (not (bp-pre-core bineq args)))
;;; list tools
(defun map (func lst)
(if (nil? lst) nil
(cons (func (car lst)) (map func (cdr lst)))))
(defun reverse-core (lst acc) (if (nil? lst) acc (reverse-core (cdr lst) (cons (car lst) acc))))
(defun reverse (lst) (reverse-core lst nil))
(defun map-reverse-core (func lst acc)
(if (nil? lst) acc
(map-reverse-core func (cdr lst) (cons (func (car lst)) acc))))
(defun map-reverse (func lst) (map-reverse-core func lst nil))
(defun map-tail (func lst) (reverse (map-reverse func lst)))
(defun filter (pred lst)
(cond (nil? lst) nil
(pred (car lst)) (cons (car lst) (filter pred (cdr lst)))
(filter pred (cdr lst))))
(defun take (n lst)
(cond (< n 1) nil
(nil? lst) nil
(cons (car lst) (take (- n 1) (cdr lst)))))
(defun drop (n lst)
(cond (< n 1) lst
(nil? lst) nil
(drop (- n 1) (cdr lst))))
(defun append-reverse (a b)
(if (nil? a) b (append-reverse (cdr a) (cons (car a) b))))
(defun append (a b) (append-reverse (reverse a) b))
(defun concat (lsts) (reduce append nil lsts))
(defun length (lst) (reduce (lambda (a b) (+ a 1)) 0 lst))
(defun all? (pred lst)
(cond (nil? lst) true
(pred (car lst)) (all? pred (cdr lst))
false))
(defun any? (pred lst)
(cond (nil? lst) false
(pred (car lst)) true
(any? pred (cdr lst))))
(defun indexof-from (elem lst idx)
(cond (nil? lst) -1
(= elem (car lst)) idx
(indexof-from elem (cdr lst) (+ 1 idx))))
(defun indexof (elem lst) (indexof-from elem lst 0))
(defun range (a b) ; [a b)
(if (< a b) (cons a (range (+ 1 a) b)) nil))
(defun range-tail-core (a b acc)
(if (< a b) (range-tail-core a (- b 1) (cons (- b 1) acc)) acc))
(defun range-tail (a b) (range-tail-core a b nil))
(defun list-ref (n lst)
(def tmp (drop n lst))
(if (nil? tmp) (raise (++ "index out of bounds: " n " of " lst))
(car tmp)))
(defun zip-with (f a b)
(if (or (nil? a) (nil? b)) nil
(cons (f (car a) (car b)) (zip-with f (cdr a) (cdr b)))))
(defun flatten (lsts)
(cond (!= (typeof lsts) "List") (list lsts)
(nil? lsts) nil
(append (flatten (car lsts)) (flatten (cdr lsts)))))
(defun distinct (lst)
(if (nil? lst) nil
(cons (car lst) (distinct (filter (lambda (v) (!= v (car lst))) (cdr lst))))))
; (defn map-n (f . l)
; (cond (nil? l) nil
; (any? nil? l) nil
; (cons (eval (cons f (map car 'l))) (eval (cons map-n (cons f (map cdr 'l)))))))
(defmacro apply-1 (f args)
(eval (cons f args)))
(defn map-n-core (f l)
(if (or (nil? l) (any? nil? l))
nil
(cons (apply-1 f (map car l))
(map-n-core f (map cdr l)))))
(defn map-n (f . l)
(map-n-core f l))
(defn cond-append (u v) (if (or (= false u) (= false v)) false (append u v))) ;; append-revers
(defn match-codegen (p v) ;; check-pattern
(def type-p (typeof p))
(cond (= type-p "Symbol") (if (= p '_) nil (list (list def p (list quote v)))) ;; TODO quote only lists
(= type-p "List")
(cond
(!= (typeof v) "List") false
(nil? p) (if (nil? v) nil false)
(= (car p) '.) (match-codegen (car (cdr p)) v)
(nil? v) false
(cond-append (match-codegen (car p) (car v))
(match-codegen (cdr p) (cdr v))))
(= p v) nil
false))
(defmacro when (c . body) (if c (eval (cons do 'body)) nil))
(defn check-pattern? (p)
(def syms (filter (lambda (e) (and (= (typeof e) "Symbol") (!= e '_) (!= e '.))) (flatten p)))
(= syms (distinct syms) syms))
(defn match-wrap (p v)
; (when (not (check-pattern? p)) (raise (++ "bad pattern: " p)))
(def code (match-codegen p v))
(if (= code false) false
(cons do (append code '(true)))))
(defmacro match (p v) (eval (match-wrap 'p v)))
(defun prints (. args) (reduce (lambda (nothing arg) (print arg)) nil args))
(prints "stdlib loaded" \n)