2

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?

5
  • You don't use the ListSexp constructor? Commented Jul 31, 2023 at 11:34
  • @coredump What do you mean? Commented Jul 31, 2023 at 12:19
  • 1
    a value of type sexp is either an Atom or a ListSexp, you produce Atom values for numbers and identifiers, but then you return sexp list directly, which is not of type sexp Commented Jul 31, 2023 at 12:22
  • You mean like this: let expr = parens (sep_by atom space) => fun l -> ListSexp l. I tried, but how to apply the function recursively on this? I got: ` 8 | let rec expr = parens (sep_by (atom <|> expr) space) => fun l -> ListSexp l ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error: This kind of expression is not allowed as right-hand side of let rec' error Commented Jul 31, 2023 at 12:23
  • 1
    @coredump Oh wow! Actually, I made it work. This: let rec expr input = (parens (sep_by (expr <|> atom) space) => fun l -> ListSexp l) input compiles and parses expressions recursively. You can make that an answer. Commented Jul 31, 2023 at 12:29

1 Answer 1

2

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
Sign up to request clarification or add additional context in comments.

1 Comment

Yes. The second approach is better. The correct function would be: let rec expr input = (parens (sep_by expr space) => (fun l -> ListSexp l) <|> atom) input

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.