Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

1

22.12.2013, 14:44

Fragen zum Expression Parsing

Hi, ich besuche zur Zeit an unserer Uni die Compiler 1 Vorlesung. Leider wurde da nicht weiter auf Expression Parsing eingegangen, was aber meiner Meinung nach am schwierigsten beim Parsen ist.
Ich habe mich ein wenig an dieser Seite orientiert und nun schon zum dritten Mal für meine Sprache die Grammatik zum Thema Expressions formuliert.
Vielleicht kann mir ja jemand weiter helfen oder gute Referenzen dazu geben. Im Internet habe ich noch nicht viel dazu gefunden und ich will mir jetzt nicht extra ein Buch dafür kaufen - hab noch so viel andere IT Bücher zulesen und komme kaum noch dazu ^^.

Die 'Einsprungpunkte' für meine Expressions sind im Grunde die "ArithmeticExpr" und "BooleanExpr" die ich für "if"-Anweisungen oder "Assignment"-Anweisungen brauche.
Mit simpler Punkt-Vor-Strich-Rechnung will ich mich jetzt nicht zufrieden geben, schließlich will ich auch solche Operatoren wie &&, ||, %=, <<= usw. wie in C++, Java, etc. haben. Ein paar Features aus den C++ Expressions lasse ich natürlich weg.
Hier ist meine Grammatik (in EBNF geschrieben):

Quellcode

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
#
# ======= Expression Grammar Productions (3rd Approach) - 22/12/2013 =======
#

# Basics
Digit               ::= '0' | ... | '9'
Letter              ::= 'a' | ... | 'z' | 'A' | ... | 'Z'
Number              ::= Digit ( Digit )*
Identifier          ::= ( '_' | Letter ) ( '_' | Letter | Digit )*

# Literals
Literal             ::= IntLiteral
IntLiteral          ::= Number
FloatLiteral        ::= Number '.' Number
BoolLitearl         ::= 'true' | 'false'

# Object identifiers
ArrayIndex          ::= ArithmeticExpr
ObjectName          ::= Identifier ( '.' Identifier )*
Object              ::= ObjectName ( '[' ArrayIndex ']' )*

# Function arguments
ArgList             ::= epsilon | SingleArg ( ',' SingleArg )*
SingleArg           ::= BooleanExpr | ArithmeticExpr

# Sum expressions
ArithmeticExpr      ::= SumExpr
SumExpr             ::= ProductExpr ( ( '+' | '-' ) ProductExpr )*
ProductExpr         ::= ValueExpr ( ( '*' | '/' | '%' ) ValueExpr )*
ValueExpr           ::= Literal | MemoryObject | BracketExpr | CastExpr | CallExpr
BracketExpr         ::= '(' ArithmeticExpr ')'
CastExpr            ::= '(' TypeDenoter ')' ValueExpr
CallExpr            ::= ObjectName '(' ArgList ')'

# Bool expressions
BooleanExpr         ::= LinkedOrExpr
LinkedOrExpr        ::= LinkedAndExpr ( '||' LinkedAndExpr )*
LinkedAndExpr       ::= ValueBoolExpr ( '&&' ValueBoolExpr )*
ValueBoolExpr       ::= BoolLitearl | RelationExpr | NegBoolExpr
RelationExpr        ::= ArithmeticExpr ( '=' | '!=' | '<' | '>' | '<=' | '>=' ) ArithmeticExpr
NegBoolExpr         ::= '!' BooleanExpr

# Assignment statements
MemoryObject        ::= ObjectName | AssignStatement
AssignStatement     ::= LeftAssign | IncDecAssign
LeftAssign          ::= MemoryObject ( ':=' | '+=' | '-=' | '*=' | '/=' | '%=' | '|=' | '&=' | '^=' | '<<=' | '>>=' ) ( BooleanExpr | ArithmeticExpr )
IncDecAssign        ::= PreIncDecAssign | PostIncDecAssign
PreIncDecAssign     ::= ( '++' | '--' ) MemoryObject
PostIncDecAssign    ::= MemoryObject ( '++' | '--' )

# === Use case example (Pseudo Code) ===

# Example code (of my language):
#
#   if x + 2 > 3 - (2 + func(x - 2, true)) * y {
#       x := 2 + ++y - func(y, false)
#       i++
#   }
#

AcceptToken(Type) {
    if (CurrentTokenType == Type)
        GetNextToken()
    else
        Error()
}

ParseIfStatement() {
    AcceptToken('if')
    ParseBooleanExpression()
    ParseStatementBlock()
}

ParseStatementBlock() {
    AcceptToken('{')
    while (CurrentTokenType != '}') {
        ParseStatement()
    }
    AcceptToken('}')
}


Über Feedback, Ideen etc. würde ich mich freuen :-)

2

22.12.2013, 21:34

Also Kontextfreie Grammatiken parsen ist nicht trivial. Wir hatten eine ganze Vorlesung die ausschließlich das behandelt hat.

Andererseits ist es aber auch ein gelöstes Problem, die Standardtools dafür sind scheinbarLex und Yacc (die man in dieser Reihenfolge zusammen verwendet).

Natürlich ist es möglich, einen Parser aus einzelnen Funktionen zu schreiben, so wie du es da angefangen hast. Die Frage ist, wie viel Spaß das macht. Was wir damals gelernt haben, ist ein Schema, um aus einer beliebigen KFG einen Parser zu bauen. Aber man darf sicherlich schon eine Woche einplanen, um das zu verstehen, und dann ist halt die Frage, ob man sich nicht doch ein Buch kaufen sollte. Das Standardwerk ist da wohl das Drachenbuch.

Ist das eigentlich eine Aufgabe für die Uni oder willst du einfach nur so mal einen Parser bauen? Wenn zweiteres der Fall ist, befürchte ich, dass du mit dem Ansatz nicht weit kommst, weil das Thema zu komplex ist, um einfach so damit anzufangen. Vielleicht wirst du ein paar Dinge parsen können, aber es ist fast sicher, dass es an irgendetwas hängen bleiben wird oder Fehler produzieren wird, wenn du einfach auf gut Glück verschiedene Parse-Funktionen schreibst.
Wenn du nicht Yacc/Lex benutzen willst, ist meines Erachtens nach der einzig sinnvolle Weg dir ein Buch (vermutlich das erwähnte Drachenbuch) zu kaufen und durchzuarbeiten. Das wird dich allerdings einige Wochen beschäftigen.
Lieber dumm fragen, als dumm bleiben!

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

3

22.12.2013, 23:12

Kann da Jonathan nur zustimmen. Ich schreibe als Bachelorarbeit gerade einen Compiler für eine eigene kleine Metasprache und ohne das Drachenbuch oder auch Grundlagen des Compilerbau wäre ich nicht sehr weit gekommen. Die sind ihr Geld wirklich wert (schlappe 80 Euro beim Drachenbuch). Außerdem hatte/habe ich, u.a., folgende Unterlagen:

Sowie den Open Source Code des D Compilers, den ich tatkräftig studiert und das ein oder andere mal modifiziert habe. Der ist auf jeden Fall mal einen Blick Wert, wenn du dich tiefer mit der Materie beschäftigten willst.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

4

23.12.2013, 00:14

Danke schon mal für's Feedback.

Lex und Yacc habe ich schon oft von gehört aber noch nie wirklich was damit gemacht. Lernen wir am Ende von Compiler 1 noch.
Und nein, das ist keine Aufgabe, aber ich will selbst von Grund auf einen Compiler bauen, daher schreibe ich auch den Parser selber.
Bis jetzt funktioniert das mit dem Aufbau schon sehr gut - durch 'rekursiven Abstieg'. Nur die Expressions sind eben noch sehr primitiv bzw. nicht korrekt.

Vielleicht hole ich mir doch noch ein Buch oder leihe mir eins an der Uni aus.

Gruß,
Lukas

5

23.12.2013, 00:40

C-/C++-Quelltext

1
2
3
4
5
ParseIfStatement() {
    AcceptToken('if')
    ParseBooleanExpression()
    ParseStatementBlock()
}

Sowas funktioniert aber auch nur dann, wenn du genau weißt, wie dein Statement aussieht. Im allgemeinen Fall ist es aber so, dass du nicht so einfach entscheiden kannst, ob es Beispielsweise ein "IfStatement" ist, oder eben etwas anderes. Je nachdem wie deine Sprache aufgebaut ist, kannst du Glück haben und weit kommen, es kann aber auch einfach passieren, dass du an irgendeiner Stelle einfach nicht mehr erkennst, was du da jetzt eigentlich parsen musst. Dann musst du anfangen dir die nächsten X Token anzusehen und zu raten, was du da jetzt eigentlich hast. Oder drauflos parsen und entweder steckenbleiben oder irgendwie zurückgehen und neu anfangen.

Triviales Beispiel:
- Präfixnotation: "+ * 3 5 2" ist leicht zu parsen. Du weißt, dass + 2 Parameter braucht und kannst die Rekursiv einlesen, der erste ist "* 3 5", der zweite "2".
- Infixnotation: "3+5*2" ist schwierig. Zunächsteinmal musst du die 3 als Parameter für irgendwas zwischenspeichern, siehst dann das plus und weißt, dass rechts noch ein Parameter stehen muss. Der ist aber NICHT die 5 sondern "5*2", durch Punkt-Vor-Strich ist das quasi implizit geklammert, was du erkennen musst.
Dieses Problem ist noch ganz gut lösbar, aber im allgemeinen Fall wirst du arge Probleme haben. Compilerbau war bei uns vom anspruch spürbar härter als so manch andere Vorlesung, und ich glaube nicht, dass ich einen Parser mal einfach so als Hobbyprojekt geschrieben hätte, ohne irgendwelche Literatur dazu gelesen zu haben.
In der Bibliothek zu gucken ist natürlich eine gute Idee. Zumindest bei uns sind die Ausleihfristen erfreulich lang.
Lieber dumm fragen, als dumm bleiben!

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

6

23.12.2013, 01:56

Infixnotation ist auch nicht so schwierig. Am einfachsten: Shunting Yard Algorithmus. Was für eine Art Parser bastelst du denn?

Toa

Alter Hase

Beiträge: 944

Beruf: Research associate

  • Private Nachricht senden

7

23.12.2013, 08:55

Damit [1] kannst du auch bisschen rumspielen. Wird in den letzten Vorlesungsterminen von Compiler 1 behandelt. Grüße T0a

[1] http://www.antlr.org/

€Dit: Ich seh grad, Teile davon wurden schon gesagt. Zu früh am morgen.
"Das ist ein Minkovski Raum, manche Menschen nennen ihn auch Weltraum" Prof. Dr. Jürgen Wambach, Theoretische Physik, TU Darmstadt | Meine Homepage

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Toa« (23.12.2013, 09:00)


LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

8

23.12.2013, 11:59

Was für eine Art Parser bastelst du denn?

Jeden Falls keinen LL(1)-Parser. Aber wie genau man meinen bezeichnen würde, weiß ich nicht.

Also bei meiner Sprache kann man eigentlich schon sehr gut erkennen, was wann kommt. Etwas schwierig wird es z.B. an der Stelle, wo man eine Variable oder Funktion deklariert.
Das ist nämlich stark an C++ orientiert.

Bsp:

Quellcode

1
2
int x
int x()

Kommt also ein TypeDenoter (hier "int") geht's in die Funktion "ParseVariableDeclOrFunctionDecl()"; dann wird der Identifier ge-parst (hier "x"); kommt dann eine Klammer '(' muss es eine Funktion sein, andern falls muss es eine Variable sein.

Die meisten anderen Sachen sich aber sehr eindeutig. Der Hauptteil meiner Grammatik sieht in etwas so aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
Program               ::= ( Command )*    // "Program" ist das Start-Symbol
CommandBlock          ::= '{' ( Command )* '}'
Command               ::= IfCommand | ClassDeclaration | CommandBlock | EnumerationDefinition
IfCommand             ::= 'if' BooleanExpression CommandBlock ( 'elif' BooleanExpression CommandBlock )* ( epsilon | 'else' CommandBlock )
ClassDeclaration      ::= 'class' Identifier ClassCommandBlock
ClassCommandBlock     ::= '{' ( ClassCommand )* '}'
ClassCommand          ::= ClassDeclaration | PrivacySpecifier | EnumerationDefinition
PrivacySpecifier      ::= 'public:' | 'private:' | 'protected:'
EnumerationDefinition ::= 'enum' Identifier EnumerationBlock
EnumerationBlock      ::= '{' ( Identifier )* '}'


Also ich finde das ziemlich eindeutig ^^. Naja, ich habe ja auch eigentlich nur mit den Expressions Probleme. Aber was anderes als Infixnotation kommt mir nicht in den Code.
Alles andere sieht mir zu sehr nach Scheme aus :P

Gruß und schon mal schöne Feiertage,

Lukas

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

9

23.12.2013, 15:40

Ich habe jetzt mal zum Arithmetischen Expression Parsing etwas Pseudocode geschrieben (aber nicht viel 'pseudo' :-) ).

Hier noch mal der etwas überarbeitete (nur Arithmetische) Teil zur Grammatik:

Quellcode

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
# Basics
Digit                   ::= '0' | ... | '9'
Letter                  ::= 'a' | ... | 'z' | 'A' | ... | 'Z'
Number                  ::= Digit ( Digit )*
Identifier              ::= ( '_' | Letter ) ( '_' | Letter | Digit )*

# Literals
Literal                 ::= IntLiteral | FloatLiteral | BoolLiteral
IntLiteral              ::= Number
FloatLiteral            ::= Number '.' Number
BoolLitearl             ::= 'true' | 'false'
StringLiteral           ::= '"' ( AnyCharacter )* '"'
ArithmeticLiteral       ::= IntLiteral | FloatLiteral

# Object identifiers
ArrayIndex              ::= ArithmeticExpr
ObjectName              ::= Identifier ( '.' Identifier )*
Object                  ::= ObjectName ( '[' ArrayIndex ']' )*

# Arithmetic expressions
ArithmeticExpr          ::= BitwiseOrExpr
BitwiseOrExpr           ::= BitwiseXorExpr ( '|' BitwiseXorExpr )*
BitwiseXorExpr          ::= BitwiseAndExpr ( '^' BitwiseAndExpr )*
BitwiseAndExpr          ::= ShiftExpr ( '&' ShiftExpr )*
ShiftExpr               ::= SumExpr ( ( '<<' | '>>' ) SumExpr )*
SumExpr                 ::= ProductExpr ( ( '+' | '-' ) ProductExpr )*
ProductExpr             ::= ValueExpr ( ( '*' | '/' | '%' ) ValueExpr )*
ValueExpr               ::= ArithmeticLiteral | MemoryObject | ArithmeticBracketExpr | CastExpr | CallExpr
ArithmeticBracketExpr   ::= '(' ArithmeticExpr ')'
CastExpr                ::= '(' TypeDenoter ')' ValueExpr
CallExpr                ::= ObjectName '(' ArgList ')'

# Assignment statements
MemoryObject            ::= Object | AssignStatement
AssignStatement         ::= LeftAssign | IncDecAssign
LeftAssign              ::= MemoryObject ( ':=' | '+=' | '-=' | '*=' | '/=' | '%=' | '|=' | '&=' | '^=' | '<<=' | '>>=' ) ( BooleanExpr | ArithmeticExpr )
IncDecAssign            ::= PreIncDecAssign | PostIncDecAssign
PreIncDecAssign         ::= ( '++' | '--' ) MemoryObject
PostIncDecAssign        ::= MemoryObject ( '++' | '--' )


Und hier der 'C++ Pseudocode':

C-/C++-Quelltext

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// ----- PSEUDOCODE for Expression Parsing -----

ParseArithmeticExpr()
{
    return ParseBitwiseOrExpr();
}

ParseBitwiseOrExpr()
{
    auto ExprAST = new BitwiseOrExpression;

    ExprAST->XorExprList.push_back(ParseBitwiseXorExpr());

    while (TokenType() == '|')
    {
        AcceptIt();
        ExprAST->XorExprList.push_back(ParseBitwiseXorExpr());
    }

    return ExprAST;
}

ParseBitwiseXorExpr()
{
    auto ExprAST = new BitwiseXorExpression;

    ExprAST->AndExprList.push_back(ParseBitwiseAndExpr());

    while (TokenType() == '^')
    {
        AcceptIt();
        ExprAST->AndExprList.push_back(ParseBitwiseAndExpr());
    }

    return ExprAST;
}

ParseBitwiseAndExpr()
{
    auto ExprAST = new BitwiseAndExpression;

    ExprAST->ShiftExprList.push_back(ParseShiftExpr());

    while (TokenType() == '&')
    {
        AcceptIt();
        ExprAST->ShiftExprList.push_back(ParseShiftExpr());
    }

    return ExprAST;
}

ParseShiftExpr()
{
    auto ExprAST = new ShiftExpression;

    ExprAST->SumExprList.push_back(ParseSumExpr());

    while (TokenType() == '<<' || TokenType() == '>>')
    {
        AcceptIt();
        ExprAST->SumExprList.push_back(ParseSumExpr());
    }

    return ExprAST;
}

ParseSumExpr()
{
    auto ExprAST = new SumExpression;

    ExprAST->ProductExprList.push_back(ParseProductExpr());

    while (TokenType() == '+' || TokenType() == '-')
    {
        AcceptIt();
        ExprAST->ProductExprList.push_back(ParseProductExpr());
    }

    return ExprAST;
}

ParseProductExpr()
{
    auto ExprAST = new ProductExpression;

    ExprAST->SumExprList.push_back(ParseValueExpr());

    while (TokenType() == '*' || TokenType() == '/' || TokenType() == '%')
    {
        AcceptIt();
        ExprAST->SumExprList.push_back(ParseValueExpr());
    }

    return ExprAST;
}

ParseValueExpr()
{
    auto ExprAST = new ValueExpression;

    switch (TokenType())
    {
        case IntLiteral:
        case FloatLiteral:
            return ParseArithmeticLiteral();

        case '(':
            return ParseArithmeticBracketExprOrCastExpr();
        
        case Identifer:
            return ParseMemoryObjectOrCallExpr();

        default:
            ErrorUnexpectedToken();
    }

    return ExprAST;
}

ParseArithmeticBracketExprOrCastExpr()
{
    // Store information where we are currently reading in file.
    Scanner_.PushPos();

    // Try to parse a cast expression ('false' means it is not required).
    auto CastExprAST = ParseCastExpr(false);

    if (CastExprAST == nullptr)
    {
        // Restore position we were reading before trying to find a cast expression.
        Scanner_.PopPosAndRestore();

        // Now try to parse bracket expression.
        return ParseArithmeticBracketExpr();
    }
    else
    {
        // Pop stored position from the scanner's stack and continue reading by ignoring the poped data.
        Scanner_.PopPosAndIgnore();

        return CastExprAST;
    }
}

ParseCastExpr(bool Required)
{
    if (TokenType() == '(')
        AcceptIt();
    else
    {
        if (Required)
            ErrorUnexpectedToken("Expected '('");
        return nullptr;
    }

    // Parse type denoter to which the value expression is to be casted.
    auto TypeDenoterAST = ParseTypeDenoter(Required);

    if (TypeDenoterAST == nullptr)
        return nullptr;

    if (TokenType() == ')')
        AcceptIt();
    else
    {
        if (Required)
            ErrorUnexpectedToken("Expected ')'");
        return nullptr;
    }

    // Parse the value expression which is to be casted.
    auto ValueExprAST = ParseValueExpr();

    auto CastExprAST = new CastExpression;

    CastExprAST->TypeDenoter    = TypeDenoterAST;
    CastExprAST->ValueExpr      = ValueExprAST;

    return CastExprAST;
}   

ParseArithmeticBracketExpr()
{
    auto ExprAST = new ArithmeticBracketExpr;

    Accept('(');
    {
        ExprAST->ArithmeticExpr = ParseArithmeticExpr();
    }
    Accept(')');

    return ExprAST;
}

ParseMemoryObjectOrCallExpr()
{
    // For memory object and call epxression we need an object-name AST node
    auto ObjectNameAST = ParseObjectName();

    if (TokenType() == '(')
    {
        // If the object name is followed by a '(' character it must be a function call.
        return ParseCallExpr(ObjectNameAST);
    }
    else
    {
        // Otherwise it must be an memory object
        return ParseMemoryObject(ObjectNameAST);
    }
}

ParseMemoryObject(ObjectName ObjectNameAST)
{
    // Check if object name has already been parsed; if not, do so.
    if (ObjectNameAST == nullptr)
        ObjectNameAST = ParseObjectName();

    auto MemObjectAST = new MemoryObject;
        
    MemObjectAST->ObjectName = ObjectNameAST;

    // Check for array indexing
    if (TokenType() == '[')
        MemObjectAST->ArrayIndexing = ParseArrayIndexing();

    // Check for assignment (:=, +=, -=, etc.)
    if (IsTokenAssignmentOperator())
        MemObjectAST->AssignStatement = ParseAssigStatement();

    return MemObjectAST;
}

ParseCallExpr(ObjectName ObjectNameAST)
{
    // Check if object name has already been parsed; if not, do so.
    if (ObjectNameAST == nullptr)
        ObjectNameAST = ParseObjectName();

    auto CallExprAST = new CallExpression;

    CallExprAST->FunctionName = ObjectNameAST;

    Accept('(');
    {
        CallExprAST->ArgList = ParseArgumentList();
    }
    Accept(')');

    return CallExprAST;
}

ParseObjectName()
{
    auto ObjectNameAST = new ObjectName;

    ObjectNameAST->IdentifierList.push_back(ParseIdentifier());

    // Parse further optional identifiers (separated by a '.' character).
    while (TokenType() == '.')
    {
        AcceptIt();
        ObjectNameAST->IdentifierList.push_back(ParseIdentifier());
    }

    return ObjectNameAST;
}

ParseIdentifier()
{
    auto IdentifierToken = Accept(Types::Identifier);
    return new Identifier(IdentifierToken);
}

ParseArithmeticLiteral()
{
    switch (TokenType())
    {
        case IntLiteral:
            auto CurrToken = AcceptIt();
            return new IntLiteral(CurrToken);
        case FloatLiteral:
            auto CurrToken = AcceptIt();
            return new FloatLiteral(CurrToken);
    }
    ErrorUnexpectedToken("Expected arithmetic literal")
}


Ich denke das sieht schon mal ganz ok aus. Ich werde dann erst mal damit weiter machen, die ganzen neuen AST-Node Klassen zu schreiben.
Bis jetzt habe ich für die Expressions nur eine Hand voll, d.h. ich muss das noch besser strukturieren.
Dann sollte das nicht mehr so kompliziert werden :-)

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

10

27.12.2013, 21:23

Mein Expression-Parsing macht große Fortschritte :D

Eigentlich will ich ja 'nur' einen Source-to-Source Compiler schreiben (der nach C++ übersetzt) aber Spaßes-halber haber ich jetzt auch mal mit einem Assembler-Code-Generator angefangen,
um mal zu schauen, wie der die Expressions in ASM auswertet.

Das ist mein Code:

Quellcode

1
int a := 7 + 3 * 2 - 5 * (6 + 3) - 4


Und das ist die Assembler Ausgabe meines Compilers:

Quellcode

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
; D:\SoftwareEntwicklung\C++\HLC\Tools\XieXie-Compiler\trunk\examples\Tests/ExpressionTest1.xx.asm
; XieXie generated source file
; Fri Dec 27 21:14:14 2013

; Variable initialization "a"
push 7
push 3
push 2
pop ebx
pop eax
mul eax, ebx
push eax
push 5
push 6
push 3
pop ebx
pop eax
add eax, ebx
push eax
pop ebx
pop eax
mul eax, ebx
push eax
pop ebx
pop eax
sub eax, ebx
push eax
push 4
pop ebx
pop eax
sub eax, ebx
push eax
pop ebx
pop eax
add eax, ebx
push eax

; ===============


Bin den Assembler Code eben mal per Hand durch gegangen. Müsste soweit alles stimmen :-)

Ein bisschen was soll der Compiler später auch noch optimieren, aber es wird sich erst mal nicht vermeiden lassen, dass so was entsteht:

Quellcode

1
2
push eax
pop eax

Durch den rekursiven Aufruf in meinem Code-Generator können solche überflüssigen Befehle entstehen. Kümmer ich mich aber erst mal nicht drum.

Werbeanzeige