antlers 0.8.0 → 0.8.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 +4 -4
- data/lib/interfaces/lexeme.rb +13 -0
- data/lib/lexemes/for_lexeme.rb +28 -0
- data/lib/lexemes/form_lexeme.rb +25 -0
- data/lib/lexemes/if_lexeme.rb +22 -0
- data/lib/lexemes/prop_lexeme.rb +43 -0
- data/lib/lexemes/slot_lexeme.rb +26 -0
- data/lib/lexemes/yield_lexeme.rb +18 -0
- data/lib/lexer.rb +19 -103
- data/lib/nodes/prop_node.rb +3 -2
- data/lib/version.rb +1 -1
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8d3bcf6e14ade37294793b87b3ab3ab152e519f8c081a130644b0d28d489b63b
|
|
4
|
+
data.tar.gz: d9886a3044fb8654b37f111a9536ba7dfef4945c26fb08112e400ab14e76d8e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4179e613ca96ea49fdbc7d991a627a45640182ae5aaa5f01dc935396586ba8b583df469113c26c7a139308956bc18980cdbd04877c73a9b7cd14617682dd2d3
|
|
7
|
+
data.tar.gz: 698769c0d6bfd0e9872c5022f72555290acff3888615f44061ed10847af8f97af58477c35059678c2e02a2bdafc16d3f06ff514ab80aa539abc2de7cbfeda6bd
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/lexeme'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
module ForLexeme
|
|
7
|
+
include Lexeme
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
KEYWORDS = ['for:', 'in:', ':for'].freeze
|
|
11
|
+
|
|
12
|
+
def match?(keywords:, **)
|
|
13
|
+
KEYWORDS.include?(keywords.keys.first)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def lexeme(keywords:, **)
|
|
17
|
+
if keywords['for:']
|
|
18
|
+
*key, value = keywords['for:'].split(',').map(&:strip)
|
|
19
|
+
for_def = { for_def: value, in: keywords['in:'] }
|
|
20
|
+
for_def[:key] = key.first unless key.empty?
|
|
21
|
+
return for_def
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# TODO: Keep track of which for loop we're in to allow nested for loops.
|
|
25
|
+
{ for_end: 'level_1' }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/lexeme'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
module FormLexeme
|
|
7
|
+
include Lexeme
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
KEYWORDS = ['form:', ':form'].freeze
|
|
11
|
+
|
|
12
|
+
def match?(keywords:, **)
|
|
13
|
+
KEYWORDS.include?(keywords.keys.first)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def lexeme(keywords:, **)
|
|
17
|
+
if keywords.key?('form:')
|
|
18
|
+
action = keywords['form:'] ? keywords['form:'][1...-1] : nil
|
|
19
|
+
return { form_def: action }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
{ form_end: 'level_1' }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/lexeme'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
module IfLexeme
|
|
7
|
+
include Lexeme
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
KEYWORDS = ['if:', ':if'].freeze
|
|
11
|
+
|
|
12
|
+
def match?(keywords:, **)
|
|
13
|
+
KEYWORDS.include?(keywords.keys.first)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def lexeme(keywords:, **)
|
|
17
|
+
return { if_def: keywords['if:'] } if keywords.key?('if:')
|
|
18
|
+
|
|
19
|
+
{ if_end: 'level_1' }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/lexeme'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
module PropLexeme
|
|
7
|
+
include Lexeme
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def match?(name:, **)
|
|
11
|
+
name && [*'A'..'Z'].include?(name[0]) && !(name.start_with?(':') || name.end_with?(':'))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def lexeme(name:, props:, **)
|
|
15
|
+
prop = { prop: name }
|
|
16
|
+
prop[:props] = props(props) unless props.empty?
|
|
17
|
+
prop
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def props(props)
|
|
23
|
+
odd_props = props.join(' ').split(/(=)|\s/)
|
|
24
|
+
|
|
25
|
+
return {} unless odd_props.any?
|
|
26
|
+
|
|
27
|
+
props = {}
|
|
28
|
+
until odd_props.empty?
|
|
29
|
+
prop = odd_props.shift
|
|
30
|
+
value = nil
|
|
31
|
+
|
|
32
|
+
if odd_props.first == '='
|
|
33
|
+
odd_props.shift
|
|
34
|
+
value = odd_props.shift
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
props[prop.to_sym] = value
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
props
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/lexeme'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
module SlotLexeme
|
|
7
|
+
include Lexeme
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
KEYWORDS = ['slot:', ':slot'].freeze
|
|
11
|
+
|
|
12
|
+
def match?(name:, **)
|
|
13
|
+
name && (name.start_with?(':') || name.end_with?(':'))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def lexeme(name:, props:, **)
|
|
17
|
+
if name.end_with?(':')
|
|
18
|
+
slot_def = { slot_def: name.delete_suffix(':') }
|
|
19
|
+
slot_def[:props] = props(props) unless props.empty?
|
|
20
|
+
return slot_def
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
{ slot_end: name.delete_prefix(':') }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/lexeme'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
module YieldLexeme
|
|
7
|
+
include Lexeme
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def match?(keywords:, **)
|
|
11
|
+
keywords.keys.include?(':slot')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def lexeme(**)
|
|
15
|
+
{ slot: :default }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/lexer.rb
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'lexemes/for_lexeme'
|
|
4
|
+
require_relative 'lexemes/form_lexeme'
|
|
5
|
+
require_relative 'lexemes/if_lexeme'
|
|
6
|
+
require_relative 'lexemes/prop_lexeme'
|
|
7
|
+
require_relative 'lexemes/slot_lexeme'
|
|
8
|
+
require_relative 'lexemes/yield_lexeme'
|
|
3
9
|
require_relative 'support/queries'
|
|
4
10
|
|
|
5
11
|
module Antlers
|
|
@@ -7,14 +13,20 @@ module Antlers
|
|
|
7
13
|
|
|
8
14
|
class LexerParseError < StandardError; end
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
LEXEMES = [
|
|
17
|
+
# FormLexeme must be ordered before ForLexeme for regex to work.
|
|
18
|
+
Antlers::FormLexeme,
|
|
19
|
+
Antlers::ForLexeme,
|
|
20
|
+
Antlers::IfLexeme,
|
|
21
|
+
Antlers::PropLexeme,
|
|
22
|
+
Antlers::SlotLexeme,
|
|
23
|
+
Antlers::YieldLexeme,
|
|
24
|
+
]
|
|
14
25
|
|
|
26
|
+
class Lexer
|
|
15
27
|
def initialize
|
|
16
28
|
@delimiters = ['<{', '}>', '{', '}']
|
|
17
|
-
@keywords =
|
|
29
|
+
@keywords = LEXEMES.flat_map { |lexeme| lexeme.const_get(:KEYWORDS) }
|
|
18
30
|
@cursor = 0
|
|
19
31
|
end
|
|
20
32
|
|
|
@@ -55,12 +67,8 @@ module Antlers
|
|
|
55
67
|
|
|
56
68
|
name, props, keywords = parse_segment(antlers_segment:)
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
return
|
|
60
|
-
return prop(name:, props:) if prop?(name)
|
|
61
|
-
return if_block(keywords:) if if_block?(keywords:)
|
|
62
|
-
return for_loop(keywords:) if for_loop?(keywords:)
|
|
63
|
-
return form(keywords:) if form?(keywords:)
|
|
70
|
+
lexeme = LEXEMES.find { it.match?(name:, keywords:) }
|
|
71
|
+
return lexeme.lexeme(name:, props:, keywords:) if lexeme
|
|
64
72
|
return var(antlers_segment:, raw: true) if deerheads?(segments:)
|
|
65
73
|
|
|
66
74
|
raise LexerParseError, "Unrecognised syntax: '#{antlers_segment}'"
|
|
@@ -100,30 +108,6 @@ module Antlers
|
|
|
100
108
|
first == '<{' && last == '}>'
|
|
101
109
|
end
|
|
102
110
|
|
|
103
|
-
def if_block?(keywords:)
|
|
104
|
-
IF_KEYWORDS.include?(keywords.keys.first)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def for_loop?(keywords:)
|
|
108
|
-
FOR_KEYWORDS.include?(keywords.keys.first)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def form?(keywords:)
|
|
112
|
-
FORM_KEYWORDS.include?(keywords.keys.first)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def slot?(name)
|
|
116
|
-
name && (name.start_with?(':') || name.end_with?(':'))
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def slot_yield?(keywords:)
|
|
120
|
-
keywords.keys.include?(':slot')
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def prop?(name)
|
|
124
|
-
name && [*'A'..'Z'].include?(name[0])
|
|
125
|
-
end
|
|
126
|
-
|
|
127
111
|
def var(antlers_segment:, raw: false)
|
|
128
112
|
# String is already interpolated or not depending on user input on the template layer, now we store it without those template quotes.
|
|
129
113
|
antlers_segment = antlers_segment[1..-2] if Queries.user_defined_string?(antlers_segment)
|
|
@@ -132,73 +116,5 @@ module Antlers
|
|
|
132
116
|
|
|
133
117
|
{ var: antlers_segment }
|
|
134
118
|
end
|
|
135
|
-
|
|
136
|
-
def if_block(keywords:)
|
|
137
|
-
return { if_def: keywords['if:'] } if keywords.key?('if:')
|
|
138
|
-
|
|
139
|
-
{ if_end: 'level_1' }
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def for_loop(keywords:)
|
|
143
|
-
if keywords['for:']
|
|
144
|
-
*key, value = keywords['for:'].split(',').map(&:strip)
|
|
145
|
-
for_def = { for_def: value, in: keywords['in:'] }
|
|
146
|
-
for_def[:key] = key.first unless key.empty?
|
|
147
|
-
return for_def
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# TODO: Keep track of which for loop we're in to allow nested for loops.
|
|
151
|
-
{ for_end: 'level_1' }
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def form(keywords:)
|
|
155
|
-
if keywords.key?('form:')
|
|
156
|
-
action = keywords['form:'] ? keywords['form:'][1...-1] : nil
|
|
157
|
-
return { form_def: action }
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
{ form_end: 'level_1' }
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def slot(name:, props:)
|
|
164
|
-
if name.end_with?(':')
|
|
165
|
-
slot_def = { slot_def: name.delete_suffix(':') }
|
|
166
|
-
slot_def[:props] = props(props) unless props.empty?
|
|
167
|
-
return slot_def
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
{ slot_end: name.delete_prefix(':') }
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def slot_yield
|
|
174
|
-
{ slot: :default }
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def prop(name:, props:)
|
|
178
|
-
prop = { prop: name }
|
|
179
|
-
prop[:props] = props(props) unless props.empty?
|
|
180
|
-
prop
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def props(props)
|
|
184
|
-
odd_props = props.join(' ').split(/(=)|\s/)
|
|
185
|
-
|
|
186
|
-
return {} unless odd_props.any?
|
|
187
|
-
|
|
188
|
-
props = {}
|
|
189
|
-
until odd_props.empty?
|
|
190
|
-
prop = odd_props.shift
|
|
191
|
-
value = nil
|
|
192
|
-
|
|
193
|
-
if odd_props.first == '='
|
|
194
|
-
odd_props.shift
|
|
195
|
-
value = odd_props.shift
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
props[prop.to_sym] = value
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
props
|
|
202
|
-
end
|
|
203
119
|
end
|
|
204
120
|
end
|
data/lib/nodes/prop_node.rb
CHANGED
|
@@ -23,8 +23,8 @@ module Antlers
|
|
|
23
23
|
# TODO: Get LowLoad to load constants defined in "<{ MyNode }>" syntax so that we can resolve namespace/params on class load.
|
|
24
24
|
klass = class_constant(namespace: @namespace&.split('::') || [], name: @name)
|
|
25
25
|
class_proxy = Lowkey[klass.to_s].first[klass.to_s]
|
|
26
|
-
instance = create_instance(class_proxy:, klass:, event:)
|
|
27
26
|
|
|
27
|
+
instance = create_instance(class_proxy:, klass:, event:, props:)
|
|
28
28
|
return instance.render_template(event:, parent_binding:, props:) if klass.template
|
|
29
29
|
|
|
30
30
|
render_args(class_proxy:, instance:, event:, props:)
|
|
@@ -32,9 +32,10 @@ module Antlers
|
|
|
32
32
|
|
|
33
33
|
private
|
|
34
34
|
|
|
35
|
-
def create_instance(class_proxy:, klass:, event:)
|
|
35
|
+
def create_instance(class_proxy:, klass:, event:, props:)
|
|
36
36
|
initialize_params = class_proxy.instance_methods[:initialize]&.tagged_params(:keyword)&.map(&:name) || []
|
|
37
37
|
return klass.new(event:, **props) if initialize_params.include?(:event) && initialize_params.count > 1
|
|
38
|
+
return klass.new(**props) if initialize_params.count > 1
|
|
38
39
|
return klass.new(event:) if initialize_params.include?(:event)
|
|
39
40
|
|
|
40
41
|
klass.new
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: antlers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -64,6 +64,13 @@ files:
|
|
|
64
64
|
- lib/interfaces/antler_node.rb
|
|
65
65
|
- lib/interfaces/branch_node.rb
|
|
66
66
|
- lib/interfaces/leaf_node.rb
|
|
67
|
+
- lib/interfaces/lexeme.rb
|
|
68
|
+
- lib/lexemes/for_lexeme.rb
|
|
69
|
+
- lib/lexemes/form_lexeme.rb
|
|
70
|
+
- lib/lexemes/if_lexeme.rb
|
|
71
|
+
- lib/lexemes/prop_lexeme.rb
|
|
72
|
+
- lib/lexemes/slot_lexeme.rb
|
|
73
|
+
- lib/lexemes/yield_lexeme.rb
|
|
67
74
|
- lib/lexer.rb
|
|
68
75
|
- lib/modules/namespace.rb
|
|
69
76
|
- lib/modules/props.rb
|