02 September 2009

9. Parentheses

From now on I will not build on the same source file every time. Because it becomes so long and tedious to manage. Instead I will show you the concepts on a small scale, and you can then implement it yourself in a bigger project if you want to.

In this chapter, we will build on mul.pb, so make a copy of it (name it "parens.pb").
To have the same source as I do, search and replace ecx into ebp.

Here is the old grammar:
digit      ::= 0..9
integer ::= <digit> {<digit>}
value ::= <integer>
mulop ::= * | /
addop ::= + | -
mulexp ::= <value> { <mulop> <value> }
addexp ::= <mulexp> { <addop> <mulexp> }
expression ::= <addexp>

What does value() do? It puts a value in eax.
What does expression() do? It puts a value in eax.
What is inside a parenthesis? Is it not just another ordinary expression? Yes.

So, why not do it like this?
value      ::= <integer> | '(' <expression> ')'

That's what we are going to do. Change Value() into this:

Declare Expression()
Procedure Value()
; value ::= <integer> | '(' <expression> ')'
If IsDigit(Look)
Emit("mov eax, " + GetInteger())
ElseIf Look = '('
MatchWhite('(')
Expression()
MatchWhite(')')
EndIf
EndProcedure


We had to forward declare Expression() to avoid an error message (since it's defined later in the source code). Also you can see two calls to a new function here: MatchWhite(). What it does is to verify that Look is a certain character, discard Look, and then eat any following whitespace. Here's the implementation, put it in base2.pb:
Procedure Match(C.c)
If Look <> C
Expected(Chr(C))
EndIf
GetLook()
EndProcedure

Procedure MatchWhite(C.c)
Match(C)
EatWhite()
EndProcedure


Now that was simple, right? Unfortunately it's not quite right just yet. Currently Expression() gives an error message if it does not reach the end of the line. We must change that. Here is the entire code, with the end of line check moved out of Expression():


; paren.pb
IncludeFile "..\base2.pb"

; Procedure.s VarRef(Var.s)
; ProcedureReturn "[v_" + Var + "]"
; EndProcedure

Declare Expression()
Procedure Value()
; value ::= <integer> | '(' <expression> ')'
If IsDigit(Look)
Emit("mov eax, " + GetInteger())
ElseIf Look = '('
MatchWhite('(')
Expression()
MatchWhite(')')
EndIf
EndProcedure

Procedure MulExp()
; mulexp ::= <value> {* <value>}
Value()
While Look = '*'
GetLookWhite()
Emit("push eax")
Value()
Emit("pop ebp")
Emit("imul eax, ebp")
Wend
EndProcedure

Procedure AddExp()
; addexp ::= <mulexp> {+ <mulexp> }
MulExp()
While Look = '+'
GetLookWhite()
Emit("push eax")
MulExp()
Emit("pop ebp")
Emit("add eax, ebp")
Wend
EndProcedure

Procedure Expression()
; expression ::= <AddExp>
AddExp()
EndProcedure

Init()
Expression()
If Look <> 0
Expected("End of line")
EndIf
Input()

No comments: