aboutsummaryrefslogtreecommitdiffstats
path: root/examples/json.aup
blob: 9e6faec74668f6c7717f220ce93bb1ce57e3d481 (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
# Whether a message's bytes are one JSON text (RFC 8259), read once from the start with a stack of the arrays and
# objects open. Input that is not UTF-8 fails as encoding: it is no JSON text either.
state valid = false
input check = message { data: bytes }

fn space(b) = b == 0x20 || b == 0x09 || b == 0x0a || b == 0x0d
fn digit(b) = b >= 0x30 && b <= 0x39
fn hex(b) = digit(b) || (b >= 0x61 && b <= 0x66) || (b >= 0x41 && b <= 0x46)

on check {
  let t = text(check.data)
  let d = bytes(t)
  let n, i, mode, ok = d.bytes, 0, 0, true    # mode 0: a value, 1: after one, 2: a key, 3: a key or }, 4: a value or ]
  let open: list of i64
  while ok {
    while i < n && space(d[i]) { i = i + 1 }
    if i == n {
      ok = mode == 1 && open.count == 0
      break
    }
    let b = d[i]
    if mode == 1 {
      if open.count == 0 { ok = false }
      else if b == 0x2c { mode = if open.last == 0x5b { 0 } else { 2 } }
      else if b == open.last + 2 { open.remove(open.count - 1) }
      else { ok = false }
      i = i + 1
    } else if mode == 3 && b == 0x7d || mode == 4 && b == 0x5d {
      open.remove(open.count - 1)
      mode = 1
      i = i + 1
    } else if mode == 2 || mode == 3 {
      if b != 0x22 { ok = false }
      mode = 5                                   # the key, then ':'
    } else if b == 0x5b || b == 0x7b {
      open.append(b)
      mode = if b == 0x5b { 4 } else { 3 }
      i = i + 1
    } else if b == 0x22 || mode == 5 {
      i = i + 1                                  # a string: no control bytes, escapes as RFC 8259 writes them
      while i < n && d[i] != 0x22 && ok {
        if d[i] < 0x20 { ok = false }
        else if d[i] == 0x5c && i + 1 < n && d[i + 1] == 0x75 {
          ok = i + 5 < n && hex(d[i + 2]) && hex(d[i + 3]) && hex(d[i + 4]) && hex(d[i + 5])
          i = i + 6
        } else if d[i] == 0x5c {
          ok = i + 1 < n && (d[i + 1] == 0x22 || d[i + 1] == 0x5c || d[i + 1] == 0x2f || d[i + 1] == 0x62 ||
                             d[i + 1] == 0x66 || d[i + 1] == 0x6e || d[i + 1] == 0x72 || d[i + 1] == 0x74)
          i = i + 2
        } else { i = i + 1 }
      }
      ok = ok && i < n
      i = i + 1
      if mode == 5 {
        while i < n && space(d[i]) { i = i + 1 }
        ok = ok && i < n && d[i] == 0x3a
        i = i + 1
        mode = 0
      } else { mode = 1 }
    } else if b == 0x2d || digit(b) {            # -?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?
      if b == 0x2d { i = i + 1 }
      ok = i < n && digit(d[i])
      if ok && d[i] == 0x30 { i = i + 1 } else { while i < n && digit(d[i]) { i = i + 1 } }
      if ok && i < n && d[i] == 0x2e {
        i = i + 1
        ok = i < n && digit(d[i])
        while i < n && digit(d[i]) { i = i + 1 }
      }
      if ok && i < n && (d[i] == 0x65 || d[i] == 0x45) {
        i = i + 1
        if i < n && (d[i] == 0x2b || d[i] == 0x2d) { i = i + 1 }
        ok = i < n && digit(d[i])
        while i < n && digit(d[i]) { i = i + 1 }
      }
      mode = 1
    } else {
      let word = if b == 0x74 { "true" } else if b == 0x66 { "false" } else { "null" }
      ok = i + word.bytes <= n && t[i .. i + word.bytes] == word
      i = i + word.bytes
      mode = 1
    }
  }
  valid = ok
}