Toon posts:

[COMPILER] Van parse tree naar syntax tree

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben bezig een compiler te schrijven voor een simpele taal in C#. De taal bestaat uit expressies en wat input/ouput statements:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{String Char} = {Printable} - ["]

ID             = {Letter}{Alphanumeric}*
IntegerLiteral = {Digit}+
StringLiteral  = '"' {String Char}* '"'

"Start Symbol" = <Program>


<Program>    ::= start <Statements> end

<Statements> ::= <Statement> <Statements>
               | <Statement>

<Statement> ::= <Declarations> ;
              | <Assignment> ;
              | read '(' ID ')' ;
              | write '(' <Expression> ')' ;

<Declarations> ::= <Declaration>, <Declarations>
                 | <Declaration>

<Declaration>  ::= <Var Declare>

<Var Declare>  ::= <Type> ID

! ===============================
!  Variabelen
! ===============================
<Assignment>   ::= ID ':=' <Expression>

<Type>         ::= int
                 | string


! ===============================
!  Operator precedence
! ===============================

<Expression> ::= <Add Exp>

<Add Exp>    ::= <Mult Exp> '+' <Add Exp>
               | <Mult Exp> '-' <Add Exp>
               | <Mult Exp>

<Mult Exp>   ::= <Negate Exp> '*' <Mult Exp>
               | <Negate Exp> '/' <Mult Exp>
               | <Negate Exp>

<Negate Exp> ::= '-' <Value>
               | <Value>

<Value>      ::= ID
               | IntegerLiteral
               | StringLiteral
               | '(' <Expression> ')'

Als parser gebruik ik goldparser om mijn source te parsen aangezien dit de enigste parser is die naar mijn weten ondersteuning bied voor .NET :( . De meeste parsers genereren meteen uit de input een syntax tree maar de goldparser genereert alleen een parse tree. Nu moet ik dus zelf code kloppen om de parse tree om te zetten naar een AST (hoe meer code, hoe meer vreugde toch? :Y) ).
Nu is mijn vraag: Hoe kan ik het beste dit aanpakken?
Zelf zit ik de denken aan een visitor waarmee ik de parse tree ga 'traversen'. Deze wil ik een factory meegeven die een stukje parse tree als argument neemt, en dit omzet naar een AST node. Nu zit ik nog met de structuur van de AST. Ik moet zowieso een root node hebben die als base dient voor anderen. Maar welke klassen heb ik nog meer nodig? Moet ik bijv. een Statement klasse maken die als base classes weer ReadStatement, WriteStatement, etc. heeft?

Verwijderd

http://www.antlr.org/ heeft een C# code generator, daar zou je ook eens naar kunnen kijken :)

Verwijderd

Topicstarter
ANTLR ben ik idd ook tegengekomen maar het zag er op het eerste gezicht wat complex uit. Wat ik ook niet echt super vind is het feit dat je de AST generatie code in de grammatica beschrijving moet opgeven.

Verwijderd

Ik ben ooit begonnen om SableCC zo om te bouwen dat hij C# code genereerd, maar dat heb ik nooit afgemaakt.

Verwijderd

Topicstarter
Klinkt interessant. Ben je ergens op vastgelopen of heb je het gewoon even in de koelkast gezet :) ?
Er staat trouwens niet veel info over die ANTLR C# code generator. Ik zal eens even op google zoeken.

Verwijderd

Topicstarter
Ziet er goed uit. Ik zal het even goed doorlezen.

Verwijderd

Verwijderd schreef op 17 augustus 2002 @ 17:16:
Klinkt interessant. Ben je ergens op vastgelopen of heb je het gewoon even in de koelkast gezet :) ?
In de koelkast, op zich moet het niet verschrikkelijk moeilijk zijn, vrijwel alles (behalve de .cs extentie) staat in templates, die hoef je alleen aan te passen (duurde ook even voor ik dat doorhad ;) )
Er staat trouwens niet veel info over die ANTLR C# code generator. Ik zal eens even op google zoeken.
Ik neem aan dat hij hetzelfde werkt als ANTLR standaard maar dat de uitvoer dus C# code is.

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Als parser gebruik ik goldparser om mijn source te parsen aangezien dit de enigste parser is die naar mijn weten ondersteuning bied voor .NET
cewl :) die .NET module heb ik geschreven 8)
Nu is mijn vraag: Hoe kan ik het beste dit aanpakken?
ik weet niet of het de beste manier is (zal wel niet), maar zelf doe ik het zo:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
while (!done && !fatal)
{                               
    ParseMessage result = m_parser.Parse();
                    
    switch (result)
    {
    case ParseMessage.Reduction:
        HandleReduction(m_parser.CurrentReduction);
        break;
    case ParseMessage.Accept:
        // program accepted
        done = true;
        break;
    // ... (foutafhandeling)
    }
    
    errors = (fatal ? errors + 1 : errors);
    fatal = fatal || (errors > 7);
}

private void HandleReduction(Reduction p_reduction)
{
    String str = "";
    Console.WriteLine(p_reduction);

    switch ((RuleConstants)p_reduction.RuleIndex)
    {
    /* function declaration */
    case RuleConstants.FunDecl:
        PushFunction();
        break;
    case RuleConstants.Param:
        PushVariable();
        break;
    /* statements */
    case RuleConstants.StmtListStart:
        PushCodeBlock(true);
        break;
    case RuleConstants.StmtList:
        PushCodeBlock(false);
        break;
    case RuleConstants.VarDecl:
        PushDeclaration();
        break;
    case RuleConstants.Assign:
        PushAssignment();
        break;
    case RuleConstants.IfStmt:
        PushIfStatement(false);
        break;
    case RuleConstants.ReturnStmt:
        PushReturnStatement();
        break;
    /* expressions */
    case RuleConstants.IntLiteral:
        str = (String)p_reduction.GetToken(0).Data;
        PushIntLiteral(str);
        break;
    case RuleConstants.Ident:
        str = (String)p_reduction.GetToken(0).Data;
        PushIdentifier(str);
        break;
    case RuleConstants.IdentList:
        PushIdentList(false);
        break;
    case RuleConstants.IdentListEnd:
        PushIdentList(true);
        break;
    /* types */
    case RuleConstants.TypeVoid:
        PushStandardType(StdType.Void);
        break;
    case RuleConstants.TypeInt:
        PushStandardType(StdType.Int);
        break;
//  ...
    }
}
        
private void PushFunction()
{
    CodeBlock code = (CodeBlock)m_stack.Pop();
    String name = ((Identifier)m_stack.Pop()).Name;
    IType type = (IType)m_stack.Pop();
    m_stack.Push(new Function(name, type, code));
}
        
private void PushVariable()
{
    Identifier id = (Identifier)m_stack.Pop();
    IType type = (IType)m_stack.Pop();
    m_stack.Push(new Variable(id.Name, type));
}

// etc

(vrij veel typwerk ;))

Verwijderd

Topicstarter
Voor ANTLR heb je dus JAVA nodig. Hier heb ik echter nog geen ervaring mee.
Ik denk dat ik eerst zelf een oplossing ga proberen te vinden. Het stukje over treecc ziet er ook goed uit. Staan ook wel leuke opmerkingen over het visitor pattern in ('Design patterns aren't always what they are cracked up to be'). Hier gaan ze in op de nadelen van de visitor. Ik vroege ontwikkeling van de compiler ben je regelmatig bezig met het veranderen van de tree. In dit geval is een visitor een 'maintaince nightmare' :) .

Verwijderd

Topicstarter
marcusk schreef op 17 augustus 2002 @ 17:34:
[...]

cewl :) die .NET module heb ik geschreven 8)
Heb je deze vanuit Java geport? In ieder geval wel handig. Scheelt mij weer een boel typewerk :) .
ik weet niet of het de beste manier is (zal wel niet), maar zelf doe ik het zo:

Stuk code
Dit lijkt wel wat op wat ik in gedachten had. Ik ga echter al die functies (PushFunction, PushCodeBlock, etc.) in een visitor onderbrengen. Zo hou je al je analyses van tree apart. Zo kan ik later bijv. een visitor schrijven die de tree doorloopt en code genereert. Volgens mij was hiervoor al een interface gecode (IGoldVisitor).
Waar de grote truuk in zit, wat doe je met die stack? Zet je deze weer om naar een AST?
edit:
Het is quote, niet qoute :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

ANTLR gebruikt een LL(k) grammatica en SableCC gebruikt een LALR(1) grammatica. Het voordeel aan een LR grammatica is dat je veel minder last hebt van ambiguiteiten zoals je dat hebt wel hebt met LL grammatica. Daar zul je mbv een syntactisch of semantisch predikaat de juiste alternatief moeten kiezen en door die predicaten gaat je grammatica naar zijn grootje.

Ik heb met uitzonderlijk veel plezier ANTLR verwijderd uit het system en vervangen door SableCC. Zoals Zef al zegt genereerd SableCC dus wel een AST met ingebouwde visitor en een aantal guide`s en het werkt echt geweldig.

Ik zou dus niets doen met ANTLR en kijken of je een LR parser generator kan vinden voor C# en mbv semantische acties die zelf geschreven AST gaan vullen.

En misschien komt er in de toekomst nog wel een c# versie voor SableCC. Maar ik neem aan dat je niet zo lang wilt wachten.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Verder kan je trouwens heel aardig je object ontwerp laten volgen uit je grammatica. Als bv de volgende productie hebt:
code:
1
2
3
4
persoon_declr
     = timmerman_declr
     | slager_declr
     ;

Dan lijkt het me vrij duidelijk hoe je je object model kan maken. Je maakt bv een interface voor de productie naam, en voor ieder alternatief maak je dus een object die die interface implementeerd en referentie naar alle terminal en non terminals.
En verder heeft iedere non terminal aan de rhs weer een referentie in zich naar de productie die is uitgevoerd. En voor tokens kan je iets vergelijkbaars doen (vaak is het wel dezelfde grammatica als je een scannerless parser hebt).

Anders moet je even kijken naar SableCC en wat voor code die geneerd. Verder staat er op de site een uitstekende handleiding.

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Verwijderd schreef op 17 augustus 2002 @ 17:58:
Heb je deze vanuit Java geport? In ieder geval wel handig. Scheelt mij weer een boel typewerk :) .
De Java versie was toen nog niet open-source, dus het is min of meer een port van de VB-versie (heb vrij veel aangepast :))
Dit lijkt wel wat op wat ik in gedachten had. Ik ga echter al die functies (PushFunction, PushCodeBlock, etc.) in een visitor onderbrengen. Zo hou je al je analyses van tree apart. Zo kan ik later bijv. een visitor schrijven die de tree doorloopt en code genereert. Volgens mij was hiervoor al een interface gecode (IGoldVisitor).
Die IGoldVisitor (die ik overigens heb bedacht) heeft achteraf gezien eigenlijk weinig nut omdat het alleen een visitor-methode voor het Reduction object hebt. Dan zul je dus alsnog een switch moeten gebruiken voor de verschillende RuleIndex-en.
Waar de grote truuk in zit, wat doe je met die stack? Zet je deze weer om naar een AST?
Na het parsen staan er alleen nog Function objecten op de stack. Daarvan wordt dan de methode aangeroepen om de IL code te genereren.

Verwijderd

Topicstarter
Alarmnummer schreef op 17 augustus 2002 @ 17:59:
ANTLR gebruikt een LL(k) grammatica en SableCC gebruikt een LALR(1) grammatica. Het voordeel aan een LR grammatica is dat je veel minder last hebt van ambiguiteiten zoals je dat hebt wel hebt met LL grammatica. Daar zul je mbv een syntactisch of semantisch predikaat de juiste alternatief moeten kiezen en door die predicaten gaat je grammatica naar zijn grootje.
GoldParser gebruikt ook LALR(1). Het automatisch creeren van een AST ontbreekt echter. Ook wordt EBNF niet ondersteund waardoor je van die diep geneste trees krijgt.
Ik heb met uitzonderlijk veel plezier ANTLR verwijderd uit het system en vervangen door SableCC. Zoals Zef al zegt genereerd SableCC dus wel een AST met ingebouwde visitor en een aantal guide`s en het werkt echt geweldig.
Kun je misschien een voorbeeldje geven van een AST die sablecc produceert? Ik denk dat ik dan wel weer verder kan. Waar in echt tegenaan loop is het feit dat ik niet weet welke nodes ik nodig heb, en hoe deze nodes informatie over elkaar opslaan.
Ik zou dus niets doen met ANTLR en kijken of je een LR parser generator kan vinden voor C# en mbv semantische acties die zelf geschreven AST gaan vullen.

En misschien komt er in de toekomst nog wel een c# versie voor SableCC. Maar ik neem aan dat je niet zo lang wilt wachten.
De enige optie is volgens mij nu nog goldparser. Op zich vind ik het ook niet erg om zelf een AST te bouwen. Ik vind deze materie nl wel interessant.
offtopic:
Ik wacht nog steeds op 'Modern Compiler Design' van Dick Grune.

Verwijderd

Topicstarter
marcusk schreef op 17 augustus 2002 @ 18:08:

Die IGoldVisitor (die ik overigens heb bedacht) heeft achteraf gezien eigenlijk weinig nut omdat het alleen een visitor-methode voor het Reduction object hebt. Dan zul je dus alsnog een switch moeten gebruiken voor de verschillende RuleIndex-en.

[...]
Ik heb hem daarom ook uitgebreid met extra 'Visit' methoden.
Na het parsen staan er alleen nog Function objecten op de stack. Daarvan wordt dan de methode aangeroepen om de IL code te genereren.
Aha, wat jouw code doet is dus het genereren van CodeDOM objecten. Deze zet je dan om naar IL. Wat ik echter wil is een eigen taal maken (de compiler wordt dus in c# geschreven), compleet met semantics, codegeneratie etc.

[ Voor 0% gewijzigd door Verwijderd op 17-08-2002 18:25 . Reden: Typo's ]


Verwijderd

Topicstarter
Alarmnummer schreef op 17 augustus 2002 @ 18:07:
Verder kan je trouwens heel aardig je object ontwerp laten volgen uit je grammatica. Als bv de volgende productie hebt:
code:
1
2
3
4
persoon_declr
     = timmerman_declr
     | slager_declr
     ;

Dan lijkt het me vrij duidelijk hoe je je object model kan maken. Je maakt bv een interface voor de productie naam, en voor ieder alternatief maak je dus een object die die interface implementeerd en referentie naar alle terminal en non terminals.
En verder heeft iedere non terminal aan de rhs weer een referentie in zich naar de productie die is uitgevoerd. En voor tokens kan je iets vergelijkbaars doen (vaak is het wel dezelfde grammatica als je een scannerless parser hebt).
Dus in mijn geval krijg ik dus een ProgramNode, StatementNode, WriteStatementNode, etc.
Anders moet je even kijken naar SableCC en wat voor code die geneerd. Verder staat er op de site een uitstekende handleiding.
Hmm, hier heb ik dus ook Java voor nodig. Moet het toch maar eens downloaden dan (heb 56k dus ik ben wat conservatief ;) ).

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Verwijderd schreef op 17 augustus 2002 @ 18:18:
Ik heb hem daarom ook uitgebreid met extra 'Visit' methoden.
Visit metoden voor welke objecten dan? :?
Aha, wat jou code doet is dus het genereren van CodeDOM objecten. Deze zet je dan om naar IL.
Zoiets is de bedoeling idd, maar daar ben ik nog niet erg ver mee (project staat even in de koelkast :))
Wat ik echter wil is een eigen taal maken (de compiler wordt dus in c# geschreven), compleet met semantics, codegeneratie etc.
Jep, ik maak ook een eigen (simpele) taal. Wat is (volgens jou) het verschil met hoe ik het doe dan? :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

marcusk schreef op 17 augustus 2002 @ 18:26:
Visit metoden voor welke objecten dan? :?
Als een visitor bij een visitable op bezoek wil zal bij de visitable wel een 'accepts visitor' methode geimplementeerd worden met als argument de visitor die op bezoek gaat.

Verwijderd

Topicstarter
marcusk schreef op 17 augustus 2002 @ 18:26:
[...]
Visit metoden voor welke objecten dan? :?

[...]
Zoiets is de bedoeling idd, maar daar ben ik nog niet erg ver mee (project staat even in de koelkast :))

[...]
Jep, ik maak ook een eigen (simpele) taal. Wat is (volgens jou) het verschil met hoe ik het doe dan? :)
Als ik het goed heb dan wordt jouw taal uiteindelijk omgezet in IL bytecode. Je genereert uit de source dus eerst C# code (in DOM vorm dan), en laat de c# compiler hier overheen gaan met als resultaat een .NET assembly. Ik wil dus een 'eigen DOM' maken + compiler (misschien wat te hoog gegrepen maar we zien wel waar het schip strandt ;) ).

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Verwijderd schreef op 17 augustus 2002 @ 18:34:
Als ik het goed heb dan wordt jouw taal uiteindelijk omgezet in IL bytecode. Je genereert uit de source dus eerst C# code (in DOM vorm dan), en laat de c# compiler hier overheen gaan met als resultaat een .NET assembly. Ik wil dus een 'eigen DOM' maken + compiler.
Aha, ik begrijp het. Jij wilt dus Reflection.Emit gebruiken neem ik aan?

Overigens maak ik de compiler als voorbeeld voor GoldParser, vandaar dat ik de boel simpel houdt :) Er waren namelijk nogal wat mensen die me mailden voor een beter voorbeeld voor het gebruik van GP.Net (een parse tree printen is niet bepaald een nuttige toepassing ;)).
(misschien wat te hoog gegrepen maar we zien wel waar het schip strandt ;) )
Succes iig :)

Verwijderd

Ik vind SableCC echt een heerlijke parser generator. Juist omdat hij AST genereerd en de daarbij horende tree walkers. Als je de tijd hebt en een beetje java kan lezen (lijkt erg op C#) dan zou je veel mensen blij maken met een C# emitter ;) maar misschien is dit wel te veel werk. Mocht je hier voor voelen kun je natuurlijk het deel dat ik al gedaan heb krijgen :)

Verwijderd

Topicstarter
marcusk schreef op 17 augustus 2002 @ 19:00:
[...]
Aha, ik begrijp het. Jij wilt dus Reflection.Emit gebruiken neem ik aan?
Nou, eigenlijk niet :) . De enigste baseclasses die ik nodig heb zijn die in System en System.Collections. De taak die nu iig voor me ligt is het schrijven van alle klassen van de AST, en de procedures die de Reductions en Tokens omzet in de juiste klassen. Uiteindelijk krijg je dan een boomstructuur met bovenaan bijv. ProgramNode. Deze bevat dan weer een aantal StamentNode's, die op zijn beurt als child's bijv. een VariableDeclarationNode heeft.
Wanneer dit karwei af is komt er een visitor bij die deze boom doorloopt, en een Win32 EXE genereert (waarschijnlijk wordt het eerst interpreted) en later wil ik nog een VM er voor bouwen :7 .
Overigens maak ik de compiler als voorbeeld voor GoldParser, vandaar dat ik de boel simpel houdt :) Er waren namelijk nogal wat mensen die me mailden voor een beter voorbeeld voor het gebruik van GP.Net (een parse tree printen is niet bepaald een nuttige toepassing ;)).
Hmm, misschien dat ik je hierbij kan helpen. Wanneer ik iets concreets heb kun je het misschien wel gebruiken (ik heb nogal eens het probleem dat projecten vaak een tijdje in de koelkast gaan wanneer ik tegen frustrerende details aanloop :) ).
Succes iig :)
Thx! Ik zal eerst even de AST's die SableCC genereert bekijken (beter goed gejat dan slecht bedacht ;) ).

Verwijderd

Topicstarter
Ik zal iig kijken wat ik allemaal met SableCC kan. Als ik de reply's zo lees dan lijkt het me dat dit de beste optie is wanneer ik ergens op stuk loop.

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Verwijderd schreef op 17 augustus 2002 @ 19:18:
Nou, eigenlijk niet :) . De enigste baseclasses die ik nodig heb zijn die in System en System.Collections. De taak die nu iig voor me ligt is het schrijven van alle klassen van de AST, en de procedures die de Reductions en Tokens omzet in de juiste klassen. Uiteindelijk krijg je dan een boomstructuur met bovenaan bijv. ProgramNode. Deze bevat dan weer een aantal StamentNode's, die op zijn beurt als child's bijv. een VariableDeclarationNode heeft.
Wanneer dit karwei af is komt er een visitor bij die deze boom doorloopt, en een Win32 EXE genereert (waarschijnlijk wordt het eerst interpreted) en later wil ik nog een VM er voor bouwen :7 .
Oh hehe, op die manier :) Dat gaat best veel werk worden schat ik ;)
Hmm, misschien dat ik je hierbij kan helpen. Wanneer ik iets concreets heb kun je het misschien wel gebruiken (ik heb nogal eens het probleem dat projecten vaak een tijdje in de koelkast gaan wanneer ik tegen frustrerende details aanloop :) ).
Bedankt voor het aanbod, maar ik maak et liever zelf :) (In dit geval was ik even gestopt omdat ik het nogal druk had met m'n studie (dat houdt me van nuttige dingen af ;)), niet omdat het niet wilde lukken.)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Hum, het schrijven van deze transformatie is in principe nogal vervelend (en dom) werk. Dat roept dus om code-generatie ;) .

Waarom neem je niet even de extra moeite en schrijf je (waarschijnlijk als uitbreiding op de GoldParser) niet een stuk code die AST klassen genereert en een transformatie van de parse-tree naar deze AST klassen? Het lijkt misschien erg ingewikkeld (en het is ook niet helemaal triviaal), maar het kan wel een hele leuke oefening zijn en een hele nuttige bijdrage.

Als je je grammatica flink gaat uitbreiden verdien je je investering vrij snel terug: opnieuw code genereren kost een paar seconde, de transformatie handmatig aanpassen kan een grotere (en vervelende) klus worden...

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
Dit lijkt me idd wel een interessant plan. Ik heb even op de GoldParser site rondgekeken en het blijkt dat er ook al iets dergelijks in C++ is geschreven is waarvan de source beschikbaar is. Ik denk dat ik dit even ga bestuderen om te kijken of dit bruikbaar is voor een C# versie.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Verwijderd schreef op 17 augustus 2002 @ 17:41: ('Design patterns aren't always what they are cracked up to be'). Hier gaan ze in op de nadelen van de visitor. Ik vroege ontwikkeling van de compiler ben je regelmatig bezig met het veranderen van de tree. In dit geval is een visitor een 'maintaince nightmare' :) .
Niet als je de (templated) visitor gebruikt die Alexandrescu beschrijft in z'n boek. Sorry moest het even zeggen, ben nl. DP junk :P

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Visitor interfaces kunnen sowieso ook gegenereerd worden door een parser-generator. Sommige parser-generators doen dit dus ook en genereren daarbij ook nog allemaal helpers zoals een Visitor implementatie voor generieke visits, een Visitor implementatie met lege visits (vergelijk met de adapters in de Java event API), verschillende traversal-orders enz enz. Als je al echt serieuze code hebt geschreven in de vorm van visitors, zal je die uiteraard moeten aanpassen.

Bij het Visitor pattern zijn methoden die samen een bepaalde operatie implementeren, gegroepeerd in 1 klasse en dus niet verspreid over de object-structuur. Hierdoor is het makkelijk om een nieuwe operatie toe te voegen (in de vorm van een visitor), maar het is minder makkelijk als de boom-structuur verandert.

Het Visitor pattern komt op dit punt sterk overeen met de aanpak in functioneel programmeren, waar het toevoegen van een nieuwe functie over een data-structuur ook erg eenvoudig is, maar een aanpassing van de data-structuur erg vervelend kan zijn....

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Verwijderd schreef op 17 augustus 2002 @ 17:41:
Staan ook wel leuke opmerkingen over het visitor pattern in ('Design patterns aren't always what they are cracked up to be'). Hier gaan ze in op de nadelen van de visitor. Ik vroege ontwikkeling van de compiler ben je regelmatig bezig met het veranderen van de tree. In dit geval is een visitor een 'maintaince nightmare' :) .
Daarom is code generatie ook zo handig, en eigelijk onmisbaar bij het het gebruik van een AST. Zie verder verhaal van mbravenboer. Hij zegt dat je beter even tijd kan steken in een tooltje dat een AST kan maken op basis van een grammatica ipv het iedere keer met de hand te doen. Vooral na verloop van tijd zul je hier een grote tijdswinst mee behalen.

Verwijderd

Topicstarter
Templated visitor? Kun je hier wat meer over vertellen?
Het stuk waar dit in stond was trouwens niet echt professioneel. In het voorbeeld waar ze uitlegden waarom een visitor nadelen had, lieten ze ong. het volgende voorbeeld zien:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
void Visit(Node node)
{
    switch(node.type)
    {
        case Negate:
            ...
            break;

        case UnaryPlus:
            ...
            break;
    }
}

Al die case's van het switch statement moeten natuurlijk afzonderlijke Visit methoden worden.
Maar ja, feit blijft natuurlijk dat bij elke nieuwe Node er een nieuwe Visit methode moet worden geschreven.
ydejager is nu wel heel benieuwd wat een templated visitor voor oplossing biedt

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Anders moet je dit even doorkijken. Mbv de element adder (extensie op de visitor design pattern) is het wel eenvoudig om nieuwe classes toe te voegen aan de hierarchie.

Maar zo gauw je dus wel beschikking hebt over alle sourcecode (in jouw geval dus) hoef je dit dus niet te te passen. Ik zou dus echt gebruik blijven maken van de normale visitor. Verder zou ik me even gaan verdiepen in visitor guide`s zodat je traversals kan hergebruiken.

code:
1
2
3
4
5
class VariableCollector extends VisitorAdapter{
    List _varList = new LinkedList();
 
    void visit(Variable var){_varList.add(v);}
}


VariableCollector varCol = new VariableCollector();
expression.accepts(new DepthFirstGuide(varCol));

Verwijderd

Topicstarter
Nu je het zegt doet me dit idd denken aan tijd dat ik nog wel eens iets in C schreef. Een verandering van datastructuur had als gevolg dat alle functies aangepast moesten worden.
Alarmnummer schreef op 18 augustus 2002 @ 13:20:
Daarom is code generatie ook zo handig, en eigelijk onmisbaar bij het het gebruik van een AST. Zie verder verhaal van mbravenboer. Hij zegt dat je beter even tijd kan steken in een tooltje dat een AST kan maken op basis van een grammatica ipv het iedere keer met de hand te doen. Vooral na verloop van tijd zul je hier een grote tijdswinst mee behalen.
Ik heb al even nagedacht over de aanpak hiervan. Ik zie in principe 2 oplossingen:
1 Een algemeen framework schrijven waarbij de gebruiker een library schrijft met alle ASTNode's welke doormiddel van reflection wordt ingeladen. Hier moet dan nog een beschrijving bij die opgeeft welke node uit de parse tree moet worden omgezet in een bijbehorende AST node (bijv. een simpele XML file.
2 Een eigen parser schrijven die de grammatica van de betreffende taal inleest en voor elke rule een bijbehorende node genereert. Hier kan dan, zoals mbravenboer al opmerkte, een framework voor het Visitor pattern aan worden toegevoegd.
De laatste optie is natuurlijk het mooist, maar zou betekenen dat ik een grammatica in BNF voor BNF moet schrijven 8). Ik denk dat ik eerst voor de eerste optie ga (in de goldparser c++ source wordt dit ook op deze manier opgelost).

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 30-08 17:59

Gerco

Professional Newbie

Is het bij dat visitor pattern niet vreselijk lastig om een nieuwe Visitor toe te voegen? Je moet dan toch in alle visited klassen een methode accepts(BlablaVisitor) maken? Dan moet je alsnog je hele hierarchie bijwerken als je een operatie wilt toevoegen.

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Voor het doen van een traversal over zeer in beweging zijnde of zelfs (gedeeltelijk) onbekende data-structuren is er nog een andere oplossing: de Walkabout.

Dit staat omschreven in The Essence of the Visitor Pattern van Palsberg en ik heb later nog een logischere en snellere variant gepresenteerd in Guiding visitors: Separating navigation from computation.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
Gerco schreef op 18 augustus 2002 @ 13:38:
Is het bij dat visitor pattern niet vreselijk lastig om een nieuwe Visitor toe te voegen? Je moet dan toch in alle visited klassen een methode accepts(BlablaVisitor) maken? Dan moet je alsnog je hele hierarchie bijwerken als je een operatie wilt toevoegen.
Nee, het visitor pattern is ervoor om juist dit te voorkomen. Alle visitors erven nl over van een abstracte visitor klasse. Alle elementen in je structuur accepten deze abstracte visitor. Alle operaties stop je dus in van de abstracte visitor afgeleide klassen.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Gerco: Is het bij dat visitor pattern niet vreselijk lastig om een nieuwe Visitor toe te voegen? Je moet dan toch in alle visited klassen een methode accepts(BlablaVisitor) maken?
Er is maar 1 interface voor een Visitor over een bepaalde data-structuur. Alle klassen in de structuur moeten een methode accept hebben die deze Visitor accepteert. Concrete Visitor implementaties (operaties over de data-structuur dus) implementeren deze interface dus.

Het grote voordeel van het Visitor pattern is juist dat je niet in alle klassen in de structuur methoden op hoeft te gaan nemen en je operatie zo helemaal verspreid geimplementeerd is. Zie voor meer en betere uitleg de papers die ik net linkte :) (op Javahova is er trouwens ook een leuk topic over).

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
Er wordt hier nu met aardig wat documentatie gesmeten wat ik eerst eens zal doorploegen. Ik hou nl niet de actuele ontwikkelingen op het gebied van design patterns bij. Ik heb iig weer wat te lezen :Y) .

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
ydejager: Een eigen parser schrijven die de grammatica van de betreffende taal inleest en voor elke rule een bijbehorende node genereert.
In principe hoef je niet zelf een parser te gaan schrijven voor een bepaalde grammatica (tenzij je dat natuurlijk erg leuk vindt ;) ). Je zou gewoon de bestaande toolkits uit kunnen breiden (wat bij vele toolkits trouwens geeneens nodig is omdat daar deze functionaliteit al aanwezig is).

GoldParser zal vast een parser en een AST hebben voor de grammatica-taal die wordt gebruikt. Je kan misschien zelfs nog wat code-generatie componenten gebruiken...

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
mbravenboer schreef op 18 augustus 2002 @ 13:51:
[...]

In principe hoef je niet zelf een parser te gaan schrijven voor een bepaalde grammatica (tenzij je dat natuurlijk erg leuk vindt ;) ). Je zou gewoon de bestaande toolkits uit kunnen breiden (wat bij vele toolkits trouwens geeneens nodig is omdat daar deze functionaliteit al aanwezig is).
Ik bedoelde natuurlijke een eigen grammatica schrijven 8)7 .
GoldParser zal vast een parser en een AST hebben voor de grammatica-taal die wordt gebruikt. Je kan misschien zelfs nog wat code-generatie componenten gebruiken...
Op zich moet het wel te doen zijn wanneer ik eenmaal de grammatica die goldparser hanteerd (BNF dus) kan uitlezen. Er moet dan denk ik nog iets door de gebruiker aangeboden worden wat aangeeft welke reductions worden omgezet in een AST. De code generatie kan dan mooi via de .NET CodeDOM.
Het stukje over 'The Essence of the Visitor pattern' lijkt op het eerste gezicht wel interessant maar (als ik het goed heb) vertrouwd er dus op dat er reflection mogelijkheden in de taal aanwezig zijn. Als ik tijd heb zal ik me hier eens in gaan verdiepen.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

mbravenboer schreef op 18 augustus 2002 @ 13:47:
(op Javahova is er trouwens ook een leuk topic over).
Die kan hij niet lezen ;) Maar als het klaar is zal ik het ook op GoT plaatsen ivm dalend nivo en om een start te maken met de nieuwe 'tutorial/artikel' gebeuren op GoT.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
ydejager: Er moet dan denk ik nog iets door de gebruiker aangeboden worden wat aangeeft welke reductions worden omgezet in een AST.
Dat zou in principe ook nog wel automagisch afgeleid moeten kunnen worden uit de grammatica. Deze aanpak zie je terug in veel parser-generators en bijvoorbeeld ook bij SDF/SGLR. Uit de syntax definitie wordt in dit systeem een parse-table gegenereerd. SGLR parset een input tegen deze parse-table en levert een parse-tree op in asfix formaat. Via implode asfix wordt deze geimplodeerd naar een AST.

Hiervoor is er wel 1 ding nodig: productie-regels moeten namen/constructoren hebben. Ik zie deze niet terug in de grammatica voor GoldParser die je postte en dat is op zich wel erg spijtig....
De code generatie kan dan mooi via de .NET CodeDOM.
Inderdaad, dat is een mooie aanpak.

Als je echt veel code wilt moet gaan genereren kan het opbouwen van al deze ASTs wel erg vervelend worden. Het gaat waarschijnlijk veel te ver voor dit project, maar je zou dan gebruik kunnen maken van concrete syntax voor C# om zo code te genereren. Als je dit goed wilt doen, heb je echter behoorlijk wat tools (en ervaring met deze tools) nodig ...

Voor een blik op de mogelijkheden: Meta Programming with Concrete Object Syntax
vertrouwd er dus op dat er reflection mogelijkheden in de taal aanwezig zijn.
De Walkabout vereist inderdaad reflectie.
Alarmnummer: Die kan hij niet lezen
Oh ja tuurlijk.... Stom |:( . Sorry :o .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Pagina: 1