XMConC
Введение
правитьДанный учебник учит программированию на языке XMConC.
XMConC (также XmConC) — это высокоуровневый стековый язык программирования, который можно использовать для написания программ под Xmtwolime[1] и GNU+Linux[2]. XmConC многословен, и, в некоторых местах, усложняет написание программы, но его можно использовать как промежуточный язык. См. также c-to-xmconc.
Синтаксис
правитьXMConC использует для передачи параметров и возвращаемых значений стек.
2 3 + putn ;
Приведённый выше образец программы делает следующее:
- кладёт число 2 на вершину стека
- помещает число 3 на вершину стека
- вызывает функцию «+», которая складывает два числа из стека и кладёт результат в стек
- вызывает функцию «putn» для вывода числа из стека на экран
- очищает указатель стека, пробел перед «;» необязателен
Можно также записывать со скобками для упрощения чтения: (2 3 +) putn;
.
- ASCII-строка создаётся в XMConC следующим образом:
"строка" название_строки
. Далее, можно помещать адрес созданной строки в стек так:&название_строки
- препроцессорные директивы начинаются со знака «/». Пример —
/define one :1
.
- указание на то, что следующий код предназначен для другого потока, осуществляется командами
thrd_0
иthrd_1
.
@функция
эквивалентно~__Cx ~функция goto __Cx:
(x может быть 0, 1 и т. д.). Используется для вызова собственных функций (см. также программу из README-файла репозитория xmconcc).
- ассемблерные вставки задаются в формате
$ ассемблерный_код
(работает только для Xmtwolime).
- комментарии начинаются со знака решётки (
#
).
- именованные константы вставляются в код вот так:
{konstanta}
.
- вместо целых чисел, в стек можно помещать коды символов вот так (если символ — не пробел):
'символ'
.
- метки можно создавать в формате
название_метки:
. Адрес метки можно положить в стек, используя конструкцию~название_метки
.<название_метки>
— для вставки адреса внешней метки (только для Xmtwolime).
- последовательности
\<новая строка>
в коде игнорируются.
- Выражения
{переменная}!
и{переменная} .
синонимичны.
Препроцессор
правитьПрепроцессор выполняет обработку препроцессорных директив. Ниже представлена таблица всех его команд:
Название | Описание | Примеры использования |
---|---|---|
define | Создание именованной константы. | /define CONST :12345
|
include | Включение содержимого файла, если файл с таким именем не был включен раньше. | /include "file"
|
alloc | Статическое выделение памяти. Адрес начала выделенной области можно вставлять как им. константу. Когда выделяется массив, также создаётся им. константа название_массива.length |
/alloc variable
|
free | Статическое освобождение n ячеек памяти. | /free 64
|
Первая программа
правитьНу что ж, пришла пора написать программу! Обычно, программирование на новом языке начинается с программы, печатающей на экран фразу «hello, world» («Здравствуй, мир!», "Hello World" и т. д.). Давайте сделаем это!
Откройте любой удобный вам текстовый редактор с моноширинным шрифтом, напишите в нём следующее:
"hello, world" s &s puts newline 0 exit
Теперь сохраните документ в файл (можно выбрать название «hey.xcc»), скомпилируйте его[3], запустите полученную программу. На экране должно отобразиться «hello, world».
Разбор кода
правитьОтлично! Давайте разберём текст нашей программы:
- сперва, мы создаём строку «hello, world» с названием
s
- кладём адрес строки в стек, вызываем функцию puts, печатающую строку на экран; далее вызывается функция newline, которая выполняет переход на новую строку (иными словами, печатает «
\n
») - помещаем число 0 в стек и завершаем программу с кодом из стека, вызывая exit; успешное завершение
Продолжаем программировать: метки, переходы и «сон»
правитьФункция goto
выполняет переход без условия. sleep
и msleep
приостанавливают выполнение на определённое кол-во секунд или миллисекунд. Сделаем так, чтобы программа выводила на экран каждую секунду:
loop: "hello, world" s &s puts 1 sleep ~loop goto
Новые cat
и echo
…
править
Следующая программа принимает от пользователя символ, после чего печатает его, используя функцию putc:
loop: getc putc ~loop goto
Как вам идея создать программу, которая будет выводит свои аргументы командной строки? Для XMConC, это легко!:
/alloc buf[128] # копирование аргументов в массив buf {buf} getargs {buf} puts newline 0 exit
Радуга
правитьФункции setbg
и setcolor
меняют цвета фона и текста. Они принимают один аргумент. Коды цветов практически как в iiixmish2:
Значение | Цвет |
---|---|
1 | белый |
2 | зелёный |
3 | голубой или синий |
4 | тёмно-зелёный |
5 | серый |
6 | красный |
7 | жёлтый |
Напишите определения им. констант с кодами цветов и создайте с ними программу, выводящую цвета радуги.
Что можно делать с числами
правитьУзнаем, как производить вычитание, умножение и др. операции.
Азы
правитьСложение (+
), вычитание (-
), умножение (*
) и деление (/
) принимают два аргумента-числа. Точно также и с возведением в степень (**
), ИЛИ (|
), И (and
), исключающим ИЛИ (^
), побитовыми сдвигами (lsh
и rsh
) и получением остатка от деления (mod
).
Инкремент и декремент
правитьчисло ++
число --
Изменение знака (сделать отрицательное целое число положительным и наоборот)
правитьчисло neg
Генерация
правитьСледующая программа генерирует число от 0 до 7 (восемь минус один) и выводит результат на экран:
8 sel putn
Память
правитьФункция .
пишет в стек значение ячейки памяти:
{var} .
Команда =
присваивает ячейке значение:
12345 {var} =
Как вы могли заметить, стек — не единственное хранилище. Можно создавать переменные и массивы, получать и изменять их содержимое. Кстати, значение на вершине стека можно «выбросить» командой drop
; dup
дублирует последний элемент стека.
Ветвления
правитьЧетыре команды — =?
, !?
, gt?
и lt?
, (равно, не равно, больше или меньше) — возвращают 0 или 1 в зависимости от того, истинно ли условие. Обычно, эти функции используются вместе с then
. Пример перехода к метке «label», если переменные «a» и «b» равны:
{a}! {b}! =? ~label then
?
возвращает истину только тогда, когда два предыдущих условия истинны. |?
кладёт в стек число 1 лишь тогда, когда истинно хотя бы одно из двух предыдущих условий. !
инвертирует результат выполнения предыдущего условия.
Кто ты?
правитьНиже представлен текст программы, выводящей на экран идентификатор текущего пользователя:
getuid putn newline 0 exit
Работа с указателем вывода
правитьnewline
выполняет переход на новую строку, backspace
выполняет переход на один символ назад (в зависимости от реализации, действие может различаться), clear_output
стирает текст с экрана. Напишем программу наподобие ncurses «clear»:
clear_output 0 exit
Как получить число?
правитьДля получения 6-значного беззнакового числа от пользователя, можно воспользоваться приведённым ниже кодом:
getc 48 - getc 48 - getc 48 - getc 48 - getc 48 - getc 48 - # cat склеивает шесть чисел в стеке cat # выводим результат на экран putn newline 0 exit
Ниже представлен код функции для вывода n числа Фибоначчи (без рекурсии):
/alloc __ret /define function :{__ret} = /define return :({__ret} .) goto /alloc fib.a /alloc fib.b /alloc fib.c fib: {function} /alloc fib.n -- {fib.n} = {fib.n}! 1 lt? ~fib.endif0 else # { 1 {return} fib.endif0: # } 0 {fib.a} = 1 {fib.b} = /alloc fib.i (0 {fib.i} =) fib.for0: ({fib.i}! {fib.n}! lt? ~fib.endfor0 else) # { {fib.a}! {fib.c} = {fib.b}! {fib.a} = ({fib.c}! {fib.a}! +) {fib.b} = (({fib.i}! ++) {fib.i} =) (~fib.for0 goto) fib.endfor0: # } ({fib.a}! {fib.b}! +) {return}
Пример использования:
/alloc i (0 {i} =) for0: ({i}! 20 lt? ~endfor0 else) # { {i}! @fib putn 32 putc # печатает пробел (({i}! ++) {i} =) (~for0 goto) endfor0: # } newline 0 exit
Ещё немного сведений о строках
править- строки, как и в языке Си, заканчиваются символом NUL (
'\0'
). - длину строки можно легко получить функцией strlen:
{my_string12345} strlen
- не рекомендуется (для Xmtwolime) изменять содержимое строки, созданной конструкцией
"строка" название
.
Многопоточность
правитьXMConC поддерживает многопоточность. Потоков, способных работать в одно время, всего лишь два. Ниже представлен образец многопоточной программы:
~loop2 create_thrd1 loop: "hello, thread 0!" s0 &s0 puts newline 1 sleep ~loop goto loop2: thrd_1 "hello, thread 1!" s1 &s1 puts newline 1500 msleep ~loop2 goto
Функция halt
останавливает текущий поток.
Проверка операционной системы
правитьgnu_code
выполняет переход, если программа выполняется в GNU+Linux; другими словами, она аналогична goto
в операционной системе GNU. xm2_code
делает то же, но только если программу исполняет iiixmish2.
Программа «Числа»
правитьЗадание
Найдите ошибку в данной программе, исправьте её.
/alloc c # установка белого цвета 1 setcolor loop: getc {c} = ; ({c} .) '0' =? ~zero then ({c} .) '1' =? ~one then ({c} .) '2' =? ~two then ({c} .) '3' =? ~three then ({c} .) '4' =? ~four then ({c} .) '5' =? ~five then ({c} .) '6' =? ~six then ({c} .) '7' =? ~seven then ({c} .) '8' =? ~eight then ({c} .) '9' =? ~nine then # 27 = ESC ({c} .) 27 =? ~exit then ~loop goto zero: "zero, " s0 &s0 puts ~loop goto one: "one, " s1 &s1 puts ~loop goto two: "two, " s2 &s2 puts ~loop goto three: "three, " s3 &s3 puts ~loop goto four: "four, " s3 &s3 puts ~loop goto five: "five, " s5 &s5 puts ~loop goto six: "six, " s6 &s6 puts ~loop goto seven: "seven, " s7 &s7 puts ~loop goto eight: "eight, " s8 &s8 puts ~loop goto nine: "nine, " s9 &s9 puts ~loop goto exit: newline 0 exit
На XmConC можно писать игры!
правитьЗадание
Скомпилируйте и запустите программу у себя на компьютере. Переведите все {переменная} .
в коде, в {переменная}!
.
"\ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \ @................@..@.....................@.....@ \ @................@..@...................@...@...@ \ @.....@@@@@@@@@@@@@.@.................@@@@@@@@..@ \ @...................@.............@@@@@.........@ \ @....@@@@@@@@@@@@@@...@@@@@@@@@@@@@.............@ \ @....@.............@..@.........................@ \ @....@........@.....@..@...........@@@..........@ \ @....@@@@@@@@@@......@..@..........@.....@@@@@@@@ \ @.....................@..@.........@............@ \ @.........................@..............@......@ \ @@@@@@@@@@@@....@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...@ \ @....................@..........................@ \ @..........@.........@.......................@@@@ \ @..........@.........@..........................@ \ @..........@.........,........................,...............\ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \ " map /define WIDTH :62 /alloc P 63 {P} = mainLoop: clear_output /alloc i 0 {i} = # цикл вывода карты loop: ; ({P} .) ({i} .) !? ~loop_1 then 'P' putc (({i} .) ++) {i} = ~loop_2 goto loop_1: (({i} .) {WIDTH} mod) 0 !? ~loop_1_1 then newline loop_1_1: ((&map ({i} .) +) .) putc (({i} .) ++) {i} = loop_2: (((&map ({i} .) +) ++) .) 0 !? ~loop then ##################################### /alloc c getc {c} = ({c} .) 'w' =? ~up then ({c} .) 'a' =? ~left then ({c} .) 's' =? ~down then ({c} .) 'd' =? ~right then # 27 = ESC ({c} .) 27 =? ~exit then ~mainLoop goto exit: clear_output 0 exit up: ((((&map ({P} .) +) {WIDTH} -) .) '.' !? ~exit then (({P} .) {WIDTH} -) {P} = ~mainLoop goto down: ((((&map ({P} .) +) {WIDTH} +) .) '.' !? ~exit then (({P} .) {WIDTH} +) {P} = ~mainLoop goto left: ((((&map ({P} .) +) --) .) '.' !? ~exit then (({P} .) --) {P} = ~mainLoop goto right: ((((&map ({P} .) +) ++) .) '.' !? ~exit then (({P} .) ++) {P} = ~mainLoop goto
«Hello, world!», но на русском
править…/xmconcc$ cat > ru s::Привет, мир! …/xmconcc$ ./unicode-tool.py ru > data.xcch …/xmconcc$ cat > hey.xcc /include "data.xcch" ~gnu gnu_code {X_s} puts newline 0 exit gnu: {G_s} puts newline 0 exit
Примечания
править- ↑ Репозиторий
- ↑ Репозиторий
- ↑ Репозитории компиляторов указаны в «Примечаниях».