把文法存成数据
文法也可以当数据存:每条产生式是「左边 → 右边的候选列表」。看 stmt 有几个候选、第一个候选是什么: G = { "stmt": [["let", "IDENT",
补全:看开头选规则
(每道题开头都有同一段:T() 是上一站 lexer 的替身——源码里 token 之间用空格分开,它切成 (kind, text, line, col);show() 把树写成一行:二元运算全加括号,语句之间用 |。) 补全 which(
补全:数一数 sum 里有几个 term
(每道题开头都有同一段:T() 是上一站 lexer 的替身——源码里 token 之间用空格分开,它切成 (kind, text, line, col);show() 把树写成一行:二元运算全加括号,语句之间用 |。) sum := te
递归下降的核心做法
「递归下降」写 parser 的核心做法是【0】。
peek 和 advance
parser 里两个最基本的动作:peek() 和 advance()。它们的区别是【0】。
expect 干什么
expect(";") 的意思是【0】。
为什么叫「递归」
块里套块,一层层走进去 走到最里层,还记得从哪儿出来吗 玩具语言的文法(八节共用):program := stmt*;stmt := "let" IDENT "=" expr ";"
最小的 parser 走一句
(每道题开头都有同一段:T() 是上一站 lexer 的替身——源码里 token 之间用空格分开,它切成 (kind, text, line, col);show() 把树写成一行:二元运算全加括号,语句之间用 |。) 一个只认「let
补全:写出 let 语句的函数
(每道题开头都有同一段:T() 是上一站 lexer 的替身——源码里 token 之间用空格分开,它切成 (kind, text, line, col);show() 把树写成一行:二元运算全加括号,语句之间用 |。) 补全 let_st
补全:看开头分两路
(每道题开头都有同一段:T() 是上一站 lexer 的替身——源码里 token 之间用空格分开,它切成 (kind, text, line, col);show() 把树写成一行:二元运算全加括号,语句之间用 |。) 补全 stmt:开