求指导:计算器语言文法的First集与Follow集计算纠错
Alright, let's work through this step by step. First, I'll fill in the obvious missing pieces of your grammar (since your snippet cut off mid-production for term—I'll assume standard calculator grammar conventions for the factor tail and operators, which makes sense for a calculator language). Then we'll compute First and Follow sets properly, and highlight the common pitfalls where folks usually mess up Follow sets.
Step 1: Complete Grammar (Assumed)
First, let's formalize the full grammar with reasonable completions for the truncated parts:
program → stmt_list $$$ stmt_list → stmt stmt_list | ε stmt → id = expr | input id | print expr expr → term term_tail term_tail → add_op term term_tail | ε term → factor factor_tail factor_tail → mul_op factor factor_tail | ε factor → id | num | ( expr ) // Where: // add_op = + | - // mul_op = * | / // $$$ = end-of-program marker
Step 2: Calculating First Sets
First sets are straightforward—they're the set of terminal symbols that can start any string derived from a non-terminal. Here's the breakdown:
First(program): Derives fromstmt_list $$$. Sincestmt_listcan produce ε, we includeFirst(stmt_list)and$$$. Result:{ id, input, print, $$$, ε }First(stmt_list): Can be ε or start with anystmt. Result:{ id, input, print, ε }First(stmt): Has three alternatives, all starting with distinct terminals. Result:{ id, input, print }First(expr): Starts with whatevertermstarts with (sincetermcan't be ε). Result:{ id, num, ( }First(term_tail): Can be ε or start with anadd_op. Result:{ +, -, ε }First(term): Starts with whateverfactorstarts with (sincefactorcan't be ε). Result:{ id, num, ( }First(factor_tail): Can be ε or start with amul_op. Result:{ *, /, ε }First(factor): Directly maps to its terminal alternatives. Result:{ id, num, ( }
Step 3: Calculating Follow Sets (The Tricky Part)
Follow sets are the terminals that can appear immediately after a non-terminal in a valid derivation. Let's use the standard rules and call out common mistakes as we go:
Key Rules to Remember
- For the start symbol (
program), its Follow set is empty here—since it's followed directly by$$$(a terminal end marker) in its only production. - If you have
A → αBβ, add all non-ε symbols fromFirst(β)toFollow(B). IfFirst(β)includes ε, add all symbols fromFollow(A)toFollow(B). - If you have
A → αB, add all symbols fromFollow(A)toFollow(B).
Follow(program)
- No symbols come after
programin any valid derivation. Result:∅
Follow(stmt_list)
- Used in
program → stmt_list $$$: Add$$$to its Follow set. - Used in
stmt_list → stmt stmt_list: Since the right-hand side ends withstmt_list, we addFollow(stmt_list)to itself (no new symbols here).
Result:{ $$$ }
Follow(stmt)
- Used in
stmt_list → stmt stmt_list: AddFirst(stmt_list) - {ε}({ id, input, print }) to its Follow set. SinceFirst(stmt_list)includes ε, also addFollow(stmt_list)({ $$$ }).
Result:{ id, input, print, $$$ }
Follow(expr)
- Used in
stmt → id = exprandstmt → print expr: Both end withexpr, so addFollow(stmt)({ id, input, print, $$$ }). - Used in
factor → ( expr ): The)comes right afterexpr, so add)to its Follow set.
Result:{ id, input, print, $$$, ) }
Follow(term_tail)
- Used in
expr → term term_tail: Ends withterm_tail, so addFollow(expr)({ id, input, print, $$$, ) }).
Result:{ id, input, print, $$$, ) }
Follow(term)
- Used in
expr → term term_tail: AddFirst(term_tail) - {ε}({ +, - }). SinceFirst(term_tail)includes ε, also addFollow(expr)({ id, input, print, $$$, ) }).
Result:{ +, -, id, input, print, $$$, ) }
Follow(factor_tail)
- Used in
term → factor factor_tail: Ends withfactor_tail, so addFollow(term)({ +, -, id, input, print, $$$, ) }).
Result:{ +, -, id, input, print, $$$, ) }
Follow(factor)
- Used in
term → factor factor_tail: AddFirst(factor_tail) - {ε}({ *, / }). SinceFirst(factor_tail)includes ε, also addFollow(term)({ +, -, id, input, print, $$$, ) }). - Used in
factor_tail → mul_op factor factor_tail: AddFirst(factor_tail) - {ε}({ *, / }) and (sinceFirst(factor_tail)has ε) addFollow(factor_tail)(which is same asFollow(term)—no new symbols here).
Result:{ *, /, +, -, id, input, print, $$$, ) }
Common Mistakes You Might Have Made
- Missing Follow set propagation for nullable β: This is the biggest one. For example, in
expr → term term_tail, sinceterm_tailcan be ε, you must addFollow(expr)toFollow(term)—not justFirst(term_tail). Many people forget this step. - Overlooking parentheses: In
factor → ( expr ), the closing)must be added toFollow(expr). It's easy to miss this if you only focus on top-level statements. - Incorrect start symbol Follow set: Since
programis followed directly by$$$(a terminal), its Follow set is empty—not including$$$. If you added$$$toFollow(program), that's a mistake. - Recursive production confusion: For
stmt_list → stmt stmt_list, the Follow set of the recursivestmt_listis the same as the parentstmt_list's Follow set. You don't need to loop here—just inherit the set from wherestmt_listis used inprogram.
内容的提问来源于stack exchange,提问作者Felauras

