hokusai-zero 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Dockerfile +26 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +91 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/ast/genheader +3 -0
- data/ast/include/hashmap.c +1151 -0
- data/ast/include/hashmap.c.license +20 -0
- data/ast/include/hashmap.h +54 -0
- data/ast/src/core/ast.c +448 -0
- data/ast/src/core/ast.h +259 -0
- data/ast/src/core/common.h +24 -0
- data/ast/src/core/component.c +85 -0
- data/ast/src/core/component.h +35 -0
- data/ast/src/core/hml.c +665 -0
- data/ast/src/core/hml.h +11 -0
- data/ast/src/core/input.c +458 -0
- data/ast/src/core/input.h +118 -0
- data/ast/src/core/style.c +101 -0
- data/ast/src/core/style.h +41 -0
- data/ast/src/core/text.c +784 -0
- data/ast/src/core/text.h +93 -0
- data/ast/src/core/util.c +140 -0
- data/ast/src/core/util.h +48 -0
- data/ast/src/hokusai.c +6 -0
- data/ast/src/hokusai.h +6 -0
- data/ast/test/fixtures/test.ui +13 -0
- data/ast/test/greatest.h +1266 -0
- data/ast/test/hokusai.c +14 -0
- data/ast/test/parser.c +234 -0
- data/ast/test/text.c +116 -0
- data/ext/extconf.rb +27 -0
- data/grammar/Cargo.lock +80 -0
- data/grammar/Cargo.toml +26 -0
- data/grammar/binding.gyp +20 -0
- data/grammar/bindings/node/binding.cc +28 -0
- data/grammar/bindings/node/index.js +19 -0
- data/grammar/bindings/rust/build.rs +40 -0
- data/grammar/bindings/rust/lib.rs +52 -0
- data/grammar/corpus/1_document.txt +131 -0
- data/grammar/corpus/2_selectors.txt +58 -0
- data/grammar/corpus/3_spaces.txt +69 -0
- data/grammar/corpus/4_errors.txt +10 -0
- data/grammar/corpus/5_macros.txt +175 -0
- data/grammar/corpus/6_styles.txt +81 -0
- data/grammar/grammar.js +275 -0
- data/grammar/package-lock.json +34 -0
- data/grammar/package.json +33 -0
- data/grammar/src/grammar.json +1269 -0
- data/grammar/src/node-types.json +474 -0
- data/grammar/src/parser.c +5772 -0
- data/grammar/src/scanner.c +258 -0
- data/grammar/src/tree_sitter/parser.h +230 -0
- data/grammar/src/tree_sitter/scanner.h +12 -0
- data/grammar/test.nml +10 -0
- data/hokusai.gemspec +19 -0
- data/ui/examples/assets/DigitalDisplay.ttf +0 -0
- data/ui/examples/assets/OpenSans-Regular.ttf +0 -0
- data/ui/examples/assets/addy.png +0 -0
- data/ui/examples/assets/baby_sean.png +0 -0
- data/ui/examples/assets/football-troll.png +0 -0
- data/ui/examples/assets/gear.png +0 -0
- data/ui/examples/assets/icecold.ttf +0 -0
- data/ui/examples/assets/science-troll.png +0 -0
- data/ui/examples/buddy.rb +31 -0
- data/ui/examples/clock.rb +58 -0
- data/ui/examples/counter.rb +123 -0
- data/ui/examples/dynamic.rb +147 -0
- data/ui/examples/foobar.rb +236 -0
- data/ui/examples/stock.rb +115 -0
- data/ui/examples/stock_decider/option.rb +74 -0
- data/ui/examples/tic_tac_toe.rb +246 -0
- data/ui/lib/lib_hokusai.rb +425 -0
- data/ui/spec/hokusai/ast_spec.rb +88 -0
- data/ui/spec/hokusai/automation/keys_transcoder_spec.rb +50 -0
- data/ui/spec/hokusai/automation/selector_spec.rb +68 -0
- data/ui/spec/hokusai/block_spec.rb +126 -0
- data/ui/spec/hokusai/directives_spec.rb +327 -0
- data/ui/spec/hokusai/e2e/client_spec.rb +58 -0
- data/ui/spec/hokusai/e2e/meta_spec.rb +42 -0
- data/ui/spec/hokusai/publisher_spec.rb +38 -0
- data/ui/spec/hokusai/slots_spec.rb +150 -0
- data/ui/spec/hokusai/util/piece_table_spec.rb +90 -0
- data/ui/spec/hokusai_spec.rb +0 -0
- data/ui/spec/spec_helper.rb +30 -0
- data/ui/src/hokusai/ast.rb +446 -0
- data/ui/src/hokusai/automation/client.rb +167 -0
- data/ui/src/hokusai/automation/constants.rb +98 -0
- data/ui/src/hokusai/automation/converters/selector_converter.rb +61 -0
- data/ui/src/hokusai/automation/driver.rb +54 -0
- data/ui/src/hokusai/automation/driver_command_queue.rb +50 -0
- data/ui/src/hokusai/automation/driver_commands/base.rb +79 -0
- data/ui/src/hokusai/automation/driver_commands/get_attribute.rb +41 -0
- data/ui/src/hokusai/automation/driver_commands/invoke.rb +33 -0
- data/ui/src/hokusai/automation/driver_commands/locate.rb +48 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_keyboard.rb +94 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_mouse.rb +213 -0
- data/ui/src/hokusai/automation/keys_transcoder.rb +128 -0
- data/ui/src/hokusai/automation/selector.rb +39 -0
- data/ui/src/hokusai/automation/server.rb +114 -0
- data/ui/src/hokusai/automation.rb +3 -0
- data/ui/src/hokusai/backends/raylib/config.rb +47 -0
- data/ui/src/hokusai/backends/raylib/font.rb +113 -0
- data/ui/src/hokusai/backends/raylib/keys.rb +124 -0
- data/ui/src/hokusai/backends/raylib.rb +449 -0
- data/ui/src/hokusai/backends/sdl2/Monaco.ttf +0 -0
- data/ui/src/hokusai/backends/sdl2/color.rb +12 -0
- data/ui/src/hokusai/backends/sdl2/config.rb +31 -0
- data/ui/src/hokusai/backends/sdl2/font.rb +127 -0
- data/ui/src/hokusai/backends/sdl2/keys.rb +119 -0
- data/ui/src/hokusai/backends/sdl2.rb +529 -0
- data/ui/src/hokusai/block.rb +237 -0
- data/ui/src/hokusai/blocks/button.rb +100 -0
- data/ui/src/hokusai/blocks/checkbox.rb +51 -0
- data/ui/src/hokusai/blocks/circle.rb +28 -0
- data/ui/src/hokusai/blocks/clipped.rb +23 -0
- data/ui/src/hokusai/blocks/cursor.rb +49 -0
- data/ui/src/hokusai/blocks/dynamic.rb +37 -0
- data/ui/src/hokusai/blocks/empty.rb +10 -0
- data/ui/src/hokusai/blocks/hblock.rb +35 -0
- data/ui/src/hokusai/blocks/image.rb +18 -0
- data/ui/src/hokusai/blocks/input.rb +200 -0
- data/ui/src/hokusai/blocks/label.rb +39 -0
- data/ui/src/hokusai/blocks/panel.rb +126 -0
- data/ui/src/hokusai/blocks/rect.rb +24 -0
- data/ui/src/hokusai/blocks/scissor_begin.rb +18 -0
- data/ui/src/hokusai/blocks/scissor_end.rb +12 -0
- data/ui/src/hokusai/blocks/scrollbar.rb +103 -0
- data/ui/src/hokusai/blocks/selectable.rb +77 -0
- data/ui/src/hokusai/blocks/svg.rb +20 -0
- data/ui/src/hokusai/blocks/text.rb +214 -0
- data/ui/src/hokusai/blocks/titlebar/osx.rb +145 -0
- data/ui/src/hokusai/blocks/toggle.rb +55 -0
- data/ui/src/hokusai/blocks/vblock.rb +35 -0
- data/ui/src/hokusai/commands/base.rb +22 -0
- data/ui/src/hokusai/commands/circle.rb +47 -0
- data/ui/src/hokusai/commands/image.rb +45 -0
- data/ui/src/hokusai/commands/rect.rb +158 -0
- data/ui/src/hokusai/commands/scissor.rb +22 -0
- data/ui/src/hokusai/commands/text.rb +92 -0
- data/ui/src/hokusai/commands.rb +87 -0
- data/ui/src/hokusai/diff.rb +124 -0
- data/ui/src/hokusai/error.rb +3 -0
- data/ui/src/hokusai/event.rb +54 -0
- data/ui/src/hokusai/events/keyboard.rb +84 -0
- data/ui/src/hokusai/events/mouse.rb +172 -0
- data/ui/src/hokusai/font.rb +280 -0
- data/ui/src/hokusai/meta.rb +152 -0
- data/ui/src/hokusai/mounting/loop_entry.rb +230 -0
- data/ui/src/hokusai/mounting/mount_entry.rb +74 -0
- data/ui/src/hokusai/mounting/update_entry.rb +101 -0
- data/ui/src/hokusai/node.rb +98 -0
- data/ui/src/hokusai/node_mounter.rb +102 -0
- data/ui/src/hokusai/painter.rb +214 -0
- data/ui/src/hokusai/publisher.rb +32 -0
- data/ui/src/hokusai/style.rb +72 -0
- data/ui/src/hokusai/types.rb +266 -0
- data/ui/src/hokusai/util/clamping_iterator.rb +202 -0
- data/ui/src/hokusai/util/piece_table.rb +111 -0
- data/ui/src/hokusai/util/selection.rb +145 -0
- data/ui/src/hokusai.rb +120 -0
- data/ui/vendor/.gitkeep +0 -0
- data/vendor/.gitkeep +0 -0
- data/xmake.lua +192 -0
- metadata +222 -0
@@ -0,0 +1,230 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require_relative "../diff"
|
3
|
+
|
4
|
+
module Hokusai
|
5
|
+
module Mounting
|
6
|
+
class LoopContext
|
7
|
+
attr_reader :table
|
8
|
+
def initialize
|
9
|
+
@table = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_entry(var, value)
|
13
|
+
table[var] = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def send_target(target, func)
|
17
|
+
args = func.args.map do |arg|
|
18
|
+
table[arg]
|
19
|
+
end
|
20
|
+
|
21
|
+
target.public_send(func.method, *args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class LoopEntry
|
26
|
+
extend Forwardable
|
27
|
+
|
28
|
+
INDEX_KEY = "index".freeze
|
29
|
+
|
30
|
+
def_delegators :@entry, :ast, :block, :target, :parent
|
31
|
+
|
32
|
+
def initialize(mount_entry)
|
33
|
+
@entry = mount_entry
|
34
|
+
end
|
35
|
+
|
36
|
+
def register
|
37
|
+
child_block_class = target.class.use(ast.type)
|
38
|
+
values = target.public_send(ast.loop.method)
|
39
|
+
|
40
|
+
unless values.is_a?(Enumerable)
|
41
|
+
raise Hokusai::Error.new("Loop directive `#{ast.loop.method}` on #{target.class} must return an Enumerable")
|
42
|
+
end
|
43
|
+
|
44
|
+
entries_to_return = []
|
45
|
+
secondary_entries = []
|
46
|
+
|
47
|
+
# puts "COMPILING #{ast.type}"
|
48
|
+
# portal = Node.new(ast)
|
49
|
+
# node = child_block_class.compile(ast.type, portal)
|
50
|
+
|
51
|
+
values.each_with_index do |value, index|
|
52
|
+
ctx = LoopContext.new
|
53
|
+
ctx.add_entry(ast.loop.var, value)
|
54
|
+
ctx.add_entry(INDEX_KEY, index)
|
55
|
+
|
56
|
+
if ast.has_if_condition?
|
57
|
+
if ast.if.args.size > 0
|
58
|
+
ctx.send_target(target, target.if)
|
59
|
+
else
|
60
|
+
condition = target.public_send(ast.if.method)
|
61
|
+
end
|
62
|
+
|
63
|
+
next if condition
|
64
|
+
end
|
65
|
+
|
66
|
+
portal = Node.new(ast)
|
67
|
+
node = child_block_class.compile(ast.type, portal)
|
68
|
+
child_block = child_block_class.new(node: node)
|
69
|
+
child_block.node.add_styles(target.class)
|
70
|
+
child_block.node.add_props_from_block(target, context: ctx)
|
71
|
+
child_block.node.meta.props[ast.loop.var.to_sym] = value
|
72
|
+
child_block.node.meta.publisher.add(target)
|
73
|
+
|
74
|
+
UpdateEntry.new(child_block, block, target).register(context: ctx)
|
75
|
+
|
76
|
+
child_block.class.provides.merge!(parent.class.provides)
|
77
|
+
|
78
|
+
block.node.meta << child_block
|
79
|
+
|
80
|
+
node.ast.children.each_with_index do |child, idx|
|
81
|
+
entries_to_return << MountEntry.new(index, child, child_block, child_block, child_block, context: nil)
|
82
|
+
end
|
83
|
+
|
84
|
+
siblings = []
|
85
|
+
portal.ast.children.each_with_index do |child, idx|
|
86
|
+
siblings << MountEntry.new(idx, child, child_block, child_block, target, context: ctx)
|
87
|
+
end
|
88
|
+
|
89
|
+
secondary_entries << siblings
|
90
|
+
end
|
91
|
+
|
92
|
+
update_loop
|
93
|
+
|
94
|
+
[entries_to_return, secondary_entries]
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_loop
|
98
|
+
block.node.meta.on_update(target) do |ublock, uparent, utarget|
|
99
|
+
values = utarget.public_send(ast.loop.method)
|
100
|
+
|
101
|
+
unless values.is_a?(Enumerable)
|
102
|
+
raise Hokusai::Error.new("Loop directive `#{ast.loop.method}` on #{target.class} must return an Enumerable")
|
103
|
+
end
|
104
|
+
|
105
|
+
key_prop = ast.props["key"]
|
106
|
+
|
107
|
+
raise Hokusai::Error.new("Loop children must have a :key method defined") if key_prop.nil?
|
108
|
+
|
109
|
+
key_ctx = LoopContext.new
|
110
|
+
|
111
|
+
new_values = []
|
112
|
+
|
113
|
+
index_key = "index".freeze
|
114
|
+
values.each_with_index do |value, index|
|
115
|
+
key_ctx.add_entry(ast.loop.var, value)
|
116
|
+
key_ctx.add_entry(index_key, index)
|
117
|
+
|
118
|
+
if key_prop.value.args.size > 0
|
119
|
+
key = key_ctx.send_target(utarget, key_prop.value)
|
120
|
+
elsif key_ctx.table[key_prop.value.method]
|
121
|
+
key = key_ctx.table[key_prop.value.method]
|
122
|
+
else
|
123
|
+
key = utarget.public_send(key_prop.value.method)
|
124
|
+
end
|
125
|
+
|
126
|
+
new_values << [key, value]
|
127
|
+
end
|
128
|
+
|
129
|
+
previous_values = []
|
130
|
+
children = []
|
131
|
+
loop_var = ast.loop.var.to_sym
|
132
|
+
|
133
|
+
ublock.children?&.each do |child|
|
134
|
+
if key = child.node.meta.get_prop(:key)
|
135
|
+
raise Hokusai::Error.new("Loop children must use :key field") unless key
|
136
|
+
|
137
|
+
previous_values << [key, child.node.meta.get_prop(loop_var)]
|
138
|
+
end
|
139
|
+
|
140
|
+
children << child
|
141
|
+
end
|
142
|
+
|
143
|
+
next if new_values == previous_values
|
144
|
+
|
145
|
+
Diff.new(previous_values, new_values).patch do |patch|
|
146
|
+
case patch
|
147
|
+
when UpdatePatch
|
148
|
+
ctx = LoopContext.new
|
149
|
+
ctx.add_entry("index", patch.target)
|
150
|
+
ctx.add_entry(ast.loop.var, patch.value)
|
151
|
+
children[patch.target].node.add_styles(target.class)
|
152
|
+
children[patch.target].node.add_props_from_block(target, context: ctx)
|
153
|
+
children[patch.target].node.meta.set_prop(ast.loop.var.to_sym, patch.value)
|
154
|
+
|
155
|
+
UpdateEntry.new(children[patch.target], uparent, utarget).register(context: ctx)
|
156
|
+
when MovePatch
|
157
|
+
if patch.delete
|
158
|
+
from = children[patch.from]
|
159
|
+
children[patch.to] = from
|
160
|
+
children[patch.from].public_send(:on_destroy) if children[patch.from].respond_to? :on_destroy
|
161
|
+
children[patch.from].node.destroy
|
162
|
+
children[patch.from] = nil
|
163
|
+
else
|
164
|
+
from = children[patch.from]
|
165
|
+
to = children[patch.to]
|
166
|
+
|
167
|
+
children[patch.to] = from
|
168
|
+
children[patch.from] = to
|
169
|
+
end
|
170
|
+
|
171
|
+
ctx = LoopContext.new
|
172
|
+
ctx.add_entry(INDEX_KEY, patch.to)
|
173
|
+
children[patch.to].node.meta.props.each do |k, v|
|
174
|
+
ctx.add_entry(k.to_s, v)
|
175
|
+
end
|
176
|
+
|
177
|
+
children[patch.to].node.add_styles(target.class)
|
178
|
+
children[patch.to].node.add_props_from_block(target, context: ctx)
|
179
|
+
when InsertPatch
|
180
|
+
target_ast = ast
|
181
|
+
ctx = LoopContext.new
|
182
|
+
ctx.add_entry(INDEX_KEY, patch.target)
|
183
|
+
ctx.add_entry(ast.loop.var, patch.value)
|
184
|
+
|
185
|
+
if ast.has_if_condition?
|
186
|
+
if ast.if.args.size > 0
|
187
|
+
condition = ctx.send_target(target, ast.if.method)
|
188
|
+
else
|
189
|
+
condition = target.public_send(ast.if.method)
|
190
|
+
end
|
191
|
+
|
192
|
+
if !condition && ast.has_else_condition?
|
193
|
+
target_ast = ast.else_ast
|
194
|
+
elsif !condition
|
195
|
+
children[patch.target].public_send(:on_destroy) if children[patch.target].respond_to? :on_destroy
|
196
|
+
children[patch.target].node.destroy
|
197
|
+
children[patch.target] = nil
|
198
|
+
next
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
child_block_class = utarget.class.use(target_ast.type)
|
203
|
+
portal = Node.new(ast)
|
204
|
+
node = child_block_class.compile(target_ast.type, portal)
|
205
|
+
node.add_props_from_block(target, context: ctx)
|
206
|
+
child_block = NodeMounter.new(node, child_block_class).mount(context: ctx)
|
207
|
+
child_block.node.add_styles(target.class)
|
208
|
+
child_block.node.meta.publisher.add(target)
|
209
|
+
|
210
|
+
child_block.class.provides.merge!(parent.class.provides)
|
211
|
+
|
212
|
+
if patch.delete
|
213
|
+
children[patch.target] = child_block
|
214
|
+
else
|
215
|
+
children.insert(patch.target, child_block)
|
216
|
+
end
|
217
|
+
when DeletePatch
|
218
|
+
children[patch.target].public_send(:on_destroy) if children[patch.target].respond_to? :on_destroy
|
219
|
+
children[patch.target].node.destroy
|
220
|
+
children[patch.target] = nil
|
221
|
+
# TODO: update rest of block props
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
ublock.node.meta.children = children.reject(&:nil?)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Hokusai
|
2
|
+
module Mounting
|
3
|
+
class MountEntry
|
4
|
+
attr_reader :block, :parent, :ast, :target, :index, :ctx
|
5
|
+
|
6
|
+
def initialize(index, ast, block, parent, target = parent, context: nil)
|
7
|
+
@index = index
|
8
|
+
@ast = ast
|
9
|
+
@block = block
|
10
|
+
@parent = parent
|
11
|
+
@target = target
|
12
|
+
@ctx = context
|
13
|
+
end
|
14
|
+
|
15
|
+
def loop?
|
16
|
+
ast.loop?
|
17
|
+
end
|
18
|
+
|
19
|
+
def virtual?
|
20
|
+
ast.virtual?
|
21
|
+
end
|
22
|
+
|
23
|
+
def slot?
|
24
|
+
ast.slot?
|
25
|
+
end
|
26
|
+
|
27
|
+
def debug
|
28
|
+
StringIO.open do |io|
|
29
|
+
io << "#{block.class} | #{ast.type} (#{index})\n"
|
30
|
+
io << "#{block.node.ast.children.map(&:type)}"
|
31
|
+
io << "parent: #{parent.class}\n"
|
32
|
+
io << "target: #{target.class}\n\n"
|
33
|
+
end.string
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_block(new_block, supercede_parent: false)
|
37
|
+
parent_block = supercede_parent ? block : parent
|
38
|
+
MountEntry.new(index, ast, new_block, parent_block, target, context: ctx)
|
39
|
+
end
|
40
|
+
|
41
|
+
def mount(context: nil)
|
42
|
+
klass = target.class.use(ast.type)
|
43
|
+
portal = Node.new(ast)
|
44
|
+
|
45
|
+
node = klass.compile(ast.type, portal)
|
46
|
+
node.add_styles(target.class)
|
47
|
+
node.add_props_from_block(target, context: context || ctx)
|
48
|
+
|
49
|
+
child_block = klass.new(node: node)
|
50
|
+
child_block.node.meta.publisher.add(target) # todo
|
51
|
+
UpdateEntry.new(child_block, block, target).register(context: context || ctx)
|
52
|
+
|
53
|
+
block.class.provides.each do |k, v|
|
54
|
+
if v.is_a?(Symbol)
|
55
|
+
Node.provide(child_block.node, k, ->{ block.public_send(v) })
|
56
|
+
|
57
|
+
# child_block.node.meta.provides[k] = -> { block.public_send(v) }
|
58
|
+
# child_block.class.provides[k] = -> { block.public_send(v) }
|
59
|
+
else
|
60
|
+
Node.provide(child_block.node, k, v)
|
61
|
+
# child_block.node.meta.provides[k] = v
|
62
|
+
# child_block.class.provides[k] = v
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
block.node.meta << child_block
|
67
|
+
|
68
|
+
yield child_block
|
69
|
+
|
70
|
+
block.public_send(:on_mounted) if block.respond_to?(:on_mounted)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Hokusai
|
2
|
+
module Mounting
|
3
|
+
class UpdateEntry
|
4
|
+
attr_reader :block, :parent, :target
|
5
|
+
|
6
|
+
def initialize(block, parent, target)
|
7
|
+
@block = block
|
8
|
+
@parent = parent
|
9
|
+
@target = target
|
10
|
+
end
|
11
|
+
|
12
|
+
def meta
|
13
|
+
block.node.meta
|
14
|
+
end
|
15
|
+
|
16
|
+
def register(context: nil)
|
17
|
+
meta.on_update(target) do |ublock, uparent, utarget|
|
18
|
+
if portal = ublock.node.portal
|
19
|
+
portal.ast.children.each_with_index do |child, index|
|
20
|
+
next unless child.has_if_condition?
|
21
|
+
|
22
|
+
child_present = ->(child) do
|
23
|
+
meta.has_ast?(child, index)
|
24
|
+
end
|
25
|
+
|
26
|
+
if child.if.args.size > 0
|
27
|
+
visible = utarget.public_send(child.if.method, context: context)
|
28
|
+
else
|
29
|
+
visible = utarget.public_send(child.if.method)
|
30
|
+
end
|
31
|
+
child_block_klass = target.class.use(child.type)
|
32
|
+
|
33
|
+
if !!visible
|
34
|
+
if child.else_condition_active?
|
35
|
+
meta.child_delete(index) if child_present.call(child)
|
36
|
+
child.else_active = 0
|
37
|
+
end
|
38
|
+
|
39
|
+
unless child_present.call(child)
|
40
|
+
portal = Node.new(child, Node.new(child))
|
41
|
+
node = child_block_klass.compile("root", portal)
|
42
|
+
|
43
|
+
stack = []
|
44
|
+
child.children.each_with_index do |ast, ast_index|
|
45
|
+
stack << MountEntry.new(ast_index, ast, ublock, uparent, utarget)
|
46
|
+
end
|
47
|
+
|
48
|
+
child_block = NodeMounter.new(node, child_block_klass, [stack]).mount(context: context)
|
49
|
+
UpdateEntry.new(child_block, block, target).register(context: context)
|
50
|
+
|
51
|
+
child_block.node.add_styles(target.class)
|
52
|
+
child_block.node.add_props_from_block(target, context: context)
|
53
|
+
child_block.node.meta.publisher.add(target)
|
54
|
+
|
55
|
+
meta.children!.insert(index, child_block)
|
56
|
+
|
57
|
+
child_block.public_send(:before_updated) if child_block.respond_to?(:before_updated)
|
58
|
+
child_block.update
|
59
|
+
child.else_active = 0
|
60
|
+
end
|
61
|
+
elsif !visible
|
62
|
+
if !child.has_else_condition? || (child.has_else_condition? && !child.else_condition_active?)
|
63
|
+
if (child_present.call(child))
|
64
|
+
meta.child_delete(index)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if child.has_else_condition? && !child.else_condition_active?
|
69
|
+
portal = Node.new(child, Node.new(child))
|
70
|
+
else_child_block_klass = target.class.use(child.else_ast.type)
|
71
|
+
node = else_child_block_klass.compile(child.else_ast.type, portal)
|
72
|
+
|
73
|
+
stack = []
|
74
|
+
child.else_ast.children.each_with_index do |ast, ast_index|
|
75
|
+
stack << MountEntry.new(ast_index, ast, ublock, uparent, utarget)
|
76
|
+
end
|
77
|
+
|
78
|
+
child_block = NodeMounter.new(node, else_child_block_klass, [stack]).mount(context: context)
|
79
|
+
UpdateEntry.new(child_block, block, utarget).register(context: context)
|
80
|
+
|
81
|
+
child_block.node.add_styles(utarget.class)
|
82
|
+
child_block.node.add_props_from_block(utarget, context: context)
|
83
|
+
child_block.node.meta.publisher.add(utarget)
|
84
|
+
meta.children!.insert(index, child_block)
|
85
|
+
|
86
|
+
child_block.public_send(:before_updated) if child_block.respond_to?(:before_updated)
|
87
|
+
child_block.update
|
88
|
+
child.else_active = 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
ublock.public_send(:before_updated) if ublock.respond_to?(:before_updated)
|
95
|
+
ublock.node.add_props_from_block(utarget, context: context)
|
96
|
+
ublock.public_send(:after_updated) if ublock.respond_to?(:after_updated)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "random/formatter"
|
4
|
+
require "forwardable"
|
5
|
+
require_relative "./node_mounter"
|
6
|
+
require_relative "./meta"
|
7
|
+
|
8
|
+
module Hokusai
|
9
|
+
class Node
|
10
|
+
extend Forwardable
|
11
|
+
attr_reader :ast, :node, :uuid, :meta, :portal
|
12
|
+
|
13
|
+
def_delegators :@ast, :slot?, :type, :event
|
14
|
+
|
15
|
+
def self.provides
|
16
|
+
@provides ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.provide(node, key, value)
|
20
|
+
provides[node.uuid] ||= {}
|
21
|
+
provides[node.uuid][key] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse(template, name = "root", parent = nil)
|
25
|
+
ast = Ast.parse(template, name)
|
26
|
+
|
27
|
+
new(ast, parent)
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(ast, portal = nil)
|
31
|
+
@ast = ast
|
32
|
+
@portal = portal
|
33
|
+
@uuid = Random.hex(6).freeze
|
34
|
+
@meta = Meta.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def mount(klass)
|
38
|
+
NodeMounter.new(self, klass).mount
|
39
|
+
end
|
40
|
+
|
41
|
+
def destroy
|
42
|
+
# meta.children?&.each do |child|
|
43
|
+
# child.node.destroy
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# ast.destroy
|
47
|
+
end
|
48
|
+
|
49
|
+
def emit(name, **args)
|
50
|
+
if node = portal
|
51
|
+
if event = node.event(name)
|
52
|
+
meta.publisher.notify(event.value.name, **args)
|
53
|
+
else
|
54
|
+
raise Hokusai::Error.new("Invocation failed: @#{name} doesn't exist on #{node.type}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_styles(klass)
|
60
|
+
return if portal.nil?
|
61
|
+
|
62
|
+
portal.ast.style_list.each do |style_name|
|
63
|
+
style = klass.styles_get[style_name]
|
64
|
+
|
65
|
+
raise Hokusai::Error.new("Style #{style_name} doesn't exist in the styles for this block #{klass} - #{klass.styles_get.keys}") if style.nil?
|
66
|
+
|
67
|
+
style.each do |key, value|
|
68
|
+
meta.set_prop(key.to_sym, value)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_props_from_block(parent, context: nil)
|
74
|
+
if local_portal = portal
|
75
|
+
if block = parent
|
76
|
+
local_portal.ast.props.each do |_, prop|
|
77
|
+
method = prop.value.method
|
78
|
+
|
79
|
+
case prop.computed?
|
80
|
+
when true
|
81
|
+
if prop.value.args.size > 0 && context
|
82
|
+
value = context.send_target(block, prop.value)
|
83
|
+
elsif context&.table&.[](method)
|
84
|
+
value = context.table[method]
|
85
|
+
else
|
86
|
+
value = block.public_send(method)
|
87
|
+
end
|
88
|
+
else
|
89
|
+
value = method
|
90
|
+
end
|
91
|
+
|
92
|
+
meta.set_prop(prop.name.to_sym, value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require_relative "./mounting/loop_entry.rb"
|
2
|
+
require_relative "./mounting/mount_entry.rb"
|
3
|
+
require_relative "./mounting/update_entry.rb"
|
4
|
+
|
5
|
+
module Hokusai
|
6
|
+
class NodeMounter
|
7
|
+
attr_accessor :primary_stack, :secondary_stack
|
8
|
+
attr_reader :root
|
9
|
+
|
10
|
+
def initialize(node, klass, secondary_stack = [], previous_target = nil)
|
11
|
+
@root = klass.new(node: node)
|
12
|
+
@secondary_stack = secondary_stack
|
13
|
+
@primary_stack = []
|
14
|
+
|
15
|
+
node.ast.children.each_with_index do |child, index|
|
16
|
+
primary_stack << Mounting::MountEntry.new(index, child, root, root, previous_target || root)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def mount(context: nil)
|
21
|
+
while entry = primary_stack.shift
|
22
|
+
next if entry.virtual?
|
23
|
+
|
24
|
+
if entry.loop?
|
25
|
+
entries, secondary_entries = Mounting::LoopEntry.new(entry).register
|
26
|
+
|
27
|
+
self.primary_stack = entries + primary_stack
|
28
|
+
self.secondary_stack = secondary_entries + secondary_stack
|
29
|
+
|
30
|
+
next
|
31
|
+
end
|
32
|
+
|
33
|
+
if entry.ast.has_if_condition?
|
34
|
+
next unless entry.target.public_send(entry.ast.if.method)
|
35
|
+
end
|
36
|
+
|
37
|
+
if entry.slot?
|
38
|
+
while siblings = secondary_stack.shift
|
39
|
+
next if siblings.empty?
|
40
|
+
|
41
|
+
continue = false
|
42
|
+
|
43
|
+
while sibling_entry = siblings.pop
|
44
|
+
# if we encounter a nested slot, we will
|
45
|
+
# add the current siblings to the end of the next
|
46
|
+
# non-empty slot sibling group
|
47
|
+
# and continue processing slots
|
48
|
+
if sibling_entry.slot?
|
49
|
+
continue = true
|
50
|
+
|
51
|
+
secondary_stack.each_with_index do |previous_siblings, i|
|
52
|
+
next if previous_siblings.empty?
|
53
|
+
|
54
|
+
secondary_stack[i] = siblings + previous_siblings
|
55
|
+
siblings.clear
|
56
|
+
|
57
|
+
break
|
58
|
+
end
|
59
|
+
else
|
60
|
+
primary_stack.unshift sibling_entry.with_block(entry.block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
next if continue
|
65
|
+
break
|
66
|
+
end
|
67
|
+
|
68
|
+
next
|
69
|
+
end
|
70
|
+
|
71
|
+
entry.mount(context: context) do |child_block|
|
72
|
+
# create a subentry to register event handling and prop passing
|
73
|
+
Mounting::UpdateEntry.new(child_block, entry.block, entry.target).register(context: context || entry.ctx)
|
74
|
+
|
75
|
+
# Populate the secondary stack with the portal children
|
76
|
+
# this stack will be used to populate any slots in the primary_stack
|
77
|
+
items = []
|
78
|
+
|
79
|
+
entry.ast.children.each_with_index do |child, child_index|
|
80
|
+
child.has_if_condition?
|
81
|
+
items << Mounting::MountEntry.new(child_index, child, child_block, entry.parent, entry.target, context: entry.ctx)
|
82
|
+
end
|
83
|
+
|
84
|
+
secondary_stack.unshift items
|
85
|
+
|
86
|
+
# populate the primary stack with the newly compiled
|
87
|
+
# ast from child_block
|
88
|
+
primary_items = []
|
89
|
+
|
90
|
+
child_block.node.ast.children.each_with_index do |child, child_index|
|
91
|
+
primary_items << Mounting::MountEntry.new(child_index, child, child_block, child_block, context: entry.ctx)
|
92
|
+
end
|
93
|
+
|
94
|
+
self.primary_stack = primary_items + primary_stack
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
root.public_send(:on_mounted) if root.respond_to?(:on_mounted)
|
99
|
+
root
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|