cadenza 0.7.0.rc1 → 0.7.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.
- data/lib/cadenza.rb +15 -0
- data/lib/cadenza/base_renderer.rb +26 -1
- data/lib/cadenza/context.rb +141 -9
- data/lib/cadenza/error.rb +13 -0
- data/lib/cadenza/filesystem_loader.rb +27 -0
- data/lib/cadenza/lexer.rb +52 -1
- data/lib/cadenza/nodes/block_node.rb +17 -3
- data/lib/cadenza/nodes/boolean_inverse_node.rb +11 -0
- data/lib/cadenza/nodes/constant_node.rb +10 -0
- data/lib/cadenza/nodes/document_node.rb +22 -0
- data/lib/cadenza/nodes/filter_node.rb +20 -1
- data/lib/cadenza/nodes/for_node.rb +42 -26
- data/lib/cadenza/nodes/generic_block_node.rb +22 -1
- data/lib/cadenza/nodes/if_node.rb +55 -29
- data/lib/cadenza/nodes/inject_node.rb +24 -1
- data/lib/cadenza/nodes/operation_node.rb +41 -1
- data/lib/cadenza/nodes/text_node.rb +9 -0
- data/lib/cadenza/nodes/variable_node.rb +13 -0
- data/lib/cadenza/parser.rb +82 -940
- data/lib/cadenza/racc_parser.rb +911 -0
- data/lib/cadenza/text_renderer.rb +12 -2
- data/lib/cadenza/version.rb +1 -1
- metadata +21 -30
@@ -1,17 +1,31 @@
|
|
1
1
|
module Cadenza
|
2
|
-
|
2
|
+
# The {BlockNode} is a general container with a given name meant to implement
|
3
|
+
# Cadenza's template inheritance feature. Blocks defined in a template will
|
4
|
+
# override the definition of blocks defined in the parent template.
|
3
5
|
class BlockNode
|
4
|
-
|
6
|
+
# @return [String] the name of the node
|
7
|
+
attr_accessor :name
|
5
8
|
|
6
|
-
|
9
|
+
# @return [Array] the child nodes belonging to this block
|
10
|
+
attr_accessor :children
|
11
|
+
|
12
|
+
# creates a new block node with the given name and child nodes
|
13
|
+
# @param [String] name the name for this block node
|
14
|
+
# @param [Array] children the child nodes belonging to this block node
|
15
|
+
def initialize(name, children=[])
|
7
16
|
@name = name
|
8
17
|
@children = children
|
9
18
|
end
|
10
19
|
|
20
|
+
# @return [Array] a list of any implied global variable names defined by
|
21
|
+
# this block's children.
|
11
22
|
def implied_globals
|
12
23
|
@children.map(&:implied_globals).flatten.uniq
|
13
24
|
end
|
14
25
|
|
26
|
+
# @param [BlockNode] rhs
|
27
|
+
# @return [Boolean] true if the given {BlockNode} is equivalent to the this
|
28
|
+
# node by value.
|
15
29
|
def ==(rhs)
|
16
30
|
@name == rhs.name and
|
17
31
|
@children == rhs.children
|
@@ -1,16 +1,27 @@
|
|
1
1
|
module Cadenza
|
2
|
+
# The {BooleanInverseNode} takes an expression and evaluates the logical inverse
|
3
|
+
# of that expression so any expression that evaluates to true will return false
|
4
|
+
# and vice versa.
|
2
5
|
class BooleanInverseNode
|
6
|
+
# @return [OperationNode] an evaluatable expression node
|
3
7
|
attr_accessor :expression
|
4
8
|
|
9
|
+
# creates a new {BooleanInverseNode} with the given expression
|
10
|
+
# @param [OperationNode] the node this one will invert
|
5
11
|
def initialize(expression)
|
6
12
|
@expression = expression
|
7
13
|
end
|
8
14
|
|
15
|
+
# @param [BooleanInverseNode] rhs
|
16
|
+
# @return [Boolean] if the given {BooleanInverseNode} is equivalent by value
|
9
17
|
def ==(rhs)
|
10
18
|
@expression == rhs.expression
|
11
19
|
end
|
12
20
|
|
21
|
+
# @param [Context] context
|
22
|
+
# @return the value of this node evaluated with the data in the given {Context}
|
13
23
|
def eval(context)
|
24
|
+
#TODO: rename me to .evaluate
|
14
25
|
!@expression.eval(context)
|
15
26
|
end
|
16
27
|
end
|
@@ -1,19 +1,29 @@
|
|
1
1
|
module Cadenza
|
2
|
+
# The {ConstantNode} holds a value which is not affected by any context given
|
3
|
+
# to it, such as numbers or strings.
|
2
4
|
class ConstantNode
|
5
|
+
# @return [Object] the value of this node
|
3
6
|
attr_accessor :value
|
4
7
|
|
8
|
+
# constructs a new {ConstantNode} with the given value.
|
9
|
+
# @param [Object] value the value of this constant node
|
5
10
|
def initialize(value)
|
6
11
|
@value = value
|
7
12
|
end
|
8
13
|
|
14
|
+
# @return [Array] any global variable applied to this node (none)
|
9
15
|
def implied_globals
|
10
16
|
[]
|
11
17
|
end
|
12
18
|
|
19
|
+
# @param [Context] context
|
20
|
+
# @return [Object] the value of this node evaluated in the given context
|
13
21
|
def eval(context)
|
14
22
|
@value
|
15
23
|
end
|
16
24
|
|
25
|
+
# @param [ConstantNode] rhs
|
26
|
+
# @return [Boolean] if this node and the given one are equivalent by value
|
17
27
|
def ==(rhs)
|
18
28
|
@value == rhs.value
|
19
29
|
end
|
@@ -1,24 +1,46 @@
|
|
1
1
|
module Cadenza
|
2
|
+
# The {DocumentNode} is intended to be the root node of any parsed AST in
|
3
|
+
# Cadenza. In addition to holding the primary children it also holds data
|
4
|
+
# that affects the entire document, such as block definitions and the name of
|
5
|
+
# any extended template.
|
2
6
|
class DocumentNode
|
7
|
+
# @return [String] the name of the template this document will inherit from
|
3
8
|
attr_accessor :extends
|
9
|
+
|
10
|
+
# @return [Array] any child nodes belonging to this one
|
4
11
|
attr_accessor :children
|
12
|
+
|
13
|
+
# @return [Hash] a mapping of any blocks defined in this document where the
|
14
|
+
# key is the name of the block and the value is the {BlockNode}
|
15
|
+
# itself
|
5
16
|
attr_accessor :blocks
|
6
17
|
|
18
|
+
# creates a new {DocumentNode} with the optional children nodes attached to
|
19
|
+
# it.
|
20
|
+
# @param [Array] children any child nodes to initially assign to this node.
|
7
21
|
def initialize(children=[])
|
8
22
|
@children = children
|
9
23
|
@blocks = {}
|
10
24
|
end
|
11
25
|
|
26
|
+
# @param [DocumentNode] rhs
|
27
|
+
# @return [Boolean] if the given {DocumentNode} is equivalent by value to this one.
|
12
28
|
def ==(rhs)
|
13
29
|
@children == rhs.children and
|
14
30
|
@extends == rhs.extends and
|
15
31
|
@blocks == rhs.blocks
|
16
32
|
end
|
17
33
|
|
34
|
+
# adds the given {BlockNode} to this document replacing any existing definition
|
35
|
+
# of the same name.
|
36
|
+
# @param [BlockNode] block
|
18
37
|
def add_block(block)
|
19
38
|
@blocks[block.name] = block
|
20
39
|
end
|
21
40
|
|
41
|
+
# returns a list of any global variable names implied by examining the children
|
42
|
+
# of this node.
|
43
|
+
# @return [Array]
|
22
44
|
def implied_globals
|
23
45
|
@children.map(&:implied_globals).flatten.uniq
|
24
46
|
end
|
@@ -1,22 +1,41 @@
|
|
1
1
|
|
2
2
|
module Cadenza
|
3
|
+
# The {FilterNode} is a node which contains the definition for a variable
|
4
|
+
# filter along with any parameters it is defined with.
|
3
5
|
class FilterNode
|
4
|
-
|
6
|
+
# @return [String] the name of the filter
|
7
|
+
attr_accessor :identifier
|
5
8
|
|
9
|
+
# @return [Array] a list of parameter nodes given to the filter
|
10
|
+
attr_accessor :parameters
|
11
|
+
|
12
|
+
# constructs a new {FilterNode} with the given identifier and parameters
|
13
|
+
# @param [String] identifier the name of the filter
|
14
|
+
# @param [Array] parameters the parameters given to the filter
|
6
15
|
def initialize(identifier, parameters=[])
|
7
16
|
@identifier = identifier
|
8
17
|
@parameters = parameters
|
9
18
|
end
|
10
19
|
|
20
|
+
# @param [FilterNode] rhs
|
21
|
+
# @return [Boolean] true if the given filter node is equivalent by value to
|
22
|
+
# this node.
|
11
23
|
def ==(rhs)
|
12
24
|
@identifier == rhs.identifier and
|
13
25
|
@parameters == rhs.parameters
|
14
26
|
end
|
15
27
|
|
28
|
+
# @return [Array] a list of implied global variable names for this node
|
16
29
|
def implied_globals
|
17
30
|
@parameters.map(&:implied_globals).flatten.uniq
|
18
31
|
end
|
19
32
|
|
33
|
+
# evaluates the filter with the given context and input value and returns
|
34
|
+
# the output of the evaluation.
|
35
|
+
#
|
36
|
+
# @param [Context] context the context to evaluate with
|
37
|
+
# @param [String] value the input value to filter
|
38
|
+
# @return the input value when passed through this evaluated filter
|
20
39
|
def evaluate(context, value)
|
21
40
|
params = [value] + @parameters.map {|x| x.eval(context) }
|
22
41
|
context.evaluate_filter(@identifier, params)
|
@@ -1,37 +1,53 @@
|
|
1
1
|
module Cadenza
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
# The {ForNode} describe a loop in the template code. The loop should iterate
|
3
|
+
# over all the elements of the iterable and render its children each time.
|
4
|
+
class ForNode
|
5
|
+
# @return [VariableNode] the iterator object for the loop
|
6
|
+
attr_accessor :iterator
|
7
|
+
|
8
|
+
# @return [VariableNode] the iterable object for the loop
|
9
|
+
attr_accessor :iterable
|
10
|
+
|
11
|
+
# @return [Array] the list of children associated with this loop
|
12
|
+
attr_accessor :children
|
13
|
+
|
14
|
+
MAGIC_LOCALS = %w(forloop.counter forloop.counter0 forloop.first forloop.last)
|
15
|
+
|
16
|
+
# constructs a new {ForNode} with the given iterator, iterable and child
|
17
|
+
# nodes.
|
18
|
+
def initialize(iterator, iterable, children)
|
19
|
+
@iterator = iterator
|
20
|
+
@iterable = iterable
|
21
|
+
@children = children
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Array] of all global variable names defined by this node and
|
25
|
+
# it's child nodes.
|
26
|
+
def implied_globals
|
27
|
+
iterable_globals = @iterable.implied_globals
|
28
|
+
iterator_globals = @iterator.implied_globals
|
12
29
|
|
13
|
-
|
14
|
-
iterable_globals = @iterable.implied_globals
|
15
|
-
iterator_globals = @iterator.implied_globals
|
30
|
+
iterator_regex = Regexp.new("^#{@iterator.identifier}[\.](.+)$")
|
16
31
|
|
17
|
-
|
32
|
+
all_children_globals = @children.map(&:implied_globals).flatten
|
18
33
|
|
19
|
-
|
34
|
+
children_globals = all_children_globals.reject {|x| x =~ iterator_regex }
|
20
35
|
|
21
|
-
|
36
|
+
iterator_children_globals = all_children_globals.select {|x| x =~ iterator_regex }.map do |identifier|
|
37
|
+
"#{iterable.identifier}.#{iterator_regex.match(identifier)[1]}"
|
38
|
+
end
|
22
39
|
|
23
|
-
|
24
|
-
"#{iterable.identifier}.#{iterator_regex.match(identifier)[1]}"
|
40
|
+
(iterable_globals | children_globals | iterator_children_globals) - MAGIC_LOCALS - iterator_globals
|
25
41
|
end
|
26
42
|
|
27
|
-
|
28
|
-
|
43
|
+
# @param [ForNode] rhs
|
44
|
+
# @return [Boolean] true if the given {ForNode} is equivalent by value to
|
45
|
+
# this node.
|
46
|
+
def ==(rhs)
|
47
|
+
@iterator == rhs.iterator and
|
48
|
+
@iterable == rhs.iterable and
|
49
|
+
@children == rhs.children
|
50
|
+
end
|
29
51
|
|
30
|
-
def ==(rhs)
|
31
|
-
@iterator == rhs.iterator and
|
32
|
-
@iterable == rhs.iterable and
|
33
|
-
@children == rhs.children
|
34
52
|
end
|
35
|
-
|
36
|
-
end
|
37
53
|
end
|
@@ -1,17 +1,38 @@
|
|
1
1
|
module Cadenza
|
2
|
+
# The {GenericBlockNode} allows the end user of Cadenza to provide custom
|
3
|
+
# block rendering logic via a proc defined on {Context}.
|
2
4
|
class GenericBlockNode
|
3
|
-
|
5
|
+
# @return [String] the name of the block as defined in {Context}
|
6
|
+
attr_accessor :identifier
|
4
7
|
|
8
|
+
# @return [Array] a list of Node objects which are this block's child nodes
|
9
|
+
attr_accessor :children
|
10
|
+
|
11
|
+
# @return [Array] a list of Node objects which hold the value of parameters
|
12
|
+
# passed to this block.
|
13
|
+
attr_accessor :parameters
|
14
|
+
|
15
|
+
# Creates a new generic block with the given name, children and parameters.
|
16
|
+
# @param [String] identifier the name of the node as defined in {Context}
|
17
|
+
# @param [Array] children the child nodes belonging to this block
|
18
|
+
# @param [Array] parameters the nodes holding the value of parameters
|
19
|
+
# passed to this block.
|
5
20
|
def initialize(identifier, children, parameters=[])
|
6
21
|
@identifier = identifier
|
7
22
|
@children = children
|
8
23
|
@parameters = parameters
|
9
24
|
end
|
10
25
|
|
26
|
+
# @return [Array] a list of variable names implied to be globals in the node
|
27
|
+
# @note not yet implemented
|
11
28
|
def implied_globals
|
29
|
+
#TODO: implement me please, kthxbai
|
12
30
|
[]
|
13
31
|
end
|
14
32
|
|
33
|
+
# @param [GenericBlockNode] rhs
|
34
|
+
# @return [Boolean] true if the given node is equivalent by value to the
|
35
|
+
# current node.
|
15
36
|
def ==(rhs)
|
16
37
|
@identifier == rhs.identifier and
|
17
38
|
@children == rhs.children and
|
@@ -1,42 +1,68 @@
|
|
1
1
|
module Cadenza
|
2
|
-
|
3
|
-
|
2
|
+
# The {IfNode} is a structure for rendering one of it's two given blocks
|
3
|
+
# based on the evaluation of an expression in the current {Context}.
|
4
|
+
class IfNode
|
5
|
+
# @return [OperationNode|BooleanInverseNode] the evaluatable expression
|
6
|
+
# used to determine which set of nodes to render.
|
7
|
+
attr_accessor :expression
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@false_children = false_children
|
9
|
-
end
|
9
|
+
# @return [Array] A list of nodes which will be rendered if the {#expression}
|
10
|
+
# evaluates to true in the given {Context}.
|
11
|
+
attr_accessor :true_children
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
# @return [Array] A list of nodes which will be rendered if the {#expression}
|
14
|
+
# evaluates to false in the given {Context}.
|
15
|
+
attr_accessor :false_children
|
16
|
+
|
17
|
+
# creates a new {IfNode} with the given evaluatable expression and pair of
|
18
|
+
# blocks.
|
19
|
+
# @param [OperationNode|BooleanInverseNode] expression the expression to evaluate
|
20
|
+
# @param [Array] true_children a list of Node objects which will be rendered
|
21
|
+
# if the {#expression} evaluates to true in the given {Context}.
|
22
|
+
# @param [Array] false_children a list of Node objects which will be
|
23
|
+
# rendered if the {#expression} evaluates to false in the given {Context}.
|
24
|
+
def initialize(expression, true_children=[], false_children=[])
|
25
|
+
@expression = expression
|
26
|
+
@true_children = true_children
|
27
|
+
@false_children = false_children
|
28
|
+
end
|
14
29
|
|
15
|
-
|
16
|
-
|
30
|
+
# @return [Array] a list of variable names implied to be global for this node.
|
31
|
+
def implied_globals
|
32
|
+
(@expression.implied_globals + true_children.map(&:implied_globals).flatten + false_children.map(&:implied_globals).flatten).uniq
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [Context] context
|
36
|
+
# @return [Array] evalutes the expression in the given context and returns
|
37
|
+
# a list of nodes which should be rendered based on the result of
|
38
|
+
# that evaluation.
|
39
|
+
def evaluate_expression_for_children(context)
|
40
|
+
value = @expression.eval(context)
|
17
41
|
|
18
|
-
|
19
|
-
|
42
|
+
if value == true
|
43
|
+
return @true_children
|
20
44
|
|
21
|
-
|
22
|
-
|
45
|
+
elsif value == false
|
46
|
+
return @false_children
|
23
47
|
|
24
|
-
|
25
|
-
|
48
|
+
elsif value.is_a?(String)
|
49
|
+
return value.length == 0 || value =~ /\s+/ ? @false_children : @true_children
|
26
50
|
|
27
|
-
|
28
|
-
|
51
|
+
elsif value.is_a?(Float) or value.is_a?(Fixnum)
|
52
|
+
return value == 0 ? @false_children : @true_children
|
29
53
|
|
30
|
-
|
31
|
-
|
32
|
-
|
54
|
+
else
|
55
|
+
return !!value ? @true_children : @false_children
|
56
|
+
|
57
|
+
end
|
33
58
|
end
|
34
|
-
end
|
35
59
|
|
36
|
-
|
37
|
-
@
|
38
|
-
|
39
|
-
|
60
|
+
# @param [IfNode] rhs
|
61
|
+
# @return [Boolean] true if the given node is equivalent by value to this node.
|
62
|
+
def ==(rhs)
|
63
|
+
@expression == rhs.expression and
|
64
|
+
@true_children == rhs.true_children and
|
65
|
+
@false_children == rhs.false_children
|
66
|
+
end
|
40
67
|
end
|
41
|
-
end
|
42
68
|
end
|
@@ -1,23 +1,46 @@
|
|
1
1
|
module Cadenza
|
2
|
+
# The {InjectNode} is intended to write the given variable into the rendered
|
3
|
+
# output by evaluating it in the given {Context} and passing it through an
|
4
|
+
# optional series of {FilterNode}s.
|
2
5
|
class InjectNode
|
3
|
-
|
6
|
+
# @return [VariableNode|OperationNode|BooleanInverseNode|ConstantNode] the value being evaluated
|
7
|
+
attr_accessor :value
|
8
|
+
|
9
|
+
# @return [Array] a list of {FilterNode} to evaluate the value with, once the
|
10
|
+
# value has itself been evaluated.
|
11
|
+
attr_accessor :filters
|
12
|
+
|
13
|
+
# @return [Array] a list of Node objects passed to the {#value} for use in a
|
14
|
+
# functional variable. See {Context#define_functional_variable}.
|
15
|
+
attr_accessor :parameters
|
4
16
|
|
17
|
+
# creates a new {InjectNode} with the given value, filters and parameters
|
18
|
+
# @param [VariableNode|OperationNode|BooleanInverseNode|ConstantNode] value see {#value}
|
19
|
+
# @param [Array] filters see {#filters}
|
20
|
+
# @param [Array] parameters see {#parameters}
|
5
21
|
def initialize(value, filters=[], parameters=[])
|
6
22
|
@value = value
|
7
23
|
@filters = filters
|
8
24
|
@parameters = parameters
|
9
25
|
end
|
10
26
|
|
27
|
+
# @param [InjectNode] rhs
|
28
|
+
# @return [Boolean] true if the given InjectNode is equivalent by value to this node.
|
11
29
|
def ==(rhs)
|
12
30
|
self.value == rhs.value and
|
13
31
|
self.filters == rhs.filters and
|
14
32
|
self.parameters == rhs.parameters
|
15
33
|
end
|
16
34
|
|
35
|
+
# @return [Array] a list of variable names implied to be global by this node
|
17
36
|
def implied_globals
|
18
37
|
(@value.implied_globals + @filters.map(&:implied_globals).flatten).uniq
|
19
38
|
end
|
20
39
|
|
40
|
+
# @param [Context] context
|
41
|
+
# @return [String] returns the evaluated {#value} of this node in the given
|
42
|
+
# {Context} with any applicable {#parameters} after passed through
|
43
|
+
# the given {#filters}.
|
21
44
|
def evaluate(context)
|
22
45
|
value = @value.eval(context)
|
23
46
|
|