我正在尝试为ocaml中的精简C语言手写一个递归下降解析器。这是语法(删除了左递归并保留了左因子):
program -> decls EOF
decls -> typ id decls_prime
decls -> ε
decls_prime -> vdecl decls
decls_prime -> fdecl decls
fdecl -> LPAREN formals_opt RPAREN LBRACE vdecl_list stmt_list RBRACE
formals_opt -> formal_list
formals_opt -> ε
formal_list -> typ ID formal_list_prime
formal_list_prime -> COMMA formal_list
formal_list_prime -> ε
typ -> INT
typ -> BOOL
typ -> VOID
vdecl_list -> vdecl vdecl_list
vdecl_list -> ε
vdecl -> SEMI
stmt_list -> stmt stmt_list
stmt_list -> ε
stmt -> expr SEMI
stmt -> RETURN SEMI
stmt -> RETURN expr SEMI
stmt -> LBRACE stmt_list RBRACE
stmt -> IF LPAREN expr RPAREN stmt stmt_prime
stmt -> FOR LPAREN expr_opt SEMI expr SEMI expr_opt RPAREN stmt
stmt -> WHILE LPAREN expr RPAREN stmt
stmt_prime -> NOELSE
stmt_prime -> ELSE stmt
expr_opt -> expr
expr_opt -> ε
expr -> INTLITERAL expr_prime
expr -> TRUE expr_prime
expr -> FALSE expr_prime
expr -> MINUS expr NEG expr_prime
expr -> NOT expr expr_prime
expr -> ID expr_prime_prime
expr -> LPAREN expr RPAREN expr_prime
expr_prime -> PLUS expr expr_prime
expr_prime -> MINUS expr expr_prime
expr_prime -> TIMES expr expr_prime
expr_prime -> DIVIDE expr expr_prime
expr_prime -> EQ expr expr_prime
expr_prime -> NEQ expr expr_prime
expr_prime -> LT expr expr_prime
expr_prime -> LEQ expr expr_prime
expr_prime -> GT expr expr_prime
expr_prime -> GEQ expr expr_prime
expr_prime -> AND expr expr_prime
expr_prime -> OR expr expr_prime
expr -> ε
expr_prime_prime -> expr_prime
expr_prime_prime -> ASSIGN expr expr_prime
expr_prime_prime -> LPAREN actuals_opt RPAREN expr_prime
actuals_opt -> actuals_list
actuals_opt -> ε
actuals_list -> expr actuals_list_prime
actuals_list_prime -> COMMA expr actuals_list_prime
actuals_list_prime -> ε
我的语法也有第一套和第二套。
我一直在查看示例here和here。我想我了解递归下降解析背后的基本概念:
语法中的每个非终结A都有一个parseA
方法,该方法读取令牌列表并消耗令牌列表中与A语句相对应的部分。另外,对于语法中的每个终端,都有一个parseT方法,该方法从令牌列表头消耗该终端并返回令牌列表的其余部分。
但是,当我尝试编写递归解析函数时,我迷路了。
到目前为止我的尝试:
let parseProgram toklis = let(r, parseTree) = parseDecl (tl toklis)
in (if hd r = EOF then (SOME KIND OF AST THING)
else raise SyntaxError)
and parse_decl toklis =
let identity toklis =
match toklis.lookahead with
Lexer.ID -> (next toklis, Ast.ID??)
in
let(toklis, decl_type) = parse_type toklis in
let(toklis, decl_id) = identity toklis in
let(toklis, decl_prime) = parse_decl_prime toklis
in
(toklis, Ast.decl(decl_type, decl_id, decl_prime))
and parse_decl_prime toklis =
if toklis.lookahead = Lexer.Semi then
let(toklis, vdecl) = parse_decl next toklis
Ast.Semi???
else
match toklis.lookahead with
| Lexer.Semi -> (next toklis, Ast.Semi)
| Lexer.Lapren ->
let(toklis, e) = parse_fdecl toklis
总的来说,我感到很失落,但是在这里我至少可以弄清楚如何表达:
1. parseA
何时递归,何时不递归?例如,将此语法用于语法A -> id | (B) ; B -> int | A
let rec parseA toklis = match (hd toklis) with
IDENT x -> (tl toklis, A1 (IDENT x))
| LPAREN -> let (r,t) = parseB (tl toklis)
in (if hd r = RPAREN then (tl r, A2(LPAREN, t, RPAREN))
else raise SyntaxError)
| _ -> raise SyntaxError
and parseB toklis = match (hd toklis) with
INT i -> (tl toklis, B1 (INT i))
| _ -> parseA toklis ;;
此外,对于语法中非起始字符集中不是起始符号的每个A,parseA都带有一个“和”(即and parseB
),考虑到每个非末端A都可以从起始符号派生?
何时输入parseA
,何时检查令牌列表的头部以及何时调用解析方法?
在这里首先如何遵循设置因素?在学校中,构造LL解析表是LL解析不可或缺的部分(我相信递归下降本质上是?)。但是在这里,似乎我们只是在ocaml中重写了生产规则...