-
Notifications
You must be signed in to change notification settings - Fork 15
Fenia: Grammar
Kit Oliynyk edited this page Oct 13, 2021
·
1 revision
(оригинал этой книги находится в библиотеке на Улице Вязов)Generated from areafile by feniatoxml.pl
Ходят слухи, что язык феня (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
Пускай метод lookup() реализован как в example throw. var o, default_o; ... try { o = lookup("бебе"); /* * если управление дошло до этого месте - lookup завершился * успешно (без исключения). */ } catch(e) { /* * во время выполнения lookup() произощло исключение e */ if(e == "не найдено") o = default_o; // это исключение было кинуто явно lookupом else throw e; // неизвестное исключение. кидаем дальше, // может кто-то поймает } ...
lookup = function (n) { var i; for(i = .list_head; i != null; i = i.next) { if(i.name == n) return i; // нашли i. завершить выполнение ф-и и вернуть i. } /* * если управление дошло до этого места - ничего не найдено. * генерируем исключение. */ throw "не найдено"; /* * до этого места управление не доходит. * бессмысленно что-то писать после throw. */ }
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(); } }
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; }
правая часть приводится к типу левой: "2" + 2 // - строка '22' но 2 + "2" // - число '4'
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; }; }
function () { var a, t; a = 5; t = function() { .print(a); // переменная 'a' не видна из вложенной функции. }; t(); }
... 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 xml by feniatohtml.xsl