antlers 0.1.0 → 0.3.0
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/antlers.rb +7 -3
- data/lib/factories/node_factory.rb +12 -2
- data/lib/interfaces/antler_node.rb +5 -1
- data/lib/interfaces/branch_node.rb +26 -0
- data/lib/interfaces/leaf_node.rb +8 -0
- data/lib/lexer.rb +51 -15
- data/lib/modules/props.rb +49 -0
- data/lib/modules/variables.rb +19 -0
- data/lib/nodes/for_node.rb +36 -0
- data/lib/nodes/prop_node.rb +14 -6
- data/lib/nodes/root_node.rb +3 -7
- data/lib/nodes/slot_node.rb +18 -4
- data/lib/nodes/var_node.rb +18 -2
- data/lib/nodes/yield_node.rb +23 -0
- data/lib/parser.rb +22 -13
- data/lib/queries.rb +15 -0
- data/lib/version.rb +1 -1
- metadata +28 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57d224246ca8ba02aac3045b0f06904d8cebb0cfdb7f3d518d3c646324a1e3b6
|
|
4
|
+
data.tar.gz: e8b7787ccfc284dbb1d370292ba4238576e346c75bc957d5da70067220635ba7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4cb6632bbbafb3ec12cc27256b2ae9e2134e3b621e28a7edcfd269bfff828d128cd0f659d7135a734b7792d121477c0ca012c3215a1eef442d3106bb637e89d
|
|
7
|
+
data.tar.gz: 0a0f5f6fc83e72a7253a737956c3a4cff5961999722b23b96ad44716b3ad2b2e40ce7742145cea8610f9604467a438a12000a0609cd9408487f85472f362cb3d
|
data/lib/antlers.rb
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'lexer'
|
|
4
|
+
require_relative 'parser'
|
|
5
|
+
|
|
3
6
|
module Antlers
|
|
4
7
|
class << self
|
|
5
|
-
def
|
|
8
|
+
def ast(template)
|
|
6
9
|
return template unless template.include?('<{') || template.include?('{')
|
|
7
10
|
|
|
8
11
|
lexemes = Lexer.new.parse(template)
|
|
9
12
|
Parser.parse(lexemes)
|
|
10
13
|
end
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
def render(ast:, current_binding:, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
16
|
+
ast.render(current_binding:, parent_binding:, slot_node:, namespace:)
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
19
|
end
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../nodes/for_node'
|
|
3
4
|
require_relative '../nodes/prop_node'
|
|
4
5
|
require_relative '../nodes/slot_node'
|
|
5
6
|
require_relative '../nodes/var_node'
|
|
7
|
+
require_relative '../nodes/yield_node'
|
|
6
8
|
|
|
7
9
|
module Antlers
|
|
8
10
|
class NodeFactory
|
|
9
11
|
class << self
|
|
10
|
-
def
|
|
11
|
-
|
|
12
|
+
def for_node(segment:)
|
|
13
|
+
ForNode.new(name: segment[:for_def], item: segment[:for_def], items: segment[:in])
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def prop_node(segment:)
|
|
@@ -18,6 +20,14 @@ module Antlers
|
|
|
18
20
|
def slot_node(segment:)
|
|
19
21
|
SlotNode.new(name: segment[:slot_def], props: segment[:props])
|
|
20
22
|
end
|
|
23
|
+
|
|
24
|
+
def var_node(segment:)
|
|
25
|
+
VarNode.new(value: segment[:var])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def yield_node(segment:)
|
|
29
|
+
YieldNode.new(name: segment[:slot])
|
|
30
|
+
end
|
|
21
31
|
end
|
|
22
32
|
end
|
|
23
33
|
end
|
|
@@ -8,7 +8,11 @@ module Antlers
|
|
|
8
8
|
@name = name
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
12
|
+
raise NotImplementedError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Consider instance a value object on comparison.
|
|
12
16
|
def ==(other) = other.class == self.class
|
|
13
17
|
def eql?(other) = self == other
|
|
14
18
|
def hash = [self.class].hash
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'antler_node'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
class BranchNode < AntlerNode
|
|
7
|
+
attr_accessor :children
|
|
8
|
+
|
|
9
|
+
def initialize(name:, children: [])
|
|
10
|
+
super(name:)
|
|
11
|
+
|
|
12
|
+
@children = children
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
16
|
+
output = ''
|
|
17
|
+
|
|
18
|
+
@children.each do |child|
|
|
19
|
+
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
20
|
+
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node:, namespace:) : child) || ''
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
output
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/lexer.rb
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'queries'
|
|
4
|
+
|
|
3
5
|
module Antlers
|
|
6
|
+
extend Queries
|
|
7
|
+
|
|
4
8
|
class LexerParseError < StandardError; end
|
|
5
9
|
|
|
6
10
|
class Lexer
|
|
7
11
|
def initialize
|
|
8
12
|
@delimiters = ['<{', '}>', '{', '}']
|
|
9
|
-
@keywords = ['if:', 'for:', 'in:']
|
|
13
|
+
@keywords = ['if:', 'for:', 'in:', ':for', 'slot:', ':slot']
|
|
10
14
|
@cursor = 0
|
|
11
15
|
end
|
|
12
16
|
|
|
@@ -20,7 +24,7 @@ module Antlers
|
|
|
20
24
|
until segments[@cursor].nil?
|
|
21
25
|
if (antlers_segment = antlers_segment(segments:))
|
|
22
26
|
sequence << antlers_lexeme(antlers_segment:, segments:)
|
|
23
|
-
# Skipping: ['{', '
|
|
27
|
+
# Skipping: ['{', 'expression', '}']
|
|
24
28
|
# Skipping: ['<{', 'name + props + keywords', '}>']
|
|
25
29
|
@cursor += 3
|
|
26
30
|
else
|
|
@@ -37,44 +41,72 @@ module Antlers
|
|
|
37
41
|
|
|
38
42
|
def antlers_segment(segments:)
|
|
39
43
|
next_segment = segments[@cursor + 1]
|
|
40
|
-
return nil unless next_segment && (segments[@cursor] == '<{' ||
|
|
44
|
+
return nil unless next_segment && (segments[@cursor] == '<{' || var?(segments:))
|
|
41
45
|
|
|
42
46
|
next_segment
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
def antlers_lexeme(antlers_segment:, segments:)
|
|
46
|
-
return
|
|
50
|
+
return var(antlers_segment:) if var?(segments:)
|
|
47
51
|
|
|
48
|
-
name, props,
|
|
52
|
+
name, props, keywords = parse_segment(antlers_segment:)
|
|
49
53
|
|
|
54
|
+
return slot_yield if slot_yield?(keywords)
|
|
50
55
|
return slot(name:, props:) if slot?(name)
|
|
51
56
|
return prop(name:, props:) if prop?(name)
|
|
57
|
+
return for_loop(keywords:) if for_loop?(keywords:)
|
|
52
58
|
|
|
53
|
-
raise LexerParseError, "
|
|
59
|
+
raise LexerParseError, "Unrecognised syntax: '#{antlers_segment}'"
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def parse_segment(antlers_segment:)
|
|
57
63
|
name_and_props, *keywords = antlers_segment.split(/(#{Regexp.union(@keywords)})/)
|
|
58
64
|
name, *props = name_and_props.split(' ')
|
|
59
65
|
|
|
60
|
-
[name, props, keywords]
|
|
66
|
+
[name, props, keywords.map(&:strip)]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def var?(segments:)
|
|
70
|
+
first, middle, last = segments[@cursor..@cursor + 3].map(&:strip)
|
|
71
|
+
first == '{' && last == '}'
|
|
61
72
|
end
|
|
62
73
|
|
|
63
|
-
def
|
|
64
|
-
first
|
|
65
|
-
first == '{' && second&.start_with?('@') && third == '}'
|
|
74
|
+
def for_loop?(keywords:)
|
|
75
|
+
keywords.first == 'for:' || keywords.first == ':for'
|
|
66
76
|
end
|
|
67
77
|
|
|
68
78
|
def slot?(name)
|
|
69
|
-
name.start_with?(':') || name.end_with?(':')
|
|
79
|
+
name && (name.start_with?(':') || name.end_with?(':'))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def slot_yield?(keywords)
|
|
83
|
+
keywords.include?(':slot')
|
|
70
84
|
end
|
|
71
85
|
|
|
72
86
|
def prop?(name)
|
|
73
|
-
[*'A'..'Z'].include?(name[0])
|
|
87
|
+
name && [*'A'..'Z'].include?(name[0])
|
|
74
88
|
end
|
|
75
89
|
|
|
76
|
-
def
|
|
77
|
-
|
|
90
|
+
def var(antlers_segment:)
|
|
91
|
+
# String is already interpolated or not depending on user input on the template layer, now we store it without those template quotes.
|
|
92
|
+
if Queries.user_defined_string?(antlers_segment)
|
|
93
|
+
antlers_segment = antlers_segment[1..-2]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
{ var: antlers_segment }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def for_loop(keywords:)
|
|
100
|
+
key_values = keywords.count % 2 == 0 ? keywords.each_slice(2).to_h : {}
|
|
101
|
+
|
|
102
|
+
if key_values['for:']
|
|
103
|
+
for_def = { for_def: key_values['for:'] }
|
|
104
|
+
for_def[:in] = key_values['in:']
|
|
105
|
+
return for_def
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# TODO: Keep track of which for loop we're in to allow nested for loops.
|
|
109
|
+
{ for_end: 'level_1' }
|
|
78
110
|
end
|
|
79
111
|
|
|
80
112
|
def slot(name:, props:)
|
|
@@ -87,6 +119,10 @@ module Antlers
|
|
|
87
119
|
{ slot_end: name.delete_prefix(':') }
|
|
88
120
|
end
|
|
89
121
|
|
|
122
|
+
def slot_yield
|
|
123
|
+
{ slot: :default }
|
|
124
|
+
end
|
|
125
|
+
|
|
90
126
|
def prop(name:, props:)
|
|
91
127
|
prop = { prop: name }
|
|
92
128
|
prop[:props] = props(props) unless props.empty?
|
|
@@ -108,7 +144,7 @@ module Antlers
|
|
|
108
144
|
value = odd_props.shift
|
|
109
145
|
end
|
|
110
146
|
|
|
111
|
-
props[prop] = value
|
|
147
|
+
props[prop.to_sym] = value
|
|
112
148
|
end
|
|
113
149
|
|
|
114
150
|
props
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'low_event'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
module Props
|
|
7
|
+
attr_accessor :props
|
|
8
|
+
|
|
9
|
+
def initialize(name:, props: {}, **)
|
|
10
|
+
super(name:, **)
|
|
11
|
+
|
|
12
|
+
@props = props
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def create_render_event(props:)
|
|
18
|
+
Low::Events::RenderEvent.new(action: :render, props:)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def evaluate_props(props:, current_binding:)
|
|
22
|
+
return {} if props.nil?
|
|
23
|
+
|
|
24
|
+
evaluated_props = {}
|
|
25
|
+
|
|
26
|
+
props.each do |name, value|
|
|
27
|
+
receiver = current_binding.receiver
|
|
28
|
+
|
|
29
|
+
if receiver.respond_to?(value.to_sym)
|
|
30
|
+
evaluated_props[name] = receiver.send(value.to_sym)
|
|
31
|
+
elsif value.start_with?('@')
|
|
32
|
+
evaluated_props[name] = receiver.instance_variable_get(value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
evaluated_props
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def class_from_namespace(namespace:, name:)
|
|
40
|
+
return Object.const_get(name) if Object.const_defined?(name) || name.start_with?('::') || namespace.empty?
|
|
41
|
+
|
|
42
|
+
namespace_with_name = [namespace, name].join('::')
|
|
43
|
+
return Object.const_get(namespace_with_name) if Object.const_defined?(namespace_with_name)
|
|
44
|
+
|
|
45
|
+
namespace.pop
|
|
46
|
+
class_from_namespace(namespace:, name:)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Antlers
|
|
2
|
+
module Variables
|
|
3
|
+
# A variable is deliberately limited in what it can represent.
|
|
4
|
+
# 1. An instance variable
|
|
5
|
+
# 2. A method call/local variable
|
|
6
|
+
# 3. A static string
|
|
7
|
+
def evaluate_variable(name:, current_binding:)
|
|
8
|
+
if current_binding
|
|
9
|
+
return current_binding.receiver.instance_variable_get(name) if name.start_with?('@')
|
|
10
|
+
return current_binding.local_variable_get(name) if current_binding.local_variable_defined?(name)
|
|
11
|
+
return current_binding.receiver.send(name.to_sym) if current_binding.receiver.respond_to?(name.to_sym)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@value.to_s
|
|
15
|
+
rescue NameError
|
|
16
|
+
@value.to_s
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/branch_node'
|
|
4
|
+
require_relative '../modules/props'
|
|
5
|
+
require_relative '../modules/variables'
|
|
6
|
+
|
|
7
|
+
module Antlers
|
|
8
|
+
class ForNode < BranchNode
|
|
9
|
+
include Props
|
|
10
|
+
include Variables
|
|
11
|
+
|
|
12
|
+
attr_accessor :children
|
|
13
|
+
|
|
14
|
+
def initialize(name:, item:, items:, props: [], children: [])
|
|
15
|
+
super(name:, props:, children:)
|
|
16
|
+
|
|
17
|
+
@item = item
|
|
18
|
+
@items = items
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
22
|
+
output = ''
|
|
23
|
+
|
|
24
|
+
evaluate_variable(name: @items, current_binding:).each do |item|
|
|
25
|
+
current_binding.local_variable_set(@item, item)
|
|
26
|
+
|
|
27
|
+
@children.each do |child|
|
|
28
|
+
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
29
|
+
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node:, namespace:) : child) || ''
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
output
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/nodes/prop_node.rb
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '../interfaces/
|
|
3
|
+
require_relative '../interfaces/leaf_node'
|
|
4
|
+
require_relative '../modules/props'
|
|
4
5
|
|
|
5
6
|
module Antlers
|
|
6
|
-
|
|
7
|
+
class PropNode < LeafNode
|
|
8
|
+
include Props
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
11
|
+
props = evaluate_props(props: @props, current_binding:)
|
|
12
|
+
event = create_render_event(props:)
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
renderable_klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
|
|
15
|
+
renderable_instance = renderable_klass.new(event:)
|
|
16
|
+
|
|
17
|
+
# Classes referenced via "<{ ChildNode }>" must implement class/instance render/render_template methods (See LowNode).
|
|
18
|
+
return renderable_instance.render_template(event:, parent_binding:, props:) if renderable_klass.template
|
|
19
|
+
|
|
20
|
+
props.empty? ? renderable_instance.render(event:) : renderable_instance.render(event:, **props)
|
|
13
21
|
end
|
|
14
22
|
end
|
|
15
23
|
end
|
data/lib/nodes/root_node.rb
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '../interfaces/
|
|
3
|
+
require_relative '../interfaces/branch_node'
|
|
4
4
|
|
|
5
5
|
module Antlers
|
|
6
|
-
class RootNode <
|
|
7
|
-
attr_accessor :children
|
|
8
|
-
|
|
6
|
+
class RootNode < BranchNode
|
|
9
7
|
def initialize(name: :root_node, children: [])
|
|
10
|
-
super(name:)
|
|
11
|
-
|
|
12
|
-
@children = children
|
|
8
|
+
super(name:, children:)
|
|
13
9
|
end
|
|
14
10
|
end
|
|
15
11
|
end
|
data/lib/nodes/slot_node.rb
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '../
|
|
3
|
+
require_relative '../interfaces/branch_node'
|
|
4
|
+
require_relative '../modules/props'
|
|
4
5
|
|
|
5
6
|
module Antlers
|
|
6
|
-
class SlotNode <
|
|
7
|
+
class SlotNode < BranchNode
|
|
8
|
+
include Props
|
|
9
|
+
|
|
7
10
|
attr_accessor :children
|
|
8
11
|
|
|
9
12
|
def initialize(name:, props: [], children: [])
|
|
10
|
-
super(name:, props:)
|
|
13
|
+
super(name:, props:, children:)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
17
|
+
props = evaluate_props(props: @props, current_binding:)
|
|
18
|
+
event = create_render_event(props:)
|
|
19
|
+
|
|
20
|
+
klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
|
|
21
|
+
instance = klass.new(event:)
|
|
22
|
+
|
|
23
|
+
# Classes referenced via "<{ ChildNode }>" must implement class/instance render/render_template methods (See LowNode).
|
|
24
|
+
return instance.render_template(event:, parent_binding: current_binding, slot_node: self, props:) if klass.template
|
|
11
25
|
|
|
12
|
-
|
|
26
|
+
props.empty? ? instance.render(event:) : instance.render(event:, **props)
|
|
13
27
|
end
|
|
14
28
|
end
|
|
15
29
|
end
|
data/lib/nodes/var_node.rb
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require 'erb'
|
|
4
|
+
|
|
5
|
+
require_relative '../interfaces/leaf_node'
|
|
6
|
+
require_relative '../modules/variables'
|
|
4
7
|
|
|
5
8
|
module Antlers
|
|
6
|
-
class VarNode <
|
|
9
|
+
class VarNode < LeafNode
|
|
10
|
+
include Variables
|
|
11
|
+
|
|
12
|
+
attr_reader :value
|
|
13
|
+
|
|
14
|
+
def initialize(name: :var, value:)
|
|
15
|
+
super(name:)
|
|
16
|
+
|
|
17
|
+
@value = value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
21
|
+
ERB::Util.html_escape(evaluate_variable(name: @value, current_binding:) || @value)
|
|
22
|
+
end
|
|
7
23
|
end
|
|
8
24
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/leaf_node'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
class YieldNode < BranchNode
|
|
7
|
+
def initialize(name: :default)
|
|
8
|
+
super(name:)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Renders the children of the parent node in the binding of the parent.
|
|
12
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
13
|
+
output = ''
|
|
14
|
+
|
|
15
|
+
slot_node.children.each do |child|
|
|
16
|
+
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
17
|
+
output += (child.respond_to?(:render) ? child.render(current_binding: parent_binding, parent_binding: nil, slot_node: nil, namespace:) : child) || ''
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
output
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/parser.rb
CHANGED
|
@@ -7,31 +7,40 @@ module Antlers
|
|
|
7
7
|
module Parser
|
|
8
8
|
class << self
|
|
9
9
|
def parse(sequence, id: :root_node)
|
|
10
|
-
branch(
|
|
10
|
+
branch(node: RootNode.new(name: id), sequence:)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def branch(
|
|
13
|
+
def branch(node:, sequence:) # rubocop:disable Metrics/AbcSize
|
|
14
14
|
until sequence.empty?
|
|
15
15
|
segment = sequence.shift
|
|
16
16
|
|
|
17
17
|
if segment.is_a?(String)
|
|
18
|
-
|
|
19
|
-
elsif segment[:
|
|
20
|
-
|
|
18
|
+
node.children << segment
|
|
19
|
+
elsif segment[:var]
|
|
20
|
+
node.children << NodeFactory.var_node(segment:)
|
|
21
21
|
elsif segment[:prop]
|
|
22
|
-
|
|
22
|
+
node.children << NodeFactory.prop_node(segment:)
|
|
23
|
+
elsif segment[:slot]
|
|
24
|
+
node.children << NodeFactory.yield_node(segment:)
|
|
23
25
|
elsif segment[:slot_def]
|
|
24
26
|
slot_node = NodeFactory.slot_node(segment:)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
node.children << slot_node
|
|
28
|
+
sub_branch(node: slot_node, sequence:, end_key: :slot_end, end_name: slot_node.name)
|
|
29
|
+
elsif segment[:for_def]
|
|
30
|
+
for_node = NodeFactory.for_node(segment:)
|
|
31
|
+
node.children << for_node
|
|
32
|
+
sub_branch(node: for_node, sequence:, end_key: :for_end, end_name: 'level_1')
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
node
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def sub_branch(node:, sequence:, end_key:, end_name: nil)
|
|
40
|
+
sub_sequence = []
|
|
41
|
+
sub_sequence << sequence.shift until sequence.first.is_a?(Hash) && sequence.first[end_key] == end_name
|
|
42
|
+
|
|
43
|
+
branch(node:, sequence: sub_sequence)
|
|
35
44
|
end
|
|
36
45
|
end
|
|
37
46
|
end
|
data/lib/queries.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Antlers
|
|
4
|
+
module Queries
|
|
5
|
+
class << self
|
|
6
|
+
def user_defined_string?(string)
|
|
7
|
+
wrapped_in?(string, %q{'}) || wrapped_in?(string, %q{"})
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def wrapped_in?(string, delimeter)
|
|
11
|
+
string[0] == delimeter && string[-1] == delimeter
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -10,19 +10,33 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: low_event
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '0'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: erb
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
description: A new Ruby templating language. Coming soon...
|
|
27
41
|
email:
|
|
28
42
|
- maediprichard@gmail.com
|
|
@@ -33,12 +47,19 @@ files:
|
|
|
33
47
|
- lib/antlers.rb
|
|
34
48
|
- lib/factories/node_factory.rb
|
|
35
49
|
- lib/interfaces/antler_node.rb
|
|
50
|
+
- lib/interfaces/branch_node.rb
|
|
51
|
+
- lib/interfaces/leaf_node.rb
|
|
36
52
|
- lib/lexer.rb
|
|
53
|
+
- lib/modules/props.rb
|
|
54
|
+
- lib/modules/variables.rb
|
|
55
|
+
- lib/nodes/for_node.rb
|
|
37
56
|
- lib/nodes/prop_node.rb
|
|
38
57
|
- lib/nodes/root_node.rb
|
|
39
58
|
- lib/nodes/slot_node.rb
|
|
40
59
|
- lib/nodes/var_node.rb
|
|
60
|
+
- lib/nodes/yield_node.rb
|
|
41
61
|
- lib/parser.rb
|
|
62
|
+
- lib/queries.rb
|
|
42
63
|
- lib/version.rb
|
|
43
64
|
homepage: https://codeberg.org/raindeer/antlers
|
|
44
65
|
licenses: []
|
|
@@ -59,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
59
80
|
- !ruby/object:Gem::Version
|
|
60
81
|
version: '0'
|
|
61
82
|
requirements: []
|
|
62
|
-
rubygems_version:
|
|
83
|
+
rubygems_version: 4.0.10
|
|
63
84
|
specification_version: 4
|
|
64
85
|
summary: Deer to be different
|
|
65
86
|
test_files: []
|