Да. Спустя наверно 3 дня я решил сделать это. Долго конечно, но что тут поделаешь.
Новый синтатикс: изучаем.
До этого мы могли писать простые факториалы и Фибоначчи.
Но я собираюсь сделать его более-менее серьёзнее чтобы энтузиасты портировали его на микроконтроллеры.
Так что вот новый синтатикс:
and
or
not
struct
and, or, not - ну... Ловите примеры:
;nil потому что 3 == 3 но 3 != 2
(and (= 3 3) (= 3 2))
;t потому что 3 != 2
(not (= 3 2))
;t потому что 3 == 3
(or (= 3 3) (= 3 2))
Как видите - or если одно из выражение = t, and если все два выражения = t, not если выражение = nil.
struct - другая тема.
Возможно вы знаете классы в Python, структуры в C, C++ (наверно) и других языках программирования.
Вот пример кода для структуры на C:
struct point {
int x;
int y;
}
Конечно это не все прелести структур но такой код будет выглядит так на моём LISP:
(struct point (x y))
Разбираем этот код:
Сначала пишем имя (логично)
Потом элементы структуры
У меня есть (возможно) неверное мнение что структуры в C это как массивы с динамической типизации и именованием элементов.
Но не об этом.
Продолжим!
Реализуем and, or, not.
Я не буду писать код в функции eval.
А добавлю and, or, not как функции.
Вот новое env:
genv.env = {
'princ': Proc(lambda *x: print(*x), 'builtins'),
'list': Proc(lambda *x: [*x], 'builtins'),
'nth': Proc(lambda lst, ind: lst[ind], ['lst', 'ind']),
'+': Proc(lambda first, sec: first + sec, ['first', 'sec']),
'-': Proc(lambda first, sec: first - sec, ['first', 'sec']),
'*': Proc(lambda first, sec: first * sec, ['first', 'sec']),
'/': Proc(lambda first, sec: first / sec, ['first', 'sec']),
'>': Proc(lambda first, sec: first > sec, ['first', 'sec']),
'<': Proc(lambda first, sec: first < sec, ['first', 'sec']),
'=': Proc(lambda first, sec: first == sec, ['first', 'sec']),
'/=': Proc(lambda first, sec: first != sec, ['first', 'sec']),
'py': Proc(lambda exp: old_eval(exp, globals() | genv.env), ['exp']),
'getk': Proc(lambda dct, name: dct[name], ['dct', 'name']),
'append': Proc(lambda lst, el: lst + [el], ['lst', 'el']),
'remove': Proc(lambda lst, el: rem(lst, el), ['lst', 'el']),
'car': Proc(lambda lst: lst[0], ['lst']),
'cdr': Proc(lambda lst: rem(lst, lst[0]), ['lst']),
'cons': Proc(lambda el, lst: [el] + lst, ['el', 'lst']),
'and': Proc(lambda f, s: f and s, ['f', 's']),
'or': Proc(lambda f, s: f or s, ['f', 's']),
'not': Proc(lambda expr: not expr, ['expr'])
}
Обратите внимание на эти строки:
'and': Proc(lambda f, s: f and s, ['f', 's']),
'or': Proc(lambda f, s: f or s, ['f', 's']),
'not': Proc(lambda expr: not expr, ['expr'])
Именно это функции and, or, not.
В них параметры:
or - f и s (также как первое выражение и второе выражение)
and - f и s (также как первое выражение и второе выражение)
not - expr (выражение)
Логично.
Также используем на это всё обычные питоньи операции.
А точнее - and, or, not. Логично.
Реализуем struct.
Тут конечно же чуть-чуть посложнее. Но мы справимся!
Для начало давайте с того, как я реализовал концепцию структур:
Определяет определение структуры (и так понятно).
Получает... Ну, элементы структуры.
Когда программа видит использование структуры то имя на которое надо регистрировать элементы структуры хранится в ключевом параметре "nam" (получше не придумал) у которого обычное значение = "nil".
Получает значения на элементы структур и создаёт переменные типа имя.элемент_структуры с этими значениями.
К примеру мы создали структуру point с элементами x y.
Потом на переменную my_point зададим значение (point 5 6).
И пишем для прибавление x и y (+ my_point.x my_point.y).
Вот новая функция eval:
def eval(ast, nam='nil'):
try:
name = ast[0] if type(ast) != str else ast
except Exception as err:
return ast
if name == 'if':
return eval(ast[2] if eval(ast[1]) else eval(ast[3]))
elif name == 'def':
name = ast[1]
if type(name) == list and len(name) == 2:
args = name[1]
if type(args) != list:
args = [args]
name = name[0]
procedure = Proc(lambda *args: eval(ast[2]), args)
genv.add(name, procedure)
return None
val = eval(ast[2], nam=name)
genv.add(name, val)
elif name[0] == "'":
res = ast[0].replace("'", '')
for i in ast[1:]:
res += to_lisp(i) + ' '
return res[0:-1]
elif name == 'lambda':
args = ast[1]
if type(args) != list:
args = [args]
return Proc(lambda *args: eval(ast[2]), args)
elif name == 'quote':
res = ''
for i in ast[1:]:
res += to_lisp(i) + ' '
return res[0:-1]
elif name == 'begin':
res = 'NIL'
for i in ast[1:]:
res = eval(i)
return res
elif name == 'struct':
name = ast[1]
struct = ast[2]
if type(struct) != list:
struct = list(struct)
genv.add(name, Structure(struct))
elif name == 'dict':
res = {}
for i in ast[1:]:
name = i[0]
val = eval(i[1])
res[name] = val
return res
elif name == 'nil':
return False
elif name == 't':
return True
elif name == ';':
pass
else:
fn = genv.get(name)
if type(fn) != Proc and not type(fn) == Structure:
return fn
if type(fn) == Structure:
args = [eval(i) for i in ast[1:]]
cur = 0
for i in fn.args:
eval(parse(lexer(f'(def {nam}.{i} {arg_to_lisp(args[cur])})')))
cur += 1
return fn
args = [eval(i) for i in ast[1:]]
if fn.args == 'builtins':
return fn.lambda_(*args)
cur = 0
for i in fn.args:
eval(parse(lexer(f'(def {i} {arg_to_lisp(args[cur])})')))
cur += 1
return fn.lambda_(*args)
Теперь про непонятное:
Что за класс Structure? Ответ: дальше реализуем
Что за функция arg_to_lisp? Она нужна была чтобы решить проблему с тем что когда к примеру передаёшь аргумент (list 1 2 3) то он некорректно работал. Дальше реализуем.
Теперь решаем:
Реализация класса Structure:
class Structure:
def __init__(self, args):
self.args = args #элементы структуры
def __repr__(self):
return f'#<struct {self.args}>'
Ну... ОООЧЕНЬ легко.
Реализация функции arg_to_lisp:
def arg_to_lisp(val):
if isinstance(val, list):
cur = 0
for i in val:
val[cur] = str(arg_to_lisp(i))
cur += 1
return '(list' + ' '.join(val) + ')'
elif isinstance(val, bool) or val == None:
if val == True:
return 't'
return 'nil'
elif isinstance(val, dict):
cur = 0
res = '(dict '
for i in val.values():
val[list(val.keys())[cur]] = str(arg_to_lisp(i))
res += '(' + list(val.keys())[cur] + ' ' + str(arg_to_lisp(i)) + ') '
cur += 1
res = res[0:-1]
print(res + ')')
return res + ')'
return val
Тоже легко. Чуть-чуть переделанная функция to_lisp где просто для list и dict ставится (list элементы).
Конечно затруднения есть но про словарь не думайте.
А что по поводу синтатического сахара? Если изучить код то его можно заметить:
К примеру:
Чтобы не писать (def x (lambda r (* r r))) теперь можно делать так:
(def (square x) (* x x))
Это я взял со scheme.
И также с quote:
Раньше: (quote hello world!).
Теперь: ('hello world!)
Всё.
Итоги.
В этой статье мы не только добавили struct и and, or, not. Но ещё и добавили синтатический сахар вместе с багфиксами.
Теперь можно поменять MyLISP 1.0 на MyLISP 1.5.
Удачи!
Комментарии (8)
vadimr
13.07.2025 09:39quote
и ' имеют только один аргумент. Это связано с тем, что('hello world!)
– это форма из литерала
'hello
и значения атомаworld!
. Иначе вы так всю программу заквотите одной кавычкой.SystemSoft Автор
13.07.2025 09:39Ну... За квотировать всю программу не получится ведь ('hello world!) отделен от всего.
vadimr
13.07.2025 09:39Ну вы ж кавычку не обязательно используете на самом нижнем уровне вложенности, как здесь.
vadimr
and
иor
– это специальные формы, а не функции! Это принципиально, и на этом в Лиспе много построено. Второй аргумент не вычисляется, если первого достаточно.Попробуйте у себя и в clisp:
Специальные формы реализуются макросами.
SystemSoft Автор
А макросов пока нет.
vadimr
Ну тогда пишите логику в парсере.
В Лиспе
or
иand
ничем не отличаются отif
(собственно,or
– это сокращённая формаcond
без лишних скобок).SystemSoft Автор
Ну, информация полезная для меня, но у меня пока or, and это обычные логические операции.