Русские вычислители

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Русские вычислители » Русский язык программирования: от слов к делу » Разделение проекта на отдельные модули


Разделение проекта на отдельные модули

Сообщений 1 страница 11 из 11

1

Право слово господа/товарищи, не пора ли начать делать проект?

Предлагаю разбить задачу на отдельные модули, и согласовать интерфейсы между ними.
От себя имею парсер (как правильно назвать "парсер" по-русски обсуждают тут), если у кого нибудь найдется готовый синтаксис для ЯП уже сможем сделать пробный черновой вариант с мета-компиляцией в код для какого нибудь существующего языка или интерпретацией.
Если кто то хочет работать над компилятором, но затрудняется с парсером я бы мог переписать свой парсер под его интерфейс.

0

2

рст256
Хочу аналог Bison который бы создавал словоукладчик.
На входе грамматические правила. На выходе словоукладчик.
Словоукладчик должен строить грамматическое-дерево. Иметь рекурсивный вид с элементами автоматов: аналогично тому как это сделано в LCC, TurboPascal, Clang, Хоха - а никак у Бизона
Восстановление в случае ошибки с выдачей кода и места ошибки.

И да документация и примеры использования обязательны, примерно как у Бизона по ссылке выше.

0

3

Павиа
Хорошо, тогда я займусь написанием документации.

Павиа написал(а):

Хочу аналог Bison который бы создавал словоукладчик.
На входе грамматические правила. На выходе словоукладчик.
Словоукладчик должен строить грамматическое-дерево.

Уточните пожалуйста вам нужна возможность создания словоукладчика только на основе входящих грамматических правил?
Дело в том что именно на подобный принцип работы я в первую очередь его и ориентировался, но для  этого к традиционной ВНФ пришлось добавить рад новых конструкций:
Например в качестве элемента последовательности может использоваться конструкция вида:
"<название_поля>=<под_правило>"
За счет этого становится возможным генерировать весь объем связанного с такой последовательностью кода, включая также отределение типа. Пример:

Код:
элемент_массива := массив=переменная, '[', индекс=список_выражений, ']'

Сгенерированный код:

Код:
struct элемент_массива {
	переменная массив;
	список_выражений индекс;
};
элемент_массива * разбор_правила__элемент_массива(...){
	элемент_массива * объект = malloc(sizeof(элемент_массива));
	объект->массив = assert(разбор_правила__переменная(...);
	assert(разбор_лексемы('[', ...);
	объект->массив = assert(разбор_правила__список_выражений(...));
	assert(разбор_лексемы(']', ...);	
	return объект;
}

0

4

Грубо говоря, у меня есть компилятор.
Хочу добавить поддержку 2-х языков.
Для уменьшения труда и снежения ошибок мне нужен генератор.

Генератор не простой. Он должен распознавать определённые конструкции грамматики.
И использовать нужный словоукладчик:
- простая рекурсия
- автомат
- свёртка.

Чистую БНФ, я не рассматриваю. Нужна расширенная, как то со служебными словами у Бисона.

Ваш проект я видел. Но он не вяжется с моим! Как бы нам это устранить? У вас это Си. У меня Паскаль. В первую очередь меня неустраивает устройство выдаваемого кода. Выше я свои требования в кратце описал. Попробую более подробно изложить.
В догонку Assert'ы вы используете неправильно.

По поводу вашей идеи с распознаванием полей. При написание генератора-объектного кода такая вот неоднозначность у меня вызывала трудности.
Так что хотелось бы увидеть практический пример. Что-бы разобраться как надо и как ненадо делать.

0

5

Павиа написал(а):

Ваш проект я видел. Но он не вяжется с моим! Как бы нам это устранить? У вас это Си. У меня Паскаль.

Я думаю тут стоит прибегнуть к обычной в таких случаях практике (как например в antlr) и использовать шаблоны кода.

Павиа написал(а):

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

НУ тут если потребуется я готов даже переписать код полностью, такую необходимость я даже предполагал.

Павиа написал(а):

Генератор не простой. Он должен распознавать определённые конструкции грамматики.
И использовать нужный словоукладчик:
- простая рекурсия
- автомат
- свёртка.

Т.е. определять оптимальный метод обработки?
Что бы к примеру код для правила "правило":

Код:
правило := 
   имя "(" [список-аргументов] ")" |
   имя "[" [список-аргументов] "]" |
   имя

имел бы вид:

Код:
   имя = вызов_правила_имя()
   лексема = получить_лексему()
   если лексема=="(" тогда 
      ...
      возврат вызов_функции
   иначе 
      если лексема=="[" тогда 
         ...
         возврат доступ_по_индексу
      иначе
         возврат имя
      конец
   конец

вместо простого перебора обработок "вызов_функции", "доступ_по_индексу" и "имя"?
Я правильно вас понял?

0

6

рст256

вместо простого перебора обработок "вызов_функции", "доступ_по_индексу" и "имя"?
Я правильно вас понял?

Напротив.

Т.е. определять оптимальный метод обработки?

Оптимальный, но не в смысле скорости, а в смысле человеко понятности выдаваемого кода. Таблицы и перебор оно гораздо понятнее. Только не надо доводить до предела. Когда табличка трёх-этажная и без бутылке не разберёшь, что и куда.

Код разбора разименовывания переменных немогу привести. Так как табличкой я его не успел сделать, но в планах есть.

К примеру грамматика секция описания стыка отдела является вида LR(1). Там нет перебора служебных слов нет надобности делать автомат. И всё легко выражается функциями вида NeedSimvol, NeedIndent,

NeedSimvol(TS('procedure'));
    ASTFabric.ActivLeaf:=FunctionHeadin;
    NeedIndent(Indent);
      if CheckSimvol(TS('(')) then
         ParseFormalParametrs(FormalParametrs)
         else   FormalParametrs:=TFormalParametrsAST.Create;
      NeedSimvol(TS(';'));

Но вот к примеру с табличкой

function TDeclSectionParser.Parse(Anchor: TSimvol): TDeclarationAST;
// Машина конечного числа состояний
const
  CommandCount=10;
var
  Prog:array [0..CommandCount-1] of TCaseCommand;
  procedure Init;
  begin
  Prog[0].Value:='Type';           Prog[0].Woker:=TypeDeclaration;
  Prog[1].Value:='Var';            Prog[1].Woker:=VarDeclaration;
  Prog[2].Value:='Const';          Prog[2].Woker:=ConstDeclaration;
  Prog[3].Value:='Label';          Prog[3].Woker:=LabelDeclaration;
  Prog[4].Value:='function';       Prog[4].Woker:=FunctionHeading;
  Prog[5].Value:='procedure';      Prog[5].Woker:=ProcedureHeading;
  Prog[6].Value:='asm';            Prog[6].Woker:=Nop;
  Prog[7].Value:='Implementation'; Prog[7].Woker:=Nop;
  Prog[8].Value:='Begin';          Prog[8].Woker:=Nop;
  Prog[9].Value:='End';            Prog[9].Woker:=Nop;
  end;

var
  Woker:TParserProcedure;
  NodeAST:TNodeAST;
begin
Init;
Self.Anchor:=Anchor;

Result:=ASTFabric.DeclarationCreate;
ASTFabric.ActivLeaf:=Result;
if ResultExist then AddResult(Result, ResultType);
  repeat
    NodeAST:=Nil;
    Woker:=ChooseOfWork(CommandCount,@Prog, CurrentSimvol());

    if @Woker=nil then
       begin
       Compiler.Error.Add(105,CurrentSimvol.Value);
       ErrorWay(Anchor);
       Break;
       end else
       if Not IsNop(Woker) then
          begin
          Woker(NodeAST);
          Result.Add(NodeAST);
          end;
  until  (IsAnyAnchor) or (NodeAST=Nil);
  ASTFabric.LevalUp;
end;

0

7

Павиа написал(а):

Оптимальный, но не в смысле скорости, а в смысле человеко понятности выдаваемого кода. Таблицы и перебор оно гораздо понятнее. Только не надо доводить до предела. Когда табличка трёх-этажная и без бутылке не разберёшь, что и куда.

Для повышения удобочитаемости кода я выделил некоторые распространенные в ЯП грамматические конструкции в виде отдельных процедур. Например
1. Список с разделителями.
list(item : TNodeAST_Parser, separator : TSymbol)
Данная конструкция немного облегчает код и значительно упрощает процесс восстановления в случае ошибки.

2. Список с разделителями и явным окончанием
closed_list(item : TNodeAST_Parser, separator : TSymbol, close_sym: TSymbol)

3. Список с разделителями, с явным началом и окончанием
delimed_list(item : TNodeAST_Parser, separator : TSymbol, open_sym: TSymbol, close_sym: TSymbol)

4. Заключение в скобки
delimed(body : TNodeAST_Parser, open_sym: TSymbol, close_sym: TSymbol)
Для разбора конструкций вида: <open_sym>  <body>  <close_sym>

5. Разбор бинарных операций с учетом приоритета
binop(value: TNodeAST_Parser, operations : TMap <TSymbol, integer>)
operations - асоц. массив, где ключ - символ операции а значение ее приоритет.

6. Разбор атрибутов
attributes(attrib_map : TMap <TSymbol, integer>)
attrib_map - асоц. массив, где ключ - символ (лексема) обозначающая атрибут а значение битовая маска соответствующая данному атрибуту.

7. Условное вхождение со значением по умолчанию.
optional(rule: TNodeAST_Parser, default_value : TNodeAST)

Тогда ваш пример описания грамматики примет вот такой вид:

Код:
NeedSimvol(TS('procedure'));
    ASTFabric.ActivLeaf:=FunctionHeadin;
    NeedIndent(Indent);
      FormalParametrs:=optional(
         delimed_list(ParseFormalParametr, TS(','), TS('('), TS(')')), 
         TFormalParametrsAST.Create
      );
      NeedSimvol(TS(';'));

0

8

У меня такого нету. Просто решил что оно мне не к чему так как то колдобина, то загогулина.
Если советуете то попробую.
1)  У меня за это отвечает array [] of TCaseCommand; Чисто из-за ограничений D7
2) Такого у меня нет. Окончания я передаю через Anchor либо же оно жёстко зашито в методе IsAnyAnchor.

function TParser.IsAnyAnchor: Boolean;
begin
  result:=SimvolInArray(CurrentSimvol,[Anchor, NilSimvol, EOFSimvol]);
end;

Но метод не ахти какой, так как требует переопределения постоянно.
Думаю над списком.

6)

attrib_map - асоц. массив, где ключ - символ (лексема) обозначающая атрибут а значение битовая маска соответствующая данному атрибуту.

Эта пагубная привычка Сишников делать битовыми полями.
7)
Условное вхождение со значением по умолчанию.
optional(rule: TNodeAST_Parser, default_value : TNodeAST)
Наверно красиво надо к себе утащить.

0

9

Забыл спросить все ваши функции гарантируют порядок с лева на права? Что-бы параметры в функцию верно уходили нужно что-бы они шли как в тексте с лева на право и не переворачивались.

0

10

Павиа
Основное преимущество данного подхода проявляется в обработке ошибок. В случае использования стандартных БНФ конструкций, обработка ошибок для более менее сложной грамматики превращается в сущий ад, и практически не может быть автоматизированна на должном уровне.

Так в приведенном мною примере присутствует "главный враг" для выявления ошибок - операция "Условное вхождение", внутри которой как известно ошибок не бывает. В результате ошибку тут выявит уже только следующее правило (в данном случае это ";"), сможет ли оно вразумительно, без нашего участия, пояснить пользователю в чем конкретно заключается ошибка? Например в подобной ситуации:

Код:
procedure foo[u](bar: Boolean,)[/u];
---------------^
Ошибка, ожидается ";"

Думаю от правила "NeedSimvol(TS(';'))", на котором и будет обнаружена ошибка, более точной информации об ошибке ждать и не стоит. Обычно тут применяется сбор сообщений об ошибках в стек:

Код:
procedure foo[u](bar: Boolean,)[/u];
---------------^
Ошибка в правиле "NeedSimvol(TS(';'))", ожидается ";" 
Ошибка в правиле "ParseFormalParametrs", ожидается "ParseFormalParametr" 
Ошибка в правиле "ParseFormalParametr", ожидается "Ident" 
Ошибка в правиле "Ident"

По традиции если в цепочке вызовов имелись правила Выбора, туда следует также включить и сообщения и от всех ее ветвей... В общем это тоже далеко не то сообщение об ошибке которое можно без стыда показывать пользователю.
Итог один - обработку ошибок придется делать "ручками".

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

Код:
procedure foo[u](bar: Boolean,)[/u];
---------------------------------^
Ошибка, требуется FormalParametr после разделителя ","
Код:
procedure foo[u](bar: Boolean bar2: Boolean)[/u];
---------------------------------^
Ошибка, требуется разделитель "," перед следующим FormalParametr или символ окончания списка ")"
Код:
procedure foo[u](bar: Boolean[/u];
---------------------------------^
Ошибка, требуется разделитель "," перед следующим FormalParametr или символ окончания списка ")"
Также это дает некоторые доп. возможности

Появляется возможность распознания пропущенных разделителей "," и закрывающих скобок ")". Но я бы не рискнул внедрять для подобных случаев автоматическое исправление без контроля со стороны человека. Так что это наверное неприменимо для классического "консольного" интерфейса компилятора. Но вот в тесной связке с ИДЕ это полагаю будет довольно интересная "плюшка": возможность исправить ошибку одним кликом, да к тому же не прерывая компиляцию.

Павиа написал(а):

Эта пагубная привычка Сишников делать битовыми полями.

Ну почему же сразу битовыми полями, там у меня просто "int" без этих самых новомодных битовых полей был :-)
Против битовых полей я и сам выступаю, для таких задач они не нужны.
А вообще это же лишь в качестве примера. Там может быть применен любой иной принцип на ваш выбор, ведь я буду подстраиваться под уже существующие у вас структуры АСТ.

Отредактировано рст256 (29.01.2017 01:15:00)

0

11

Павиа написал(а):

Условное вхождение со значением по умолчанию.
optional(rule: TNodeAST_Parser, default_value : TNodeAST)
Наверно красиво надо к себе утащить.

Забирайте, на здоровье.

Павиа написал(а):

Забыл спросить все ваши функции гарантируют порядок с лева на права? Что-бы параметры в функцию верно уходили нужно что-бы они шли как в тексте с лева на право и не переворачивались.

Я даже не представляю как может быть возможно иначе, только как в тексте с лева на право.
Даже при разборе кода операций с разными приоритетами, как например такой:

Код:
5+6*х

Изменится очередность вызова но никак не порядок аргументов:

Код:
операция_сложения(5, операция_умножения(6, х))

0

Быстрый ответ

Напишите ваше сообщение и нажмите «Отправить»



Вы здесь » Русские вычислители » Русский язык программирования: от слов к делу » Разделение проекта на отдельные модули