aboutsummaryrefslogtreecommitdiffstats
path: root/alv/module.moon
blob: 8513192049970289c973b444c48075411d2d2302 (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
----
-- Per-file execution context.
--
-- @module module
import Registry from require 'alv.registry'
import Error from require 'alv.error'
import Scope from require 'alv.scope'
import program from require 'alv.parsing'
builtins = require 'alv.builtins'

--- Base class for Modules.
-- @type Module
class Module
  --- create a new Module.
  -- @classmethod
  new: =>
    @registry = Registry!

  --- check when the module source has last changed.
  -- @function poll
  -- @treturn number timestamp of last change

  --- get the module name.
  -- @function name
  -- @tparam[default=false] boolean full path
  -- @treturn string

  --- get the module contents.
  -- @function slurp
  -- @treturn string

  --- update the module contents.
  -- @function spit
  -- @tparam string str the updated contents

  --- start an evaluation cycle.
  --
  -- If the module has already been evaluated this tick, this is a noop.
  -- Otherwise, register the module with the `Copilot`. Updates `root`.
  eval: =>
    @registry\begin_eval!
    @ast = Error.wrap "parsing '#{@name true}'", -> program\match @slurp!
    assert @ast, Error 'syntax', "failed to parse"

    scope = Scope builtins!
    @root = Error.wrap "evaluating '#{@name true}'", @ast\eval, scope, @registry

  --- rollback the last evaluation cycle.
  rollback: => @registry\rollback_eval!

  --- finish the last evaluation cycle.
  finish: =>
    tags_changed = @registry\end_eval!
    if tags_changed
      @spit @ast\stringify!

  --- destroy this module.
  destroy: =>
    @registry\destroy!

  __tostring: => "<#{@@__name} #{@name!}>"
  __inherited: (cls) => cls.__base.__tostring = @__tostring

  --- the last updated AST tree for this module.
  -- @tfield ?AST ast

  --- the runtime graph root of this module.
  -- @tfield ?RTNode root

--- Module type for modules loaded from the filesystem.
-- @type FSModule
class FSModule extends Module
  --- create a new FSModule.
  -- @classmethod
  -- @tparam string file filepath
  new: (@file) =>
    super!

  slurp: =>
    file = assert (io.open @file, 'r'), Error 'io', "couldn't open '#{@file}'"
    with file\read '*all'
      file\close!

  spit: (str) =>
    file = io.open @file, 'w'
    file\write str
    file\close!

  poll: =>
    { :mode, :modification } = (lfs.attributes @file) or {}
    assert mode == 'file', Error 'io', "not a file: '#{@file}'"
    modification

  name: (full) =>
    if full then @file else @file\match '([^/\\]+)$'

--- Module type for modules loaded from RAM.
-- @type StringModule
class StringModule extends Module
  --- create a new StringModule.
  -- @classmethod
  -- @tparam string name_ module name
  -- @tparam string source module source code
  new: (@name_, @source) =>
    super!
    @updated = os.time!

  slurp: => @source

  spit: (str) =>
    @source = str
    @updated = os.time!

  poll: => @updated

  name: => @name_

{
  :Module
  :FSModule
  :StringModule
}