aboutsummaryrefslogtreecommitdiffstats
path: root/core/cell.moon
blob: 724b74e126bd83a92fe01cf9a5f31bffe94a42ef (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
-- ALV Cell type
import Value from require 'core.value'
import op_invoke, fn_invoke from require 'core.invoke'
import Tag from require 'core.tag'

-- An S-Expression with a head expression and any number of tail expressions,
-- an optional tag, and optionally the internal whitespace as parsed.
class Cell
-- AST interface
  eval: (scope) =>
    head = (@head!\eval scope)\const!
    Action = switch head.type
      when 'opdef'
        -- scope\get 'op-invoke'
        op_invoke
      when 'fndef'
        -- scope\get 'fn-invoke'
        fn_invoke
      when 'builtin'
        head\unwrap!
      else
        error "cannot evaluate expr with head #{head}"

    Action\eval_cell scope, @tag, head, @tail!

  -- quoting a Cell recursively quotes children, but preserves identity. This
  -- means that a quoted Cell may only be 'used' once. Use :clone() otherwise.
  quote: (scope) =>
    children = [child\quote scope for child in *@children]
    Cell @tag, children, @white

  -- creates a clone of this Cell with its own identity by prepending a `parent`
  -- tag and cloning all child expressoins recursively.
  clone: (parent) =>
    tag = @tag\clone parent
    children = [child\clone parent for child in *@children]
    Cell tag, children, @white

  stringify: (depth=-1) =>
    buf = ''
    buf ..= if depth > 0 then '' else @white[0]
    if depth == 0
      buf ..= '...'
    else
      for i, child in ipairs @children
        buf ..= child\stringify depth - 1
        buf ..= if depth > 0 then ' ' else @white[i]

      if depth > 0
        buf = buf\sub 1, #b@uf - 1

    '(' .. @tag\stringify! .. buf .. ')'

-- internal
  -- tag:      the parsed Tag
  -- children: sequence of child AST Nodes
  -- white:    optional sequence of whitespace segments ([0 .. #@children])
  new: (@tag=Tag.blank!, @children, @white) =>
    if not @white
      @white = [' ' for i=1,#@children]
      @white[0] = ''

    assert #@white == #@children, "mismatched whitespace length"

  head: => @children[1]
  tail: => [c for c in *@children[2,]]

-- static
  __tostring: => @stringify 2

  parse_args = (tag, parts) ->
    if not parts
      parts, tag = tag, nil

    children, white = {}, { [0]: parts[1] }

    for i = 2,#parts,2
      children[i/2] = parts[i]
      white[i/2] = parts[i+1]

    tag, children, white
  @parse: (...) =>
    tag, children, white = parse_args ...
    @ tag, children, white

-- A parenthesis-less Cell (root of an ALV document).
--
-- has an implicit head of 'do'.
class RootCell extends Cell
  head: => Value.sym 'do'
  tail: => @children

  stringify: =>
    buf = ''
    buf ..= @white[0]

    for i, child in ipairs @children
      buf ..= child\stringify!
      buf ..= @white[i]

    buf

  @parse: (...) =>
    @__parent.parse @, (Tag\parse '0'), ...

{
  :Cell
  :RootCell
}