Да. Спустя наверно 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.
Удачи!
ссылка на оригинал статьи https://habr.com/ru/articles/927158/
Добавить комментарий