Initial commit - fix problems & refactor code
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
__pycache__
|
||||||
|
.venv
|
||||||
|
.idea
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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 ''
|
||||||
@@ -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):]
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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 ""
|
||||||
@@ -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
|
||||||
@@ -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 = "_=_"
|
||||||
@@ -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)))
|
||||||
@@ -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)))
|
||||||
@@ -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))))
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
(def fact (dambda (n) (
|
||||||
|
if (_<_ n 2) 1
|
||||||
|
(_*_ n (fact (_-_ n 1)))
|
||||||
|
)))
|
||||||
|
|
||||||
@@ -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))))
|
||||||
@@ -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)))
|
||||||
@@ -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)))
|
||||||
@@ -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))
|
||||||
|
)
|
||||||
@@ -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)))
|
||||||
@@ -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))))))))
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
(defun selfactorial (self, n) (* n (if (< n 3) 1 (f f (- n 1)))))
|
||||||
|
(defun factorial (n) (selfactorial selfactorial n))
|
||||||
@@ -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='utf‑8') 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
@@ -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)
|
||||||
Reference in New Issue
Block a user