aboutsummaryrefslogtreecommitdiff
path: root/main.roc
blob: 3d48fcd51dff3d68aaebe3ac1a62ec0dcd377f26 (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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
app "roctorrent"
    packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br" }
    imports [pf.Stdout]
    provides [main] to pf


# Decodes an exact string, errors if there is remaining unused data
bDecodeStr: List U8 -> Result (List U8) [Malformatted]
bDecodeStr = \i ->
  when bDecodeStrAndRemainder i is
    Ok {before, others} -> 
      if List.len others == 0 then
        Ok before
      else 
        Err Malformatted
    Err _ -> Err Malformatted

# Decodes a string and returns the remainder of the input as well if there was any
bDecodeStrAndRemainder: List U8 -> Result {before: List U8, others: List U8} [Malformatted]
bDecodeStrAndRemainder = \i -> 
  when (List.splitFirst i ':') is
    Ok {before, after} -> 
      when (Str.fromUtf8 before) is 
      Ok ls -> 
        when (Str.toNat ls) is
        Ok l -> 
          if List.len after >= l then 
            Ok (List.split after l)
          else 
            Err Malformatted
        Err _err -> Err Malformatted
      Err _err -> Err Malformatted
    Err _err -> Err Malformatted

expect 
  res = bDecodeStr (Str.toUtf8 "3:foo")
  res == Ok (Str.toUtf8 "foo")

expect 
  errCases = [
    "", 
    "foo", 
    "1:foo", 
    "4:foo",
    ":", 
    "foo:", 
    "1:",
    "::",
  ]
  List.all errCases \t -> 
    res = bDecodeStr (Str.toUtf8 t)
    res == Err Malformatted

# Decodes an exact number, exta input causes an error
bDecodeNum: List U8 -> Result I64 [Malformatted Str]
bDecodeNum = \i ->
  when bDecodeNumAndRem i is
    Ok {num, rem} -> 
      if (List.len rem) == 0 then
        Ok num
      else 
        Err (Malformatted "remaining characters after number")
    Err e -> Err e

# Decodes a number and returns the number and any unused input
bDecodeNumAndRem: List U8 -> Result {num: I64, rem: (List U8)} [Malformatted Str]
bDecodeNumAndRem = \i ->
  when (List.first i) is
    Ok f -> 
      if f == 'i' then
        when List.findFirstIndex i (\j -> j == 'e') is
          Ok ix -> 
            {before, others} = List.split i (ix+1)
            numl = List.sublist before {start: 1, len: ix-1}
            when (Str.fromUtf8 numl) is
              Ok nums ->
                when (Str.toI64 nums) is
                  Ok num -> Ok {num: num, rem: others}
                  Err _e -> Err (Malformatted "failed to parse number from \(nums)")
              Err _e -> Err (Malformatted "failed to parse utf8")
          Err _e -> Err (Malformatted "failed to find number end char 'e'")
      else 
        Err (Malformatted "number didn't start with 'i'")
    Err _e -> Err (Malformatted "number string was empty")

expect 
  res = bDecodeNum (Str.toUtf8 "i32e")
  res == Ok 32i64

expect 
  errCases = [
    {case: "", msg: "number string was empty"}, 
    {case: "foo", msg: "number didn't start with 'i'"},
    {case: "i32", msg: "failed to find number end char 'e'"},
    {case: "32e", msg: "number didn't start with 'i'"},
    {case: "ifooe", msg: "failed to parse number from foo"},
    {case: "ie", msg: "failed to parse number from "},
    {case: "i", msg: "failed to find number end char 'e'"}, 
    {case: "e", msg: "number didn't start with 'i'"},
    {case: "i522222222222222222343423432434322e", msg: "failed to parse number from 522222222222222222343423432434322"},
  ]
  List.all errCases \{case, msg} -> 
    res = bDecodeNum (Str.toUtf8 case)
    res == Err (Malformatted msg)

# Tags for storing heterogenous collections
BVal: [
  BStr  (List U8),
  BNum  I64,
  BList (List BVal),
]

# Detects and decodes any kind of element.
bDecodeValueAndRem: (List U8) -> Result {res: BVal, others: (List U8)} [Malformatted, End (List U8)]
bDecodeValueAndRem = \i ->
  when (List.first i) is
    Ok first -> 
      when first is
        '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' -> 
          when bDecodeStrAndRemainder i is
            Ok {before, others} -> 
              Ok {res: BStr before, others: others}
            Err _err -> 
              Err Malformatted
        'i' ->
          when bDecodeNumAndRem i is
            Ok {num, rem} -> 
              Ok {res: BNum num, others: rem}
            Err _err -> 
              Err Malformatted
        'l' ->
          when bDecodeListAndRem i is
            Ok {res, others} ->
              Ok {res: BList res, others: others}
            Err _err -> 
              Err Malformatted
        'e' ->
          {before, others} = List.split i 1
          Err (End others)
        _ -> 
          Err Malformatted
    Err _err -> Err Malformatted

expect
  res = bDecodeValueAndRem (Str.toUtf8 "i64efoo")
  res == Ok {res: BNum 64, others: (Str.toUtf8 "foo")}

expect
  res = bDecodeValueAndRem (Str.toUtf8 "3:foo3:foo")
  res == Ok {res: BStr (Str.toUtf8 "foo"), others: (Str.toUtf8 "3:foo")}

expect
  res = bDecodeValueAndRem (Str.toUtf8 "efoo")
  res == Err (End (Str.toUtf8 "foo"))

expect
  res = bDecodeValueAndRem (Str.toUtf8 "z")
  res == Err Malformatted

# Decodes a list. List elements can be any 
bDecodeList: (List U8) -> Result {res: (List BVal)} [Malformatted]
bDecodeList = \i ->
  when bDecodeListAndRem i is
    Ok {res, others} ->
      if (List.len others) == 0 then
        Ok {res: res}
      else 
        Err Malformatted
    Err e -> 
      Err e

bDecodeListAndRem: (List U8) -> Result {res: (List BVal), others: (List U8)} [Malformatted]
bDecodeListAndRem = \i -> 
  when List.first i is
    Ok f -> 
      if f == 'l' then 
        {before, others} = List.split i 1
        bDecodeListAndRemInternal [] others
      else 
        Err Malformatted
    Err _e -> Err Malformatted

bDecodeListAndRemInternal: List BVal, List U8 -> Result {res: (List BVal), others: (List U8)} [Malformatted]
bDecodeListAndRemInternal = \l, i ->
  when bDecodeValueAndRem i is
    Ok {res, others} ->
      l2 = List.append l res
      bDecodeListAndRemInternal l2 others
    Err e -> when e is
      End others -> 
        Ok {res: l, others: others}
      Malformatted -> 
        Err Malformatted

expect 
  res = bDecodeList (Str.toUtf8 "le")
  res == Ok { res: []}

expect 
  res = bDecodeList (Str.toUtf8 "li123e3:fooe")
  res == Ok { res: [BNum 123i64, BStr (Str.toUtf8 "foo")] }

expect 
  res = bDecodeList (Str.toUtf8 "li123e3:fooe123")
  res == Err Malformatted

expect 
  res = bDecodeList (Str.toUtf8 "lli123eee")
  res == Ok { res: [BList [BNum 123i64]]}

main = 
  Stdout.line "Hello, World"