rupkl 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,125 @@
2
2
 
3
3
  module RuPkl
4
4
  module Node
5
+ module Operatable
6
+ def s_op(operator, key, context, position)
7
+ check_s_op(operator, position)
8
+
9
+ k = key.evaluate(context)
10
+ valid_key_operand?(k) ||
11
+ raise_invalid_key_error(operator, k, position)
12
+
13
+ (v = find_by_key(k)) ||
14
+ raise_no_key_found_error(k, context, position)
15
+
16
+ v.evaluate
17
+ end
18
+
19
+ def u_op(operator, position)
20
+ check_u_op(operator, position)
21
+ __send__(u_op_method(operator), position)
22
+ end
23
+
24
+ def b_op(operator, operand, context, position)
25
+ check_b_op(operator, position)
26
+ return self if short_circuit?(operator)
27
+
28
+ r_operand = operand.evaluate(context)
29
+ if valid_r_operand?(operator, r_operand)
30
+ __send__(b_op_method(operator), r_operand, position)
31
+ elsif [:==, :'!='].any?(operator)
32
+ Boolean.new(nil, operator != :==, position)
33
+ else
34
+ raise_invalid_r_operand_error(operator, r_operand, position)
35
+ end
36
+ end
37
+
38
+ def b_op_eq(r_operand, position)
39
+ result = self == r_operand
40
+ Boolean.new(nil, result, position)
41
+ end
42
+
43
+ def b_op_ne(r_operand, position)
44
+ result = self != r_operand
45
+ Boolean.new(nil, result, position)
46
+ end
47
+
48
+ private
49
+
50
+ def check_s_op(operator, position)
51
+ check_operator(operator, position)
52
+ end
53
+
54
+ def valid_key_operand?(_key)
55
+ true
56
+ end
57
+
58
+ def raise_invalid_key_error(operator, key, position)
59
+ message =
60
+ "invalid key operand type #{key.class_name} is given " \
61
+ "for operator '#{operator}'"
62
+ raise EvaluationError.new(message, position)
63
+ end
64
+
65
+ def raise_no_key_found_error(key, context, position)
66
+ message = "cannot find key '#{key.to_pkl_string(context)}'"
67
+ raise EvaluationError.new(message, position)
68
+ end
69
+
70
+ def check_u_op(operator, position)
71
+ op = :"#{operator}@"
72
+ check_operator(op, position)
73
+ end
74
+
75
+ def u_op_method(operator)
76
+ { '-': :u_op_minus, '!': :u_op_negate }[operator]
77
+ end
78
+
79
+ def check_b_op(operator, position)
80
+ check_operator(operator, position)
81
+ end
82
+
83
+ def short_circuit?(_operator)
84
+ false
85
+ end
86
+
87
+ def valid_r_operand?(_operator, operand)
88
+ operand.is_a?(self.class)
89
+ end
90
+
91
+ def raise_invalid_r_operand_error(operator, r_operand, position)
92
+ message =
93
+ "invalid operand type #{r_operand.class_name} is given " \
94
+ "for operator '#{operator}'"
95
+ raise EvaluationError.new(message, position)
96
+ end
97
+
98
+ def b_op_method(operator)
99
+ {
100
+ '**': :b_op_exp,
101
+ '*': :b_op_mul, '/': :b_op_div,
102
+ '~/': :b_op_truncating_div, '%': :b_op_rem,
103
+ '+': :b_op_add, '-': :b_op_sub,
104
+ '<': :b_op_lt, '>': :b_op_gt,
105
+ '<=': :b_op_le, '>=': :b_op_ge,
106
+ '==': :b_op_eq, '!=': :b_op_ne,
107
+ '&&': :b_op_and, '||': :b_op_or
108
+ }[operator]
109
+ end
110
+
111
+ def check_operator(operator, position)
112
+ return if
113
+ [:==, :'!='].any?(operator) || defined_operator?(operator)
114
+
115
+ message = "operator '#{operator}' is not defined for #{class_name} type"
116
+ raise EvaluationError.new(message, position)
117
+ end
118
+
119
+ def defined_operator?(_operator)
120
+ false
121
+ end
122
+ end
123
+
5
124
  module OperationCommon
6
125
  include NodeCommon
7
126
 
@@ -14,7 +133,7 @@ module RuPkl
14
133
  attr_reader :operator
15
134
  attr_reader :operands
16
135
 
17
- def copy(parent = nil)
136
+ def copy(parent = nil, position = @position)
18
137
  copied_operands = operands.map(&:copy)
19
138
  self.class.new(parent, operator, *copied_operands, position)
20
139
  end
@@ -23,18 +142,7 @@ module RuPkl
23
142
 
24
143
  def s_op(context)
25
144
  r = receiver.evaluate(context)
26
- check_operator(r)
27
-
28
- k = key.evaluate(context)
29
- check_key_operand(r, k)
30
-
31
- (v = r.find_by_key(k)) ||
32
- begin
33
- message = "cannot find key '#{k.to_pkl_string(context)}'"
34
- raise EvaluationError.new(message, position)
35
- end
36
-
37
- v.evaluate
145
+ r.s_op(operator, key, context, position)
38
146
  end
39
147
 
40
148
  def non_null_op(context)
@@ -47,28 +155,12 @@ module RuPkl
47
155
 
48
156
  def u_op(context)
49
157
  o = operand.evaluate(context)
50
- check_operator(o)
51
-
52
- result =
53
- if operator == :-
54
- -o.value
55
- else
56
- !o.value
57
- end
58
- create_op_result(result)
158
+ o.u_op(operator, position)
59
159
  end
60
160
 
61
161
  def b_op(context)
62
162
  l = l_operand.evaluate(context)
63
- check_operator(l)
64
- return l if short_circuit?(l)
65
-
66
- r = r_operand.evaluate(context)
67
- check_r_operand(l, r)
68
- .then { return _1 if _1 }
69
-
70
- l, r = l.coerce(operator, r)
71
- create_op_result(l.__send__(ruby_op, r))
163
+ l.b_op(operator, r_operand, context, position)
72
164
  end
73
165
 
74
166
  def null_coalescing_op(context)
@@ -77,75 +169,6 @@ module RuPkl
77
169
 
78
170
  r_operand.evaluate(context)
79
171
  end
80
-
81
- def check_operator(operand)
82
- undefined_operator?(operand) &&
83
- begin
84
- message =
85
- "operator '#{operator}' is not defined for " \
86
- "#{operand.class.basename} type"
87
- raise EvaluationError.new(message, position)
88
- end
89
- end
90
-
91
- def undefined_operator?(operand)
92
- !operand.respond_to?(:undefined_operator?) ||
93
- operand.undefined_operator?(operator)
94
- end
95
-
96
- def check_key_operand(receiver, key)
97
- invalid_key_operand?(receiver, key) &&
98
- begin
99
- message =
100
- "invalid key operand type #{key.class.basename} is given " \
101
- "for operator '#{operator}'"
102
- raise EvaluationError.new(message, position)
103
- end
104
- end
105
-
106
- def invalid_key_operand?(receiver, key)
107
- receiver.respond_to?(:invalid_key_operand?) &&
108
- receiver.invalid_key_operand?(key)
109
- end
110
-
111
- def check_r_operand(l_operand, r_operand)
112
- return unless invalid_r_operand?(l_operand, r_operand)
113
-
114
- if [:==, :'!='].include?(operator)
115
- create_op_result(operator != :==)
116
- else
117
- message =
118
- "invalid operand type #{r_operand.class.basename} is given " \
119
- "for operator '#{operator}'"
120
- raise EvaluationError.new(message, position)
121
- end
122
- end
123
-
124
- def invalid_r_operand?(l_operand, r_operand)
125
- if l_operand.respond_to?(:invalid_r_operand?)
126
- l_operand.invalid_r_operand?(r_operand)
127
- else
128
- !r_operand.is_a?(l_operand.class)
129
- end
130
- end
131
-
132
- def short_circuit?(l_operand)
133
- l_operand.respond_to?(:short_circuit?) &&
134
- l_operand.short_circuit?(operator)
135
- end
136
-
137
- def ruby_op
138
- { '&&': :&, '||': :|, '~/': :/ }.fetch(operator, operator)
139
- end
140
-
141
- def create_op_result(result)
142
- case result
143
- when ::Integer then Int.new(parent, result, position)
144
- when ::Float then Float.new(parent, result, position)
145
- when ::String then String.new(parent, result, nil, position)
146
- when true, false then Boolean.new(parent, result, position)
147
- end
148
- end
149
172
  end
150
173
 
151
174
  class SubscriptOperation
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Pair < Any
6
+ include Operatable
7
+
8
+ uninstantiable_class
9
+
10
+ def first
11
+ @children[0]
12
+ end
13
+
14
+ def second
15
+ @children[1]
16
+ end
17
+
18
+ def evaluate(_context = nil)
19
+ self
20
+ end
21
+
22
+ def to_ruby(context = nil)
23
+ [first, second].map { _1.to_ruby(context) }
24
+ end
25
+
26
+ def to_pkl_string(context = nil)
27
+ to_string(context)
28
+ end
29
+
30
+ def to_string(context = nil)
31
+ element_strings =
32
+ [first, second].map { _1.to_pkl_string(context) }
33
+ "Pair(#{element_strings[0]}, #{element_strings[1]})"
34
+ end
35
+
36
+ def ==(other)
37
+ other.is_a?(self.class) &&
38
+ first == other.first && second == other.second
39
+ end
40
+
41
+ define_builtin_property(:first) do
42
+ first
43
+ end
44
+
45
+ define_builtin_property(:key) do
46
+ first
47
+ end
48
+
49
+ define_builtin_property(:second) do
50
+ second
51
+ end
52
+
53
+ define_builtin_property(:value) do
54
+ second
55
+ end
56
+ end
57
+ end
58
+ end
@@ -8,10 +8,6 @@ module RuPkl
8
8
  abstract_class
9
9
  klass_name :Module
10
10
 
11
- def properties
12
- @body&.properties(visibility: :object)
13
- end
14
-
15
11
  def pkl_methods
16
12
  @body&.pkl_methods
17
13
  end
@@ -25,6 +21,10 @@ module RuPkl
25
21
  super
26
22
  end
27
23
 
24
+ def to_ruby(context = nil)
25
+ to_pkl_object(context)
26
+ end
27
+
28
28
  private
29
29
 
30
30
  def properties_not_allowed?
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Regex < Any
6
+ include Operatable
7
+
8
+ uninstantiable_class
9
+
10
+ def initialize(parent, pattern, position)
11
+ super
12
+ @pattern = Regexp.new(pattern.value)
13
+ end
14
+
15
+ attr_reader :pattern
16
+
17
+ def evaluate(_context = nil)
18
+ self
19
+ end
20
+
21
+ def to_ruby(_context = nil)
22
+ pattern
23
+ end
24
+
25
+ def to_pkl_string(context = nil)
26
+ to_string(context)
27
+ end
28
+
29
+ def to_string(context = nil)
30
+ source_string = source.to_pkl_string(context)
31
+ "Regex(#{source_string})"
32
+ end
33
+
34
+ def ==(other)
35
+ other.is_a?(self.class) && pattern == other.pattern
36
+ end
37
+
38
+ define_builtin_property(:pattern) do
39
+ source
40
+ end
41
+
42
+ define_builtin_property(:groupCount) do
43
+ Int.new(nil, group_count, position)
44
+ end
45
+
46
+ define_builtin_method(:findMatchesIn, input: String) do |args, parent, position|
47
+ find_matches(args[:input].value, parent, position)
48
+ end
49
+
50
+ define_builtin_method(:matchEntire, input: String) do |args, parent, position|
51
+ match_entire(args[:input].value, parent, position)
52
+ end
53
+
54
+ private
55
+
56
+ def source
57
+ @children[0]
58
+ end
59
+
60
+ def group_count
61
+ Regexp::Parser
62
+ .parse(pattern)
63
+ .each_expression.count { |exp, _| exp.capturing? }
64
+ end
65
+
66
+ def find_matches(string, parent, position)
67
+ matches = []
68
+ offset = 0
69
+ while (match_data = match_pattern(string, offset))
70
+ matches << RegexMatch.create(match_data, nil, position)
71
+ offset = calc_next_offset(match_data, offset)
72
+ end
73
+ List.new(parent, matches, position)
74
+ end
75
+
76
+ def match_entire(string, parent, position)
77
+ match_pattern(string, 0).then do |m|
78
+ if m && m.end(0) == string.size
79
+ RegexMatch.create(m, parent, position)
80
+ else
81
+ Null.new(parent, position)
82
+ end
83
+ end
84
+ end
85
+
86
+ def match_pattern(string, offset)
87
+ return if offset > string.size
88
+
89
+ pattern.match(string, offset)
90
+ end
91
+
92
+ def calc_next_offset(match_data, offset)
93
+ if match_data[0].empty?
94
+ offset + 1
95
+ else
96
+ match_data.end(0)
97
+ end
98
+ end
99
+ end
100
+
101
+ class RegexMatch < Any
102
+ include Operatable
103
+
104
+ uninstantiable_class
105
+
106
+ class << self
107
+ def create(match_data, parent, position)
108
+ groups = Array.new(match_data.size) do |i|
109
+ if match_data[i]
110
+ create_regex_match(
111
+ match_data[i], match_data.begin(i),
112
+ match_data.end(i), nil, nil, position
113
+ )
114
+ else
115
+ Null.new(nil, position)
116
+ end
117
+ end
118
+ create_regex_match(
119
+ match_data[0], match_data.begin(0),
120
+ match_data.end(0), groups, parent, position
121
+ )
122
+ end
123
+
124
+ private
125
+
126
+ def create_regex_match(value, start_offset, end_offset, groups, parent, position)
127
+ v = String.new(nil, value, nil, position)
128
+ s = Int.new(nil, start_offset, position)
129
+ e = Int.new(nil, end_offset, position)
130
+ g = List.new(nil, groups, position)
131
+ new(parent, v, s, e, g, position)
132
+ end
133
+ end
134
+
135
+ def value
136
+ @children[0]
137
+ end
138
+
139
+ def start
140
+ @children[1]
141
+ end
142
+
143
+ alias_method :start_offset, :start
144
+
145
+ def end
146
+ @children[2]
147
+ end
148
+
149
+ alias_method :end_offset, :end
150
+
151
+ def groups
152
+ @children[3]
153
+ end
154
+
155
+ def evaluate(_context = nil)
156
+ self
157
+ end
158
+
159
+ def to_ruby(context = nil)
160
+ {
161
+ value: value, start: start_offset, end: end_offset, groups: groups
162
+ }.transform_values { |v| v.to_ruby(context) }
163
+ end
164
+
165
+ def to_pkl_string(context = nil)
166
+ to_string(context)
167
+ end
168
+
169
+ def to_string(context)
170
+ value.to_string(context)
171
+ end
172
+
173
+ def ==(other)
174
+ other.is_a?(self.class) &&
175
+ value == other.value && start_offset == other.start &&
176
+ end_offset == other.end && groups == other.groups
177
+ end
178
+
179
+ define_builtin_property(:value) do
180
+ value
181
+ end
182
+
183
+ define_builtin_property(:start) do
184
+ start_offset
185
+ end
186
+
187
+ define_builtin_property(:end) do
188
+ end_offset
189
+ end
190
+
191
+ define_builtin_property(:groups) do
192
+ groups
193
+ end
194
+ end
195
+ end
196
+ end