Upload
vuhanh
View
260
Download
0
Embed Size (px)
Citation preview
Top-down Syntax Analysis
Top-down Syntax Analysis
– Wilhelm/Maurer: Compiler Design, Chapter 8 –
Reinhard WilhelmUniversität des [email protected]
andMooly Sagiv
Tel Aviv [email protected]
Top-down Syntax Analysis
Subjects
◮ Functionality and Method
◮ Recursive Descent Parsing
◮ Using parsing tables
◮ Explicit stacks
◮ Creating the table
◮ LL(k)–grammars
◮ Other properties
◮ Handling Limitations
Top-down Syntax Analysis
Top-Down Syntax Analysis
input: A sequence of symbols (tokens)
output: A syntax tree or an error message
method ◮ Read input from left to right◮ Construct the syntax tree in a top-down manner
starting with a node labeled with the startsymbol
◮ until input accepted (or error) do◮ Predict expansion for the actual leftmost
nonterminal (maybe using some lookahead intothe remaining input) or
◮ Verify predicted terminal symbol against nextsymbol of the remaining input
Finds leftmost derivations.
Top-down Syntax Analysis
Grammar for Arithmetic Expressions
Left factored grammar G2, i.e. left recursion removed.
S → EE → TE ′ E generates T with a continuation E ′
E ′ → +E |ǫ E ′ generates possibly empty sequence of +T sT → FT ′ T generates F with a continuation T ′
T ′ → ∗T |ǫ T ′ generates possibly empty sequence of ∗F sF → id|(E )
Top-down Syntax Analysis
Recursive Descent Parsing
◮ parser is a program,
◮ a procedure X for each non-terminal X ,◮ parses words for non-terminal X ,◮ starts with the first symbol read (into variable nextsym),◮ ends with the following symbol read (into variable nextsym).
◮ uses one symbol lookahead into the remaining input.
◮ uses the FiFo sets to make the expansion transitionsdeterministic
FiFo(N → α) = FIRST1(α)⊕1FOLLOW1(N) ={
FIRST1(α) ∪ FOLLOW1(N) α∗
=⇒ ǫ
FIRST1(α) otherwise
Top-down Syntax Analysis
Parser for G2
program parser;var nextsym: string;proc scan;{reads next input symbol into nextsym}
proc error (message: string);{issues error message and stops parser}
proc accept; {terminates successfully}
proc S;begin Eend ;
proc E;begin T; E’end ;
Top-down Syntax Analysis
proc E’;begin
case nextsym in
{”+”}: if nextsym = "+ "then scanelse error( "+ expected") fi ; E;
otherwise ;endcase
end ;
proc T;begin F; T’ end ;
proc T’;begin
case nextsym in
{” ∗ ”}: if nextsym = "*"then scanelse error( "* expected") fi ; T;
otherwise ;endcase
end ;
Top-down Syntax Analysis
proc F;begin
case nextsym in
{”(”}: if nextsym = "("then scanelse error( "( expected") fi ; E;if nextsym = ”)”then scan else error(" ) expected") fi;
otherwise if nextsym =”id”then scan else error("id expected") fi;
endcase
end ;begin
scan; S;if nextsym = ”#” then accept else error("# expected") fi
end .
Top-down Syntax Analysis
How to Construct such a Parser Program
Observation: Much redundant code generated. Why this?Code was automatically generated from the grammar and the FiFosets.Nice application for a functional programming language!Let G = (VN ,VT ,P ,S) be a context-free grammar and FiFo bethe computed lookahead sets.The functional program generating the parser would have thefunctions:N_prog : VN → code nonterminalsC_prog : (VN ∪ VT )∗ → code concantenationsS_prog : VN ∪ VT → code symbols
Top-down Syntax Analysis
Parser Schema
program parser;var nextsym: symbol;proc scan;
(∗ reads next input symbol into nextsym ∗)proc error (message: string);
(∗ issues error message and stops the parser ∗)proc accept;
(∗ terminates parser successfully ∗)
N_prog(X0); (* X0 start symbol *)N_prog(X1);
...N_prog(Xn);
Top-down Syntax Analysis
beginscan;X0;if nextsym = ”#”
then acceptelse error(". . . ")
fiend
Top-down Syntax Analysis
The Non-terminal Procedures
N = Non-terminal, C = Concatenation, S = Symbol
N_prog(X ) = (* X → α1|α2| · · · |αk−1|αk *)proc X;begincase nextsym inFiFo(X → α1) : C_progr(α1);FiFo(X → α2) : C_progr(α2);
...FiFo(X → αk−1) : C_progr(αk−1);otherwise C_progr(αk);endcaseend ;
Top-down Syntax Analysis
C_progr(α1α2 · · ·αk) =S_progr(α1); S_progr(α2); . . . S_progr(αk);
S_progr(a) =if nextsym = a then scanelse error ( "a expected")fi
S_progr(Y ) = Y
FiFo–sets should be disjoint (LL(1)–grammar)
Top-down Syntax Analysis
A Generative Solution
Generate the control of a deterministic PDA from the grammar andthe FiFo sets.
◮ At compiler–generation time construct a table MM : VN × VT → PM[N, a] is the production used to expand nonterminal N whenthe current symbol is a
◮ For some grammars report that the table cannot beconstructedThe compiler writer can then decide to:
◮ change the grammar (but not the language)◮ use a more general parser-generator◮ “Patch” the table (manually or using some rules)
Top-down Syntax Analysis
Creating the table
Input: cfg G , FIRST1 und FOLLOW1 for G .
Output: The parsing table M or an indication that such atable cannot be constructed
Method: M is constructed as follows:For all X → α ∈ P and a ∈ FIRST1(α), setM[X , a] = (X → α).If ε ∈ FIRST1(α), for all b ∈ FOLLOW1(X ), setM[X , b] = (X → α).Set all other entries of M to error .
Parser table cannot be constructed if at least one entry is set twice.G is not LL(1)
Top-down Syntax Analysis
Example – arithmetic expressions
nonterminal symbol Production
S (, id S → ES +, ∗, ), # errorE (, id E → TE ′
E +, ∗, ), # errorE ′ + E ′ → +EE ′ ), # E ′ → ǫE ′ (, ∗, id errorT (, id T → FT ′
T +, ∗, ), # errorT ′ ∗ T ′ → ∗TT ′ +, ), # T ′ → ǫT ′ (, id errorF id F → idF ( F → (E)F +, ∗, ) error
Top-down Syntax Analysis
LL-Parser Driver (interprets the table M)
program parser;var nextsym: symbol;var st: stack of item;proc scan;
(∗ reads next input symbol into nextsym ∗)proc error (message: string);
(∗ issues error message and stops the parser ∗)proc accept;
(∗ terminates parser successfully ∗)proc reduce;
(∗ replaces [X → β.Y γ][Y → α.] by [X → βY .γ] ∗)proc pop;
(∗ removes topmost item from st ∗)proc push ( i : item);
(∗ pushes i onto st ∗)proc replaceby ( i: item);
(∗ replaces topmost item of st by i ∗)
Top-down Syntax Analysis
beginscan; push( [S ′ → .S] );while nextsym 6= "#"docase top in[X → β.aγ]: if nextsym = a
then scan; replaceby([X → βa.γ]else error fi ;
[X → β.Y γ] : if M[Y , nextsym] = (Y → α)then push([Y → .α])else error fi ;
[X → α.]: reduce;[S ′ → S .] : if nextsym = "#"then accept
else error fiendcaseod
end .
Top-down Syntax Analysis
Explicit StackDeterministic Pushdown Automaton
6
?
ρ
tree
M
vaw
[X → α.Y β]
#
Parser–Table
Control
Stack
Output
Input
Top-down Syntax Analysis
LL(k)-grammar
Goal: formalizing our intuition when the expand-transitionsof the Item-Pushdown-Automaton can be madedeterministic.
Means: k-symbol lookahead into the remaining input.
Top-down Syntax Analysis
LL(k)-grammar
Let G = (VN ,VT ,P ,S) be a cfg and k be a natural number.G is an LL(k)-grammar iff the following holds:if there exist two leftmost derivations
S∗
=⇒lm
uY α =⇒lm
uβα∗
=⇒lm
ux and
S∗
=⇒lm
uY α =⇒lm
uγα∗
=⇒lm
uy , and if k : x = k : y ,
then β = γ.The expansion of the leftmost non-terminal is always uniquelydetermined by
◮ the consumed part of the input and
◮ the next k symbols of the remaining input
Top-down Syntax Analysis
LL(k)-grammar
Let G = (VN ,VT ,P ,S) be a cfg and k be a natural number.G is an LL(k)-grammar iff the following holds:if there exist two leftmost derivations
S∗
=⇒lm
uY α =⇒lm
uβα∗
=⇒lm
ux and
S∗
=⇒lm
uY α =⇒lm
uγα∗
=⇒lm
uy , and if k : x = k : y ,
then β = γ.The expansion of the leftmost non-terminal is always uniquelydetermined by
◮ the consumed part of the input and
◮ the next k symbols of the remaining input
Top-down Syntax Analysis
Example 1Let G1 be the cfg with the productions
STAT → if id then STAT else STAT fi |while id do STAT od |begin STAT end |id := id
G1 is an LL(1)-grammar.
STAT∗
=⇒lm
w STAT α =⇒lm
w β α∗
=⇒lm
w x
STAT∗
=⇒lm
w STAT α =⇒lm
w γ α∗
=⇒lm
w y
From 1 : x = 1 : y follows β = γ,e.g., from 1 : x = 1 : y = if followsβ = γ = ”if id then STAT else STAT fi”
Top-down Syntax Analysis
Example 1Let G1 be the cfg with the productions
STAT → if id then STAT else STAT fi |while id do STAT od |begin STAT end |id := id
G1 is an LL(1)-grammar.
STAT∗
=⇒lm
w STAT α =⇒lm
w β α∗
=⇒lm
w x
STAT∗
=⇒lm
w STAT α =⇒lm
w γ α∗
=⇒lm
w y
From 1 : x = 1 : y follows β = γ,e.g., from 1 : x = 1 : y = if followsβ = γ = ”if id then STAT else STAT fi”
Top-down Syntax Analysis
Example 2
Let G2 be the cfg with the productions
STAT → if id then STAT else STAT fi |while id do STAT od |begin STAT end |id := id |id: STAT | (∗ labeled statem. ∗)id (id ) (∗ procedure call ∗)
Top-down Syntax Analysis
Example 2 (cont’d)
G2 is not an LL(1)–grammar.
STAT∗
=⇒lm
w STAT α =⇒lm
w
β︷ ︸︸ ︷
id := id α∗
=⇒lm
w x
STAT∗
=⇒lm
w STAT α =⇒lm
w
γ︷ ︸︸ ︷
id : STAT α∗
=⇒lm
w y
STAT∗
=⇒lm
w STAT α =⇒lm
w
δ︷ ︸︸ ︷
id(id) α∗
=⇒lm
w z
and 1 : x = 1 : y = 1 : z = ” id”,and β, γ, δ are pairwise different.G2 is an LL(2)–grammar.2 : x = ” id :=”, 2 : y = ” id :”, 2 : z = ” id(” are pairwise different.
Top-down Syntax Analysis
Example 3
Let G3 have the productions
STAT → if id then STAT else STAT fi |while id do STAT od |begin STAT end |VAR := VAR |id( IDLIST ) (∗ procedure call ∗)
VAR → id | id (IDLIST ) (∗ indexed variable ∗)IDLIST → id | id, IDLIST
G3 is not an LL(k)–grammar for any k .
Top-down Syntax Analysis
Example 3
Let G3 have the productions
STAT → if id then STAT else STAT fi |while id do STAT od |begin STAT end |VAR := VAR |id( IDLIST ) (∗ procedure call ∗)
VAR → id | id (IDLIST ) (∗ indexed variable ∗)IDLIST → id | id, IDLIST
G3 is not an LL(k)–grammar for any k .
Top-down Syntax Analysis
Proof:
Assume G3 to be LL(k) for a k > 0.
Let STAT ⇒ β∗
=⇒lm
x and STAT ⇒ γ∗
=⇒lm
y with
x = id (id, id, . . . , id︸ ︷︷ ︸
⌈ k
2⌉ times
) := id and y = id (id, id, . . . , id︸ ︷︷ ︸
⌈ k
2⌉ times
)
Then k : x = k : y ,but β = ”VAR := VAR ” 6= γ = ”id (IDLIST)”.
Top-down Syntax Analysis
Transforming to LL(k)
Factorization creates an LL(2)–grammar, equivalent to G3.
The productionsSTAT → VAR := VAR | id(IDLIST)
are replaced bySTAT → ASSPROC | id := VARASSPROC → id(IDLIST) APRESTAPREST → := VAR | ε
Top-down Syntax Analysis
A non–LL(k)–language
Let G4 = ({S , A, B}, {0, 1, a, b}, P4, S)
P4 =
S → A | B
A → aAb | 0B → aBbb | 1
L(G4) = {an0bn | n ≥ 0} ∪ {an1b2n | n ≥ 0}.G4 is not LL(k) for any k .
Consider the two leftmost derivations
S0
=⇒lm
S =⇒lm
A∗
=⇒lm
ak0bk
S0
=⇒lm
S =⇒lm
B∗
=⇒lm
ak1b2k
With u = α = ε, β = A, γ = B, x = ”ak0bk”, y = ”ak1b2k” it holdsk : x = k : y , but β 6= γ.Since k can be chosen arbitrarily, we have G4 is not LL(k) for any k .
There even is no LL(k)-grammar for L(G4) for any k .
Top-down Syntax Analysis
Towards Checkable LL(k)–conditions
TheoremG is LL(k)–grammar iff the following condition holds:Are A → β and A → γ different productions in P , then
FIRSTk(βα) ∩ FIRSTk(γα) = ∅ for all α with S∗
=⇒lm
wAα
TheoremLet G be a cfg without productions of the form X → ε.G is an LL(1)–grammar ifffor each non-terminal X with the alternatives X → α1| . . . |αn
the sets FIRST1(α1), . . . , FIRST1(αn) are pairwise disjoint.
Top-down Syntax Analysis
TheoremG is LL(1) iffFor different productions A → β and A → γFIRST1(β)⊕1FOLLOW1(A) ∩ FIRST1(γ)⊕1FOLLOW1(A) = ∅ .
Corollary:G is LL(1) iff for all alternatives A → α1| . . . |αn:
1. FIRST1(α1), . . . ,FIRST1(αn) are pairwise disjoint; inparticular, at most one of them may contain ε
2. αi∗
=⇒ ε implies:
FIRST1(αj) ∩ FOLLOW1(A) = ∅ for 1 ≤ j ≤ n, j 6= i .
The condition of the Theorem was used in the parserconstruction!
Top-down Syntax Analysis
Further Definitions and Theorems
◮ G is called a strong LL(k)-grammar if for each two differentproductions A → β and A → γ
FIRSTk(β)⊕kFOLLOWk(A) ∩ FIRSTk(γ)⊕kFOLLOWk(A) = ∅,
◮ A production is called directly left recursive, if it has theform A → Aα
◮ A non-terminal A is called left recursive if it has a derivationA
+=⇒ Aα.
◮ A cfg G is called left recursive, if G contains at least one leftrecursive non-terminal
Top-down Syntax Analysis
Theorem
(a) G is not LL(k) for any k if G is left recursive.
(b) G is not ambiguous if G is LL(k)-grammar.
Top-down Syntax Analysis
Regular Right Sides
Left recursion
◮ prevents LL parsing,
◮ is used for lists and sequences,
◮ can be replaced by iteration, i.e., the Kleene star
Needs new definitions for derivation, First, and Follow!
Top-down Syntax Analysis
Right-regular Context-free Grammar
P : VN → RA is now a function from VN into the set RA of regularexpressions over VN ∪ VT .A pair (X , r) with p(X ) = r is written as X → r .New causes for non-determinism! Which?
Top-down Syntax Analysis
Example: Arithmetic Expressions
S → EE → T{{+ | −}T}∗
T → F{{∗ | /}F}∗
F → (E ) | Id
Top-down Syntax Analysis
Regular Derivations
A derivation step
(a) w X β =⇒R,lm
w αβ mit α = p(X )
(b) w (r1 | . . . | rn)β =⇒R,lm
w ri β für 1 ≤ i ≤ n
(c) w (r)∗ β =⇒R,lm
w β
(d) w (r)∗ β =⇒R,lm
w r (r)∗ β
Top-down Syntax Analysis
Regular leftmost derivation for id + id ∗ idS =⇒
R,lm
E =⇒R,lm
T{{+|−}T}∗
=⇒R,lm
F{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
{(E)|id}{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id{{+|−}T}∗
=⇒R,lm
id{+|−}T{{+|−}T}∗
=⇒R,lm
id + T{{+|−}T}∗
=⇒R,lm
id + F{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id + {(E)|id}{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id + id{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id + id{∗|/}F{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id + id ∗ F{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id + id ∗ {(E)|id}{{∗|/}F}∗{{+|−}T}∗
=⇒R,lm
id + id ∗ id{{∗|/}F}∗{{+|−}T}∗
=⇒ id + id ∗ id{{+|−}T}∗
Top-down Syntax Analysis
Computation of First
Compute ε-productivity first.
eps(a) = false, for a ∈ VT
eps(ε) = trueeps(r∗) = trueeps(X ) = eps(r), if p(X ) = r for X ∈ VN
eps((r1| . . . |rn)) =n∨
i=1
eps(ri )
eps((r1 . . . rn)) =
n∧
i=1
eps(ri )
Top-down Syntax Analysis
then ε-free First
ε-ffi(ε) = ∅ε-ffi(a) = {a}ε-ffi(r∗) = ε-ffi(r)ε-ffi(X ) = ε-ffi(r), if p(X ) = r
ε-ffi((r1| . . . |rn)) =⋃
1 ≤ i ≤ nε-ffi(ri )
ε-ffi((r1 . . . rn)) =⋃
1 ≤ j ≤ n{ε-ffi(rj) |
∧
1≤i<j
eps(ri )}
Top-down Syntax Analysis
Computation of Follow
Follow depends on the right context of a subexpression:Unusual “bottom-up” recursion!
(1) FOLLOW1([S ′ → .S]) = {#} The eof symbol ’#’ follows after each input word.
(2) FOLLOW1([X → · · · (r1| · · · |.ri | · · · |rn) · · · ]) =FOLLOW1([X → · · · .(r1| · · · |ri | · · · |rn) · · · ]) for 1 ≤ i ≤ n
(3) FOLLOW1([X → · · · (· · · .ri ri+1 · · · ) · · · ]) =
ε-ffi(ri+1) ∪
8
<
:
FOLLOW1([X → · · · (· · · ri .ri+1 · · · ) · · · ]),if eps(ri+1) = true
∅ otherwise
(4) FOLLOW1([X → · · · (r1 · · · rn−1.rn) · · · ]) = (FOLLOW1)FOLLOW1([X → · · · .(r1 · · · rn−1rn) · · · ])
(5) FOLLOW1([X → · · · (.r)∗ · · · ]) =ε-ffi(r) ∪ FOLLOW1([X → · · · .(r)∗ · · · ])
(6) FOLLOW1([X → .r ]) =S
FOLLOW1([Y → · · · .X · · · ])
Top-down Syntax Analysis
then the FiFo-Sets
FiFo(N → α) = FIRST1(α)⊕1FOLLOW1(N) ={
FIRST1(α) ∪ FOLLOW1(N) α∗
=⇒ ǫ
FIRST1(α) otherwise
This formulation allows efficient computation, see Pure UnionProblems in the Book!
Top-down Syntax Analysis
Recursive Descent Parsing
s t r uc t symbol nextsym ;
/∗ Returns nex t i npu t symbol ∗/vo id scan ( ) ;
/∗ P r i n t s the e r r o r message ands top s the run o f the p a r s e r ∗/
vo id e r r o r ( S t r i n g e r ro rMes sage ) ;
/∗ Announces the end o f the a n a l y s i s ands t op s the run o f the p a r s e r ∗/
vo id accept ( ) ;
/∗ Tr a n s l a t i n g the i npu t grammar ∗/p_progr (X0 → α0 ) ;p_progr (X1 → α1 ) ;
...p_progr (Xn → αn ) ;
Top-down Syntax Analysis
vo id p a r s e r ( ) {scan ( ) ;X0 ( ) ;
i f ( nextsym == "\#" )accept ( ) ;
e l s e
e r r o r ( ". . ." ) ;}
Top-down Syntax Analysis
p_progr (X → .α)
/∗ . . .we c r e a t e an a c co r d i ng method l i k e t h i s . ∗/vo id X() {
prog r ( [X → .α ] ) ;}
vo id prog r ( [X → · · · .(α1|α2| · · · |αk−1|αk ) · · · ] ) {switch ( ) {
case ( nextsym ∈ FiFo ( [X → · · · (.α1|α2| · · · |αk−1|αk) · · · ] ) ) :p rog r ( [X → · · · (.α1|α2| · · · |αk−1|αk) · · · ] ) ;
break ;case ( nextsym ∈ FiFo ( [X → · · · (α1|.α2| · · · |αk−1|αk) · · · ] ) ) :
p rog r ( [X → · · · (α1|.α2| · · · |αk−1|αk) · · · ] ) ;break ;
...case ( nextsym ∈ FiFo ( [X → · · · (α1|α2| · · · |.αk−1|αk) · · · ] ) ) :
p rog r ( [X → · · · (α1|α2| · · · |.αk−1|αk) · · · ] ) ;break ;de f au l t :
p rog r ( [X → · · · (α1|α2| · · · |αk−1|.αk) · · · ] ) ;}
}
Top-down Syntax Analysis
vo id prog r ( [X → · · · .(α)∗ · · · ] ) {whi le ( nextsym ∈ FIRST1(α) ) ) {
prog r ( [X → · · · .α · · · ] ) ;}
}
vo id prog r ( [X → · · · .(α)+ · · · ] ) {do {
prog r ( [X → · · · .α · · · ] ) ;} whi le ( nextsym ∈ FIRST1(α) ) ;
}
vo id prog r ( [X → · · · .ǫ · · · ] ) {}
Top-down Syntax Analysis
For a ∈ VT is
vo id prog r ( [X → · · · .a · · · ] ) {i f ( nextsym == a)
scan ( ) ;e l s e
e r r o r ( ". . ." ) ;}
For Y ∈ VN is
vo id prog r ( [X → · · · .Y · · · ] ) = vo id Y()
Top-down Syntax Analysis
RLL Parser for the Expression Grammar
symbol nextsym ;
/∗ Returns nex t i npu t symbol ∗/symbol scan ( ) ;
/∗ P r i n t s the e r r o r message ands top s the run o f the p a r s e r ∗/
vo id e r r o r ( S t r i n g e r ro rMes sage ) ;
/∗ Announces the end o f the a n a l y s i s ands t op s the run o f the p a r s e r ∗/
vo id accept ( ) ;
Top-down Syntax Analysis
RLL Parser for the Expression Grammarvo id S ( ) {
E ( ) ;}vo id E( ) {
T( ) ;whi le ( nextsym == "+" | | nextsym == "−" ) {
switch ( nextsym ) {case "+" :
i f ( nextsym == "+" )scan ( ) ;
e l s e
e r r o r ( "+␣ expected " ) ;break ;de f au l t :
i f ( nextsym == "−" )scan ( ) ;
e l s e
e r r o r ( "−␣ expected " ) ;}T( ) ;
}}
Top-down Syntax Analysis
RLL Parser for the Expression Grammar
vo id T() {F ( ) ;whi le ( nextsym == "∗" | | nextsym == "/" ) {
switch ( nextsym ) {case "∗" :
i f ( nextsym == "∗" )scan ( ) ;
e l s e
e r r o r ( "∗␣ expected " ) ;break ;de f au l t :
i f ( nextsym == "/" )scan ( ) ;
e l s e
e r r o r ( "/␣ expected " ) ;}F ( ) ;
}}
Top-down Syntax Analysis
RLL Parser for the Expression Grammar
vo id F ( ) {switch ( nextsym ) {
case " ( " :E ( ) ;i f ( nextsym == " )" )
scan ( ) ;e l s e
e r r o r ( " ) ␣ expec ted " ) ;de f au l t :
i f ( nextsym == " i d " )scan ( ) ;
e l s e
e r r o r ( " i d ␣ expec ted " ) ;}
}
Top-down Syntax Analysis
RLL Parser for the Expression Grammar
vo id p a r s e r ( ) {scan ( ) ;S ( ) ;i f ( nextsym == "#" )
accept ( ) ;e l s e
e r r o r ( "#␣ expected " ) ;}