Clem - это минимальный стековый язык программирования с функциями первого класса. Ваша цель - написать переводчика для языка Clem. Следует правильно выполнить все примеры, включенные в справочную реализацию, которая доступна здесь .
- Как обычно, применяются стандартные лазейки .
- Наименьший вход по количеству байтов выигрывает.
Клемский язык
Clem - это стековый язык программирования с первоклассными функциями. Лучший способ выучить Клема - запустить clem
интерпретатор без аргументов. Он начнется в интерактивном режиме, что позволит вам играть с доступными командами. Чтобы запустить примеры программ, введите clem example.clm
где example - название программы. Этого краткого урока должно быть достаточно, чтобы вы начали.
Есть два основных класса функций. Атомарные функции и составные функции. Составные функции - это списки, составленные из других составных функций и атомарных функций. Обратите внимание, что составная функция не может содержать себя.
Атомные функции
Первый тип атомарной функции является константой . Константа это просто целое значение. Например, -10. Когда интерпретатор встречает константу , он помещает ее в стек. Беги clem
сейчас. Введите -10
в командной строке. Тебе следует увидеть
> -10
001: (-10)
>
Значение 001
описывает положение функции в стеке и (-10)
является константой, которую вы только что ввели. Теперь введите +11
в подсказке. Тебе следует увидеть
> +11
002: (-10)
001: (11)
>
Обратите внимание, что (-10)
переместился на вторую позицию в стеке и (11)
теперь занимает первую. Это природа стека! Вы заметите, что -
это также команда декремента. Всякий раз, когда -
или +
перед числом, они обозначают знак этого числа, а не соответствующую команду. Все остальные элементарные функции являются командами . Всего 14:
@ Rotate the top three functions on the stack
# Pop the function on top of the stack and push it twice
$ Swap the top two functions on top of the stack
% Pop the function on top of the stack and throw it away
/ Pop a compound function. Split off the first function, push what's left,
then push the first function.
. Pop two functions, concatenate them and push the result
+ Pop a function. If its a constant then increment it. Push it
- Pop a function. If its a constant then decrement it. Push it
< Get a character from STDIN and push it to the stack. Pushes -1 on EOF.
> Pop a function and print its ASCII character if its a constant
c Pop a function and print its value if its a constant
w Pop a function from the stack. Peek at the top of the stack. While it is
a non-zero constant, execute the function.
Ввод команды в командной строке выполнит команду. Введите #
в командной строке (дублирующая команда). Тебе следует увидеть
> #
003: (-10)
002: (11)
001: (11)
>
Обратите внимание, что (11) было продублировано. Теперь введите %
в командной строке (команда сброса). Тебе следует увидеть
> %
002: (-10)
001: (11)
>
Чтобы поместить команду в стек, просто заключите ее в скобки. Введите (-)
в командной строке. Это перенесет оператор декремента в стек. Тебе следует увидеть
> (-)
003: (-10)
002: (11)
001: (-)
>
Составные функции
Вы также можете заключить несколько атомарных функций в круглые скобки, чтобы сформировать составную функцию. Когда вы вводите составную функцию в приглашении, она помещается в стек. Введите ($+$)
в командной строке. Тебе следует увидеть
> ($+$)
004: (-10)
003: (11)
002: (-)
001: ($ + $)
>
Технически все в стеке является составной функцией. Однако некоторые составные функции в стеке состоят из одной атомарной функции (в этом случае мы будем считать их атомарными функциями для удобства). При манипулировании составными функциями в стеке .
команда (сцепление) часто бывает полезна. Введите .
сейчас. Тебе следует увидеть
> .
003: (-10)
002: (11)
001: (- $ + $)
>
Обратите внимание, что первая и вторая функции в стеке были объединены, и что вторая функция в стеке стоит первой в результирующем списке. Чтобы выполнить функцию, находящуюся в стеке (атомарную или составную), мы должны выполнить w
команду (while). Команда w
вставляет первую функцию в стек и выполняет ее многократно, если вторая функция в стеке является ненулевой константой. Попробуйте предсказать, что произойдет, если мы введем w
. Теперь введите w
. Тебе следует увидеть
> w
002: (1)
001: (0)
>
Это то, что вы ожидали? Два числа, находящиеся на вершине стека, были добавлены, и их сумма остается. Давайте попробуем это снова. Сначала мы опустим ноль и нажмем 10, набрав %10
. Тебе следует увидеть
> %10
002: (1)
001: (10)
>
Теперь мы напечатаем всю функцию за один раз, но %
в конце добавим дополнительную, чтобы избавиться от нуля. Введите (-$+$)w%
в командной строке. Тебе следует увидеть
> (-$+$)w%
001: (11)
>
(Обратите внимание, что этот алгоритм работает, только если первая константа в стеке положительна).
Струны
Строки также присутствуют. Они в основном синтаксические сахара, но могут быть весьма полезными. Когда интерпретатор встречает строку, он помещает каждый символ от последнего к первому в стек. Тип, %
чтобы отбросить 11 из предыдущего примера. Теперь введите 0 10 "Hi!"
приглашение. 0
Будет вставить NULL терминатора и 10
вставляет символ новой строки. Тебе следует увидеть
> 0 10 "Hi!"
005: (0)
004: (10)
003: (33)
002: (105)
001: (72)
>
Печатайте (>)w
символы из стека, пока мы не встретим терминатор NULL. Тебе следует увидеть
> (>)w
Hi!
001: (0)
>
Выводы
Надеюсь, этого будет достаточно, чтобы вы начали работать с переводчиком. Дизайн языка должен быть относительно простым. Дайте мне знать, если что-то ужасно неясно :) Несколько вещей были намеренно оставлены неопределенными: значения должны быть подписаны и не менее 16 бит, стек должен быть достаточно большим, чтобы запускать все справочные программы и т. Д. Многие детали не были вырезаны здесь, потому что полная спецификация языка была бы слишком большой для публикации (а я еще не написал: P). Если есть сомнения, имитируйте эталонную реализацию.
Ответы:
Хаскелл,
931921875это еще не полностью игра в гольф, но это, вероятно, никогда не будет. Тем не менее, он уже короче всех других решений.
Я буду играть в гольф в ближайшее время.Мне не хочется играть в гольф больше, чем это.вероятно, есть несколько тонких ошибок, потому что я не играл с эталонной реализацией Си.
это решение использует тип
StateT [String] IO ()
для хранения "работающей" программы clem. Большинство программ - это синтаксический анализатор, который анализирует «работающую программу».для того, чтобы запустить это использование
r "<insert clem program here>"
.источник
Питон,
16841281 символовЕсть все основные вещи в гольф сделано. Он запускает все примеры программ и соответствует выводимому символу за символом.
Тестирование :
Соберите clemint.py , clemtest_data.py , clemtest.py и скомпилированный
clem
двоичный файл в каталог и запуститеclemtest.py
.Расширение :
Самая безудержная версия - эта . Следуйте вместе с этим.
S
основной стек Каждый элемент стека состоит из 3-х списков, один из:Для констант,
f
это функция, которая помещает константу в стек. Для атмосферы,f
это функция, которая выполняет одну из операций (например-
,+
). Для соединений,fs
это список предметов.xec
выполняет элемент. Если это константа или атом, он просто выполняет функцию. Если это соединение, если рекурсии еще не было, выполняется каждая функция. Таким образом , выполнение(10 20 - 30)
будет выполнять каждую из функций10
,20
,-
и30
, оставляя10 19 30
в стеке. Если была рекурсия, то она просто помещает составную функцию в стек. Например, при выполнении(10 20 (3 4) 30)
, результат должен быть10 20 (3 4) 30
, а не10 20 3 4 30
.Вложенность была немного хитрой. Что вы делаете во время чтения
(1 (2 (3 4)))
? Решение состоит в том, чтобы иметь стек стеков. На каждом уровне вложенности новый стек помещается в стек, и все операции переноса выполняются в этом стеке. Далее, если было вложение, то атомарные функции выдвигаются вместо выполнения. Так что если вы видите10 20 (- 30) 40
,10
толкается, затем20
, затем новый стек создается,-
и30
помещаются в новый стек, и)
выскакивает с нового стека, превращает его в объект и помещает его в стек на один уровень вниз.endnest()
ручки)
. Это было немного сложно, так как есть особый случай, когда только один элемент был перемещен, и мы возвращаемся в основной стек. То есть(10)
должен давить постоянную10
, а не композит с одной константой, потому что тогда-
и+
не сработает. Я не уверен, что это принципиально, но так оно и есть ...Мой интерпретатор является посимвольным процессором - он не создает токены - поэтому с цифрами, строками и комментариями было несколько неприятно иметь дело. Существует отдельный стек,
N
для текущего обрабатываемого числа, и каждый раз, когда обрабатывается символ, который не является числом, я должен позвонить,endnum()
чтобы узнать, должен ли я сначала заполнить это число и поместить его в стек. Будь мы в строке или комментарии отслеживаются логическими переменными; когда строка закрыта, она выталкивает все внутренности из стека. Отрицательные числа также требуют особой обработки.Вот и все для обзора. Остальные реализовывались все встроенные модули, и убедившись , что делать глубокие копии в
+
,-
и#
.источник
С 837
Спасибо @ceilingcat за то, что нашли намного лучшую (и более короткую) версию
Это рассматривает все как простые строки - все элементы стека являются строками, даже константы являются строками.
Попробуйте онлайн!
Версия моего оригинала с меньшим количеством полей для гольфа (в отличие от версии для гольфа, эта версия печатает стек, когда он заканчивается, если он не пустой, и принимает параметр -e, чтобы вы могли указать сценарий в командной строке вместо чтения из файла):
источник