Clem - це мінімальна мова програмування на основі стека, що має функції першого класу. Ваша мета - написати перекладача для мови Clem. Він повинен належним чином виконати всі приклади, включені до посилання на реалізацію, яка доступна тут .
- Як завжди, застосовуються стандартні лазівки .
- Найменший запис за кількістю байтів виграє.
Мова Клема
Clem - це мова програмування на основі стека з функціями першого класу. Найкращий спосіб навчитися Клему - запустити clem
перекладача без аргументів. Він запуститься в інтерактивному режимі, що дозволяє грати з доступними командами. Для запуску прикладних програм введіть, clem example.clm
де приклад - ім'я програми. Цього короткого підручника має бути достатньо для початку роботи.
Є два основні класи функцій. Атомні функції та складові функції. Складені функції - це списки, що складаються з інших складних функцій та атомних функцій. Зауважте, що складна функція не може містити себе.
Атомні функції
Перший тип атомної функції - константа . Константа це просто ціле значення. Наприклад, -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) дублюється. Тепер введіть %
підказку (команда drop). Ви повинні побачити
> %
002: (-10)
001: (11)
>
Щоб натиснути команду на стек, просто укладіть її в круглі дужки. Введіть (-)
підказку. Це підштовхне оператора decrement до стеку. Ви повинні побачити
> (-)
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). Коли ви сумніваєтесь, імітуйте референтну реалізацію.