Ocaml中的递归下降解析器

时间:2018-11-04 22:46:18

标签: parsing recursion ocaml ll

我正在尝试为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 -> ε

我的语法也有第一套和第二套。

我一直在查看示例herehere。我想我了解递归下降解析背后的基本概念:

语法中的每个非终结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都可以从起始符号派生?

  1. 何时输入parseA,何时检查令牌列表的头部以及何时调用解析方法?

  2. 在这里首先如何遵循设置因素?在学校中,构造LL解析表是LL解析不可或缺的部分(我相信递归下降本质上是?)。但是在这里,似乎我们只是在ocaml中重写了生产规则...

0 个答案:

没有答案