mazdaywik / refal-5-framework Goto Github PK
View Code? Open in Web Editor NEWФреймворк для трансформации программ на Рефале-5
Home Page: https://mazdaywik.github.io/refal-5-framework
License: MIT License
Фреймворк для трансформации программ на Рефале-5
Home Page: https://mazdaywik.github.io/refal-5-framework
License: MIT License
Предлагается в парсер добавить не только выдачу ошибок, но и выдачу предупреждений.
Возможные предупреждения:
/*
внутри комментария: /* … /* … */
.\x00
внутри составного литерала: "abc\x00def".$EXTERN
’ы в соответствии с #9 (comment).Эта задача — подзадача для #5.
Следует подготовить набор примеров инструментов для данного фреймворка: препроцессор, верификатор, оптимизатор, альтернативный компилятор. Попутно вылезут проблемы в API компонентов, которые потребуется исправить. Я об этих проблемах знаю, но пока (2018-11-01) хочу полностью зафиксировать в документации текущее состояние.
Нужны следующие тестовые примеры:
Map
на явные циклы.CallCC
(call with current continuation)Примеры не должны рассматриваться как законченные инструментальные средства, они лишь proof of concept.
Для каждого примера должно быть описание в документации.
Рассмотрим 4 файла:
redudand-externs1.BAD-SYNTAX.ref
:
$ENTRY Go { = }
$EXTERN Go;
redudand-externs2.BAD-SYNTAX.ref
:
$EXTERN L;
$ENTRY Go { = <L> }
L { = }
redudand-externs3.BAD-SYNTAX.ref
:
$EXTERN Foo, Bar;
$EXTERN Foo, Bar;
$ENTRY Go { = <Foo> <Bar> }
redudand-externs4.BAD-SYNTAX.ref
:
$EXTERN Foo, Bar, Foo, Bar;
$ENTRY Go { = <Foo> <Bar> }
Все эти файлы лежат в папке tests/parser
, т.е. могут быть запущены скриптом run.bat
или run.sh
.
Посмотрим, как эти файлы компилируются официальными реализациями Рефала:
…\refal-5-framework\tests\parser>refc redudand-externs1.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
…\refal-5-framework\tests\parser>refc redudand-externs2.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
…\refal-5-framework\tests\parser>refc redudand-externs3.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Error: 201. Doubly defined function Foo . May be the function is built. on line 2.
Error: 103. Expected ';' on line 2.
2 errors found in function
2 syntax errors found.
…\refal-5-framework\tests\parser>refc redudand-externs4.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Error: 201. Doubly defined function Foo . May be the function is built. on line 1.
Error: 103. Expected ';' on line 1.
2 errors found in function
2 syntax errors found.
Компилятор refc
первые два файла откомпилировал, на два других выдал ошибки.
…\refal-5-framework\tests\parser>crefal redudand-externs1.BAD-SYNTAX.ref
Copyright (C): RefalScope Project, 2004-2018
Error on line 2: Redefinition of function Go .
crefal: 1 syntax error found.
…\refal-5-framework\tests\parser>crefal redudand-externs2.BAD-SYNTAX.ref
Copyright (C): RefalScope Project, 2004-2018
Error on line 3: Redefinition of external function L .
crefal: 1 error found in function L
crefal: 1 syntax error found.
…\refal-5-framework\tests\parser>crefal redudand-externs3.BAD-SYNTAX.ref
Copyright (C): RefalScope Project, 2004-2018
Warning: Repeated external declaration of Foo on line 2 .
Warning: Repeated external declaration of Bar on line 2 .
crefal: 2 warnings found.
…\refal-5-framework\tests\parser>crefal redudand-externs4.BAD-SYNTAX.ref
Copyright (C): RefalScope Project, 2004-2018
Warning: Repeated external declaration of Foo on line 1 .
Warning: Repeated external declaration of Bar on line 1 .
crefal: 2 warnings found.
Компилятор crefal
, наоборот, первые два файла отверг, два последних откомпилировал, выдав, однако, предупреждение.
Актуальная реализация парсера (85fb3dd) все четыре файла принимает:
D:\Mazdaywik\Documents\refal-5-framework\tests\parser>run.cmd redudand-externs1.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Parsing redudand-externs1.BAD-SYNTAX.ref...
redudand-externs1.BAD-SYNTAX.ref: PARSER MISSED ERRORS!
AST:
(Function (1 1 redudand-externs1.BAD-SYNTAX.ref)(Go)Entry (()RETURN ()))
(Extern ((2 9 redudand-externs1.BAD-SYNTAX.ref)Go))
Parser failed, see __err.txt for details
D:\Mazdaywik\Documents\refal-5-framework\tests\parser>run.cmd redudand-externs2.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Parsing redudand-externs2.BAD-SYNTAX.ref...
redudand-externs2.BAD-SYNTAX.ref: PARSER MISSED ERRORS!
AST:
(Extern ((1 9 redudand-externs2.BAD-SYNTAX.ref)L))
(Function (2 1 redudand-externs2.BAD-SYNTAX.ref)(Go)Entry (()RETURN ((Call (2 15 redudand-externs2.BAD-SYNTAX.ref)(L)))))
(Function (3 1 redudand-externs2.BAD-SYNTAX.ref)(L)Local (()RETURN ()))
Parser failed, see __err.txt for details
D:\Mazdaywik\Documents\refal-5-framework\tests\parser>run.cmd redudand-externs3.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Parsing redudand-externs3.BAD-SYNTAX.ref...
redudand-externs3.BAD-SYNTAX.ref: PARSER MISSED ERRORS!
AST:
(Extern ((1 9 redudand-externs3.BAD-SYNTAX.ref)Foo)((1 14 redudand-externs3.BAD-SYNTAX.ref)Bar))
(Extern ((2 9 redudand-externs3.BAD-SYNTAX.ref)Foo)((2 14 redudand-externs3.BAD-SYNTAX.ref)Bar))
(Function (4 1 redudand-externs3.BAD-SYNTAX.ref)(Go)Entry (()RETURN ((Call (4 15 redudand-externs3.BAD-SYNTAX.ref)(Foo))(Call (4 21 redudand-externs3.BAD-SYNTAX.ref)(Bar)))))
Parser failed, see __err.txt for details
D:\Mazdaywik\Documents\refal-5-framework\tests\parser>run.cmd redudand-externs4.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Parsing redudand-externs4.BAD-SYNTAX.ref...
redudand-externs4.BAD-SYNTAX.ref: PARSER MISSED ERRORS!
AST:
(Extern ((1 9 redudand-externs4.BAD-SYNTAX.ref)Foo)((1 14 redudand-externs4.BAD-SYNTAX.ref)Bar)((1 19 redudand-externs4.BAD-SYNTAX.ref)Foo)((1 24 redudand-externs4.BAD-SYNTAX.ref)Bar))
(Function (3 1 redudand-externs4.BAD-SYNTAX.ref)(Go)Entry (()RETURN ((Call (3 15 redudand-externs4.BAD-SYNTAX.ref)(Foo))(Call (3 21 redudand-externs4.BAD-SYNTAX.ref)(Bar)))))
Parser failed, see __err.txt for details
Неочевидно, т.к. обе официальные реализации друг другу противоречат, а в документации об этом нюансе явно ничего не говорится.
Если во всех четырёх случаях выдавать ошибку, то текст, принимаемый этим парсером, будет совместим с обоими официальными реализациями.
Кроме того, тогда реализация будет совместимой с Рефалом-05, который в этих случаях уже выдаёт ошибки, а значит, парсер Рефала-05 можно будет упростить, удалив из него проверки.
Обе официальные реализации выдают ошибки, если пользователь старается переопределить встроенные функции:
$ENTRY Prout { = }
…\refal-5-framework\tests\parser>refc builtin-redefine-entry.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Error: 201. Doubly defined function Prout . May be the function is built. on line 1.
Error: 102. Expected '}' on line 1.
2 errors found in function Prout
2 syntax errors found.
…\refal-5-framework\tests\parser>crefal builtin-redefine-entry.BAD-SYNTAX.ref
Copyright (C): RefalScope Project, 2004-2018
Error on line 1: Redefinition of built-in function Prout .
crefal: 1 error found in function Prout
crefal: 1 syntax error found.
Аналогично ведёт себя и парсер фреймворка:
D:\Mazdaywik\Documents\refal-5-framework\tests\parser>run builtin-redefine-entry.BAD-SYNTAX.ref
Refal-5 Compiler. Version PZ Oct 29 2004
Copyright: Refal Systems Inc.
Parsing builtin-redefine-entry.BAD-SYNTAX.ref...
builtin-redefine-entry.BAD-SYNTAX.ref: errors was expected, found by parser:
builtin-redefine-entry.BAD-SYNTAX.ref:1:1:Redefinition of builtin function Prout
Здесь всё в порядке, проблемы нет.
$EXTERN
для встроенной функцииАналогично все три реализации (crefal
, refc
и парсер фреймворка) одинаково ведут себя и в случае объявления встроенной функции как внешней:
…
$EXTERN Prout;
…
Все три реализации молча принимают данный файл, не вывода ошибок.
Такое поведение мне кажется неконсистентным — функция в файле может быть или встроенной (нет ни объявлений, ни определений), либо имеет единственное объявление $EXTERN
, либо единственное определение. А тут возникает исключение из общего правила для имён встроенных функций.
Что делать? Если проще будет реализовать общее правило (единственное объявление/определение), то парсер фреймворка вопреки обеим официальным реализациям будет выдавать ошибку на $EXTERN Prout;
. Если проще будет иначе — сделаю иначе.
На текущий момент парсер по потоку лексем порождает абстрактное синтаксическое дерево. Дерево содержит в целом только ту информацию, которая нужна для компиляции программы на Рефале. Из избыточной информации только псевдокомментарии *$
, координаты имён в $EXTERN
’ах, определений функций, переменных, термов конкретизации и символов-слов.
Парсер изначально писался для проекта 5-to-basis
, конвертора полного Рефала-5 в его базисное подмножество. Собственно, содержательной задачей было написание конвертора, а парсер был второстепенным. Поэтому он, в частности, жертвует эффективностью ради простоты — сначала строится дерево, а затем отдельным проходом ищутся в нём ошибки. Это требует копирования всего дерева, что в списковой реализации не эффективно.
Парсер возвращает именно то дерево, которое также кидается в проход проверки семантики. Именно поэтому в нём присутствуют координаты функций, вызываемых функций и переменных — проверка на дубликаты определений, вызовы и используемые переменные.
Когда я стал прикручивать парсер из этого репозитория к Рефалу-05, потребовалось сохранять координаты символов-слов и extern’ов (33065bc, aecd953, Mazdaywik/Refal-05#34). Первые — поскольку они являются функциями, вторые — потому что мне хотелось, чтобы Рефал-05 ругался на неиспользуемые функции и $EXTERN
’ы.
Наиболее костыльно здесь, конечно же, выглядит добавление координат в символы-слова:
t.Term ::=
(Symbol Word t.SrcPos e.Chars*)
| (Symbol Number s.Number)
| (Symbol Char s.Char)
…
Остальные символы координату не несут, несут только содержательную информацию. Из-за этой координаты теперь нельзя сравнить на равенство два символа образцом
EqSym {
(Symbol s.Type e.Info) (Symbol s.Type e.Info) = True;
(Symbol s.Type1 e.Info1) (Symbol s.Type2 e.Info2) = False;
t.Term1 t.Term2 = NotASymbols;
}
Да и вообще, нельзя сравнить на равенство два выражения в принципе — они могут быть равны, но различаться координатами символов-слов.
Актуальная реализация не сохраняет комментарии (кроме псевдокомментариев). А это значит, что нельзя, например, написать практически полезный форматтер исходных текстов.
Сохраняются псевдокомментарии, но они синтаксически допустимы только вне функций. Внутри тел функций они порождают ошибку компиляции.
Ну и обидно, что кривой prefal.rsl
комментарии сохраняет, а я нет.
Предлагается парсер разбить на два прохода.
Проход первый строит дерево разбора — оно включает в себя все токены, которые есть в исходном файле, но группирует их в дерево. Также первый проход проверяет семантическую корректность в программе — все функции объявлены, все переменные в правых частях из области видимости и т.д. При этом в дереве разбора сохраняются все комментарии и ошибочные токены (нужно понять, как).
Второй проход вычищает из этого дерева избыточную информацию — дерево становится более удобным для анализа и преобразования программ (например, компиляции).
Дерево разбора хранит в себе все лексемы из исходного файла и возможно также, часть лексем, которые создаются парсером, например, парные скобки. Поэтому, например, Рефал-05 может проверять корректность символов-слов, анализируя дерево разбора, а из абстрактного синтаксического дерева лишние координаты можно убрать.
В дереве хранятся все комментарии, поэтому можно написать форматтер, который их сохраняет.
На задаче висит тег question
, это значит, что она пока не готова к решению, нужно вообще подумать, а надо ли её решать.
У других языков программирования есть свои пакетные менеджеры, а у Рефала нет. И мне обидно.
Кроме того, у меня есть несколько своих проектов на Рефале:
Пишутся и другие проекты на Рефале-5:
К каждому из них скрипт сборки и скрипт запуска пишутся индивидуально.
Между двумя моими проектами есть зависимость: Рефал-05 зависит от фреймворка. Также не исключена зависимость между фреймворком и Рефалом-5λ — не хочется синхронно поддерживать две библиотеки LibraryEx
.
Декомпилятор на выходе самостоятельно формирует .rsl-ки, но он выиграл бы, если бы для их формирования он использовал фреймворк (правда, он требует доделки для вставки комментариев в код).
Рефал-5λ содержит копию исходника декомпилятора, она подключена через хитрое слияние ветвей. Тоже было бы неплохо, если бы декомпилятор подключался бы как зависимость.
MSCP-A зависит от библиотеки prefal
и для корректной работы вынужден её включать себе в репозиторий. prefal
доступен в интернете для загрузки:
http://www.botik.ru/pub/local/scp/refal5/bin/prefal_180216.zip
Запуск программ на Рефале-5 не очень удобен: в командной строке refgo
нужно явно перечислять все имена скомпилированных модулей, например так:
https://github.com/TonitaN/MSCP-A/blob/bf4a5175549ff913c52a68f5270a3370d3bf9daa/mscpdo.bat#L1-L7
Компиляция неудобна тем, что нужно явным образом перечислять все файлы, т.к. обнаруживать зависимости между файлами refc
не умеет.
В Рефале-5λ имеется утилита rlmake
, которая умеет находить зависимые файлы в специальным образом размеченных исходниках. Она принимает в командной строке имя единственного файла, находит все файлы, связанные с ним и вызывает компилятор rlc
для сборки всех этих файлов.
Разметка осуществляется комментариями
*$FROM ‹имя-файла›
В ‹имени-файла›
расширение .ref
писать не обязательно. Файлы ищутся в т.н. путях поиска, которые явно указываются в командной строке rlmake
, опция --ref5rsl
позволяет добавить к путям поиска также и содержимое переменной среды REF5RSL
.
Имя метки *$FROM
предполагает, что после этого комментария располагается список *$EXTERN
, содержащий имена функций, импортируемых из искомого модуля:
*$FROM LibraryEx
$EXTERN ArgList, LoadFile, SaveFile, Map;
Исторически комментарии *$FROM
я использовал при раскрутке Модульного Рефала в 2007 году:
С тех пор я уже привык использовать подобного рода комментарии, не только для утилиты rlmake
, но и вообще.
Другие программисты тоже используют подобные комментарии перед списками $EXTERN
. Пример из MSCP-A:
https://github.com/TonitaN/MSCP-A/blob/bf4a5175549ff913c52a68f5270a3370d3bf9daa/Drive.ref#L69-L88
Пример из SCP4 (версия scp4_000925
, файл access.ref
):
* basic.ref:
$EXTRN SubsAs, Subs;
* drive.ref:
$EXTRN Dn0;
* trace.ref:
$EXTRN CurrFile, FOutput, Trace, Trace0, CommTrace;
* key.ref:
$EXTRN OutDir, InpDir;
$EXTRN Length, Depth, CallDepth, Simplify, Transient, PutInHistory, Strategy, GoAhead;
Источником вдохновения послужили пакетные менеджеры и системы сборки языков Go и Rust:
У языка Go была позаимствована децентрализованность, у Rust — выделение библиотечного крейта.
Предполагается, что на машине пользователя доступны команды git
, wget
или curl
и какой-то разархиватор zip-файлов (unzip
на unix-подобных, для Windows разархиватор указывает пользователь в настройках), а также имеется какая-нибудь реализация Рефала-5 (сам Рефал-5, Рефал-5λ, Рефал-05 с компилятором Си).
Проект представляет собой папку, в которой имеются следующие файлы и каталоги:
bin/
— создаётся автоматически, в неё помещаются скомпилированные файлы,lib/
— создаёт пользователь, в неё помещаются компоненты (исходники *.ref
), которые можно повторно использовать,modules/
— создаётся автоматически, в неё скачиваются зависимости,src/
— создаёт пользователь, в неё помещаются исходники, которые повторно использоваться не должны, из них собираются целевые файлы,tmp/
— папка для временных файлов (создаётся автоматически),r5pm.prj
— ini-файл, описывающий текущий проект.r5pm.prj
Файл имеет следующий формат:
[common]
; Компилятор: одно из refc, crefal, refal-5-lambda (или lambda), refal-05
compiler = refc
; Флаги для refgo
refgo_flags = -l1000 -c200
; целевые файлы, имеют вид:
; имя_в_папке_bin = имя_в_папке_src
[targets]
virus = virus.ref
trojan = my_cool_trojan.ref
backdoor = backdoor.ref
; для целей глобальные настройки можно переопределять
[targets.virus]
refgo_flags = -l2000 -c300
[targets.trojan]
compiler = lambda
lambda_flags = -ODPRS
; зависимости, имеют вид
; имя_зависимости = путь_для_скачивания
; имя_зависимости нужно для указания дополнительных опций,
; кроме того, имя папки в папке modules/ будет основываться
; на имени зависимости (возможно, в конец припишется число).
[dependencies]
; если путь заканчивается на .zip — это архив,
pr = http://www.botik.ru/pub/local/scp/refal5/bin/prefal_180216.zip
; иначе — репозиторий git, после @ — номер версии (тег)
fw = https://github.com/Mazdaywik/[email protected]
; для зависимостей можно указывать дополнительные опции
[dependencies.pr]
; если распаковать архив prefal_180216.zip, то мы увидим,
; что rsl’ка лежит не в корне архива, а в подпапке prefal
subdir = prefal
Доступны следующие команды:
r5pm fetch
— скачивает все зависимости в папку modules/
.r5pm build [цель…]
— собирает указанные цели. Если цели не указаны, собирает все цели.r5pm clean
— удаляет промежуточные скомпилированные файлы (*.rsl
для Рефала-5, *.c
для Рефала-05 и т.д.).r5pm distclean
— удаляет всё (папки bin/
, modules/
, tmp/
).Если при скачивании зависимостей в скачанном репозитории/архиве в корне нашёлся файл r5pm.prj
, извлекаются зависимости из него и скачиваются тоже и далее рекурсивно.
Процесс сборки:
В секции [targets]
указываются «корневые» имена исходных файлов (как правило, они содержат функцию Go
или GO
).
Система сборки их сканирует на предмет наличия комментариев *$FROM
— имена файлов, указанные в них, назовём импортируемыми файлами или импортами (не нашёл более подходящего термина).
Затем система сборки по очереди смотрит в папку lib/
, во все папки modules/*/lib
у скачанных зависимостей (если явно не указана другая подпапка в опции subdir
) и в папку src/
до тех пор, пока не найдёт импортируемый файл с соответствующим расширением, если его не указал пользователь явно в комментарии.
Соответствующее расширение — это сначала расширение .ref
, а затем расширение, зависящее от компилятора в опции compiler
: .rsl
для refc
или crefal
, .c
для Рефала-05 и т.д. Если файл уже скомпилирован (например, импорт hello
нашёлся как файл hello.rsl
), то рядом с этим файлом ищется файл с расширением .imports
(например, hello.imports
), который тоже сканируется на предмет строчек *$FROM
(см. далее).
Каждый найденный импортируемый файл рекурсивно сканируется на комментарии *$FROM
.
Если импортируемый файл не найден, сборка прерывается с ошибкой.
Если список импортов для некоторой цели построен полностью, импортируемые файлы компилируются и в папке bin/
создаётся запускаемый модуль.
Запускаемый модуль для компиляторов Рефал-05 и Рефал-5λ — исполнимый файл (хотя для Рефала-5λ может быть, например, .dll
или .so
в зависимости от lambda_flags
), тут всё просто.
Запускаемый модуль для Рефала-5 — это пара из скрипта ОС (.bat-файл на Windows ‹имя-цели›.cmd
и shell-скрипт для unix-подобных ОС ‹имя-цели›
без расширения и с флагом исполнимости) и папки с rsl-ками ‹имя-цели›.rsls
. Предполагается, что пользователь эти файл и папку будет копировать, перемещать и переименовывать синхронно. В папке ‹имя-цели›.rsls
.rsl-ки будут лежать, переименованными в a.rsl
, b.rsl
, …, z.rsl
, a1.rsl
… Возможный вид скриптов запуска (не уверен на счёт экранирования скобок в Bash):
@refgo "%~dpn0%.rsls(a+b+c+d)" -l2000 -c300 %*
#!/bin/sh
refgo "${0}.rsls\(a+b+c+d\)" -l2000 -c300 "$@"
Параметры -l2000 -c300
берутся из refgo_flags
в файле конфигурации.
В проекте может отсутствовать папка src/
, в этом случае проект может быть использован только как зависимость, ничего в папке bin/
не создаётся. В проекте может отсутствовать папка lib/
, в этом случае проект использовать как зависимость бессмысленно.
Предполагается, что поддержка отдельных компиляторов (значения опции compiler
: refc
, crefal
, refal-05
, refal-5-lambda
) будет оформлена как плагины, т.е. не захардкожена напрямую в сам пакетный менеджер.
Плагин будет представлять собой команду r5pm-plugin-compiler-‹имя›
вроде r5pm-plugin-compiler-refal-05
. Пакетный менеджер будет вызывать соответствующую команду функцией System
, передавая ей необходимые опции — интерфейс взаимодействия нам пока не важен. Будут отдельные команды для запроса свойств (вроде расширений импортируемых файлов) и запуска сборки (передаётся файл, где перечислены исходные файлы + всякие опции).
Предустановленными будут плагины для refc
и crefal
, плагины для Рефала-05 и Рефала-5λ будут устанавливаться вместе с соответствующими компиляторами.
rlmake
В перспективе этот сборщик должен заменить собой утилиту rlmake
, последняя скукожится до плагина.
@TonitaN, вопросы, предложения, замечания, комментарии?
Мне написала на почту Виолетта Барсук и сообщила о следующей ошибке. Далее цитирую её письмо.
Я хотела сообщить, что у вас в парсере, как мне показалось, есть небольшая ошибка. Когда производится разбор дерева и проверка в
CheckSentence-Tail
, если присутствуетCALL-BLOCK
, то запускается повторный разбор предложений с помощьюCheckSentences
, который, в свою очередь, вызываетExtractCheckable
, который вызываетExtractCheckable-Tail
, который вызываетExtractCheckable-Expr
, который вызываетExtrackCheckable-Term
.
В общем, когда в
ExtractCheckable-Term
попадает уже разобранный терм, являющийся вызовом функции, то он попросту игнорируется и в последствии не проверятеся вCheckResult
, поскольку изначально в дереве он выглядит как(Call t.Pos (e.Name) e.Expr)
, но при разборе заменяется на(Call t.Pos e.Name)
. Последняя форма не воспринимается как вызов функции и удаляется, попадая под правило:t.OtherTerm = ;
Думаю, в принципе повторный разбор уже разобранных предложений избыточен.
Нужно воспроизвести эту ошибку (подобрать тест, её выявляющий) и затем исправить.
Ограниченный Рефал — подмножество базисного Рефала, в образцах которого недопустимы открытые переменные и повторные e- и t-переменные. Такие образцы называются L-выражениями.
Подмножество интересно тем, что в нём выразима и замкнута прогонка.
Турчин также запрещал в ограниченном Рефале t-переменные, но их добавление ни сколько не влияет на выразимость прогонки.
Также, как и ранее, предлагается моделировать сопоставление с образцом, и когда фиксируются «дырки открытых переменных», что-то делать. Но не только это.
Ещё нужно внимательнее обрабатывать закрытые переменные — если закрытая переменная с таким же именем уже встречалась, то это повторная переменная.
Пока образец не преобразован к виду L-выражения, мы даже не смотрим — есть ли при нём условие. Когда уже преобразован — обработка условия становится тривиальной.
Поэтому нужно:
Предлагаются следующие шаги для развития front-end’а. Частично они навеяны #7.
Псевдокомментарии *$
не должны быть синтаксической ошибкой внутри функций.
Выкинуть костыль с (Symbol Word t.SrcPos e.Chars*)
.
Объединить модули Refal5-AST
, Refal5-Lexer
, Refal5-Parser
и Refal5-Tokens
.
Исходя из цели фреймворка (#7) фронт-энд будет использоваться преимущественно целиком — редко лексер будет использоваться отдельно от парсера. Поэтому для удобства логично их объединить в одном модуле.
Модули Refal5-AST
и Refal5-Tokens
нужны Рефалу-05 из-за *$EENUM
’ов. При использовании фреймворка вместе с Рефалом-5 в них находятся только две некритичные функции — Builtins
и TokName
.
Поэтому имеет смысл не объединять все четыре файла в один, а объединить Refal5-AST
и Refal5-Tokens
в Refal5-FrontEnd-Defs
R5FW-Parser-Defs
, а Refal5-Lexer
и Refal5-Parser
— в Refal5-FrontEnd
R5FW-Parser
(см. комментарий).
Переписать (отрефакторить) сам парсер — вместо имеющегося рекурсивного спуска и двухпроходного алгоритма, использовать «перенос-свёртку», апробированную в Mazdaywik/Refal-05#29 и одиночный проход. Будет ли это точным рефакторингом — не очевидно, поскольку при переписывании сообщения об ошибках могут измениться.
Ввести несколько функций для синтаксического и лексического анализа: R5FW-ParseFile
, R5FW-ParseText
, R5FW-ParseTokens
, R5FW-ScanFile
, R5FW-ScanText
, принимающие имя файла (***File
), последовательность строк с начальной позицией (***Text
) и список токенов (R5FW-ParseTokens
). Функции, работающие со строкой (R5FW-***String
), ввести тоже можно, но они будут эквивалентны R5FW-***Text
с передачей единственной строки.
Функции должны возвращать результат в формате
<R5FW-Parse…> == s.Ok e.AST (e.Messages)
<R5FW-Scan…> == s.Ok t.Token* (e.Messages)
s.Ok ::= Ok | Fails
e.Messages ::= (t.Pos s.Severity s.Type e.Info)*
s.Severity ::= Error | Warning
<R5FW-FormatMessage s.Type e.Info> == s.CHAR+
s.Ok
облегчает анализ: не нужно среди e.Messages
искать Error
, чтобы понять, что были ошибки.
s.Type
может быть, например, Error-UndeclaredFunction
, а e.Info
для него — именем функции.
Рефал исходно создавался как «метаалгоритмический язык» — язык для преобразования программ на других языках:
В частности, объектом преобразования программ могут быть программы на Рефале тоже. (Примером того является этот репозиторий в текущей редакции — cdf26e7.) А это значит, нужны библиотечные средства для (а) разбора программ на Рефале и (б) генерации программ на Рефале.
Одно из таких библиотечных средств есть — библиотека prefal
, разработанная Андреем Немытых:
ftp://ftp.botik.ru/pub/local/scp/refal5/bin/prefal_180216.zip
Но я (@Mazdaywik) не нахожу её удовлетворительной, поскольку (а) писал её не я 😊, (б) её интерфейс далёк от совершенства. В частности, синтаксические ошибки выводятся на stderr
(причём в нестандартном формате), само дерево не сохраняет позиции лексем исходного текста. Однако, сильной стороной библиотеки является сохранение комментариев.
Я начинал попытку сделать такой фреймворк на базе Рефала-05:
но от этой идеи отказался (Mazdaywik/Refal-05#33). Причина в том, что парсер несовместимого диалекта не нужен.
Цель — разработать на базе этого репозитория фреймворк для преобразования программ на Рефале.
Фреймворк должен включать в себя
Plainer.ref
,LibraryEx
, адаптированную к классическому Рефалу-5.Все компоненты должны быть совместимы с Рефалом-05 — недопустимы имена, различающиеся знаками -
и _
, для каждого используемого идентификатора должна быть определена функция (обычная функция или *$ENUM
/*$EENUM
) и т.д. Условия и блоки использовать можно, поскольку (а) уже есть трансформер в базисный Рефал, (б) эти конструкции могут появиться и в Рефале-05.
Front-end должен сохранять как минимум псевдокомментарии (начинающиеся на *$
), back-end их должен печатать. Поддержка сохранения всех комментариев в парсере на начальном этапе не требуется.
Вспомогательные инструменты — это, собственно, то, что сейчас называется Transformer.ref
и функция NewName
в нём. Система эквивалентных преобразований хорошо проработана только для ограниченного Рефала — подмножества базисного подмножества, где в образцах запрещены открытые e-переменные и повторные e- и t-переменные. Поэтому при написании программ, использующих такие преобразования, нужны инструменты автоматического преобразования кода к подмножеству.
Библиотека LibraryEx
доказала (мне) своё удобство на практике, предлагается перетащить её из Рефала-05 сюда. Если путь к фреймворку будет лежать в переменной REF5RSL
(а он там должен лежать), то всегда будет удобно использовать LibraryEx
.
Результат самоприменения 5-to-basis
должен находиться в общем подмножестве Рефала-5 и Рефала-05.
Для этого нужно:
*$ENUM
и *$EENUM
и соответствующие им $EXTERN
’ы. К счастью, препроцессор уже поддерживает сохранение комментариев такого формата в сгенерированном коде, поэтому тут всё хорошо.Mu
там, где они есть (Tests.ref
и Plainer.ref
).Эта задача блокирует задачи Mazdaywik/Refal-05#28, Mazdaywik/Refal-05#33 и #5.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.