英文:
Recursive Lisp parser with Ocaml Opal
问题
我正在尝试使用OCaml Opal编写一个简单的Lisp解析器:
这是我的AST:
type atom = Num of int | Ident of string [@@deriving show]
type sexp = Atom of atom | ListSexp of sexp list [@@deriving show]
这是解析器:
open Opal
open Ast
let integer = many1 digit => implode % int_of_string => fun n -> Atom (Num n)
let ident = many alpha_num => implode => fun i -> Atom (Ident i)
let parens = between (token "(") (token ")")
let atom = integer <|> ident
let expr = parens (sep_by atom space)
let parse_expr input =
match parse expr input with
| Some ans -> List.iter (fun a -> Printf.printf "%s" (show_sexp a)) ans
| None -> print_endline "ERROR!"
当我尝试解析Parser.parse_expr (LazyStream.of_string "(5 3 abc)")
时,这个解析器工作得很好。
然而,我尝试递归地解析S表达式,通过简单地修改我的expr
函数为:
let rec expr = parens (sep_by (atom <|> expr) space)
这会产生一个错误:
8 | let rec expr = parens (sep_by (atom <|> expr) space)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type char input -> (sexp list * char input) option
but an expression was expected of type
char input -> (sexp * char input) option
Type sexp list is not compatible with type sexp
这个错误似乎是合理的,因为atom
函数返回的类型是char input -> (sexp * char input) option
,而expr
函数返回的类型是char input -> (sexp list * char input) option
。
但我不确定如何修复这个问题。有什么想法吗?
英文:
I'm trying to write a simple Lisp parser with OCaml Opal:
This is my AST:
type atom = Num of int | Ident of string [@@deriving show]
type sexp = Atom of atom | ListSexp of sexp list [@@deriving show]
And this is the parser:
open Opal
open Ast
let integer = many1 digit => implode % int_of_string => fun n -> Atom (Num n)
let ident = many alpha_num => implode => fun i -> Atom (Ident i)
let parens = between (token "(") (token ")")
let atom = integer <|> ident
let expr = parens (sep_by atom space)
let parse_expr input =
match parse expr input with
| Some ans -> List.iter (fun a -> Printf.printf "%s" (show_sexp a)) ans
| None -> print_endline "ERROR!"
This works ok when I try to parse Parser.parse_expr (LazyStream.of_string "(5 3 abc)")
However, the next thing I tried is to parse S-Expressions recursively by naively modifying my expr
function to:
let rec expr = parens (sep_by (atom <|> expr) space)
And this produces an error:
8 | let rec expr = parens (sep_by (atom <|> expr) space)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type char input -> (sexp list * char input) option
but an expression was expected of type
char input -> (sexp * char input) option
Type sexp list is not compatible with type sexp
This seems ok since atom
function returns char input -> (sexp * char input) option
while expr
function returns char input -> (sexp list * char input) option
.
But I'm not sure how can to fix this. Ideas?
答案1
得分: 2
你需要将 sexp lisp
包装在 ListSexp
构造函数中,以便它可以成为 sexp
类型,否则你会有两个冲突的类型,sexp
可以是 Atom
或 ListSexp
,以及 sexp list
。
你测试了以下代码,并且它是有效的:
let rec expr input =
(parens (sep_by (expr <|> atom) space) => fun l -> ListSexp l) input
但我认为一个表达式并不总是一个列表,在 EBNF 表示法中,我会这样写:
EXPR := ATOM | "(" EXPR* ")"
所以也许可以尝试这样修改:
let rec expr input =
(parens (sep_by expr space) => (fun l -> ListSexp l) <|> atom) input
英文:
You need to wrap sexp lisp
in a ListSexp
constructor so that it can be of type sexp
, otherwise you have two conflicting types, sexp
which is either an Atom
or a ListSexp
, and sexp list
.
You tested the following, and it works:
let rec expr input =
(parens (sep_by (expr <|> atom) space) => fun l -> ListSexp l) input
But I think an expression is not always a list, in EBNF notation I would write:
EXPR := ATOM | "(" EXPR* ")"
So maybe something like this instead?
let rec expr input =
(parens (sep_by expr space) => (fun l -> ListSexp l) <|> atom) input
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论