re_duxml 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 996c30dfa1b40d7aa2d27de06d5bb770e4858f23
4
+ data.tar.gz: 20462c1ca90131bff9c05fbe9826001fd46f0cec
5
+ SHA512:
6
+ metadata.gz: 46717577e85998db3f56ca0e26fd87b28f6220908fac32002683190b85d550340d3ca9b324163474f47a9ca5888b9f122fd22ed47af48a5c8abd528a071418e0
7
+ data.tar.gz: 2a04ed33b59e2209fb77c240d305233547af83973cb85e67c1d44cb9c14a9a96025f54d68b0b0b3f0e18c6ba5cc29d87e7a3dd369079182cc319450a5c9e284c
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require 'ast'
3
+
4
+ module AST
5
+ # redefining in order to allow type itself to be any type e.g. String, Symbol, Fixnum, etc.
6
+ class Node
7
+ include AST
8
+
9
+ # The `properties` hash is passed to {#assign_properties}.
10
+ def initialize(type, children=[], properties={})
11
+ @type, @children = type, children.to_a.freeze
12
+
13
+ assign_properties(properties)
14
+
15
+ @hash = [@type.object_id, @children, self.class].hash
16
+
17
+ freeze
18
+ end
19
+
20
+ # @param logic [Hash] hash of operators allowed in this AST containing each operator's print properties
21
+ # @return [String] string reconstituted from polish-notation into notation normally required by each operator
22
+ def print(logic=nil)
23
+ return type.to_s if children.empty?
24
+ str = ''
25
+ op = type.respond_to?(:text) ? type : logic[type.to_s]
26
+ return str unless op
27
+ case op.position
28
+ when :prefix
29
+ str << op.symbol
30
+ children.each do |c| str << c.print(logic) end
31
+ when :postfix
32
+ children.each do |c| str << c.print(logic) end
33
+ str << op.symbol
34
+ when :infix
35
+ if op.arity > 2
36
+ str << children.first.print(logic) << op.symbol << children[1].print(logic) << op.pair.to_s << children.last.print
37
+ else
38
+ str << (children.first.respond_to?(:print) ? children.first.print(logic) : children.first.to_s) << op.symbol << children.last.print
39
+ end
40
+ else # should not happen
41
+ end
42
+ str
43
+ end # to_s
44
+ end # class Node
45
+
46
+ def new_ast(op, *obj)
47
+ args = obj.collect do |o| o.is_a?(Node) ? o : Node.new(o) end
48
+ args.unshift self if is_a?(Node)
49
+ args.unshift Node.new(self) if is_a?(Fixnum)
50
+ args.unshift Node.new(self) if self.is_a?(Symbolic::Variable)
51
+ Node.new(op, args)
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require 'con_duxml/instance'
3
+
4
+ module ConDuxml
5
+ # XML object array
6
+ # represents a pattern of copies of a this object's children or referents
7
+ # differentiates between copies using iterator Parameter
8
+ module Array
9
+ include Instance
10
+
11
+ # @return [Array[Element]] flattened array of all duplicated Elements
12
+ def activate
13
+ # TODO add implicit @iterator param
14
+ size_expr = size.respond_to?(:to_i) ? size.to_i : size.to_s
15
+ if size_expr.is_a? Fixnum
16
+ new_children = []
17
+ size_expr.times do
18
+ source_nodes = if nodes.empty? and self[:ref]
19
+ [resolve_ref.clone]
20
+ else
21
+ nodes.collect do |node| node.clone end
22
+ end
23
+ source_nodes.each do |node|
24
+ new_children << node
25
+ end
26
+ end
27
+ new_children.flatten
28
+ else
29
+ [self]
30
+ end
31
+ end # def instantiate
32
+
33
+ # size can be Fixnum or a Parameter expression
34
+ def size
35
+ self[:size]
36
+ end
37
+ end # class Array
38
+ end # module Dux
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../ruby_ext/string')
3
+
4
+ # methods to extend Dux::Object with methods needed to process parameterized XML content
5
+ module Parameterization
6
+ # returns true if self[:if] is true or indeterminate (because condition is currently parameterized)
7
+ # returns false otherwise i.e. this node does not exist in this design build
8
+ def if?
9
+ return true unless (if_str = self[:if])
10
+ if_str.parameterized? || if_str == 'true' ? true : false
11
+ end
12
+
13
+ # changes condition of this object's existence
14
+ def if=(condition)
15
+ # check for valid conditional
16
+ change_attr_value :if, condition
17
+ end
18
+ end # module Parameterization
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative 'element/parameterization'
3
+ require 'duxml'
4
+
5
+ module Duxml
6
+ class Element
7
+ include Parameterization
8
+ end
9
+ end
@@ -0,0 +1,107 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative '../../ruby_ext/regexp'
3
+ require_relative '../../../lib/symbolic_ext/symbolic'
4
+
5
+ module Lexer
6
+ @string_hash
7
+ @input
8
+ @tokens
9
+ attr_reader :string_hash
10
+ attr_accessor :input, :tokens
11
+
12
+ TOKEN_TYPES = {
13
+ string: /STRING[\d]+/,
14
+ function: /log|exp|sqrt/,
15
+ bool: /true|false/,
16
+ param: Regexp.identifier,
17
+ num: /\d+/,
18
+ grouping: /[\(\):,]/
19
+ }
20
+
21
+ Struct.new('Token', :type, :value)
22
+
23
+ def lex(expr)
24
+ @input = tag_strings(expr).split(/\b/).reverse.collect do |s| s.strip end
25
+ @tokens = []
26
+ while (sub_str = input.pop) do
27
+ type = get_type sub_str
28
+ value = formatted_value(sub_str, type)
29
+ tokens << (@last_token = Struct::Token.new(type, value)) unless value.nil?
30
+ end
31
+ @last_token = nil
32
+ tokens
33
+ end # def lex(expr)
34
+
35
+ private
36
+
37
+ attr_reader :last_token
38
+
39
+ def formatted_value(sub_str, type)
40
+ formatted_str = untag_strings subst_minus(sub_str)
41
+ case type
42
+ when :operator, :grouping
43
+ split_or_keep formatted_str
44
+ when :param then get_var(formatted_str)
45
+ when :num then formatted_str.to_i
46
+ when :bool then formatted_str == 'true'
47
+ else
48
+ formatted_str
49
+ end
50
+ end
51
+
52
+ def get_var(str)
53
+ if(var_token = tokens.find do |t| t.value.to_s == str end)
54
+ var_token.value
55
+ else
56
+ Symbolic.send(:var, name: str)
57
+ end
58
+ end
59
+
60
+ def split_or_keep(str)
61
+ if str.size > 1 && logic[str].nil?
62
+ str.split(//).reverse.each do |c| input << c unless c.strip.empty? end
63
+ nil
64
+ else
65
+ logic[str]
66
+ end
67
+ end
68
+
69
+ def get_type(sub_str)
70
+ TOKEN_TYPES.each do |type, regexp|
71
+ return type if regexp.match(sub_str).to_s == sub_str
72
+ end
73
+ :operator
74
+ end
75
+
76
+ def subst_minus(_str)
77
+ str = _str.dup
78
+ if str == '-'
79
+ unless last_token.nil? || last_token.type == 'operator' || %w(\( , :).include?(last_token.value)
80
+ str = "\u2013"
81
+ str.encode('utf-8')
82
+ end
83
+ end
84
+ str
85
+ end
86
+
87
+ #
88
+ def untag_strings(_expr)
89
+ expr = _expr.dup
90
+ string_hash.each do |k, v| expr.gsub!(k, v) end
91
+ expr
92
+ end
93
+
94
+ #strings can contain whitespaces and characters the parser may miscategorize as operators, etc.
95
+ # so they are replaced with unique keys in a module attribute hash for retrieval when doing string operations
96
+ # and returning final result
97
+ def tag_strings(expr)
98
+ tagged_str = expr.dup
99
+ @string_hash = {}
100
+ expr.scan(Regexp.string) do |s|
101
+ k = "STRING#{s.object_id}"
102
+ tagged_str[s] = k
103
+ @string_hash[k] = s
104
+ end
105
+ tagged_str
106
+ end
107
+ end # module Lexer
@@ -0,0 +1,96 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ module Operator
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,144 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require 'duxml'
3
+ require_relative 'operator'
4
+ require_relative 'lexer'
5
+
6
+ module ReDuxml
7
+ class Parser
8
+ # hash of unique strings found in the parsed expression for substitution before (@see Lexer#lex) and
9
+ # after parsing to allow parser to handle strings that may contain white spaces, operators, etc.
10
+ @string_hash
11
+
12
+ # array of Struct::Tokens from lexer
13
+ @input
14
+
15
+ # array of AST nodes produced by parser; can be popped to subordinate to higher precedence operation nodes
16
+ @output
17
+
18
+ # array of operator definitions (not classes!) in XML form, extended by module Operator
19
+ @op_stack
20
+
21
+ # hash of operator symbol strings to Operator object that contains properties and methods to access them
22
+ @logic
23
+
24
+ # stack to track argument count for operators requiring more than 2
25
+ @arities
26
+
27
+ attr_reader :string_hash, :logic
28
+ attr_accessor :input, :output, :op_stack, :arities
29
+
30
+ include Duxml
31
+ include Lexer
32
+
33
+ def to_s
34
+ doc.logic.name
35
+ end
36
+
37
+ def initialize(_logic)
38
+ if _logic
39
+ load _logic
40
+ @logic = {}
41
+ doc.logic.Operator.each do |op|
42
+ op.parent = @logic
43
+ @logic[op.symbol] = op
44
+ end
45
+ end
46
+ raise Exception if logic.nil?
47
+ end
48
+
49
+ # TODO attribute code to Dentaku
50
+ def parse(expr)
51
+ @input = lex(expr)
52
+ @output = []
53
+ @op_stack = []
54
+ @arities = []
55
+
56
+ return nil if input.empty?
57
+
58
+ while(token = input.shift)
59
+ case token.type
60
+ when :num, :bool, :string, :param
61
+ output.push AST::Node.new(token.value)
62
+ when :operator
63
+ op_prop = token.value
64
+ arities << op_prop.arity-1 if op_prop.symbol == '?'
65
+ if op_prop.right_associative?
66
+ while op_stack.last && op_prop.precedence < op_stack.last.precedence
67
+ if !op_stack.last.grouping?
68
+ consume
69
+ else
70
+ break
71
+ end
72
+ end
73
+ else
74
+ while op_stack.last && op_prop.precedence <= op_stack.last.precedence && !op_stack.last.grouping?
75
+ consume
76
+ end
77
+ end
78
+ op_stack << op_prop
79
+ when :function
80
+ arities << 0
81
+ op_stack << token.value
82
+ when :grouping
83
+ op_prop = token.value
84
+ case op_prop.to_s
85
+ when '('
86
+ if input.any? && input.first.type == :grouping && input.first.value.to_s == '('
87
+ input.shift
88
+ consume(0)
89
+ else
90
+ op_stack << op_prop
91
+ end
92
+ when ')'
93
+ while op_stack.any? && op_stack.last.symbol != op_prop.pair.to_s
94
+ consume(arities.pop || op_stack.last.arity)
95
+ end
96
+ lparen = op_stack.pop
97
+ fail ParseError, "Unbalanced parenthesis" unless lparen && lparen.grouping?
98
+
99
+ if op_stack.last && op_stack.last.position == 'prefix'
100
+ consume(arities.pop)
101
+ end
102
+ when ','
103
+ arities[-1] += 1
104
+ while op_stack.any? && op_stack.last.symbol != '('
105
+ consume
106
+ end
107
+ when ':'
108
+ while op_stack.any? && op_stack.last.symbol != '?'
109
+ consume(arities.pop)
110
+ end
111
+ arities[-1] += 1
112
+ op_stack << op_prop
113
+ else
114
+ fail ParseError, "Unknown grouping token #{ token.value }"
115
+ end # case token.value ... when :grouping
116
+ else
117
+ fail ParseError, "Not implemented for tokens of type #{ token.type }"
118
+ end # case token.type
119
+ end # while (token = input.shift)
120
+
121
+ while op_stack.any?
122
+ consume
123
+ end
124
+
125
+ unless output.count == 1
126
+ fail ParseError, "Invalid statement"
127
+ end
128
+
129
+ output.first
130
+ end # def parse(expr)
131
+
132
+ private
133
+
134
+ def consume(count=2)
135
+ op_stack.pop if op_stack.last.symbol == ':'
136
+ operator = op_stack.pop
137
+ output.push AST::Node.new(operator, get_args(operator.arity || count))
138
+ end
139
+
140
+ def get_args(count)
141
+ Array.new(count) { output.pop }.reverse.compact
142
+ end
143
+ end # class Parser
144
+ end # module ReDuxml
@@ -0,0 +1,78 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative 'evaluate/parser'
3
+ require_relative '../../lib/ruby_ext/macro'
4
+
5
+ module ReDuxml
6
+ class Evaluator
7
+ LOGIC_FILE = File.expand_path(File.dirname(__FILE__) + '/../../xml/logic.xml')
8
+ include Math
9
+ include ReDuxml
10
+ include Symbolic
11
+
12
+ @param_hash
13
+ @parser
14
+
15
+ attr_reader :param_hash, :parser
16
+
17
+ def initialize(logic=nil)
18
+ @parser = Parser.new(logic || LOGIC_FILE)
19
+ @param_hash = {}
20
+ end
21
+
22
+ def to_s
23
+ "#<Evaluator: param_hash: '#{param_hash.to_s}' parser_logic: '#{parser}'>"
24
+ end
25
+
26
+ def evaluate(_expr, _param_hash={})
27
+ @param_hash = _param_hash
28
+ expr = resolve_params _expr
29
+ return expr if expr.parameterized?
30
+ return expr if Regexp.identifier.match(expr).to_s == expr
31
+ return expr.to_i if expr.to_i.to_s == expr
32
+ result = reduce parser.parse expr
33
+ case
34
+ when result.respond_to?(:imaginary), result.class == TrueClass, result.class == FalseClass then result
35
+ when result.respond_to?(:print) then result.print(parser.logic)
36
+ else result.to_s
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def resolve_params(_expr)
43
+ expr = _expr.dup
44
+ param_hash.each do |param, val| expr.gsub!(param, val) end
45
+ expr
46
+ end
47
+
48
+ include Symbolic
49
+
50
+ def reduce(_ast)
51
+ ast = _ast.type.respond_to?(:symbol) ? _ast : new_ast(parser.logic[_ast.type.to_s], _ast.children.dup)
52
+ if ast.children.any?
53
+ operator = ast.type
54
+ args = ast.children.collect do |c|
55
+ new_child = c.children.any? ? reduce(c) : c.type
56
+ if new_child.is_a?(Node) && new_child.type.is_a?(Symbol)
57
+ new_ast(parser.logic[new_child.type.to_s], *new_child.children.dup)
58
+ else
59
+ new_child
60
+ end
61
+ end.flatten
62
+ begin
63
+ result = case operator.position
64
+ when :prefix
65
+ method(operator.ruby).call(*args)
66
+ else
67
+ args.first.send(operator.ruby, *args[1..-1])
68
+ end
69
+ result.nil? ? ast : result
70
+ rescue
71
+ ast
72
+ end
73
+ else
74
+ ast
75
+ end
76
+ end # def reduce(ast)
77
+ end # class Evaluator
78
+ end # module ReDux
data/lib/re_duxml.rb ADDED
@@ -0,0 +1,99 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require File.expand_path(File.dirname(__FILE__) + '/ruby_ext/macro')
3
+ require File.expand_path(File.dirname(__FILE__) + '/re_duxml/evaluate')
4
+ require File.expand_path(File.dirname(__FILE__) + '/re_duxml/element')
5
+ require 'con_duxml'
6
+
7
+ module ReDuxml
8
+ include ConDuxml
9
+
10
+ # @param doc_or_node [Doc, String] XML to load; can be Doc, String path or String XML; also XML Element when recursing
11
+ # @return [Doc] instantiated XML with parameters resolved with given values
12
+ def resolve(doc_or_node, parent_params={})
13
+ if doc_or_node.is_a?(Element)
14
+ resolved_node = doc_or_node.stub
15
+ this_params = get_params(doc_or_node, parent_params)
16
+ new_children = doc_or_node.nodes.collect do |child|
17
+ if child.respond_to?(:nodes)
18
+ new_child = child.clone
19
+ new_child.attributes.each do |attr, val|
20
+ new_child[attr] = resolve_str(val, this_params)
21
+ end
22
+ if new_child.if?
23
+ new_child[:if] = nil
24
+ child_params = get_params(new_child, this_params)
25
+ new_child.activate.collect do |inst|
26
+ resolve(inst, child_params)
27
+ end
28
+ end
29
+ else
30
+ resolve_str(child, this_params)
31
+ end
32
+ end.flatten.compact
33
+ resolved_node << new_children
34
+ else
35
+ @e ||= Evaluator.new
36
+ @src_doc = get_doc doc_or_node
37
+ @doc = Doc.new << resolve(src_doc.root, parent_params)
38
+ end
39
+ end
40
+
41
+ attr_reader :e, :src_doc
42
+ private
43
+
44
+
45
+ def get_params(node, param_hash)
46
+ if node.nodes.any? and !node.text? and node.nodes[0].name == 'duxml:parameters'
47
+ local_params = {}
48
+ params = node[0].nodes.clone
49
+ params.each do |param|
50
+ new_val = resolve_str(param[:value], param_hash)
51
+ node[0].delete param unless new_val.parameterized?
52
+ local_params[param[:name]] = new_val
53
+ end
54
+ node.delete node[0] unless node[0].nodes.any?
55
+ param_hash.merge local_params
56
+ else
57
+ param_hash
58
+ end
59
+ end
60
+
61
+ # finds index of close parentheses corresponding to first open parentheses found in given str
62
+ def find_close_parens_index(str)
63
+ levels = 0
64
+ index = 0
65
+ str.each_char do |char|
66
+ case char
67
+ when '(' then
68
+ levels += 1
69
+ when ')' then
70
+ levels -= 1
71
+ else
72
+ end
73
+ return index if levels == 0
74
+ index += 1
75
+ end
76
+ raise Exception, "cannot find end of parameter expression!"
77
+ end
78
+
79
+ # @param content_str [String] string that may contain parameterized expressions e.g. "a @(var0+var1) string @(var2)"
80
+ # @param param_hash [Hash] keys are parameter names, values are parameter values e.g. {var0: 'param', var1: 'eter'}
81
+ # @return [String] content_str with parameter values substituted and algebraically reduced if any unknown parameters remain e.g. 'a parameter string @(var2)'
82
+ def resolve_str(content_str, param_hash)
83
+ question = find_expr content_str
84
+ return content_str if question.nil?
85
+ reply = Macro.new e.evaluate(question, param_hash).to_s
86
+ replacement_str = reply.parameterized? ? reply.macro_string : reply.demacro
87
+ macro_string = Macro.new(question).macro_string
88
+ content_str.gsub(macro_string, replacement_str)
89
+ end
90
+
91
+ # @param str [String] string that may contain parameter expression e.g. 'asdf @(param + 2) asdf'
92
+ # @return [String] macro string e.g. 'asdf @(param + 2) asdf' => 'param + 2'
93
+ def find_expr(str)
94
+ expr_start_index = str.index('@(')
95
+ return nil if expr_start_index.nil?
96
+ expr_end_index = find_close_parens_index str[expr_start_index+1..-1]
97
+ str[expr_start_index+2, expr_end_index-1]
98
+ end
99
+ end # module Dux
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative '../../lib/ast_ext/node'
3
+
4
+ class TrueClass
5
+ def ternary(a, b)
6
+ a
7
+ end
8
+
9
+ def and(obj)
10
+ obj
11
+ end
12
+
13
+ def or(obj)
14
+ true
15
+ end
16
+
17
+ def not
18
+ false
19
+ end
20
+ end
21
+
22
+ class FalseClass
23
+ def ternary(a, b)
24
+ b
25
+ end
26
+
27
+ def and(obj)
28
+ false
29
+ end
30
+
31
+ def or(obj)
32
+ obj
33
+ end
34
+
35
+ def not
36
+ true
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative '../ast_ext/node'
3
+
4
+ class Fixnum
5
+ include AST
6
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative 'string'
3
+
4
+ # string wrapped in parameter expression macro symbol and delimiters,
5
+ # indicating content is to be parsed and resolved when building and validating XML design
6
+ class Macro
7
+ include Enumerable
8
+ include Comparable
9
+
10
+ # is '@' by default
11
+ MACRO_SYMBOL = '@'
12
+ # are parentheses by default e.g. '()'
13
+ DELIMITERS = %w{( )}
14
+
15
+ @macro_string
16
+
17
+ # string including MACRO_SYMBOL and DELIMITERS
18
+ attr_accessor :macro_string
19
+
20
+ # checks a string to see if it's a valid macro expression without leading or trailing non-expression or delimiter text
21
+ def self.is_macro?(str)
22
+ str[0,2] == MACRO_SYMBOL+DELIMITERS.first && str[-1] == DELIMITERS.last && str.balanced_parens?
23
+ end
24
+
25
+ # takes given string and wraps in MACRO_SYMBOL and DELIMITERS if not already wrapped
26
+ # e.g. str => 'asdf'
27
+ # Macro.new str => '@(asdf)'
28
+ def initialize(str)
29
+ @macro_string = Macro.is_macro?(str) ? str : "#{MACRO_SYMBOL}#{DELIMITERS.first}#{str}#{DELIMITERS.last}"
30
+ end
31
+
32
+ # compares #demacro'd @macro_string to obj
33
+ def <=>(obj)
34
+ demacro <=> obj
35
+ end
36
+
37
+ # just yields each character of #demacro'd @macro_string
38
+ def each(&block)
39
+ demacro.split(//).each do |char| yield char end
40
+ end
41
+
42
+ # returns string without MACRO_SYMBOL and DELIMITERS
43
+ def demacro
44
+ macro_string[2..-2]
45
+ end
46
+
47
+ # returns nil if not, and match data for any parameter names found
48
+ def parameterized?
49
+ macro_string.match Regexp.identifier
50
+ end
51
+ end # class Macro
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ class Regexp
3
+ # @return [Regexp] single and double quoted strings
4
+ def self.string
5
+ /['"][^'"]*['"]/
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require 'duxml'
3
+
4
+ # extending String with #parameterized? and #balanced_parens? only to assist macro.rb
5
+ class String
6
+ # returns whether or not contents include any Macro strings i.e. Parameter expressions
7
+ def parameterized?
8
+ self.include?('@(')
9
+ end
10
+
11
+ # returns whether number of open parentheses and close parentheses match
12
+ def balanced_parens?
13
+ self.match(/\(/).size == self.match(/\)/).size
14
+ end
15
+ end # class String
@@ -0,0 +1,16 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative 'variable'
3
+
4
+ module Symbolic
5
+ class Coerced
6
+ include AST
7
+
8
+ def %(numeric)
9
+ numeric.new_ast(:%, @symbolic)
10
+ end
11
+
12
+ def **(numeric)
13
+ numeric.new_ast(:**, @symbolic)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative 'coerced'
3
+
4
+ module Symbolic
5
+ include AST
6
+
7
+ def %(var)
8
+ return 0 if self.object_id == var.object_id
9
+ return self % var if self.is_a?(Numeric) && var.is_a?(Numeric)
10
+ new_ast :%, var
11
+ end
12
+
13
+ def -@(var)
14
+ return -var unless var.is_a?(Node)
15
+ return -var.type if var.type.is_a?(Numeric)
16
+ reversed = var.type.reverse
17
+ reversed ? new_ast(reversed, *var.children.dup) : new_ast(:-@, [var])
18
+ end
19
+
20
+ def not(var)
21
+ return nil if var.is_a?(Symbolic::Variable)
22
+ return !var unless var.is_a?(Node)
23
+ inverted = var.type.inverse
24
+ inverted ? new_ast(inverted, *var.children.dup) : new_ast(:!, [var])
25
+ end
26
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require 'symbolic'
3
+ require_relative '../ruby_ext/fixnum'
4
+ require_relative '../ruby_ext/boolean'
5
+
6
+ module Symbolic
7
+ class Variable
8
+ include AST
9
+
10
+ def and(obj)
11
+ return self if obj.equal?(true) || obj.equal?(self)
12
+ return false if obj.equal?(false)
13
+ nil
14
+ end
15
+
16
+ def or(obj)
17
+ return self if obj.equal?(false) || obj.equal?(self)
18
+ return true if obj.equal?(true)
19
+ nil
20
+ end
21
+
22
+ def <(obj)
23
+ eql?(obj) ? false : nil
24
+ end
25
+
26
+ def >(obj)
27
+ eql?(obj) ? false : nil
28
+ end
29
+
30
+ def !=(obj)
31
+ if obj.is_a?(Variable) || obj.is_a?(Numeric)
32
+ return object_id == obj.object_id ? false : nil
33
+ end
34
+
35
+ return !self if obj.equal?(true)
36
+ return self if obj.equal?(false)
37
+ return false if obj.equal?(self)
38
+ nil
39
+ end
40
+
41
+ def ==(obj)
42
+ if obj.is_a?(Variable) || obj.is_a?(Numeric)
43
+ result = object_id == obj.object_id ? true : nil
44
+ return result
45
+ end
46
+
47
+ return !self if obj.equal?(false)
48
+ return self if obj.equal?(true)
49
+ return true if obj.equal?(self)
50
+ nil
51
+ end
52
+
53
+ def >=(obj)
54
+ object_id == obj.object_id ? true : nil
55
+ end
56
+
57
+ def <=(obj)
58
+ object_id == obj.object_id ? true : nil
59
+ end
60
+ end # class Variable
61
+ end # module Symbolic
data/xml/logic.xml ADDED
@@ -0,0 +1,153 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <logic>
3
+ <!-- indexing -->
4
+ <operator id="op_square" precedence="90" logic="string">
5
+ <regexp>\[</regexp>
6
+ <symbol>[</symbol>
7
+ <pair>]</pair>
8
+ </operator>
9
+ <operator id="cl_square" precedence="90" logic="string">
10
+ <regexp>\]</regexp>
11
+ <symbol>]</symbol>
12
+ <position>postfix</position>
13
+ <pair>[</pair>
14
+ <arity>1</arity>
15
+ </operator>
16
+
17
+ <!-- grouping -->
18
+ <operator id="op_parens" precedence="100" logic="global">
19
+ <regexp>\(</regexp>
20
+ <symbol>(</symbol>
21
+ <position>prefix</position>
22
+ <pair>)</pair>
23
+ <arity>1</arity>
24
+ </operator>
25
+ <operator id="cl_parens" precedence="100" logic="global">
26
+ <regexp>\)</regexp>
27
+ <symbol>)</symbol>
28
+ <position>postfix</position>
29
+ <pair>(</pair>
30
+ <arity>1</arity>
31
+ </operator>
32
+
33
+ <!-- combinators -->
34
+ <operator id="interro" logic="global" precedence="0">
35
+ <regexp>\?</regexp>
36
+ <symbol>?</symbol>
37
+ <right_associative>true</right_associative>
38
+ <ruby>ternary</ruby>
39
+ <arity>3</arity>
40
+ <pair>:</pair>
41
+ </operator>
42
+ <operator id="colon" logic="global" precedence="0">
43
+ <symbol>:</symbol>
44
+ <pair>?</pair>
45
+ </operator>
46
+ <operator id="and" logic="boolean" precedence="10">
47
+ <regexp>&amp;{2}</regexp>
48
+ <symbol>&amp;&amp;</symbol>
49
+ <ruby>and</ruby>
50
+ <identity>true</identity>
51
+ </operator>
52
+ <operator id="or" logic="boolean" precedence="10">
53
+ <regexp>\|{2}</regexp>
54
+ <symbol>||</symbol>
55
+ <ruby>or</ruby>
56
+ <identity>false</identity>
57
+ </operator>
58
+ <operator id="not" logic="boolean" precedence="11">
59
+ <position>prefix</position>
60
+ <regexp>(!)(?=\w|!)</regexp>
61
+ <ruby>not</ruby>
62
+ <symbol>!</symbol>
63
+ <arity>1</arity>
64
+ <identity>monad</identity>
65
+ <right_associative>true</right_associative>
66
+ <inverse>!</inverse>
67
+ </operator>
68
+
69
+ <!-- comparators -->
70
+ <operator id="eq" logic="arithmetic string" precedence="30">
71
+ <symbol>==</symbol>
72
+ <reverse>==</reverse>
73
+ <inverse>!=</inverse>
74
+ </operator>
75
+ <operator id="lt" logic="arithmetic string" precedence="35">
76
+ <symbol>&lt;</symbol>
77
+ <reverse>&gt;</reverse>
78
+ <inverse>&gt;=</inverse>
79
+ </operator>
80
+ <operator id="gt" logic="arithmetic string" precedence="35">
81
+ <symbol>></symbol>
82
+ <reverse>&lt;</reverse>
83
+ <inverse>&lt;=</inverse>
84
+ </operator>
85
+ <operator id="le" logic="arithmetic string" precedence="35">
86
+ <symbol>&lt;=</symbol>
87
+ <reverse>&gt;=</reverse>
88
+ <inverse>&gt;</inverse>
89
+ </operator>
90
+ <operator id="ge" logic="arithmetic string" precedence="35">
91
+ <symbol>>=</symbol>
92
+ <reverse>&lt;=</reverse>
93
+ <inverse>&lt;</inverse>
94
+ </operator>
95
+ <operator id="ne" logic="arithmetic string" precedence="30">
96
+ <symbol>!=</symbol>
97
+ <reverse>!=</reverse>
98
+ <inverse>==</inverse>
99
+ </operator>
100
+
101
+ <!-- arithmetic -->
102
+ <operator id="add" precedence="55" logic="arithmetic string">
103
+ <regexp>\+</regexp>
104
+ <symbol>+</symbol>
105
+ <identity>0</identity>
106
+ <inverse>&#8211;</inverse>
107
+ </operator>
108
+ <operator id="sub" precedence="55" logic="arithmetic">
109
+ <symbol>&#8211;</symbol>
110
+ <ruby>-</ruby>
111
+ <identity>0</identity>
112
+ <inverse>+</inverse>
113
+ </operator>
114
+ <operator id="neg" precedence="65" logic="arithmetic">
115
+ <position>prefix</position>
116
+ <symbol>-</symbol>
117
+ <ruby>-@</ruby>
118
+ <arity>1</arity>
119
+ <right_associative>true</right_associative>
120
+ <identity>0</identity>
121
+ <inverse>-</inverse>
122
+ </operator>
123
+ <operator id="mul" precedence="60" logic="arithmetic">
124
+ <regexp>\*</regexp>
125
+ <symbol>*</symbol>
126
+ <print>×</print>
127
+ <identity>1</identity>
128
+ <inverse>/</inverse>
129
+ </operator>
130
+ <operator id="div" precedence="60" logic="arithmetic">
131
+ <regexp>\/</regexp>
132
+ <symbol>/</symbol>
133
+ <print>÷</print>
134
+ <identity>1</identity>
135
+ <inverse>*</inverse>
136
+ </operator>
137
+ <operator id="mod" precedence="60" logic="arithmetic">
138
+ <symbol>%</symbol>
139
+ </operator>
140
+ <operator id="exp" precedence="70" logic="arithmetic">
141
+ <regexp>\*{2}</regexp>
142
+ <symbol>**</symbol>
143
+ <ruby>**</ruby>
144
+ <print>^</print>
145
+ <identity>1</identity>
146
+ <inverse>log</inverse>
147
+ </operator>
148
+ <operator id="log" precedence="70" logic="arithmetic">
149
+ <symbol>log</symbol>
150
+ <position>prefix</position>
151
+ <inverse>^</inverse>
152
+ </operator>
153
+ </logic>
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: re_duxml
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-09-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: con_duxml
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: ast
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: symbolic
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.3'
55
+ description:
56
+ email:
57
+ - peter.kong@nxp.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/ast_ext/node.rb
63
+ - lib/re_duxml/array.rb
64
+ - lib/re_duxml/element/parameterization.rb
65
+ - lib/re_duxml/element.rb
66
+ - lib/re_duxml/evaluate/lexer.rb
67
+ - lib/re_duxml/evaluate/operator.rb
68
+ - lib/re_duxml/evaluate/parser.rb
69
+ - lib/re_duxml/evaluate.rb
70
+ - lib/re_duxml.rb
71
+ - lib/ruby_ext/boolean.rb
72
+ - lib/ruby_ext/fixnum.rb
73
+ - lib/ruby_ext/macro.rb
74
+ - lib/ruby_ext/regexp.rb
75
+ - lib/ruby_ext/string.rb
76
+ - lib/symbolic_ext/coerced.rb
77
+ - lib/symbolic_ext/symbolic.rb
78
+ - lib/symbolic_ext/variable.rb
79
+ - xml/logic.xml
80
+ homepage: http://www.github.com/re_duxml
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.9.3
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 1.8.11
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.0.14.1
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Reusable Dynamic Universal XML
104
+ test_files: []