summaryrefslogtreecommitdiff
path: root/lib/parser.dx
blob: 2525ec0af68d9fb462b0ce9c48f0ad869344099c (plain)
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
'Utilities unrelated to parsing

def from_ordinal_exc (n:Type) [Ix n] (i:Nat) : {Except} n =
  if (0 <= i) && (i < size n)
    then unsafe_from_ordinal _ i
    else throw ()

-- TODO: allow this to happen in-place
-- TODO: if it takes too long to make that possible, start with a bounded version
def push {h a} (ref:Ref h (List a)) (x:a) : {State h} Unit =
  l = get ref
  ref := l <> AsList _ [x]

def index_list {a} (l:List a) (i:Nat) : {Except} a =
  (AsList n xs) = l
  xs.(from_ordinal_exc _ i)

'The Parser type

def ParserHandle (h:Type) : Type = (String & Ref h Nat)

data Parser a:Type =
  MkParser ((h:Type) ?-> ParserHandle h -> {Except, State h} a)

def parse {a h} (handle:ParserHandle h) (parser:Parser a) : {Except, State h} a =
  (MkParser f) = parser
  f handle

def run_parser_partial {a} (s:String) (parser:Parser a) : Maybe a =
  (MkParser f) = parser
  with_state 0 \pos.
    catch $ do
      f (s, pos)

'Primitive combinators

def p_char (c:Char) : Parser Unit = MkParser \(s, posRef).
  i = get posRef
  c' = index_list s i
  assert (c == c')
  posRef := i + 1

def p_eof : Parser Unit = MkParser \(s, posRef).
  assert $ get posRef >= list_length s

def (<|>) {a} (p1:Parser a) (p2:Parser a) : Parser a = MkParser \h.
  (s, posRef) = h
  curPos = get posRef
  case catch do parse h p1 of
    Nothing ->
      assert $ curPos == get posRef
      parse h p2
    Just ans -> ans

def returnP {a} (x:a) : Parser a = MkParser \_. x

def run_parser {a} (s:String) (parser:Parser a) : Maybe a =
  run_parser_partial s $ MkParser \h.
    ans = parse h parser
    _   = parse h p_eof
    ans

def parse_any : Parser Char = MkParser \h.
  (s, posRef) = h
  i = get posRef
  c' = index_list s i
  posRef := i + 1
  c'

def try {a} (parser:Parser a) : Parser a = MkParser \h.
  (s, posRef) = h
  savedPos = get posRef
  ans = catch do parse h parser
  case ans of
    Nothing ->
      posRef := savedPos
      throw ()
    Just x -> x

'Derived combinators

def parse_digit : Parser Int = try $ MkParser \h.
  c = parse h $ parse_any
  i = w8_to_i c - 48
  assert $ 0 <= i && i < 10
  i

def optional {a} (p:Parser a) : Parser (Maybe a) =
  (MkParser \h. Just (parse h p)) <|> returnP Nothing

def parse_many {a} (parser:Parser a) : Parser (List a) = MkParser \h.
  yield_state (AsList _ []) \results.
    iter \_.
      maybeVal = parse h $ optional parser
      case maybeVal of
        Nothing -> Done ()
        Just x ->
          push results x
          Continue

def parse_unsigned_int : Parser Int = MkParser \h.
  (AsList _ digits) = parse h $ parse_many parse_digit
  yield_state 0 \ref.
    for i. ref := 10 * get ref + digits.i

def parse_int : Parser Int = MkParser \h.
  negSign = parse h $ optional $ p_char '-'
  x       = parse h $ parse_unsigned_int
  case negSign of
    Nothing -> x
    Just () -> (-1) * x

def bracketed {a} (l:Parser Unit) (r:Parser Unit) (body:Parser a) : Parser a =
  MkParser \h.
    _   = parse h l
    ans = parse h body
    _   = parse h r
    ans

def parens {a} (parser:Parser a) : Parser a =
  bracketed (p_char '(') (p_char ')') parser