пятница, 10 июля 2015 г.

Ни о чём. Про "бесконечно" рекурсивный парсинг кода и структур данных

Сделал примерно следующее:

FILE VAR File1
FILE VAR File2
...
FILE VAR FileN
// - входные файлы

Grammar VAR G
// - грамматика
G := nil
// - начальная грамматика FORTH-машины
OBJECT VAR ParseTree
// - дерево разбора
ParseTree := ( File1 ParseWith G )
// - получаем дерево разбора По файлу File1 и грамматике G
G := ( ParseTree As Grammar )
// - получаем из дерева разбора новую грамматику
ParseTree := ( File2 ParseWith G )
// - получаем дерево разбора По файлу File2 и грамматике G
G := ( ParseTree As Grammar )
// - получаем из дерева разбора новую грамматику
...
ParseTree := ( FileN ParseWith G )
// - получаем дерево разбора По файлу FileN и грамматике G
G := ( ParseTree As Grammar )
// - получаем из дерева разбора новую грамматику

Ну и есть "итераторы":

ParseTree MembersIterator ==> ( OBJECT IN aMember ... )
ParseTree CodeIterator ==> ( OBJECT IN aCodeItem ... )

Примерно вот в таком ключе:

FORWARD DumpElement

INTEGER VAR Indent

[EXECUTE] ( Indent := 0 )

VOID WORDWORKER Out 
 STRING VAR aValue
 aValue := ( WordToWork DO )
 [ Indent ' ' char:Dupe aValue ] strings:Cat .
; // Out

VOID WORDWORKER OutEx 
 STRING VAR aValue
 aValue := ( WordToWork DO )
 if ( aValue <> '' ) then
  Out aValue
; // OutEx

VOID WORDWORKER OutA
 ARRAY VAR l_Value
 l_Value := ( WordToWork DO )
 OutEx ( l_Value strings:Cat )
; // OutA

STRING FUNCTION Check STRING IN aName
 if ( '??? Unexisting word ???' aName StartsStr ) then
  ( Result := '' )
 else
  ( Result := aName ) 
; // Check

STRING FUNCTION CodeName OBJECT IN aCode
 STRING VAR l_Name 
 aCode pop:Word:Name Check >>> l_Name
 if ( l_Name <> '' ) then
  ( 
   Result := l_Name
  )
 else 
  (
   if ( aCode Is class::TkwString ) then
    (
     Result :=  ( [ '''' aCode DO '''' ] strings:Cat )
    )
   else
   if ( aCode Is class::TkwInteger ) then
    (
     Result := ( aCode DO IntToStr )
    )
   else
    (
     Result := ''
    ) 
  )
; // CodeName

FORWARD DumpCodeCall

PROCEDURE DumpMembers OBJECT IN aCode
 aCode MembersIterator ==> ( OBJECT IN aCode
  if ( aCode Is class::TkwForwardDeclaration ) then
   OutA [ 'FORWARD ' aCode CodeName ]
 )
 aCode MembersIterator ==> ( OBJECT IN aCode
  aCode DumpElement 
 )
; // DumpMembers

PROCEDURE DumpElementPrim BOOLEAN IN aNeedStereo OBJECT IN aCode

 STRING VAR l_Name
 aCode CodeName >>> l_Name
 
 if aNeedStereo then
  ( 
   if ( l_Name = 'WordToWork' ) then
    EXIT
   if ( l_Name = 'Result' ) then
    EXIT
  )  
 
 STRING VAR l_Stereo 
 STRING VAR l_Directives
 
 aCode pop:Word:Producer pop:Word:Name Check >>> l_Stereo
 aCode pop:Word:Directives >>> l_Directives
 
 if aNeedStereo then
  (
   if ( l_Stereo <> '' ) then
    ( [ l_Stereo ' ' l_Name ] strings:Cat >>> l_Name )
   if ( ( l_Directives <> '' ) AND ( l_Directives <> ' ' ) ) then 
    ( [ l_Directives l_Name ] strings:Cat >>> l_Name )
  )  

 if ( l_Name <> '' ) then
  ( 
   OutEx l_Name
   Inc Indent
  )
 
 TRY
  aCode DumpMembers
  
  aCode CodeIterator ==> ( OBJECT IN aCode
   aCode DumpCodeCall 
  )
 FINALLY
  if ( l_Name <> '' ) then
   ( 
    Dec Indent
   )
 END 
 if ( l_Name <> '' ) then
  if aNeedStereo then
  (
   STRING VAR l_Bracket
   l_Bracket := ( aCode pop:Word:Producer pop:Word:EndBracket )
   if ( l_Bracket <> '' ) then
    ( 
     OutA [ l_Bracket ' // ' l_Name ]
     Out ''
    )
  )   
; // DumpElementPrim

PROCEDURE OutBracket OBJECT IN aCode
 if ( aCode CodeName = 'TRY' ) then
  (
   EXIT
  )
 STRING VAR l_Bracket
 l_Bracket := ( aCode pop:Word:Producer pop:Word:EndBracket )
 //l_Bracket := ( aCode pop:Word:EndBracket )
 if ( l_Bracket <> '' ) then
  OutEx l_Bracket
; // OutBracket

PROCEDURE DumpCodeCall OBJECT IN aCode
 if ( aCode Is class::TkwCompiledInitParam ) then
  (
   EXIT
  )
  
 STRING VAR l_Name
 BOOLEAN VAR l_NeedBracket
 l_NeedBracket := false
 aCode CodeName >>> l_Name 
 
 INTEGER VAR l_LeftWordRefsCount
 l_LeftWordRefsCount := ( aCode pop:Word:LeftWordRefValuesCount )
 while ( l_LeftWordRefsCount > 0 )
  ( 
   Dec l_LeftWordRefsCount
   
   OBJECT VAR l_Param
   
   l_Param := ( l_LeftWordRefsCount aCode pop:Word:GetLeftWordRefValue )
   if NOT ( l_Param pop:object:IsNil ) then
    ( 
     [ 
      l_Param CodeName 
      ' ' 
      l_Param CodeIterator ==> ( OBJECT IN aCode
       aCode CodeName
      )
      l_Name 
     ] strings:Cat >>> l_Name 
    )
  ) 
  
 OutEx l_Name
  
 //Inc Indent
 TRY 
  RULES 
   ( aCode Is class::TkwCompiledWordContainer )
    ( 
      aCode CodeIterator ==> ( OBJECT IN aCode
       false aCode DumpElementPrim
       aCode OutBracket
      ) 
    )
   
   ( aCode Is class::TkwBeginLikeCompiledCode )
    (
      l_NeedBracket := true
      Inc Indent
      aCode DumpMembers
      aCode CodeIterator ==> ( OBJECT IN aCode
       aCode DumpCodeCall
      )
      Dec Indent
    )
    
   ( aCode Is class::TkwCompiledWordWorkerWordRunner )
    (
      aCode CodeIterator ==> ( OBJECT IN aCode
       aCode DumpCodeCall
      )
    )
   
   ( 
    ( aCode Is class::TkwCompiledVarWorker )
//    OR
//    ( aCode Is class::TkwVarModifier )
   ) 
    (
      Inc Indent
      aCode CodeIterator ==> ( OBJECT IN aCode
       aCode DumpCodeCall
      ) 
      Dec Indent 
    )
   
   ( aCode Is class::TkwCompiledWordWorker )
    (
     aCode CodeIterator ==> DumpCodeCall
    )
    
   ( aCode Is class::TkwForwardDeclarationHolder )
    (
     aCode CodeIterator ==> DumpCodeCall
    )
    
   ( aCode Is class::TkwCompiledRules 
     OR
     ( aCode Is class::TkwCompiledCase )
   )
    (
     l_NeedBracket := true
     BOOLEAN VAR l_Shift
     l_Shift := false
     Inc Indent
     aCode CodeIterator ==> ( OBJECT IN aCode
      l_Shift ? Inc Indent
       aCode DumpCodeCall
      l_Shift ? Dec Indent
      l_Shift := NOT l_Shift
     ) 
     Dec Indent
    )
  ; // RULES 
 FINALLY
  //Dec Indent
 END 
  
  
 if l_NeedBracket then 
  ( aCode OutBracket )
; // DumpCodeCall

PROCEDURE DumpElement OBJECT IN aCode
 true aCode DumpElementPrim
; // DumpElement

PROCEDURE DumpCode STRING IN aFileName
 aFileName script:CompileAndProcess DumpElement
 '' .
; 

https://bitbucket.org/lulinalex/mindstream/commits/51387b73dd34d9359bb8dc86f86fd2d01e198a00

Как-то так...
Практические примеры - позже.

Зачем нужно? Чтобы грузить "данные" превращать их в "код" и по этому коду грузить НОВЫЕ данные.

Вот есть мета-мета-модель UML. При помощи неё грузим мета-модель UML.

А при помощи мета-модели грузим модель UML.

А из модели UML получаем код на целевом языке.

Как-то так...

Да. Дерево разбора это байт-код стековой машины. Который вообще говоря умеет исполняться в соответствии с той грамматикой, которая его построила.

Парсинг и выполнение кода выглядит так:

ParseTree := ( File1 ParseWith G )
ParseTree DO

Ну и есть "ручки сбоку" для залезания в "кишки кода" - MembersIterator и CodeIterator (см. выше).

При этом код знает про грамматику, которая его породила.

Если написать так:

ParseTree := ( File1 ParseWith [ G1 G2 .. GN ] )

То будут последовательно применены N грамматик.

Базовая грамматика выглядит примерно так:

// Тут описываем слова, необходимые для построения дерева разбора модели MDA вылитой в формат скрипта

StereotypeStereotypeProducer @MDAClass ;
// - базовый стереотип, через который всё выводится

<<@MDAClass>> MDAClass ;
<<@MDAClass>> MDACategory ;
// - заглушки для начальной раскрутки

<<MDACategory>> Project ;
// - проект

<<@MDAClass>> MDAAbstractClass ;
<<@MDAClass>> MDADependency ;
<<@MDAClass>> MDAAttribute ;
//StereotypeProducer MDAStateMachine
//StereotypeProducer MDAState
//StereotypeProducer MDATransition
//StereotypeProducer MDATransitionAttribute
//StereotypeProducer MDAStateAction
// - а эти парни вообще выводятся на модели из первых двух
<<@MDAClass>> MDAOperation ;
// - стереотип для создания типов операций

<<MDACategory>> MDATemplates ;
<<MDACategory>> MDALibrary ;
// - MDA-библиотека
<<MDACategory>> MDALayer ;
<<MDAClass>> MDAUtilityPack ;
// - набор утилитных функций MDA

<<MDAClass>> MDAGenerator ;

StereotypeProducer mdalink

<<mdalink>> group
;
<<mdalink>> include
;
<<mdalink>> main_hierarchy
;

<<MDAOperation>> generator
;
// - генератор элементов модели
<<MDAOperation>> transformator
;
// - трансформатор (аналог CASE в mda-генераторе)
<<MDAOperation>> Operation
;
// - операция (любая, что MDA, что нет - любая операция без стереотипа)

<<@MDAClass>> MDAParameter
;
// - стереотип параметра

//WordAlias <<param>> <<MDAParameter>>
//<<MDAParameter>> param ;
StereotypeProducer param
<<param>> in
;
// - входной параметра (изменяться не может)

StereotypeProducer up
// - стереотип для типов пользовательских свойств

<<up>> bool 
;
// - булево значение UP
<<up>> string 
;
// - строковое значение UP
<<up>> list 
;
// - значение UP из заранее заданного списка значений
<<up>> color 
;
<<up>> tribool 
;
// - трёхпозиционное значение UP undefined/false/true
<<up>> text 
;

<<MDAClass>> MDAReport ;
<<MDAOperation>> report
;

А она в свою очередь выводится из аксиоматики стековой машины - Ни о чём. Обновление аксиоматики.

Тесты вида - Только код. Примеры "высокоуровневых" тестов - оттуда же выводятся.

Весь вопрос только в словарях и грамматиках.

+Виктор Морозов
+Михаил Костицын

Примеры того же, но из "прошлой жизни", без грамматик:

USES
 'W:\shared\models\NewSchool\MDProcess.root.script'
;

USES
 'W:\shared\models\NewSchool\Templates\MDALike.tpl.script'
;

: Gen

//help
WordAliasByRef X_MDAClass :: UIDS_LIST 46E1540F00AB ;
WordAliasByRef X_MDALibrary :: UIDS_LIST 46E155F80242 ;
//help

PROCEDURE OUT IN %S
 //%S |N .
 %SN .
 //%S |S .
 %SS .
 %S %ST %ST |N .
 %S %ST %ST %ST |N .
 %S %ST %ST %ST %ST |N .
 //%S |U .
 %SU .
;

PROCEDURE X IN %S
 %S OUT
 //%S %P OUT
 %S%P OUT
 //%S %P %P OUT
 %S%P%P OUT
 //%S %P %P %P OUT
 %S%P%P%P OUT
 //%S %P %P %P %P OUT
 %S%P%P%P%P OUT
 //%S %P %P %P %P %P OUT
 %S%P%P%P%P%P OUT
;

@ <<MDAClass>> %R IT X
@ <<MDALibrary>> %R IT X
@ X_MDAClass %R IT X
@ X_MDALibrary %R IT X

@ <<MDAClass>> %G IT X
@ <<MDALibrary>> %G IT X

'' .

ARRAY VAR A
@ X_MDAClass %R >>> A
//[ :: X_MDAClass %R ; ] >>> A
@ X A ITERATE

'' .

@ X_MDALibrary %R >>> A
//[ :: X_MDALibrary %R ; ] >>> A
@ X A ITERATE

//ARRAY VAR A
//[ <<MDAClass>> %G ] >>> A

;

Gen

...

USES
 'W:\shared\models\NewSchool\MDProcess.root.script'
;

USES
 'W:\shared\models\NewSchool\Templates\MDALike.tpl.script'
;

: Gen
 
 : OUT IN %S
  %SN .
  %SS .
 ;
 
 @ MDProcess$Templates |S .
 @ MDProcess$Templates MembersIterator ==> OUT
 
 @ MDProcess$ForDelphi |S .
 @ MDProcess$ForDelphi MembersIterator ==> OUT
 
 @ MDProcess$ForF1 |S .
 @ MDProcess$ForF1 MembersIterator ==> OUT
;

Gen

...

USES
 'W:\shared\models\NewSchool\Templates\MDALike.tpl.script'
;

 PROCEDURE OUTPRIM IN %S
  %S NotValid ? EXIT
  %SN .
  %SS .
  %S %ST %ST |N .
 ;
 
 FORWARD OUTSAFE
 
 PROCEDURE OUTSAFE IN %S
  %S NotValid ? EXIT
  %S OUTPRIM
  for %S%G ( 
   IN %S 
   %S OUTPRIM 
   if ( %S NotValid ! ) then 
    ( for %S%G OUTSAFE ) 
  )
 ;
 
 FORWARD OUT
 
 PROCEDURE OUT IN %S
  %S NotValid ? EXIT
  %S OUTPRIM
  for %S%G ( 
   IN %S 
   %S OUTPRIM 
  )
  for %S%Attrs OUT
  for %S%Ops OUT
  for %S%C OUT
  %S%T OUTSAFE
 ; 

USES
 'W:\shared\models\NewSchool\SharedDelphi.root.script'
;

/*{USES
 'W:\shared\models\NewSchool\SharedDelphiOperations.root.script'
;

USES
 'W:\shared\models\NewSchool\Shared Delphi Scripting\Shared Delphi Scripting.model.script'
; 

USES
 'W:\shared\models\NewSchool\SharedDelphiTests.root.script'
;}*/

Test Gen
 
 @ "Shared Delphi" OUT
// @ "Shared Delphi Scripting" OUT
// @ "Shared Delphi Operations" OUT
// @ "Shared Delphi Tests" OUT
;

Gen

Коммит:

https://bitbucket.org/lulinalex/mindstream/commits/251e79385cd307a87fad5ad851104c5814080f35

Вот тут - 'W:\shared\models\NewSchool\MDProcess.root.script' - данные.
Вот тут - 'W:\shared\models\NewSchool\Templates\MDALike.tpl.script' - грамматика.

Грамматики "без грамматик".

for %S%Attrs OUT - итератор по атрибутам.
%S - self - сам элемент.
%P или %S%P - родитель.
%P%P или %S%P%P - родитель родителя.

Жаль, что многое в "закорючках", но это наследие прошлого.

Я работаю над этим.

Что такое "грамматика" - это описание "скобочных структур" в терминах других "скобочных" структур.

Ну как DTD и XML + XSLT.

Скажем так:

DTD - описание мета-модели.
XML - модель.
XSLT - способ преобразования одной модели в другую.

Только у меня ещё можно позвать так:

%S %Meta %f method - вызов метода мета-класса.

И даже так:

%S %Meta %Meta %f method - вызов метода мета-мета-класса.

И даже так:

%S %Meta %Meta %Meta %f method - вызов метода мета-мета-мета-класса.

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

Например:

%S %Meta %f IsClass - узнаём, что %S - является классом (в терминах UML).
%S %Meta %f IsCategory - узнаём, что %S - является категорией (в терминах UML).

или:

%S %Meta %Meta %f IsGrammar UML - узнаём, что %S - относится к грамматике UML.
%S %Meta %Meta %f IsGrammar Test - узнаём, что %S - относится к грамматике тестов.
%S %Meta %Meta %f IsGrammar FORTH - узнаём, что %S - относится к грамматике FORTH-машины.

или:

%S %f GeneratesTo C++ - узнаём, что %S - генерируется в C++.
%S %f GeneratesTo Delphi - узнаём, что %S - генерируется в Delphi.

И т.д. и т.п.

А можно пристегнуть "способы работы" - "сбоку". Обрабатывая итераторы MembersIterator и CodeIterator.

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

Возможно вызывая методы мета и мета-мета классов.

Это всё дерево деревьев деревьев разбора. Где каждое предыдущее дерево описывает последующее и имеет с ним двунаправленные связи. И "данные" и "код".

Начинаем от мета-UML и приходим к Delphi. Это к примеру.

Вдогонку:

"есть базовая FORTH аксиоматика
из неё выводим FORTH1
потом FORTH2
потом GUI-Тесты
или RTF-парсер
или генератор в Delphi
это понятно?
FORTH -> FORTH1 -> FORTH2 -> RTF
примерно так
грамматику на каждом этапе подтачиваем под следующий
грамматика мимикрирует"

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

Ну и напоследок.

Я надеюсь, что товарищ не обидится на цитату:
""И наконец термин «псевдовектор» обычно объясняется студентам так: «Посмотрите, ведь мы показали, что омега — скалярная величина. А вектор мы вводим для того, чтобы выписать формулу Эйлера».""

Но как-то - да.

Мне всегда нравилась "линейная алгебра" и "векторные пространства".

Особенно - евклидово и метрики со скалярным произведением.

Комментариев нет:

Отправить комментарий