cadenza 0.7.0.rc1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|