Git Product home page Git Product logo

refal-5-framework's People

Contributors

mazdaywik avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

refal-5-framework's Issues

Предупреждения в парсере

Предлагается в парсер добавить не только выдачу ошибок, но и выдачу предупреждений.

Возможные предупреждения:

  • Наличие /* внутри комментария: /* … /* … */.
  • Наличие \x00 внутри составного литерала: "abc\x00def".
  • Избыточные $EXTERN’ы в соответствии с #9 (comment).

Уточнение API и примеры использования фреймворка

Эта задача — подзадача для #5.

Следует подготовить набор примеров инструментов для данного фреймворка: препроцессор, верификатор, оптимизатор, альтернативный компилятор. Попутно вылезут проблемы в API компонентов, которые потребуется исправить. Я об этих проблемах знаю, но пока (2018-11-01) хочу полностью зафиксировать в документации текущее состояние.

Нужны следующие тестовые примеры:

  • Препроцессор, выполняющий макроподстановки.
  • Верификатор входных форматов функций: все образцы должны быть уточнениями заявленного формата.
  • Оптимизатор на уровне дерева — заменяет вызовы Map на явные циклы.
  • Альтернативный компилятор — объединяет все файлы в один файл с необходимым переименованием локальных функций.
  • Преобразователь программ, использующих копилку, в программы с явным параметром.
  • Преобразователь программ в continuation passing style, соответственно, будет доступной функция CallCC (call with current continuation)

Примеры не должны рассматриваться как законченные инструментальные средства, они лишь proof of concept.

Для каждого примера должно быть описание в документации.

Ошибки на избыточные `$EXTERN`’ы

Воспроизведение ошибки

Рассмотрим 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.

Посмотрим, как эти файлы компилируются официальными реализациями Рефала:

Запуск refc
…\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 первые два файла откомпилировал, на два других выдал ошибки.

Запуск crefal
…\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) все четыре файла принимает:

Запуск tests\parser\run.cmd
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-выражения, мы даже не смотрим — есть ли при нём условие. Когда уже преобразован — обработка условия становится тривиальной.

Поэтому нужно:

  • описать подход к преобразованию образцов к L-виду,
  • реализовать описанный конвертор.

Развитие front-end’а

Предлагаются следующие шаги для развития 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 для него — именем функции.

Сделать этот репозиторий фреймворком

Мотивация

Рефал исходно создавался как «метаалгоритмический язык» — язык для преобразования программ на других языках:

  • Турчин В. Ф. Метаязык для формального описания алгоритмических языков // В сборнике: Цифровая вычислительная техника и программирование. М.: Советское радио, 1966, с. 116–119.
  • Турчин В. Ф. Метаалгоритмический язык // Кибернетика № 4, 1968, с. 116−124.

В частности, объектом преобразования программ могут быть программы на Рефале тоже. (Примером того является этот репозиторий в текущей редакции — cdf26e7.) А это значит, нужны библиотечные средства для (а) разбора программ на Рефале и (б) генерации программ на Рефале.

Одно из таких библиотечных средств есть — библиотека prefal, разработанная Андреем Немытых:

ftp://ftp.botik.ru/pub/local/scp/refal5/bin/prefal_180216.zip

Но я (@Mazdaywik) не нахожу её удовлетворительной, поскольку (а) писал её не я 😊, (б) её интерфейс далёк от совершенства. В частности, синтаксические ошибки выводятся на stderr (причём в нестандартном формате), само дерево не сохраняет позиции лексем исходного текста. Однако, сильной стороной библиотеки является сохранение комментариев.

Я начинал попытку сделать такой фреймворк на базе Рефала-05:

но от этой идеи отказался (Mazdaywik/Refal-05#33). Причина в том, что парсер несовместимого диалекта не нужен.

Цель

Цель — разработать на базе этого репозитория фреймворк для преобразования программ на Рефале.

Фреймворк должен включать в себя

  • средства анализа (лексер, парсер),
  • средства синтеза (pretty printer), он уже есть — Plainer.ref,
  • вспомогательные инструменты для преобразования программ,
  • библиотеку LibraryEx, адаптированную к классическому Рефалу-5.

Все компоненты должны быть совместимы с Рефалом-05 — недопустимы имена, различающиеся знаками - и _, для каждого используемого идентификатора должна быть определена функция (обычная функция или *$ENUM/*$EENUM) и т.д. Условия и блоки использовать можно, поскольку (а) уже есть трансформер в базисный Рефал, (б) эти конструкции могут появиться и в Рефале-05.

Front-end должен сохранять как минимум псевдокомментарии (начинающиеся на *$), back-end их должен печатать. Поддержка сохранения всех комментариев в парсере на начальном этапе не требуется.

Вспомогательные инструменты — это, собственно, то, что сейчас называется Transformer.ref и функция NewName в нём. Система эквивалентных преобразований хорошо проработана только для ограниченного Рефала — подмножества базисного подмножества, где в образцах запрещены открытые e-переменные и повторные e- и t-переменные. Поэтому при написании программ, использующих такие преобразования, нужны инструменты автоматического преобразования кода к подмножеству.

Библиотека LibraryEx доказала (мне) своё удобство на практике, предлагается перетащить её из Рефала-05 сюда. Если путь к фреймворку будет лежать в переменной REF5RSL (а он там должен лежать), то всегда будет удобно использовать LibraryEx.

Задачи

  • Пересмотреть интерфейсы модулей (#4).
  • Сделать исходники совместимыми с Рефалом-05 (#3).
  • Сделать Transformer.ref библиотекой.
  • Поддерживать конвертирование в ограниченный Рефал (#2).
  • Перенести сюда библиотеку LibraryEx.

Собираться при помощи Рефала-05

Результат самоприменения 5-to-basis должен находиться в общем подмножестве Рефала-5 и Рефала-05.

Для этого нужно:

  • Добавить необходимые псевдокомментарии *$ENUM и *$EENUM и соответствующие им $EXTERN’ы. К счастью, препроцессор уже поддерживает сохранение комментариев такого формата в сгенерированном коде, поэтому тут всё хорошо.
  • Исправить некорректные вызовы Mu там, где они есть (Tests.ref и Plainer.ref).

Эта задача блокирует задачи Mazdaywik/Refal-05#28, Mazdaywik/Refal-05#33 и #5.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.