ReDuxml 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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3c46a9c2cb2e9736c7e650a7ac4a709adcbafaec
4
+ data.tar.gz: ff9641aec41459ce78fa9290b392762f0c5a0f6d
5
+ SHA512:
6
+ metadata.gz: 993f3702741994531bb37a9a453576eec93002d0054ebbfd23be4569b6dc89e52abddbf2bc8cece85e0544cfc501ab3442d4d7579c15de21559b96e1600cd045
7
+ data.tar.gz: 6f8ac1fa8e955a2a15c4b3c621131f9556e1a4c49458144f2079fc77447a72f5f5d30106123b6b122ce615dc149af84e71d4f87652f2fc443820a2b3a59c369f
@@ -0,0 +1,51 @@
1
+ require 'ast'
2
+
3
+ module AST
4
+ # redefining in order to allow type itself to be any type e.g. String, Symbol, Fixnum, etc.
5
+ class Node
6
+ include AST
7
+
8
+ # The `properties` hash is passed to {#assign_properties}.
9
+ def initialize(type, children=[], properties={})
10
+ @type, @children = type, children.to_a.freeze
11
+
12
+ assign_properties(properties)
13
+
14
+ @hash = [@type.object_id, @children, self.class].hash
15
+
16
+ freeze
17
+ end
18
+
19
+ # @param logic [Hash] hash of operators allowed in this AST containing each operator's print properties
20
+ # @return [String] string reconstituted from polish-notation into notation normally required by each operator
21
+ def print(logic=nil)
22
+ return type.to_s if children.empty?
23
+ str = ''
24
+ op = type.respond_to?(:text) ? type : logic[type.to_s]
25
+ case op.position
26
+ when :prefix
27
+ str << op.symbol
28
+ children.each do |c| str << c.print(logic) end
29
+ when :postfix
30
+ children.each do |c| str << c.print(logic) end
31
+ str << op.symbol
32
+ when :infix
33
+ if op.arity > 2
34
+ str << children.first.print(logic) << op.symbol << children[1].print(logic) << op.pair.to_s << children.last.print
35
+ else
36
+ str << (children.first.respond_to?(:print) ? children.first.print(logic) : children.first.to_s) << op.symbol << children.last.print
37
+ end
38
+ else # should not happen
39
+ end
40
+ str
41
+ end # to_s
42
+ end # class Node
43
+
44
+ def new_ast(op, *obj)
45
+ args = obj.collect do |o| o.is_a?(Node) ? o : Node.new(o) end
46
+ args.unshift self if is_a?(Node)
47
+ args.unshift Node.new(self) if is_a?(Fixnum)
48
+ args.unshift Node.new(self) if self.is_a?(Symbolic::Variable)
49
+ Node.new(op, args)
50
+ end
51
+ end
@@ -0,0 +1,119 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/ruby_ext/macro')
2
+ require File.expand_path(File.dirname(__FILE__) + '/re_dux/evaluate')
3
+ require File.expand_path(File.dirname(__FILE__) + '/re_dux/element')
4
+
5
+ module ReDuxml
6
+ include Duxml
7
+
8
+ class ResolverClass < ::Ox::Sax
9
+ COND_ATTR_NAME = 'if'
10
+ REF_ATTR_NAME = 'ref'
11
+
12
+ @dead = false
13
+ @cursor_stack
14
+
15
+ attr_reader :e, :dead, :cursor_stack
16
+
17
+ # @param doc [Ox::Document] document that is being constructed as XML is parsed
18
+ # @param _observer [Object] object that will observe this document's content
19
+ def initialize(logic)
20
+ @cursor_stack = []
21
+ @e = ReDuxml::Evaluator.new(logic)
22
+ end
23
+
24
+ def cursor
25
+ @cursor_stack.last
26
+ end
27
+
28
+ def start_element(name)
29
+ new_el = Duxml::Element.new(name, line, column)
30
+ cursor << new_el unless cursor.nil?
31
+ @cursor_stack << new_el
32
+ end
33
+
34
+ def attr(name, val)
35
+ cursor[name] = val
36
+ end
37
+
38
+ def text(str)
39
+ cursor << str
40
+ end
41
+
42
+ def end_element(name)
43
+ case
44
+ when cursor.if?
45
+ cursor.remove_attribute(COND_ATTR_NAME)
46
+ when cursor.respond_to?(:instantiate)
47
+ # target = cursor.instantiate # target likely plural
48
+ @cursor_stack[-2].replace(cursor, target)
49
+ when cursor.ref?
50
+ # target = resolve_ref
51
+ @cursor_stack[-2].replace(cursor, target)
52
+ cursor.remove_attribute(REF_ATTR_NAME)
53
+ else
54
+ @cursor_stack[-2].remove(cursor)
55
+ return
56
+ end
57
+ @cursor_stack.pop
58
+ end
59
+ end
60
+
61
+ # generates new doc from current doc, resolving parameter values and instantiating Instance objects, and pruning filtered objonents.
62
+ def resolve(path=nil)
63
+ io = File.open path
64
+ saxer = ResolverClass.new(Duxml::Doc.new)
65
+ Ox.sax_parse(saxer, io, {convert_special: true, symbolize: false})
66
+ saxer.cursor
67
+ end
68
+
69
+ private
70
+
71
+ # finds index of close parentheses corresponding to first open parentheses found in given str
72
+ def find_close_parens_index(str)
73
+ levels = 0
74
+ index = 0
75
+ str.each_char do |char|
76
+ case char
77
+ when '(' then
78
+ levels += 1
79
+ when ')' then
80
+ levels -= 1
81
+ else
82
+ end
83
+ return index if levels == 0
84
+ index += 1
85
+ end
86
+ raise Exception, "cannot find end of parameter expression!"
87
+ end
88
+
89
+ # takes given potentially parameterized string, applies given param_hash's values, then resolves parameter expressions
90
+ # returning resolved result
91
+ def resolve_str(content_str, param_hash)
92
+ question = find_expr content_str
93
+ return content_str if question.nil?
94
+ reply = Macro.new e.evaluate(question, param_hash)
95
+ replacement_str = reply.parameterized? ? reply : reply.demacro
96
+ macro_string = Macro.new(question)
97
+ content_str.gsub(macro_string, replacement_str)
98
+ end
99
+
100
+ # finds macro expression within given string
101
+ # e.g. find_expr 'asdf @(param) asdf' => 'param'
102
+ def find_expr(str)
103
+ expr_start_index = str.index('@(')
104
+ return nil if expr_start_index.nil?
105
+ expr_end_index = find_close_parens_index str[expr_start_index+1..-1]
106
+ str[expr_start_index+2, expr_end_index-1]
107
+ end
108
+
109
+ # returns first ancestor that is NOT an Instance
110
+ def find_non_inst_ancestor(node)
111
+ cur = node.parent
112
+ return if cur.nil?
113
+ if cur.respond_to?(:params) && cur.simple_class != 'design'
114
+ find_non_inst_ancestor cur
115
+ else
116
+ cur
117
+ end
118
+ end
119
+ end # module Dux
@@ -0,0 +1,8 @@
1
+ require_relative 'element/parameterization'
2
+ require 'duxml'
3
+
4
+ module Duxml
5
+ class Element
6
+ include Parameterization
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../ruby_ext/string')
2
+
3
+ # methods to extend Dux::Object with methods needed to process parameterized XML content
4
+ module Parameterization
5
+ # returns true if self[:if] is true or indeterminate (because condition is currently parameterized)
6
+ # returns false otherwise i.e. this node does not exist in this design build
7
+ def if?
8
+ return true unless (if_str = xml[:if])
9
+ if_str.parameterized? || if_str == 'true' ? true : false
10
+ end
11
+
12
+ # changes condition of this object's existence
13
+ def if=(condition)
14
+ # check for valid conditional
15
+ change_attr_value :if, condition
16
+ end
17
+ end # module Parameterization
@@ -0,0 +1,74 @@
1
+ require_relative 'evaluate/parser'
2
+
3
+ module ReDuxml
4
+ class Evaluator
5
+ include Math
6
+ include ReDuxml
7
+ include Symbolic
8
+
9
+ COMPARATORS = %w()
10
+
11
+ @param_hash
12
+ @parser
13
+
14
+ attr_reader :param_hash, :parser
15
+
16
+ def initialize(logic=nil)
17
+ @parser = Parser.new(logic || '../../xml/logic.xml')
18
+ @param_hash = {}
19
+ end
20
+
21
+ def to_s
22
+ "#<Evaluator: param_hash: '#{param_hash.to_s}' parser_logic: '#{parser}'>"
23
+ end
24
+
25
+ def evaluate(_expr, _param_hash={})
26
+ @param_hash = _param_hash
27
+ expr = resolve_params _expr
28
+ result = reduce parser.parse expr
29
+ case
30
+ when result.respond_to?(:imaginary), result.class == TrueClass, result.class == FalseClass then result
31
+ when result.respond_to?(:print) then result.print(parser.logic)
32
+ else result.to_s
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def resolve_params(_expr)
39
+ expr = _expr.dup
40
+ param_hash.each do |param, val| expr.gsub!(param, val) end
41
+ expr
42
+ end
43
+
44
+ include Symbolic
45
+
46
+ def reduce(_ast)
47
+ ast = _ast.type.respond_to?(:symbol) ? _ast : new_ast(parser.logic[_ast.type.to_s], _ast.children.dup)
48
+ if ast.children.any?
49
+ operator = ast.type
50
+ args = ast.children.collect do |c|
51
+ new_child = c.children.any? ? reduce(c) : c.type
52
+ if new_child.is_a?(Node) && new_child.type.is_a?(Symbol)
53
+ new_ast(parser.logic[new_child.type.to_s], *new_child.children.dup)
54
+ else
55
+ new_child
56
+ end
57
+ end.flatten
58
+ begin
59
+ result = case operator.position
60
+ when :prefix
61
+ method(operator.ruby).call(*args)
62
+ else
63
+ args.first.send(operator.ruby, *args[1..-1])
64
+ end
65
+ result.nil? ? ast : result
66
+ rescue
67
+ ast
68
+ end
69
+ else
70
+ ast
71
+ end
72
+ end # def reduce(ast)
73
+ end # class Evaluator
74
+ end # module ReDux
@@ -0,0 +1,106 @@
1
+ require_relative '../../ruby_ext/regexp'
2
+ require_relative '../../../lib/symbolic_ext/symbolic'
3
+
4
+ module Lexer
5
+ @string_hash
6
+ @input
7
+ @tokens
8
+ attr_reader :string_hash
9
+ attr_accessor :input, :tokens
10
+
11
+ TOKEN_TYPES = {
12
+ string: /STRING[\d]+/,
13
+ function: /log|exp|sqrt/,
14
+ bool: /true|false/,
15
+ param: Regexp.identifier,
16
+ num: /\d/,
17
+ grouping: /[\(\):,]/
18
+ }
19
+
20
+ Struct.new('Token', :type, :value)
21
+
22
+ def lex(expr)
23
+ @input = tag_strings(expr).split(/\b/).reverse.collect do |s| s.strip end
24
+ @tokens = []
25
+ while (sub_str = input.pop) do
26
+ type = get_type sub_str
27
+ value = formatted_value(sub_str, type)
28
+ tokens << (@last_token = Struct::Token.new(type, value)) unless value.nil?
29
+ end
30
+ @last_token = nil
31
+ tokens
32
+ end # def lex(expr)
33
+
34
+ private
35
+
36
+ attr_reader :last_token
37
+
38
+ def formatted_value(sub_str, type)
39
+ formatted_str = untag_strings subst_minus(sub_str)
40
+ case type
41
+ when :operator, :grouping
42
+ split_or_keep formatted_str
43
+ when :param then get_var(formatted_str)
44
+ when :num then formatted_str.to_i
45
+ when :bool then formatted_str == 'true'
46
+ else
47
+ formatted_str
48
+ end
49
+ end
50
+
51
+ def get_var(str)
52
+ if(var_token = tokens.find do |t| t.value.to_s == str end)
53
+ var_token.value
54
+ else
55
+ Symbolic.send(:var, name: str)
56
+ end
57
+ end
58
+
59
+ def split_or_keep(str)
60
+ if str.size > 1 && logic[str].nil?
61
+ str.split(//).reverse.each do |c| input << c unless c.strip.empty? end
62
+ nil
63
+ else
64
+ logic[str]
65
+ end
66
+ end
67
+
68
+ def get_type(sub_str)
69
+ TOKEN_TYPES.each do |type, regexp|
70
+ return type if regexp.match(sub_str).to_s == sub_str
71
+ end
72
+ :operator
73
+ end
74
+
75
+ def subst_minus(_str)
76
+ str = _str.dup
77
+ if str == '-'
78
+ unless last_token.nil? || last_token.type == 'operator' || %w(\( , :).include?(last_token.value)
79
+ str = "\u2013"
80
+ str.encode('utf-8')
81
+ end
82
+ end
83
+ str
84
+ end
85
+
86
+ #
87
+ def untag_strings(_expr)
88
+ expr = _expr.dup
89
+ string_hash.each do |k, v| expr.gsub!(k, v) end
90
+ expr
91
+ end
92
+
93
+ #strings can contain whitespaces and characters the parser may miscategorize as operators, etc.
94
+ # so they are replaced with unique keys in a module attribute hash for retrieval when doing string operations
95
+ # and returning final result
96
+ def tag_strings(expr)
97
+ tagged_str = expr.dup
98
+ @string_hash = {}
99
+ expr.scan(Regexp.string) do |s|
100
+ k = "STRING#{s.object_id}"
101
+ tagged_str[s] = k
102
+ @string_hash[k] = s
103
+ end
104
+ tagged_str
105
+ end
106
+ end # module Lexer
@@ -0,0 +1,96 @@
1
+ module Operator
2
+ include Reportable
3
+ # @return [Boolean]
4
+ def grouping?
5
+ nodes.find do |n|
6
+ return true if n.name == 'pair'
7
+ end
8
+ false
9
+ end
10
+
11
+ def parent=(logic)
12
+ @logic = logic
13
+ end
14
+
15
+ # @return [Boolean]
16
+ def right_associative?
17
+ nodes.find do |n|
18
+ return n.text == 'true' if n.name == 'right_associative'
19
+ end
20
+ false
21
+ end
22
+
23
+ # @return [String] name of ruby method corresponding to this operator
24
+ def ruby
25
+ nodes.find do |n|
26
+ return n.text if n.name == 'ruby'
27
+ end
28
+ symbol
29
+ end
30
+
31
+ # @return [String] literal for operator e.g. '+'
32
+ def symbol
33
+ return nil unless self.respond_to?(:nodes)
34
+ nodes.find do |n|
35
+ return n.text if n.name == 'symbol'
36
+ end
37
+ raise Exception
38
+ end
39
+
40
+ # @return [Symbol] :prefix, :infix (default), or :postfix
41
+ def position
42
+ nodes.find do |n|
43
+ return n.text.to_sym if n.name == 'position'
44
+ end
45
+ :infix
46
+ end
47
+
48
+ def reverse
49
+ nodes.find do |n|
50
+ return @logic[n.text] if n.name == 'reverse'
51
+ end
52
+ nil
53
+ end
54
+
55
+ def pair
56
+ return nil unless grouping?
57
+ nodes.find do |n|
58
+ return @logic[n.text] if n.name == 'pair'
59
+ end
60
+ raise Exception
61
+ end
62
+
63
+ def inverse
64
+ nodes.find do |n|
65
+ return @logic[n.text] if n.name == 'inverse'
66
+ end
67
+ nil
68
+ end
69
+
70
+ def to_s
71
+ symbol
72
+ end
73
+
74
+ def print
75
+ nodes.find do |n|
76
+ return n.text if n.name == 'print'
77
+ end
78
+ symbol
79
+ end
80
+
81
+ # @return [Regexp] expression to find operator in string
82
+ def regexp
83
+ nodes.find do |n|
84
+ return Regexp.new(n.text) if %w(regexp symbol ).include?(n.name)
85
+ end
86
+ # TODO exception here?
87
+ end
88
+
89
+ # @return [Fixnum] number of arguments required
90
+ def arity
91
+ nodes.find do |n|
92
+ return n.text.to_i if n.name == 'arity'
93
+ end
94
+ 2
95
+ end
96
+ end
@@ -0,0 +1,143 @@
1
+ require 'duxml'
2
+ require_relative 'operator'
3
+ require_relative 'lexer'
4
+
5
+ module ReDuxml
6
+ class Parser
7
+ # hash of unique strings found in the parsed expression for substitution before (@see Lexer#lex) and
8
+ # after parsing to allow parser to handle strings that may contain white spaces, operators, etc.
9
+ @string_hash
10
+
11
+ # array of Struct::Tokens from lexer
12
+ @input
13
+
14
+ # array of AST nodes produced by parser; can be popped to subordinate to higher precedence operation nodes
15
+ @output
16
+
17
+ # array of operator definitions (not classes!) in XML form, extended by module Operator
18
+ @op_stack
19
+
20
+ # hash of operator symbol strings to Operator object that contains properties and methods to access them
21
+ @logic
22
+
23
+ # stack to track argument count for operators requiring more than 2
24
+ @arities
25
+
26
+ attr_reader :string_hash, :logic
27
+ attr_accessor :input, :output, :op_stack, :arities
28
+
29
+ include Duxml
30
+ include Lexer
31
+
32
+ def to_s
33
+ doc.logic.name
34
+ end
35
+
36
+ def initialize(_logic)
37
+ if _logic
38
+ load _logic
39
+ @logic = {}
40
+ doc.logic.Operator.each do |op|
41
+ op.parent = @logic
42
+ @logic[op.symbol] = op
43
+ end
44
+ end
45
+ raise Exception if logic.nil?
46
+ end
47
+
48
+ # TODO attribute code to Dentaku
49
+ def parse(expr)
50
+ @input = lex(expr)
51
+ @output = []
52
+ @op_stack = []
53
+ @arities = []
54
+
55
+ return nil if input.empty?
56
+
57
+ while(token = input.shift)
58
+ case token.type
59
+ when :num, :bool, :string, :param
60
+ output.push AST::Node.new(token.value)
61
+ when :operator
62
+ op_prop = token.value
63
+ arities << op_prop.arity-1 if op_prop.symbol == '?'
64
+ if op_prop.right_associative?
65
+ while op_stack.last && op_prop.precedence < op_stack.last.precedence
66
+ if !op_stack.last.grouping?
67
+ consume
68
+ else
69
+ break
70
+ end
71
+ end
72
+ else
73
+ while op_stack.last && op_prop.precedence <= op_stack.last.precedence && !op_stack.last.grouping?
74
+ consume
75
+ end
76
+ end
77
+ op_stack << op_prop
78
+ when :function
79
+ arities << 0
80
+ op_stack << token.value
81
+ when :grouping
82
+ op_prop = token.value
83
+ case op_prop.to_s
84
+ when '('
85
+ if input.any? && input.first.type == :grouping && input.first.value.to_s == '('
86
+ input.shift
87
+ consume(0)
88
+ else
89
+ op_stack << op_prop
90
+ end
91
+ when ')'
92
+ while op_stack.any? && op_stack.last.symbol != op_prop.pair.to_s
93
+ consume(arities.pop || op_stack.last.arity)
94
+ end
95
+ lparen = op_stack.pop
96
+ fail ParseError, "Unbalanced parenthesis" unless lparen && lparen.grouping?
97
+
98
+ if op_stack.last && op_stack.last.position == 'prefix'
99
+ consume(arities.pop)
100
+ end
101
+ when ','
102
+ arities[-1] += 1
103
+ while op_stack.any? && op_stack.last.symbol != '('
104
+ consume
105
+ end
106
+ when ':'
107
+ while op_stack.any? && op_stack.last.symbol != '?'
108
+ consume(arities.pop)
109
+ end
110
+ arities[-1] += 1
111
+ op_stack << op_prop
112
+ else
113
+ fail ParseError, "Unknown grouping token #{ token.value }"
114
+ end # case token.value ... when :grouping
115
+ else
116
+ fail ParseError, "Not implemented for tokens of type #{ token.type }"
117
+ end # case token.type
118
+ end # while (token = input.shift)
119
+
120
+ while op_stack.any?
121
+ consume
122
+ end
123
+
124
+ unless output.count == 1
125
+ fail ParseError, "Invalid statement"
126
+ end
127
+
128
+ output.first
129
+ end # def parse(expr)
130
+
131
+ private
132
+
133
+ def consume(count=2)
134
+ op_stack.pop if op_stack.last.symbol == ':'
135
+ operator = op_stack.pop
136
+ output.push AST::Node.new(operator, get_args(operator.arity || count))
137
+ end
138
+
139
+ def get_args(count)
140
+ Array.new(count) { output.pop }.reverse.compact
141
+ end
142
+ end # class Parser
143
+ end # module ReDuxml
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../symbolic_ext/variable')
2
+
3
+ module Duxml
4
+ module Parameter; end
5
+
6
+ # represents a parameter that can be used in any element's attribute values or text content
7
+ # and is replaced with its value when validating an XML design
8
+ class ParameterClass < ::Symbolic::Variable
9
+ # Parameter can be initialized from XML Element or Ruby args
10
+ # @param args[0] [String] must be name of Parameter
11
+ # @param args[1] [String|Fixnum|Float|Boolean] can be starting value of Parameter
12
+ # @param args[2] [String] can be description text of Parameter
13
+ def initialize(name, value=nil)
14
+ @name, @value = name, value
15
+ end
16
+ end # class Parameter
17
+
18
+ module Parameter
19
+ # @param val [String|Fixnum|Float|Boolean] changes value of Parameter and reports change
20
+ def value=(val)
21
+ if val != self[:value]
22
+ old_val = self[:value]
23
+ self[:value] = val
24
+ report :change_attribute, {old_value: old_val, new_value: val, attr_name: 'value'}
25
+ end
26
+ end # def value=
27
+ end
28
+ end # module Dux
@@ -0,0 +1,37 @@
1
+ require_relative '../../lib/ast_ext/node'
2
+
3
+ class TrueClass
4
+ def ternary(a, b)
5
+ a
6
+ end
7
+
8
+ def and(obj)
9
+ obj
10
+ end
11
+
12
+ def or(obj)
13
+ true
14
+ end
15
+
16
+ def not
17
+ false
18
+ end
19
+ end
20
+
21
+ class FalseClass
22
+ def ternary(a, b)
23
+ b
24
+ end
25
+
26
+ def and(obj)
27
+ false
28
+ end
29
+
30
+ def or(obj)
31
+ obj
32
+ end
33
+
34
+ def not
35
+ true
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ require_relative '../ast_ext/node'
2
+
3
+ class Fixnum
4
+ include AST
5
+ end
@@ -0,0 +1,50 @@
1
+ require_relative 'string'
2
+
3
+ # string wrapped in parameter expression macro symbol and delimiters,
4
+ # indicating content is to be parsed and resolved when building and validating XML design
5
+ class Macro
6
+ include Enumerable
7
+ include Comparable
8
+
9
+ # is '@' by default
10
+ MACRO_SYMBOL = '@'
11
+ # are parentheses by default e.g. '()'
12
+ DELIMITERS = %w{( )}
13
+
14
+ @macro_string
15
+
16
+ # string including MACRO_SYMBOL and DELIMITERS
17
+ attr_accessor :macro_string
18
+
19
+ # takes given string and wraps in MACRO_SYMBOL and DELIMITERS if not already wrapped
20
+ # e.g. str => 'asdf'
21
+ # Macro.new str => '@(asdf)'
22
+ def initialize(str)
23
+ @macro_string = is_macro?(str) ? str : "#{MACRO_SYMBOL}#{DELIMITERS.first}#{str}#{DELIMITERS.last}"
24
+ end
25
+
26
+ # checks a string to see if it's a valid macro expression without leading or trailing non-expression or delimiter text
27
+ def is_macro?(str)
28
+ str[0,2] == MACRO_SYMBOL+DELIMITERS.first && str[-1] == DELIMITERS.last && str.balanced_parens?
29
+ end
30
+
31
+ # compares #demacro'd @macro_string to obj
32
+ def <=>(obj)
33
+ demacro <=> obj
34
+ end
35
+
36
+ # just yields each character of #demacro'd @macro_string
37
+ def each(&block)
38
+ demacro.split(//).each do |char| yield char end
39
+ end
40
+
41
+ # returns string without MACRO_SYMBOL and DELIMITERS
42
+ def demacro
43
+ macro_string[2..-2]
44
+ end
45
+
46
+ # returns nil if not, and match data for any parameter names found
47
+ def parameterized?
48
+ macro_string.match Regexp.identifier
49
+ end
50
+ end # class Macro
@@ -0,0 +1,8 @@
1
+ require 'duxml'
2
+
3
+ class Regexp
4
+ # @return [Regexp] single and double quoted strings
5
+ def self.string
6
+ /['"][^'"]*['"]/
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ # extending String with #parameterized? and #balanced_parens? only to assist macro.rb
2
+ class String
3
+ # returns whether or not contents include any Macro strings i.e. Parameter expressions
4
+ def parameterized?
5
+ self.include?('@(')
6
+ end
7
+
8
+ # returns whether number of open parentheses and close parentheses match
9
+ def balanced_parens?
10
+ self.match('(').size == self.match(')').size
11
+ end
12
+ end # class String
@@ -0,0 +1,15 @@
1
+ require_relative 'variable'
2
+
3
+ module Symbolic
4
+ class Coerced
5
+ include AST
6
+
7
+ def %(numeric)
8
+ numeric.new_ast(:%, @symbolic)
9
+ end
10
+
11
+ def **(numeric)
12
+ numeric.new_ast(:**, @symbolic)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'coerced'
2
+
3
+ module Symbolic
4
+ include AST
5
+
6
+ def %(var)
7
+ return 0 if self.object_id == var.object_id
8
+ return self % var if self.is_a?(Numeric) && var.is_a?(Numeric)
9
+ new_ast :%, var
10
+ end
11
+
12
+ def -@(var)
13
+ return -var unless var.is_a?(Node)
14
+ return -var.type if var.type.is_a?(Numeric)
15
+ reversed = var.type.reverse
16
+ reversed ? new_ast(reversed, *var.children.dup) : new_ast(:-@, [var])
17
+ end
18
+
19
+ def not(var)
20
+ return nil if var.is_a?(Symbolic::Variable)
21
+ return !var unless var.is_a?(Node)
22
+ inverted = var.type.inverse
23
+ inverted ? new_ast(inverted, *var.children.dup) : new_ast(:!, [var])
24
+ end
25
+ end
@@ -0,0 +1,60 @@
1
+ require 'symbolic'
2
+ require_relative '../ruby_ext/fixnum'
3
+ require_relative '../ruby_ext/boolean'
4
+
5
+ module Symbolic
6
+ class Variable
7
+ include AST
8
+
9
+ def and(obj)
10
+ return self if obj.equal?(true) || obj.equal?(self)
11
+ return false if obj.equal?(false)
12
+ nil
13
+ end
14
+
15
+ def or(obj)
16
+ return self if obj.equal?(false) || obj.equal?(self)
17
+ return true if obj.equal?(true)
18
+ nil
19
+ end
20
+
21
+ def <(obj)
22
+ eql?(obj) ? false : nil
23
+ end
24
+
25
+ def >(obj)
26
+ eql?(obj) ? false : nil
27
+ end
28
+
29
+ def !=(obj)
30
+ if obj.is_a?(Variable) || obj.is_a?(Numeric)
31
+ return object_id == obj.object_id ? false : nil
32
+ end
33
+
34
+ return !self if obj.equal?(true)
35
+ return self if obj.equal?(false)
36
+ return false if obj.equal?(self)
37
+ nil
38
+ end
39
+
40
+ def ==(obj)
41
+ if obj.is_a?(Variable) || obj.is_a?(Numeric)
42
+ result = object_id == obj.object_id ? true : nil
43
+ return result
44
+ end
45
+
46
+ return !self if obj.equal?(false)
47
+ return self if obj.equal?(true)
48
+ return true if obj.equal?(self)
49
+ nil
50
+ end
51
+
52
+ def >=(obj)
53
+ object_id == obj.object_id ? true : nil
54
+ end
55
+
56
+ def <=(obj)
57
+ object_id == obj.object_id ? true : nil
58
+ end
59
+ end # class Variable
60
+ end # module Symbolic
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ReDuxml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter Kong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: duxml
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: symbolic
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ description:
42
+ email:
43
+ - peter.kong@nxp.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/ast_ext/node.rb
49
+ - lib/re_dux/element/parameterization.rb
50
+ - lib/re_dux/element.rb
51
+ - lib/re_dux/evaluate/lexer.rb
52
+ - lib/re_dux/evaluate/operator.rb
53
+ - lib/re_dux/evaluate/parser.rb
54
+ - lib/re_dux/evaluate.rb
55
+ - lib/re_dux/parameters.rb
56
+ - lib/re_dux.rb
57
+ - lib/ruby_ext/boolean.rb
58
+ - lib/ruby_ext/fixnum.rb
59
+ - lib/ruby_ext/macro.rb
60
+ - lib/ruby_ext/regexp.rb
61
+ - lib/ruby_ext/string.rb
62
+ - lib/symbolic_ext/coerced.rb
63
+ - lib/symbolic_ext/symbolic.rb
64
+ - lib/symbolic_ext/variable.rb
65
+ homepage: http://www.github.com/re_duxml
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 1.9.3
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.11
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.0.14.1
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Reusable Dynamic Universal XML
89
+ test_files: []