ReDuxml 0.1.0

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