aboutsummaryrefslogtreecommitdiffstats
path: root/core/const.moon
blob: 99e41782756f611739a7ddbb26f975a82bfceb3c (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
import Op, Action, FnDef from require 'core.base'

local Scope
load_ = ->
  import Scope from require 'core.scope'

ancestor = (klass) ->
  assert klass, "cant find the ancestor of nil"
  while klass.__parent
    klass = klass.__parent
  klass

class Ref
  new: (@original) =>

  get: (...) => @original\get ...
  getc: (...) => @original\get ...

  destroy: =>
  update: =>

class Const
  types = {
    sym: true
    str: true
    num: true
    bool: true
    scope: true
    op: true
    opdef: true
    fndef: true
    builtin: true
  }

  new: (@type, @value, @raw) =>
    assert types[@type], "invalid Const type: #{@type}"

-- Value interface
  get: (type) =>
    assert not type or type == @type, "#{@} is not a #{type}"
    @value

  getc: (type) =>
    assert not type or type == @type, "#{@} is not a #{type}"
    @value

  update: (dt) =>
    switch @type
      when 'op'
        @value\update dt

-- AST interface
  eval: (scope) =>
    switch @type
      when 'num', 'str'
        @
      when 'sym'
        assert (scope\get @value), "undefined reference to symbol '#{@value}'"
      else
        error "cannot evaluate #{@}"

  quote: => @

  stringify: => @raw

  clone: (prefix) => @
  -- in case of doubt:
  -- clone: (prefix) => Const @type, @value, @raw

-- static
  __tostring: =>
    value = if @type == 'opdef' or @type == 'builtin' then @value.__name else @value
    "<#{@type}: #{value}>"

  __eq: (other) =>
    other.type == @type and other.value == @value

  unescape = (str) -> str\gsub '\\([\'"\\])', '%1'

  @parse: (type, sep) =>
    switch type
      when 'num' then (match) -> @ 'num', (tonumber match), match
      when 'sym' then (match) -> @ 'sym', match, match
      when 'str' then (match) -> @ 'str', (unescape match), sep .. match .. sep

  @num: (num) -> Const 'num', num, tostring num
  @str: (str) -> Const 'str', str, "'#{str}'"
  @sym: (sym) -> Const 'sym', sym, sym
  @bool: (bool) -> Const 'bool', bool, tostring bool
  @empty: -> Const 'str', '', "''"

  @wrap: (val, name='(unknown)') ->
    typ = switch type val
      when 'number' then 'num'
      when 'string' then 'str'
      when 'table'
        if base = rawget val, '__base'
          -- a class
          switch ancestor val
            when Op then 'opdef'
            when Action then 'builtin'
            else
              error "#{name}: cannot wrap class '#{val.__name}'"
        elseif val.__class
          -- an instance
          switch ancestor val.__class
            when Op then 'op'
            when Scope then 'scope'
            when FnDef then 'fndef'
            when Const
              return val
            else
              error "#{name}: cannot wrap '#{val.__class.__name}' instance"
        else
          -- plain table
          return Const 'scope', Scope.from_table val
      else
        error "#{name}: cannot wrap Lua type '#{type val}'"

    Const typ, val

  @wrap_ref: (val) ->
    if base = rawget val, '__base'
      -- a class
      error "#{name}: cannot wrap_ref class '#{val.__name}'"
    elseif val.__class
      -- an instance
      switch ancestor val.__class
        when Op then Ref val
        when Const then val
        else
          error "#{name}: cannot wrap_ref '#{val.__class.__name}' instance"
    else
      error "#{name}: cannot wrap_ref Lua type '#{type val}'"

:Const, :load_