Создание нового языка программирования - с чего начать: различия между версиями
Mikst (обсуждение | вклад) |
Mikst (обсуждение | вклад) |
||
Строка 81: | Строка 81: | ||
===Основные узлы синтаксического дерева=== | ===Основные узлы синтаксического дерева=== | ||
Все узлы синтаксического дерава наследуются от базового класса tree_node | |||
<source lang="CSharp"> public class tree_node | |||
{ | |||
public tree_node() | |||
public tree_node(SourceContext _source_context) | |||
public SourceContext source_context | |||
public virtual void visit(IVisitor visitor) | |||
} | |||
</source> | |||
Для реализации механизма визиторов каждом узле дерева определен метод | |||
<source lang="CSharp"> public virtual void visit(IVisitor visitor) | |||
{ | |||
visitor.visit(this); | |||
} | |||
</source> | |||
Для обхода дерева необходимо написать класс визитор, в котором будет переопределена функция visit для всех типов узлов | |||
<source lang="CSharp"> class visualizator : IVisitor | |||
{ | |||
public void visit(tree_node _tree_node) | |||
{} | |||
. . . | |||
} | |||
</source> |
Версия от 20:34, 1 мая 2010
Мы будем создавать парсер языка - программу, переводящую текст программы на языке программирования, в так называемое синтаксическое дерево. По-существу, парсер является первой частью компилятьра, его front-endом.
Папка с грамматикой
В папке с грамматикой - 2 файла - .grm и .pgt. .grm содержит файл с грамматикой в формате Gold Parser Builder, дополненный шаблонами действий (действия - в стиле Yacc). Шаблоны действий хранятся в виде комментариев Gold Parser Builder, так что на компиляцию им грамматики не влияют.
.pgt - это шаблон для создания скелета парсера, имеется для различных языков, мы используем для C#. Это означает, что сами парсеры
Все остальные файлы, если они есть, - можно убить - они генерируются автоматически.
По .grm файлу Gold Parser Builder автоматически создает .cgt файл - это файл откомпилированной грамматики - пока без правил. Они будут учтены позже.
Шаблоны в .grm файле
Шаблоны создавались Ткачуком для облегчения автоматической генерации кода. Рассмотрим пример:
!<%NAME%> %CODE%
!*
[TERMINALTEMPLATE]
{
%NAME% _%NAME%=new %NAME%(%PARAMS%);
_%NAME%.source_context=parsertools.GetTokenSourceContext();
%CODE%
return _%NAME%;
}
*!
и связанный с ним код правила для простого токена (терминального символа):
tkIf = 'if' !*<token_info>*!
Здесь - первая строка
!<%NAME%> %CODE%
задает вид правой части: <token_info>. В данном случае %NAME%=token_info, %CODE% - пустой
Все терминальные символы подвергаются действию этого шаблона. В результате в данном случае <token_info> переходит в
token_info _token_info=new token_info(%PARAMS%);
_token_info.source_context=parsertools.GetTokenSourceContext();
// пусто
return _token_info;
%PARAMS% - это параметр в круглых скобках в <> в правилах. Поскольку правило имеет вид <token_info> и не содержит круглых скобок, то здесь здесь %PARAMS% - пустой.
source_context - это объект класса SourceСontext, хранящий строку и столбец начала и конца узла в синтаксическом дереве.
Шаблон для нетерминала
Рассмотрим нетерминал:
<if_then_else_branch>
::= tkThen <then_branch> !*3if_node<null,(statement)$2,null>
parsertools.create_source_context($$,$1,parsertools.sc_not_null($2,$1));
Цифра 3 говорит о том, что используется шаблон нетерминала №3:
!*
[NONTERMINALTEMPLATE3]
{
%NAME% _%NAME%=new %NAME%(%PARAMS%);
%CODE%
return _%NAME%;
}
*!
Если цифра не присутствует, то шаблон не применяется.
Как и в Yacc, в правилах доступны $$ (левая часть правила), $1 - первый параметр и т.д.
Как генерируется парсер
Вначале мы подаем файл .grm программе Gold Parser Builder или ее консольной версии. Если ошибок нет, то генерируется файл .cgt - это откомпилированный файл грамматики. Он не содержит правил.
Затем по файлу .cgt и файлу .pgt (заготовка для скелета парсера) с помощью программы createskelprog_main.exe (программа входит в консольную версию Gold Parser Builder, скачать ее можно со страницы [1]) генерируется скелет парсера на конкретном языке (в нашем случае - на C#). Этот скелет содержит парсер, в котором для каждого правила создан пустой метод. Эти методы и надо заполнить действиями по генерации синтаксического дерева программы.
При изменении грамматики такой подход чрезвычайно неудобен, именно поэтому Ткачук создал программу, которая по файлу грамматики и по получаемому скелету парсера на C# вписывает в этот скелет правила, содержащиеся в комментариях !* *! в .grm файле после каждого правила.
Программа Ткачука, выполняющая это действие, называется grmCommentCompiler.exe. В итоге вся последовательность действий имеет вид (на примере грамматики для языка КуМир):
goldbuilder_main.exe KuMir.grm KuMir.cgt
createskelprog_main.exe KuMir.cgt KuMir.pgt KuMir.tmpl
grmCommentCompiler.exe KuMir.grm KuMir.tmpl KuMir.cs
Заметим, что для работы парсера необходим так называемый движок парсера (Parser Engine). В нашем случае он находится в файле ParserTools.dll, построенном на движке Морозова Morozov C# Engine со страницы [2]
Основные узлы синтаксического дерева
Все узлы синтаксического дерава наследуются от базового класса tree_node
public class tree_node
{
public tree_node()
public tree_node(SourceContext _source_context)
public SourceContext source_context
public virtual void visit(IVisitor visitor)
}
Для реализации механизма визиторов каждом узле дерева определен метод
public virtual void visit(IVisitor visitor)
{
visitor.visit(this);
}
Для обхода дерева необходимо написать класс визитор, в котором будет переопределена функция visit для всех типов узлов
class visualizator : IVisitor
{
public void visit(tree_node _tree_node)
{}
. . .
}