ltdtemplate 0.1.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.
@@ -0,0 +1,46 @@
1
+ # LtdTemplate::Code::Call - Represents a method call in an LtdTemplate
2
+ #
3
+ # Author:: Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # Copyright:: 2013 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # License:: MIT License
6
+
7
+ require 'ltdtemplate/code'
8
+
9
+ class LtdTemplate::Code::Call < LtdTemplate::Code
10
+
11
+ # Initialize a method call object.
12
+ #
13
+ # @param template [LtdTemplate] The template object
14
+ # @param target [LtdTemplate::Code] The target object
15
+ # @param method [String] The method to call
16
+ # @param parameters [LtdTemplate::Code::Parameters] The call parameters
17
+ def initialize (template, target, method, parameters)
18
+ super template
19
+ @target, @method, @parameters = target, method, parameters
20
+ end
21
+
22
+ # Return the result of executing the call.
23
+ #
24
+ # @param opts [Hash] Option hash
25
+ # @option opts [String] :method A method to call on the return value
26
+ def get_value (opts = {})
27
+ # Increase the call count and call depth.
28
+ @template.use :calls
29
+ @template.use :call_depth
30
+
31
+ result = @target.get_value({ :method => @method,
32
+ :parameters => @parameters.get_value })
33
+
34
+ # Decrease the call depth.
35
+ @template.use :call_depth, -1
36
+
37
+ opts[:method] ? result.get_value(opts) : result
38
+ end
39
+
40
+ # Pass has/get/set_item calls through to result of call;
41
+ # in some cases it might be the same value each time.
42
+ def has_item? (key); get_value.has_item? key; end
43
+ def get_item (key); get_value.get_item key; end
44
+ def set_item (key, value); get_value.set_item key, value; end
45
+
46
+ end
@@ -0,0 +1,30 @@
1
+ # LtdTemplate::Code::Code_Block - Represents a code block (a list of
2
+ # code steps) in an LtdTemplate
3
+ #
4
+ # Implied code blocks do not accept parameters or generate new namespaces.
5
+ # They are used for things like call parameters and subscript expressions.
6
+ # See also: LtdTemplate::Value::Code_Block.
7
+ #
8
+ # Author:: Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
9
+ # Copyright:: 2013 Brian Katzung and Kappa Computer Solutions, LLC
10
+ # License:: MIT License
11
+
12
+ require 'ltdtemplate/code'
13
+
14
+ class LtdTemplate::Code::Code_Block < LtdTemplate::Code
15
+
16
+ def initialize (template, code)
17
+ super template
18
+ @code = code
19
+ end
20
+
21
+ def get_value (opts = {})
22
+ values = @code.map { |part| part.get_value }.flatten
23
+ case values.size
24
+ when 0 then @template.factory :nil
25
+ when 1 then values[0]
26
+ else @template.factory(:array).set_value(values)
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,40 @@
1
+ # LtdTemplate::Code::Parameters - Represents call parameters in an LtdTemplate
2
+ #
3
+ # Author:: Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # Copyright:: 2013 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # License:: MIT License
6
+
7
+ require 'ltdtemplate/code'
8
+
9
+ class LtdTemplate::Code::Parameters < LtdTemplate::Code
10
+
11
+ #
12
+ # Create a parameter list builder with code to generate positional
13
+ # values and possibly code to generate named values.
14
+ #
15
+ def initialize (template, positional = [], named = nil)
16
+ super template
17
+
18
+ # Save the code blocks for positional and named parameters.
19
+ @positional, @named = positional, named
20
+ end
21
+
22
+ #
23
+ # Evaluate the code provided for the positional and named parameters
24
+ # and return a corresponding array t-value.
25
+ #
26
+ def get_value (opts = {})
27
+ positional = @positional.map { |val| val.get_value }
28
+ named = {}
29
+ if @named
30
+ @named.each_slice(2) do |key, val|
31
+ named[key.get_value.to_native] = val.get_value
32
+ end
33
+ scalar = false
34
+ else
35
+ scalar = positional.size == 1
36
+ end
37
+ @template.factory(:array).set_value(positional, named, scalar)
38
+ end
39
+
40
+ end
@@ -0,0 +1,81 @@
1
+ # LtdTemplate::Code::Subscript - Represents an array subscript in
2
+ # an LtdTemplate
3
+ #
4
+ # Author:: Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
5
+ # Copyright:: 2013 Brian Katzung and Kappa Computer Solutions, LLC
6
+ # License:: MIT License
7
+
8
+ require 'ltdtemplate/code'
9
+
10
+ class LtdTemplate::Code::Subscript < LtdTemplate::Code
11
+
12
+ def initialize (template, base, subscripts)
13
+ super template
14
+ @base, @subscripts = base, subscripts
15
+ end
16
+
17
+ #
18
+ # Return native subscripts calculated from the supplied code blocks.
19
+ #
20
+ def native_subs (usage = false)
21
+ nsubs = @subscripts ?
22
+ @subscripts.map { |sub| sub.get_value.to_native }.flatten : []
23
+ num_subs = nsubs.size
24
+ if usage and num_subs > 0
25
+ @template.using :subscript_depth, num_subs
26
+ @template.use :subscripts, num_subs
27
+ end
28
+ nsubs
29
+ end
30
+
31
+ #
32
+ # Return the target value, variable[sub1, ..., subN]
33
+ #
34
+ def target (usage = false)
35
+ current = @base.get_value
36
+ native_subs(usage).each { |subs| current = current.get_item subs }
37
+ current
38
+ end
39
+
40
+ #
41
+ # Implement the subscript interface for the target.
42
+ #
43
+ def has_item? (key); target.has_item? key; end
44
+ def get_item (key); target.get_item key; end
45
+ def set_item (key, value); target(true).set_item key, value; end
46
+
47
+ #
48
+ # Set the target's value.
49
+ #
50
+ def set_value (value)
51
+ subs = native_subs true
52
+ if subs.size == 0
53
+ # If there are no subscripts, just use the base.
54
+ @base.set_value value
55
+ else
56
+ #
57
+ # Traverse all but the last subscript, trying to autovivicate
58
+ # new arrays as we go. This will silently fail if there is an
59
+ # existing non-array value somewhere.
60
+ #
61
+ current = @base
62
+ current.set_value @template.factory :array unless current.is_set?
63
+ subs[0..-2].each do |sub|
64
+ if !current.has_item? sub
65
+ current.set_item sub, @template.factory(:array)
66
+ end
67
+ current = current.get_item sub
68
+ end
69
+ current.set_item subs[-1], value
70
+ end
71
+ self
72
+ end
73
+
74
+ def get_value (opts = {})
75
+ case opts[:method]
76
+ when '=' then do_set opts # see LtdTemplate::Code
77
+ else target.get_value opts
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,77 @@
1
+ # LtdTemplate::Code::Variable - Represents a variable in an LtdTemplate
2
+ #
3
+ # Author:: Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # Copyright:: 2013 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # License:: MIT License
6
+
7
+ require 'ltdtemplate/code'
8
+
9
+ class LtdTemplate::Code::Variable < LtdTemplate::Code
10
+
11
+ def initialize (template, name)
12
+ super template
13
+ case name[0]
14
+ when '@', '^'
15
+ # @var is in the root namespace
16
+ # ^var is in the parent namespace
17
+ @modifier = name[0]
18
+ @name = name[1..-1]
19
+ @name = @name.to_i if @name =~ /^(?:0|[1-9]\d*)$/
20
+ else
21
+ # Standard bottom-to-top namespace search variable
22
+ @modifier = nil
23
+ @name = name
24
+ end
25
+ end
26
+
27
+ #
28
+ # Return the namespace in which this variable currently resides
29
+ # (or would reside, if it doesn't currently exist).
30
+ #
31
+ def namespace
32
+ case @modifier
33
+ when '@' then base = @template.namespace.root
34
+ when '^' then base = @template.namespace.parent || @template.namespace
35
+ else base = @template.namespace
36
+ end
37
+ base.find_item(@name) || base
38
+ end
39
+
40
+ #
41
+ # Return the namespace item for this variable.
42
+ #
43
+ def target; namespace.get_item(@name); end
44
+
45
+ #
46
+ # Implement the subscripting interface.
47
+ #
48
+ def has_item? (key); target.has_item? key; end
49
+ def get_item (key); target.get_item key; end
50
+ def set_item (key, value); target.set_item key, value; end
51
+
52
+ #
53
+ # Try to set the value.
54
+ #
55
+ def set_value (value)
56
+ namespace.set_item(@name, value)
57
+ end
58
+
59
+ #
60
+ # Is this variable set?
61
+ # Among other possible uses, this is needed for determining when to
62
+ # auto-vivicate array subscripts.
63
+ #
64
+ def is_set?; namespace.has_item? @name; end
65
+
66
+ def get_value (opts = {})
67
+ case opts[:method]
68
+ when '=' then do_set opts # see LtdTemplate::Code
69
+ when '?='
70
+ if is_set? then @template.factory :nil
71
+ else do_set opts
72
+ end
73
+ else target.get_value opts
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,102 @@
1
+ class LtdTemplate; end
2
+
3
+ # LtdTemplate::Code - Base class for LtdTemplate code/value objects
4
+ #
5
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
6
+ # @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
7
+ # @license MIT License
8
+
9
+ class LtdTemplate::Code
10
+
11
+ # @!attribute [r] tpl_methods
12
+ # @return [Array<LtdTemplate::Value::Code_Block>]
13
+ # The code blocks bound to non-array values.
14
+ attr_reader :tpl_methods
15
+
16
+ # Return a new factory object instance (or a singleton in some
17
+ # subclasses, e.g. nil).
18
+ #
19
+ # @param args [Array] Class-specific initializer parameters.
20
+ def self.instance (*args); self.new(*args); end
21
+
22
+ # Initialize the object with a link to the associated template.
23
+ #
24
+ # @param template [LtdTemplate] The associated template object.
25
+ def initialize (template)
26
+ @template = template
27
+ @tpl_methods = {}
28
+ end
29
+
30
+ # Does a non-array value have a particular template method?
31
+ #
32
+ # @param key [String] The (native) string for the method.
33
+ # @return [Boolean]
34
+ def has_item? (key); @tpl_methods.has_key? key; end
35
+
36
+ # Return a non-array value's method code block (if set).
37
+ #
38
+ # @param key [String] The (native) string for the method.
39
+ # @return [LtdTemplate::Value::Code_Block]
40
+ def get_item (key)
41
+ (@tpl_methods.has_key? key) ? @tpl_methods[key] :
42
+ @template.factory(:nil)
43
+ end
44
+
45
+ # Set a non-array value's method code block.
46
+ #
47
+ # @param key [String] The (native) string for the method.
48
+ # @param value [LtdTemplate::Value::Code_Block] The code block
49
+ # for the method.
50
+ def set_item (key, value); @tpl_methods[key] = value; end
51
+
52
+ # No-op setting a value. (Typically only variables can change their
53
+ # primary values.)
54
+ #
55
+ # @param value The value to set. (Ignored.)
56
+ # @return [LtdTemplate::Code]
57
+ def set_value (value); self; end
58
+
59
+ # Is this value set? Always true except for unset variables.
60
+ #
61
+ # @return [true]
62
+ def is_set?; true; end
63
+
64
+ # Implement "=" (assignment). Note that set_value is a no-op except
65
+ # for variables and array subscripts.
66
+ #
67
+ # @param opts [Hash] A hash of method options.
68
+ # @option opts [LtdTemplate::Value::Parameters] :parameters The method
69
+ # parameters.
70
+ # @return [LtdTemplate::Value::Nil]
71
+ def do_set (opts)
72
+ if params = opts[:parameters]
73
+ set_value(params.scalar? ? params.positional[0] : params)
74
+ end
75
+ @template.factory :nil
76
+ end
77
+
78
+ # Try to execute code-block methods bound to the object or object
79
+ # class. Returns the return value from the code block or t-nil.
80
+ def do_method (opts, class_name = nil)
81
+ method = nil
82
+ if name = opts[:method]
83
+ if @tpl_methods.has_key? name
84
+ method = @tpl_methods[name]
85
+ elsif class_name
86
+ class_var = @template.factory :variable, class_name
87
+ method = class_var.target.tpl_methods[name] if
88
+ class_var.is_set?
89
+ end
90
+ end
91
+ if method
92
+ opts[:target] = self
93
+ method.get_value opts
94
+ else
95
+ @template.factory :nil
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ # This is the parent namespace for value code classes.
102
+ class LtdTemplate::Value; end
@@ -0,0 +1,168 @@
1
+ # LtdTemplate::Value::Array - Represents a combination array/hash value
2
+ # in an LtdTemplate
3
+ #
4
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
5
+ # @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
6
+ # @license MIT License
7
+
8
+ require 'ltdtemplate/code'
9
+ require 'sarah'
10
+
11
+ class LtdTemplate::Value::Array < LtdTemplate::Code
12
+
13
+ attr_reader :sarah
14
+
15
+ def initialize (template)
16
+ super template
17
+ @sarah = Sarah.new
18
+ @scalar = false
19
+ @template.use :arrays
20
+ end
21
+
22
+ #
23
+ # Access positional (sequential) or named (random-access)
24
+ # parts of the array
25
+ #
26
+ def positional; @sarah.seq; end
27
+ def named; @sarah.rnd; end
28
+
29
+ #
30
+ # Implement the subscripting interface. Note that the most recent
31
+ # value is always used. (1 .. 0, 2, 0, 3)[0] is 3.
32
+ # Items set within (or at the end of) the positional range at the
33
+ # time will be positional. Otherwise, they will be named.
34
+ #
35
+ # Keys must be Ruby-native values; values must be template code
36
+ # or values.
37
+ #
38
+ def has_item? (key); @sarah.has_key? key; end
39
+ def get_item (key)
40
+ @sarah.has_key?(key) ? @sarah[key] : @template.factory(:nil)
41
+ end
42
+ def set_item (key, value)
43
+ @sarah[key] = value
44
+ @template.using :array_size, @sarah.size
45
+ end
46
+
47
+ def to_boolean; true; end
48
+ def to_native; @sarah.seq.map { |val| val.to_native }; end
49
+ def to_text; @sarah.seq.map { |val| val.to_text }.join ''; end
50
+
51
+ def get_value (opts = {})
52
+ case opts[:method]
53
+ when nil, 'call' then self
54
+ when 'class' then @template.factory :string, 'Array'
55
+ when 'join' then do_join opts
56
+ when 'pop', '->' then do_pop opts
57
+ when 'push', '+>' then do_push opts
58
+ when 'rnd_size' then @template.factory :number, @sarah.rnd_size
59
+ when 'seq_size' then @template.factory :number, @sarah.seq_size
60
+ when 'shift', '<-' then do_shift opts
61
+ when 'size' then @template.factory :number, @sarah.size
62
+ when 'type' then @template.factory :string, 'array'
63
+ when 'unshift', '<+' then do_unshift opts
64
+ else do_method opts, 'Array'
65
+ end
66
+ end
67
+
68
+ #
69
+ # Scalar assignment is used instead of array assignment if the
70
+ # parameter list contains exactly one positional parameter and
71
+ # the ".." operator was not used.
72
+ #
73
+ def scalar?; @scalar and @sarah.seq_size == 1; end
74
+
75
+ #
76
+ # Clear all current positional and named values
77
+ #
78
+ def clear
79
+ @sarah.clear
80
+ @scalar = false
81
+ self
82
+ end
83
+
84
+ #
85
+ # Set positional and possibly named values. Keys must be ruby-native
86
+ # values; values must be template code or values.
87
+ #
88
+ def set_value (positional, named = {}, scalar = false)
89
+ clear
90
+ @sarah.merge! positional, named
91
+ @scalar = scalar
92
+ self
93
+ end
94
+
95
+ #
96
+ # Set (recursively) from a native array.
97
+ #
98
+ def set_from_array (data)
99
+ clear
100
+ data.each_index { |i| set_item i, map_native_value(data[i]) }
101
+ self
102
+ end
103
+
104
+ #
105
+ # Set (recursively) from a native hash.
106
+ #
107
+ def set_from_hash (data)
108
+ clear
109
+ data.each { |key, val| set_item key, map_native_value(val) }
110
+ self
111
+ end
112
+
113
+ #
114
+ # Combine array element values into a string
115
+ #
116
+ def do_join (opts)
117
+ two = first = middle = last = ''
118
+ if params = opts[:parameters]
119
+ params = params.positional
120
+ if params.size > 3
121
+ two, first, middle, last =
122
+ params[0..3].map { |val| val.get_value.to_text }
123
+ elsif params.size > 0
124
+ two = first = middle = last = params[0].get_value.to_text
125
+ end
126
+ end
127
+
128
+ text = @sarah.seq.map { |val| val.get_value.to_text }
129
+ @template.factory :string, case text.size
130
+ when 0 then ''
131
+ when 1 then text[0]
132
+ when 2 then "#{text[0]}#{two}#{text[1]}"
133
+ else "#{text[0]}#{first}" + text[1..-2].join(middle) +
134
+ "#{last}#{text[-1]}"
135
+ end
136
+ end
137
+
138
+ def do_pop (opts)
139
+ @sarah.pop || @template.factory(:nil)
140
+ end
141
+
142
+ def do_push (opts)
143
+ if params = opts[:parameters] then @sarah.append! params.sarah end
144
+ @template.factory :nil
145
+ end
146
+
147
+ def do_shift (opts)
148
+ @sarah.shift || @template.factory(:nil)
149
+ end
150
+
151
+ def do_unshift (opts)
152
+ if params = opts[:parameters] then @sarah.insert! params.sarah end
153
+ @template.factory :nil
154
+ end
155
+
156
+ protected
157
+
158
+ def map_native_value (value)
159
+ return @template.factory :number, value if value.is_a? Numeric
160
+ return @template.factory :string, value if value.is_a? String
161
+ return @template.factory(:array).set_from_array value if
162
+ value.is_a? Array
163
+ return @template.factory(:array).set_from_hash value if
164
+ value.is_a? Hash
165
+ @template.factory :nil
166
+ end
167
+
168
+ end
@@ -0,0 +1,79 @@
1
+ # LtdTemplate::Value::Boolean - Represents true/false in an LtdTemplate
2
+ #
3
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # @license MIT License
6
+
7
+ require 'ltdtemplate/code'
8
+
9
+ class LtdTemplate::Value::Boolean < LtdTemplate::Code
10
+
11
+ # Use one shared true value and one shared false value per template.
12
+ def self.instance (template, bool)
13
+ template.factory_singletons[bool ? :bool_true : :bool_false] ||=
14
+ self.new(template, bool)
15
+ end
16
+
17
+ def initialize (template, bool)
18
+ super template
19
+ @bool = bool
20
+ end
21
+
22
+ def to_boolean; @bool; end
23
+ def to_native; @bool; end
24
+ def to_text; ''; end
25
+
26
+ def get_value (opts = {})
27
+ case opts[:method]
28
+ when nil, 'call' then self
29
+ when 'class' then @template.factory :string, 'Boolean'
30
+ when 'str', 'string' then @template.factory :string,
31
+ (@bool ? 'true' : 'false')
32
+ when 'type' then @template.factory :string, 'boolean'
33
+ when '+', '|', 'or' then do_or opts
34
+ when '*', '&', 'and' then do_and opts
35
+ when '!', 'not' then do_not opts
36
+ else do_method opts, 'Boolean'
37
+ end
38
+ end
39
+
40
+ # Implement +/| (or):
41
+ # bool|(bool1, ..., boolN)
42
+ # True if ANY boolean is true. Evaluates {} blocks until true.
43
+ def do_or (opts)
44
+ if not @bool and params = opts[:parameters]
45
+ params.positional.each do |tval|
46
+ return @template.factory :boolean, true if
47
+ tval.get_value(:method => 'call').to_boolean
48
+ end
49
+ end
50
+ self
51
+ end
52
+
53
+ # Implement */& (and):
54
+ # bool&(bool1, ..., boolN)
55
+ # True if ALL booleans are true. Evaluates {} blocks until false.
56
+ def do_and (opts)
57
+ if @bool and params = opts[:parameters]
58
+ params.positional.each do |tval|
59
+ return @template.factory :boolean, false unless
60
+ tval.get_value(:method => 'call').to_boolean
61
+ end
62
+ end
63
+ self
64
+ end
65
+
66
+ # Implement ! (not):
67
+ # bool!(bool1, ..., boolN)
68
+ # True if ALL booleans are false. Evaluates {} blocks until true.
69
+ def do_not (opts)
70
+ if !@bool and params = opts[:parameters]
71
+ params.positional.each do |tval|
72
+ return @template.factory :boolean, false if
73
+ tval.get_value(:method => 'call').to_boolean
74
+ end
75
+ end
76
+ @template.factory :boolean, !@bool
77
+ end
78
+
79
+ end
@@ -0,0 +1,40 @@
1
+ # LtdTemplate::Value::Code_Block - Represents an explicit code block in an
2
+ # LtdTemplate
3
+ #
4
+ # Explicit code blocks are wrappers around implied code blocks. They are
5
+ # essentially anonymous functions; they accept optional parameters and
6
+ # create a new namespace for the duration of each execution.
7
+ #
8
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
9
+ # @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
10
+ # @license MIT License
11
+
12
+ require 'ltdtemplate/code'
13
+
14
+ class LtdTemplate::Value::Code_Block < LtdTemplate::Code
15
+
16
+ attr_reader :code
17
+
18
+ def initialize (template, code)
19
+ super template
20
+ @code = code
21
+ end
22
+
23
+ def to_boolean; true; end
24
+ def to_native; self; end
25
+ def to_text; ''; end
26
+
27
+ def get_value (opts = {})
28
+ case opts[:method]
29
+ when nil then self
30
+ when 'type' then @template.factory :string, 'code'
31
+ else
32
+ @template.push_namespace opts[:method], opts[:parameters],
33
+ :target => (opts[:target] || self)
34
+ result = @code.get_value
35
+ @template.pop_namespace
36
+ result
37
+ end
38
+ end
39
+
40
+ end