Skip to content

Fenia: Grammar

Kit Oliynyk edited this page Oct 13, 2021 · 1 revision

Грамматика языка феня

(оригинал этой книги находится в библиотеке на Улице Вязов)
Ходят слухи, что язык феня (fenia) всего лишь диалект эльфийского (quenia). Данный труд призван развеять подобные заблуждения и показать, что ничего общего с эльфийской тарабарщиной феня не имеет. Введение - см intro История происхождения языка - см background Свод правил - см syntax Про все зеленые слова можно посмотреть на соответствующих страницах. Базовый вариант 52 страницы. Тираж 1 - 1. (снизу кривым почерком: и здесь тоже был Filths)

История

Первозданный вариант фени разрабатывался на чистом ansi-C как язык мобпрог для Forgotten Dungeon (киевский пулетех), но так и не был доведен до ума. В 200?м году рабочий вариант языка был защищен как дипломный проект по теме "язык моделирования поведения объектов" на мехмате Одесского нац. универа. Много лет спустя, множество раз переписанный, в конечном итоге был прикручен Руффиной к многострадальному DreamLandу почти без потери функциональности. Говорить об отличиях реализации нет смысла, так как общего кода старая и новая феня не имеет. Тем не менее принципы и синтаксис остались практически неизменными.

Введение

При понимании синтаксических структур фени (см syntax) следует иметь в виду цели и задачи, которые стоят перед приложениями, написаными на ней. Основная задача - моделирование поведения. Будем понимать под поведением набор правил, определяющих реакцию системы на внешние события. Потому язык является событийно-ориентированым. Это существенный момент для понимания полиморфизма в фене. Если в строго/слабо типизированных объектно-ориентированных языках полиморфизм достигается на уровне протитипов экземпляров (классов данных), в фене он достигается на уровне самих экземпляров. (см. example polymorph) То есть, поведение конкретного экземпляра объекта в фене может существенно отличаться от его прототипа. Более того, цикл жизни одного и того же объекта предполагает различные поведения в различные моменты времени, что реализует множество состояний одного и того же объекта. Удобней всего реализовать подобные требования объединив данные и код воедино. Иными словами, код представляется некоторым типом данных, с которым можно производить операции, подобные тем, что производятся с данными. Феня создавалась как нетипизированный язык. То есть, переменные сами по себе не имеют типа (в отличии от хранимых в них данных). Такое решение было принято для того, что бы как можно сильней облегчить процесс обучения и написания простых приложений (однако это затрудняет отладку и требует большей внимательности, чем в типизированных языках).

Синтаксис

char = 0..255 alpha = 'a'..'z' | 'A'..'Z' | 'а'..'я' | 'А'..'Я' digit = '0'..'9' whatever = char [whatever] strings = string [strings] string = '"' whatever '"' | "'" whatever "'" comment = '//' whatever '\n' | '/*' whatever '*/' number = digit [number] numalpha = digit | alpha numalphas = numalpha [numalphas] id = alpha [numalphas] varlist = id [ ',' varlist ] explist = expr [ ',' explist ] stmts = stmt [stmts] function = 'function' [number] [ '(' [varlist] ')' '{' [stmts] '}' ] const_exp = 'null' | number | strings | function unop = '!' | '~' | '-' unop_exp = unop expr mixed_binop = '+' arith_binop = '*' | '/' | '%' | '-' bit_binop = '^' | '&' | '|' pred_binop = '<' | '>' | '<=' | '>=' | '==' | '!=' log_binop = '&&' | '||' binop = arith_binop | bit_binop | pred_binop | log_binop binop_exp = expr binop expr assign_exp = ref '=' expr default_ref = id field_ref = expr '.' id array_ref = expr '[' expr ']' root_ref = '.' id ref = default_ref | field_ref | array_ref | root_ref call_exp = ref '(' [explist] ')' deref_exp = ref expr = const_exp | unop_exp | binop_exp | call_exp | deref_exp | assign_exp nop_stmt = ';' comp_stmt = '{' [stmts] '}' if_stmt = 'if' '(' expr ')' stmt [ 'else' stmt ] for_stmt = 'for' '(' [explist] ';' [expr] ';' [explist] ')' stmt break_stmt = 'break' ';' continue_stmt = 'continue' ';' exp_stmt = expr ';' return_stmt = 'return' [expr] ';' throw_stmt = 'throw' [expr] ';' var_stmt = 'var' varlist ';' try_stmt = 'try' stmt 'catch' '(' id ')' stmt stmt = nop_stmt | comp_stmt | if_stmt | for_stmt | break_stmt | continue_stmt | exp_stmt | return_stmt | throw_stmt | var_stmt | try_stmt

Пример: (example try catch)

Пускай метод lookup() реализован как в example throw. var o, default_o; ... try { o = lookup("бебе"); /* * если управление дошло до этого месте - lookup завершился * успешно (без исключения). */ } catch(e) { /* * во время выполнения lookup() произощло исключение e */ if(e == "не найдено") o = default_o; // это исключение было кинуто явно lookupом else throw e; // неизвестное исключение. кидаем дальше, // может кто-то поймает } ...

Пример: (example return 2, example throw)

lookup = function (n) { var i; for(i = .list_head; i != null; i = i.next) { if(i.name == n) return i; // нашли i. завершить выполнение ф-и и вернуть i. } /* * если управление дошло до этого места - ничего не найдено. * генерируем исключение. */ throw "не найдено"; /* * до этого места управление не доходит. * бессмысленно что-то писать после throw. */ }

Пример: (example continue)

process = function () { var i; for(i = .list_head; i != null; i = i.next) { /* если i не нуждается в обработке - сразу перейти к следующему i */ if(!i.need_process) continue; /* если i уже обработано ранее - сразу перейти к следующему i */ if(i.processed) continue; /* * следующее утверждение выполняется только для объектов, которые * нуждаются в обработке (i.need_process - 'истина') и для тех, * что еще не были обработаны ранее (i.precessed == 'ложь') */ i.process(); } }

Пример: (example break, example return 1)

lookup = function (n) { var i; for(i = .list_head; i != null; i = i.next) { if(i.name == n) break; } /* * i будет равно null, если в списке .list_head не оказалось объекта * с полем name равным параметру n. * иначе i будет содержать ссылку на объект содержащий поле name равным * параметру n. * * завершаем ф-ю с возвращаемым значением i. */ return i; }

Пример: (example mixed_binop)

правая часть приводится к типу левой: "2" + 2 // - строка '22' но 2 + "2" // - число '4'

Пример: (example polymorph)

polyInit = function() { /*конструктор абстрактной фигуры*/ .types.Figure = function() { this = .Map(); /*инициализация общих полей и методов*/ ... /*реализация подсчета площади поумолчанию*/ s = function() { throw "не умею считать площадь для этой фигуры"; }; return this; }; .types.Rect = function(x, y) { this = .types.Figure(); width = x; height = y; /*реализация подсчета площади прямоугольника*/ s = function() { return width*height; }; return this; }; .types.Circle = function(r) { this = .types.Figure(); radius = r; /*реализация подсчета площади круга*/ s = function() { return 314*r*r/100; }; return this; }; }

Пример: (example scope 2)

function () { var a, t; a = 5; t = function() { .print(a); // переменная 'a' не видна из вложенной функции. }; t(); }

Пример: (example scope 1)

... var a; a = 2; { var a; a = 1; .print(a); // напечатает 1; } .print(a); // напечатает 2; ... Т.к. составное утверждение создает новую область видимости, переменная a, объявленная внутри фигурных скобок, не будет видна снаружи.

Корневой объект

Контейнер, содержащий глобальную информацию, доступную всем потокам управления и всем функциям через root_ref.

Область видимости переменных

Область видимости - контейнер содержащий локальные переменные. Кроме переменных область видимости содержит указатель на обрамляющую область видимости. Область видимости с нулевым указателем на предыдущую область видимости называется корневой. Каждый поток управления фени имеет указатель на свою текущую область видимости. Создавая новую не корневую область видимости, ее указатель на обрамляющую область инициализируется текущей областью видимости этого потока, сам указатель но текущую область видимости устанавливается на новосозданную область. Разрушить область видимости можно только в случае, если она текущая. При этом текущая область видимости должна будет указывать на обрамляющую область видимости разрушаемой. Говоря, что утверждение создает новую область видимости понимается то, что вышеизложенным способом создается временная область видимости, которая существует пока выполняется это утверждение, после чего она разрушается. (см example scope 1) Таким образом, множество доступных локальных переменных определяется объединением переменных созданных во всех областях винимости начиная с текущей и заканчивая корневой. При чем переменные объявленные во внутренних областях видимости перекрывают переменные с тем же именем во внешних областях. Корневая область видимости создается функцией перед началом выполнения. Таки образом переменные объявленные в вызывающей функции недоступны для вызываемой. Даже если вызываемая функция объявлена в вызывающей. (см example scope 2)

Генерировать исключение

Syntax: throw_stmt = 'throw' [expr] ';' Если есть expr, вычислить его значение и привести к строке. Эта строка будет использована как текстовое представление генерируемого исключения. Перейти к пункту 3. самого внутреннего try_stmt. См. также: example throw

Поймать исключение

Syntax: try_stmt = 'try' stmt1 'catch' '(' id ')' stmt2 0. Создать новую вложенную область видимости переменных. (см scope) 1. пытается выполнить stmt1. 2. конец выполнения этого утверждения следующие действия выполняются если произошло исключение: 3. создать новую область видимости переменных, (см scope) 4. добавить в нее переменную с именем id, 5. присвоить ей строковое описание исключения 6. выполнить stmt2 См. также: example try catch, stmt

Декларировать локальные переменные

Syntax: varlist = id [ ',' varlist ] Syntax: var_stmt = 'var' varlist ';' В самой внутренней области видимости (см. scope) добавляет переменные с именами (id) из varlist. Новосозданные переменные имеют значения типа NONE.

Завершить выполнение функции

Syntax: return_stmt = 'return' [expr] ';' Если expr опущено - результат вычисления функции имеет тип NONE. Иначе - результат вычисления expr. См. также: example return 1, example return 2

Выполнить выражение

Syntax: exp_stmt = expr ';' Вычислить выражение expr, проигнорировав результат вычисления.

Перейти к следующей итерации

Syntax: continue_stmt = 'continue' ';' После выполнения этого утверждения, управление передается пункту 5 самого внутреннего цикла выполняемой функции. Если continue используется вне цикла - генерируется исключение. (см for_stmt, example continue)

Прервать цикл

Syntax: break_stmt = 'break' ';' Прервать выполнение цикла. После выполнения этого утверждения, управление передается пункту 7 самого внутреннего цикла выполняемой функции. Если break используется вне цикла - генерируется исключение. См. также: for_stmt, example break

Цикл

Syntax: for_stmt = 'for' '(' [explist1] ';' [expr] ';' [explist2] ')' stmt 0. Создать новую вложенную область видимости переменных. (см scope) 1. Вычислить выражения из explist1, игнорируя результаты. 2. Вычислить expr и привести результат к лог.значению. 3. Если результат 'ложь' - перейти к 7. 4. Выполнить stmt. 5. Вычислить выражения из explist2, игнорируя результаты. 6. Перейти к 2. 7. конец обработки этого утверждения. Принятые названия: explist1 - инициализация цикла, expr - условие выхода из цикла, explist2 - шаг цикла, stmt - тело цикла. См. также: break_stmt, continue_stmt

Условие

Syntax: if_stmt = 'if' '(' expr ')' stmt1 [ 'else' stmt2 ] Создает новую вложенную область видимости переменных. (см scope) Вычисляет значение expr и приводит (см casting) его к лог.значению. Если результат 'истина' - выполнить stmt1, иначе stmt2 (если есть).

Составное утверждение

Syntax: stmts = stmt [stmts] Syntax: comp_stmt = '{' [stmts] '}' Выполнить все утверждения (statement) из stmts. Или ничего, если stmts опущено. Создает новую вложенную область видимости переменных. (см scope)

Пустое утверждение

Syntax: nop_stmt = ';' Ничего не делать.

Утверждение

Syntax: stmt = nop_stmt | comp_stmt | if_stmt | for_stmt | break_stmt | continue_stmt | exp_stmt | return_stmt | throw_stmt | var_stmt | try_stmt Синтаксическая сруктура, семантическое значение которой позволяет выполнить то или иное действие (императив). См. также: nop_stmt, comp_stmt, if_stmt, for_stmt, break_stmt, continue_stmt, exp_stmt, return_stmt, throw_stmt, var_stmt, try_stmt

Базовые обозначения

Syntax: char = 0..255 Произвольный символ. Syntax: alpha = 'a'..'z' | 'A'..'Z' | 'а'..'я' | 'А'..'Я' Буква. Syntax: digit = '0'..'9' Цифра. Syntax: whatever = char [whatever] Последовательность произвольных символов любой длины.

Число

Syntax: number = digit [number] Константа NUMBER. Символическая запись числа в десятичном представлении. См. также: digit

Строка

Syntax: strings = string [strings] Syntax: string = '"' whatever '"' | "'" whatever "'" Константа типа STRING. \n - символ перевоа строки \r - символ возврата каретки \t - табуляция \ + любой другой символ, например кавычка, заменяется на себя же без '\' См. также: whatever

Коментарий

Syntax: comment = '//' whatever '\n' | '/*' whatever '*/' Коментарии не являются частью дерева разбора и игнорируются. См. также: whatever

Идентификатор

Syntax: numalpha = digit | alpha Syntax: numalphas = numalpha [numalphas] Syntax: id = alpha [numalphas] Последовательность букв и цифр, начинающаяся с буквы (см. digit, alpha). Используется в именах полей и переменных. Каждый идентификатор регистрируется в двух ассоциативных массивах. Один из них ставит последовательности букв и цифр в соответствие порядковый номер идентификатора, другой - наоборот, номеру ставит в соответствие строку. За время жизни программы эти массивы только растут. Все операции над идентификаторами производятся над их порядковым номером.

Функция

Syntax: varlist = id [ ',' varlist ] Syntax: stmts = stmt [stmts] Syntax: function = 'function' [number] [ '(' [varlist] ')' '{' [stmts] '}' ] Константа, определяющая некоторую последовательность действий. Необязательный параметр number - идентификатор функции, при помощи которого всегда можно изменить логику функции. Если идентификатор указан, остальную часть синтаксической структуры можно опустить - константа будет ссылаться на объявленую ранее с тем же идентификатором функцию. Если идентификатор не указан, выбирается следующий свободный. stmts - последовательность императивных утверждений (stmt), составляющих логику функции. При вызове создает корневую область видимости переменных. (см scope) В созданную область видимости добавляет переменную с именем 'this' и инициализирует ее соответствующим параметром. В ту же область добавляет переменные с именами из varlist, инициализируя их значения соответствующими значениями выбранными из списка передаваемых значений. Число переменных в varlist должно в точности совпадать с числом выражений в explist соответствующего call_exp. Последовательно выполняет утверждения из stmts. После выполнения последнего утверждения, возвращает значение типа NONE.

Константа

Syntax: const_exp = 'null' | number | strings | function Семантический узел такого типа хранит готовый регистр, представляющий собой данные, не меняющиеся в ходе выполнения программы. Результатом выполнения такого выражения служит хранимый регистр. null - константа типа NONE. См. также: number, strings, function, register

Ссылка на поле в корневом объекте

Syntax: root_ref = '.' id Ссылка указывает на поле с именем id внутри контейнера root. (см. root object)

Ссылка на элемент массива

Syntax: array_ref = exp1 '[' exp2 ']' Результат вычисления выражения exp2 является ключом внутри контейнера, получаемого в результате вычисления exp1.

Ссылка на поле

Syntax: field_ref = expr '.' id Ссылка указывает на поле с именем id в контейнере, получаемом в результате вычисления значения expr.

Ссылка по умолчанию

Syntax: default_ref = id Данная ссылка указывает на локальную переменную с именем id в текущей облати видимости переменных (см. scope). В случае если такой переменной нет, ссылка указывает на поле с именем id внутри контейнера this.

Ссылка на хранилище данных

Syntax: ref = default_ref | field_ref | array_ref | root_ref Семантическое значение ссылки остоит из двух частей: контейнера и ключа для доступа к регистру внутри контейнера. Типичный пример ссылки - пара 'массив' - 'индекс'. Ссылка реализует три метода метода доступа к адресуемому регистру: * присваивание установить значение адресуемого регистра * разыменование получить значение адресуемого регистра * вызов метода адресуемый регистр является функцией. Данный метод доступа вызывает эту ф-ю и возвращает значение ввиде регистра. См. также: default_ref, field_ref, array_ref, root_ref

Выражение

Syntax: expr = const_exp | unop_exp | binop_exp | call_exp | deref_exp | assign_exp Синтаксическая структура, семантическое значение которой позволяет вычислить некоторое значение. Хранилище таких значений называется регистром. Значения регистров бывают следующих типов: * NONE - нет значения, неопределено. * STRING - строка символов * NUMBER - целое знаковое 32битовое число, служит так же как лог.значение (если равно нулю - ложь, иначе - истина). * OBJECT - сложный объект (содержит ссылки на другие объекты) * FUNCTION - ф-ция * IDENTIFIER - испотльзуется внутрене. хранит число, являющееся семантическим значением лексемы ID. Все базовые операции с данными в фене производятся над регистрами. Существует неявная система приведений одного типа к другому (см casting). См. также: const_exp, unop_exp, binop_exp, call_exp, deref_exp, assign_exp

Разыменование

Syntax: deref_exp = ref Вызывает разыменование ссылки (получение значения). (см ref)

Логические операции (операции ветвления)

Syntax: log_binop = '&&' | '||' Syntax: expr log_binop expr Особенностью этих операций является то, что в определенных ситуациях значение правой части не вычисляется. см. logical and и logical or, expr

Бинарные предикаты

Syntax: pred_binop = '<' | '>' | '<=' | '>=' | '==' | '!=' Syntax: expr pred_binop expr Сначала вычисляется левая и правая часть (см.expr). Все бинарные предикаты определены для следующих типов левой части: NUMBER, IDENTIFIER, STRING. Правая часть приводится (см casting) к типу левой. Значения типа NUMBER сравниваются как знаковые числа, IDENTIFIER - по порядковому номеру регистрации идентификатора, STRING - лексикографически. Кроме того, операторы '==' и '!=' определены для типов OBJECT, FUNCTION и NONE. В случае если значение левой, или правой части имеет тип NONE - сравнивается не содержимле, а тип значений. Таким образом, 'null == x' и 'x == null' - истина тогда и только тогда, когда 'x' имеет тип NONE; так как приведение типов не вызывается никаких иключений возникнуть не может. В случае если тип значения левой, и правой части отличен от NONE, значение правой части приводится к типу левой. Значения типов OBJECT и FUNCTION сравниваются как указатели.

Побитовые операции

Syntax: bit_binop = '^' | '&' | '|' Syntax: expr bit_binop expr Вычисляется значение левой и правой части (см. expr). В случае, если тип вычесленного значения левой части не является NUMBER, генерируется NotImplementedException. Значение правой части приводится (см casting) к числу. Выполняется необходимая побитовая операция: - '^' - исключающее или - '&' - и - '|' - или Note: Для i = 0..31: Бит i в числе 'l & r' будет равен единице <=> биты i в l и r равны единице. Бит i в числе 'l | r' будет равен нулю <=> биты i в l и r равны нулю. Бит i в числе 'l ^ r' будет равен нулю <=> биты i в l и r равны между собой. <=> - тогда и только тогда.

Смешанные операции

Syntax: mixed_binop = '+' Syntax: expr mixed_binop expr Вычисляется левая и правая часть. Далее действие зависит от типа значения левой части: * NUMBER - значение правой части приводится к числу. Результат NUMBER = арифметическому сложению значений левой и правой части. (см. arith_binop) * STRING - значение правой части приводится к строке. Результат STRING = катанация (склейка) значений левой и правой части. * для всех остальных типов значения левой части генерируется NotImplementedException См. также: casting, example mixed_binop, expr

Арифметические операции

Syntax: arith_binop = '*' | '/' | '%' | '-' Syntax: expr arith_binop expr Вычисляется значение левой и правой части. В случае, если тип вычесленного значения левой части не является NUMBER, генерируется NotImplementedException. Значение правой части приводится (см casting) к числу. Выполняется необходимая целочисленная арифметическая операция: - '/' - деление - '%' - остаток от деления - '*' - умножение - '-' - вычитание Note: если N = A / Z - целочисленное деление R = A % Z - остаток от целочисленного деления то A = N*Z + R См. также: expr

Бинарные операции

Syntax: mixed_binop = '+' Syntax: arith_binop = '*' | '/' | '%' | '-' Syntax: bit_binop = '^' | '&' | '|' Syntax: pred_binop = '<' | '>' | '<=' | '>=' | '==' | '!=' Syntax: log_binop = '&&' | '||' Syntax: binop = arith_binop | bit_binop | pred_binop | log_binop Syntax: binop_exp = expr binop expr mixed_binop - действие операции зависит от типа левой части arith_binop - арифметические операции, определены только для чисел bit_binop - побитовые операции, определены только для чисел pred_binop - предикаты (результат - число 1 (истина), или 0 (ложь)) log_binop - операции ветвления (см. logical and и logical or) См. также: expr

Унарные операции

Syntax: unop = '!' | '~' | '-' Syntax: unop_exp = unop expr Вычисляет значение expr. * '!' - значение приводится к лог.выражению. Результат NUMBER = 1, если результат приведения - ложно Результат NUMBER = 0, если результат приведения - истина * '~' - аргумент приводится к числу. Результат NUMBER = побитовому отрицанию результата приведения * '-' - аргумент приводится к числу. Результат NUMBER = арифметическому отрицанию результата приведения См. также: casting

Вызов метода (функции)

Syntax: explist = expr [ ',' explist ] Syntax: call_exp = ref '(' [explist] ')' Вычисляет выражения перечисленные через запятую в скобках, формируя из их значений список параметров. Если explist опущен - список пуст. Вызывает метод/функцию, на которую ссылается ref, передавая в параметры сформированый список (см. ref, expr).

Присваивание

Syntax: assign_exp = ref '=' expr Вычисляет значение правой части и присваивает его ссылке, в левой части (см. ref). Возвращаемое значение то же, что было получино в результате вычисления правой части. Присваивание - правоассоциативный оператор. Потому выражения можно выстраивать в цепочку. Так выражение 'a = b = c = 0' выполняется следующим образом: '(a = (b = (c = (0))))'. См. также expr

Логическое 'или'

Syntax: expr '||' expr Вычисляет левую часть. Результат вычисления приводится (см casting) к логическому значению. Если это значение 'истина' - немедленно возвращает результат 'истина.' Иначе - возвращает результат вычисления и приведения к логическому значению правой части. Т.е. результат вычисления выражения '1 || a()' всегда 'истина' и ф-я a() никогда не вычисляется. См. также expr

Логическое 'и'

Syntax: expr '&&' expr Вычисляет левую часть. Результат вычисления приводится (см casting) к логическому значению. Если это значение 'ложь' - немедленно возвращает результат 'ложь.' Иначе - возвращает результат вычисления и приведения к логическому значению правой части. Т.е. результат вычисления выражения '0 && a()' всегда 'ложь' и ф-я a() никогда не вычисляется. См. также expr

Приведение типов

Феня не имеет специальных синтаксических структур для приведения одного типа к другому, однако, типы приводятся один к другому, в случае, если это требуется контекстом. Неявное привевдение типов, например, необходимо для бинарных операций, когда аргументы имеют различные типы, или как предикат в условных переходах. Действуют следующие правила приведения типов: * число <- NUMBER * число <- STRING - строка рассматривается как символьное представление числа 10ном коде * строка <- STRING * строка <- IDENTIFIER - строковое представление лексического идентификатора. * строка <- NUMBER - число записывается в виде строки в 10ном представлении. * строка <- FUNCTION - декомпиляция функции. преобразует пи-код скомпиллированной функции к строковому представлению в соответствии с синтаксисом языка (обратное преобразование к parse). * лог.выражение <- NUMBER - false тогда и только тогда, когда число равно нулю. * лог.выражение <- STRING - false тогда и только тогда, когда строка пустая. * идентификатор <- INDENTIFIER * сложный объект <- OBJECT * функция <- FUNCTION * все прочие попытки приведения вызывают InvalidCastException. * транзитивные правила силы не имеют.
Generated from areafile by feniatoxml.pl
Generated from xml by feniatohtml.xsl
Clone this wiki locally