re_duxml 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []