Перейти к содержанию

XMConC

завершено на 100%
Материал из Викиучебника — открытых книг для открытого мира


Введение

[править]

Данный учебник учит программированию на языке XMConC.

XMConC (также XmConC) — это высокоуровневый стековый язык программирования, который можно использовать для написания программ под Xmtwolime[1] и GNU+Linux[2]. XmConC многословен, и, в некоторых местах, усложняет написание программы, но его можно использовать как промежуточный язык. См. также c-to-xmconc.

Синтаксис

[править]

XMConC использует для передачи параметров и возвращаемых значений стек.

2 3 + putn ;

Приведённый выше образец программы делает следующее:

  1. кладёт число 2 на вершину стека
  2. помещает число 3 на вершину стека
  3. вызывает функцию «+», которая складывает два числа из стека и кладёт результат в стек
  4. вызывает функцию «putn» для вывода числа из стека на экран
  5. очищает указатель стека, пробел перед «;» необязателен

Можно также записывать со скобками для упрощения чтения: (2 3 +) putn;.


  • ASCII-строка создаётся в XMConC следующим образом: "строка" название_строки. Далее, можно помещать адрес созданной строки в стек так: &название_строки
  • препроцессорные директивы начинаются со знака «/». Пример — /define one :1.
  • указание на то, что следующий код предназначен для другого потока, осуществляется командами thrd_0 и thrd_1.
  • ассемблерные вставки задаются в формате $ ассемблерный_код (работает только для Xmtwolime).
  • комментарии начинаются со знака решётки (#).
  • именованные константы вставляются в код вот так: {konstanta}.
  • вместо целых чисел, в стек можно помещать коды символов вот так (если символ — не пробел): 'символ'.
  • метки можно создавать в формате название_метки:. Адрес метки можно положить в стек, используя конструкцию ~название_метки. <название_метки> — для вставки адреса внешней метки (только для Xmtwolime).
  • последовательности \<новая строка> в коде игнорируются.
  • Выражения {переменная}! и {переменная} . синонимичны.

Препроцессор

[править]

Препроцессор выполняет обработку препроцессорных директив. Ниже представлена таблица всех его команд:

Название Описание Примеры использования
define Создание именованной константы. /define CONST :12345
include Включение содержимого файла, если файл с таким именем не был включен раньше. /include "file"
alloc Статическое выделение памяти. Адрес начала выделенной области можно вставлять как им. константу. Когда выделяется массив, также создаётся им. константа название_массива.length /alloc variable

/alloc array[64]

free Статическое освобождение n ячеек памяти. /free 64

Первая программа

[править]

Ну что ж, пришла пора написать программу! Обычно, программирование на новом языке начинается с программы, печатающей на экран фразу «hello, world» («Здравствуй, мир!», "Hello World" и т. д.). Давайте сделаем это!

Откройте любой удобный вам текстовый редактор с моноширинным шрифтом, напишите в нём следующее:

"hello, world" s
&s puts newline
0 exit

Теперь сохраните документ в файл (можно выбрать название «hey.xcc»), скомпилируйте его[3], запустите полученную программу. На экране должно отобразиться «hello, world».

Разбор кода

[править]

Отлично! Давайте разберём текст нашей программы:

  1. сперва, мы создаём строку «hello, world» с названием s
  2. кладём адрес строки в стек, вызываем функцию puts, печатающую строку на экран; далее вызывается функция newline, которая выполняет переход на новую строку (иными словами, печатает «\n»)
  3. помещаем число 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

Примечания

[править]
  1. Репозиторий
  2. Репозиторий
  3. Репозитории компиляторов указаны в «Примечаниях».