package pratter

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file pratter.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
(* Copyright (C) Gabriel Hondet.
   Subject to the BSD-3-Clause license *)

let or_else (x : 'a option) (f : unit -> 'a) : 'a =
  match x with Some x -> x | None -> f ()

type associativity = Left | Right | Neither
type fixity = Infix of associativity | Prefix | Postfix
type 't error = [ `Op_conflict of 't | `Too_few_arguments ]
type ('a, 'b) result = ('a, 'b error) Stdlib.result

type ('tok, 'a) parser = 'tok Seq.t -> ('a * 'tok Seq.t, 'tok) result
(** Parses ['tok] to ['a]. *)

let pure (x : 'b) : ('a, 'b) parser = fun strm -> Ok (x, strm)

let bind (type a b) (p0 : ('tok, a) parser) (f : a -> ('tok, b) parser) :
    ('tok, b) parser =
 fun inp -> match p0 inp with Error _ as e -> e | Ok (out, inp') -> f out inp'

let fmap (f : 'a -> 'b) (p : ('tok, 'a) parser) : ('tok, 'b) parser =
 fun inp -> Result.map (fun (x, y) -> (f x, y)) (p inp)

let fail (e : 'i error) : ('i, _) parser = fun _ -> Error e

(* Put back a token in the stream of input. *)
let push (x : 'i) : ('i, unit) parser =
 fun inp ->
  Ok
    ( ()
    , (* This expression becomes [Seq.cons] in ocaml 4.11 *)
      fun () -> Seq.Cons (x, inp) )

(** Parse a single token if there is one. *)
let tok : ('tok, 'tok option) parser =
 fun inp ->
  (* Can be replaced by [Seq.uncons] from ocaml 4.14 *)
  match inp () with
  | Seq.Nil -> Ok (None, Seq.empty)
  | Seq.Cons (t, rest) -> Ok (Some t, rest)

let ( let* ) = bind

let run (p : ('i, 'o) parser) (inp : _ Seq.t) : ('o, _) result =
  Result.map fst (p inp)

let on_prefix (f : float -> 'a -> 'b) : fixity * float * 'a -> 'b option =
  function
  | Prefix, prio, s -> Some (f prio s)
  | (Postfix | Infix _), _, _ -> None

let on_postfix (f : float -> 'a -> 'b) : fixity * float * 'a -> 'b option =
  function
  | Postfix, prio, s -> Some (f prio s)
  | (Prefix | Infix _), _, _ -> None

let on_infix (f : associativity -> float -> 'a -> 'b) :
    fixity * float * 'a -> 'b option = function
  | Infix assoc, prio, s -> Some (f assoc prio s)
  | (Postfix | Prefix), _, _ -> None

let expression (type a b) ~(appl : b -> b -> b) ~(token : a -> b)
    ~(ops : a -> (fixity * float * b) list) : (a, b) parser =
  (* [nud tbl strm t] (for null denotation) is the production of term [t] with
     {b no} left context. If [t] is not a prefix operator, [nud] is the
     identity. Otherwise, the output is a production rule. *)
  let rec nud (t : a) : (a, b) parser =
    or_else
      (List.find_map
         (on_prefix (fun rbp t' ->
              fmap (appl t') (expression ~rbp ~rassoc:Neither)))
         (ops t))
    @@ fun () -> pure (token t)
  (* [led ~strm ~left t assoc bp] is the production of term [t] with
      left context [left]. We have the invariant that [t] is a binary operator
      with associativity [assoc] and binding power [bp]. This invariant is
      ensured while called in {!val:expression}. *)
  and led ~left t rassoc bp : (a, b) parser =
    let rbp =
      match rassoc with
      | Right -> bp *. (1. -. epsilon_float)
      | Left | Neither -> bp
    in
    fmap (appl (appl t left)) (expression ~rbp ~rassoc)
  (* [expression ~rbp ~rassoc strm] parses next token of stream
     [strm] with previous operator having a right binding power [~rbp] and
     associativity [~rassoc]. *)
  and expression ~rbp ~rassoc =
    (* [aux left] inspects the stream and may consume one of its elements, or
       return [left] unchanged. *)
    let rec aux (left : b) : (a, b) parser =
      let* next = tok in
      match next with
      | None -> pure left
      | Some pt ->
          let ops = ops pt in
          (* Process the infix case *)
          let ifx lassoc lbp next =
            if lbp > rbp || (lbp = rbp && lassoc = Right && rassoc = Right) then
              let* t = led ~left next lassoc lbp in
              aux t
            else if lbp < rbp || (lbp = rbp && lassoc = Left && rassoc = Left)
            then
              (* Put back *)
              let* () = push pt in
              pure left
            else fail (`Op_conflict pt)
          in
          let postfix lbp next =
            if lbp > rbp then aux (appl next left)
            else if lbp = rbp then fail (`Op_conflict pt)
            else
              let* () = push pt in
              pure left
          in
          or_else (List.find_map (on_infix ifx) ops) @@ fun () ->
          or_else (List.find_map (on_postfix postfix) ops) @@ fun () ->
          let* right = nud pt in
          aux (appl left right)
    in
    let* next = tok in
    match next with
    | None -> fail `Too_few_arguments
    | Some next ->
        let* left = nud next in
        aux left
  in
  expression ~rbp:neg_infinity ~rassoc:Neither