rupkl 0.1.0 → 0.3.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/rupkl/node/amend_expression.rb +48 -0
  4. data/lib/rupkl/node/any.rb +110 -0
  5. data/lib/rupkl/node/base.rb +75 -0
  6. data/lib/rupkl/node/boolean.rb +30 -5
  7. data/lib/rupkl/node/collection.rb +176 -0
  8. data/lib/rupkl/node/context.rb +27 -0
  9. data/lib/rupkl/node/data_size.rb +254 -0
  10. data/lib/rupkl/node/declared_type.rb +32 -0
  11. data/lib/rupkl/node/duration.rb +266 -0
  12. data/lib/rupkl/node/dynamic.rb +33 -60
  13. data/lib/rupkl/node/identifier.rb +19 -5
  14. data/lib/rupkl/node/if_expression.rb +45 -0
  15. data/lib/rupkl/node/intseq.rb +84 -0
  16. data/lib/rupkl/node/listing.rb +68 -0
  17. data/lib/rupkl/node/map.rb +120 -0
  18. data/lib/rupkl/node/mapping.rb +54 -0
  19. data/lib/rupkl/node/member_finder.rb +49 -0
  20. data/lib/rupkl/node/member_reference.rb +46 -21
  21. data/lib/rupkl/node/method_call.rb +63 -0
  22. data/lib/rupkl/node/method_definition.rb +199 -0
  23. data/lib/rupkl/node/node_common.rb +76 -0
  24. data/lib/rupkl/node/null.rb +24 -0
  25. data/lib/rupkl/node/number.rb +228 -10
  26. data/lib/rupkl/node/object.rb +626 -74
  27. data/lib/rupkl/node/operation.rb +175 -115
  28. data/lib/rupkl/node/pair.rb +58 -0
  29. data/lib/rupkl/node/pkl_module.rb +16 -28
  30. data/lib/rupkl/node/reference_resolver.rb +79 -0
  31. data/lib/rupkl/node/regex.rb +196 -0
  32. data/lib/rupkl/node/string.rb +415 -23
  33. data/lib/rupkl/node/struct_common.rb +150 -53
  34. data/lib/rupkl/node/this.rb +17 -0
  35. data/lib/rupkl/node/type_common.rb +34 -0
  36. data/lib/rupkl/node/value_common.rb +18 -13
  37. data/lib/rupkl/parser/expression.rb +197 -43
  38. data/lib/rupkl/parser/identifier.rb +2 -2
  39. data/lib/rupkl/parser/literal.rb +18 -12
  40. data/lib/rupkl/parser/method.rb +41 -0
  41. data/lib/rupkl/parser/misc.rb +24 -0
  42. data/lib/rupkl/parser/object.rb +141 -26
  43. data/lib/rupkl/parser/pkl_class.rb +37 -10
  44. data/lib/rupkl/parser/pkl_module.rb +5 -3
  45. data/lib/rupkl/parser/type.rb +28 -0
  46. data/lib/rupkl/parser.rb +8 -0
  47. data/lib/rupkl/pkl_object.rb +11 -7
  48. data/lib/rupkl/version.rb +1 -1
  49. data/lib/rupkl.rb +35 -6
  50. metadata +45 -7
  51. data/lib/rupkl/node/pkl_class.rb +0 -30
@@ -3,17 +3,31 @@
3
3
  module RuPkl
4
4
  module Node
5
5
  class Identifier
6
- def initialize(id, position)
6
+ include NodeCommon
7
+
8
+ def initialize(parent, id, position)
9
+ super(parent, position)
7
10
  @id = id
8
- @position = position
11
+ end
12
+
13
+ attr_reader :id
14
+
15
+ def copy(parent = nil, position = @position)
16
+ self.class.new(parent, id, position)
9
17
  end
10
18
 
11
19
  def ==(other)
12
- other.instance_of?(self.class) && id == other.id
20
+ id ==
21
+ if other.respond_to?(:id)
22
+ other.id
23
+ else
24
+ other
25
+ end
13
26
  end
14
27
 
15
- attr_reader :id
16
- attr_reader :position
28
+ def to_sym
29
+ id.to_sym
30
+ end
17
31
  end
18
32
  end
19
33
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class IfExpression
6
+ include NodeCommon
7
+
8
+ def condition
9
+ @children[0]
10
+ end
11
+
12
+ def if_expression
13
+ @children[1]
14
+ end
15
+
16
+ def else_expression
17
+ @children[2]
18
+ end
19
+
20
+ def evaluate(context = nil)
21
+ resolve_structure(context).evaluate(context)
22
+ end
23
+
24
+ def resolve_structure(context = nil)
25
+ @result ||=
26
+ if evaluate_condition(context)
27
+ if_expression
28
+ else
29
+ else_expression
30
+ end
31
+ @result.resolve_structure(context)
32
+ end
33
+
34
+ private
35
+
36
+ def evaluate_condition(context)
37
+ evaluated = condition.evaluate(context)
38
+ return evaluated.value if evaluated.is_a?(Boolean)
39
+
40
+ message = "expected type 'Boolean', but got type '#{evaluated.class_name}'"
41
+ raise EvaluationError.new(message, position)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class IntSeq < Any
6
+ include Operatable
7
+
8
+ uninstantiable_class
9
+
10
+ def initialize(parent, start, last, step, position)
11
+ super(parent, *[start, last, step].compact, position)
12
+ end
13
+
14
+ def start
15
+ children[0]
16
+ end
17
+
18
+ def last
19
+ children[1]
20
+ end
21
+
22
+ alias_method :end, :last
23
+
24
+ def step
25
+ children[2]
26
+ end
27
+
28
+ def evaluate(_context = nil)
29
+ self
30
+ end
31
+
32
+ def to_ruby(_context = nil)
33
+ to_array(self)
34
+ end
35
+
36
+ def to_pkl_string(context = nil)
37
+ to_string(context)
38
+ end
39
+
40
+ def to_string(context = nil)
41
+ start_value, last_value, step_value = children.map { _1.to_pkl_string(context) }
42
+ if step && step.value != 1
43
+ "IntSeq(#{start_value}, #{last_value}).step(#{step_value})"
44
+ else
45
+ "IntSeq(#{start_value}, #{last_value})"
46
+ end
47
+ end
48
+
49
+ def ==(other)
50
+ other.is_a?(self.class) &&
51
+ to_array(self) == to_array(other)
52
+ end
53
+
54
+ define_builtin_property(:start) do
55
+ Int.new(self, start.value, position)
56
+ end
57
+
58
+ define_builtin_property(:end) do
59
+ Int.new(self, last.value, position)
60
+ end
61
+
62
+ define_builtin_property(:step) do
63
+ Int.new(self, step&.value || 1, position)
64
+ end
65
+
66
+ define_builtin_method(:step, step: Int) do |args, parent, position|
67
+ step = args[:step]
68
+ if step.value.zero?
69
+ message = "expected a non zero number, but got '#{step.value}'"
70
+ raise EvaluationError.new(message, position)
71
+ end
72
+
73
+ IntSeq.new(parent, start, last, step, position)
74
+ end
75
+
76
+ private
77
+
78
+ def to_array(intseq)
79
+ start_value, last_value, step_value = intseq.children.map(&:value)
80
+ Range.new(start_value, last_value).step(step_value || 1).to_a
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Listing < Any
6
+ include Operatable
7
+ include StructCommon
8
+
9
+ def elements
10
+ @body&.elements
11
+ end
12
+
13
+ def to_ruby(context = nil)
14
+ to_ruby_object(context) do |_properties, _entries, elements|
15
+ replace_self_array(elements, elements) || []
16
+ end
17
+ end
18
+
19
+ def ==(other)
20
+ other.instance_of?(self.class) &&
21
+ match_members?(elements, other.elements, true)
22
+ end
23
+
24
+ def find_by_key(key)
25
+ find_element(key)&.value
26
+ end
27
+
28
+ define_builtin_property(:isEmpty) do
29
+ result = elements.nil? || elements.empty?
30
+ Boolean.new(self, result, position)
31
+ end
32
+
33
+ define_builtin_property(:length) do
34
+ result = elements&.size || 0
35
+ Int.new(self, result, position)
36
+ end
37
+
38
+ define_builtin_property(:isDistinct) do
39
+ result =
40
+ elements.nil? ||
41
+ elements.all? { |lhs| elements.one? { |rhs| rhs == lhs } }
42
+ Boolean.new(self, result || elements.nil?, position)
43
+ end
44
+
45
+ define_builtin_property(:distinct) do
46
+ result =
47
+ elements
48
+ &.each_with_object([]) { |e, l| l << e unless l.include?(e) }
49
+ body = ObjectBody.new(nil, result, position)
50
+ Listing.new(self, body, position)
51
+ end
52
+
53
+ define_builtin_method(:join, separator: String) do |args, parent, position|
54
+ result =
55
+ elements
56
+ &.map { _1.value.to_string }
57
+ &.join(args[:separator].value)
58
+ String.new(parent, result || '', nil, position)
59
+ end
60
+
61
+ private
62
+
63
+ def elements_not_allowed?
64
+ false
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Map < Any
6
+ MapEntry = Struct.new(:key, :value) do
7
+ def match_key?(other_key)
8
+ key == other_key
9
+ end
10
+ end
11
+
12
+ include Operatable
13
+ include MemberFinder
14
+ undef_method :pkl_method
15
+
16
+ uninstantiable_class
17
+
18
+ def initialize(parent, entries, position)
19
+ @entries = compose_entries(entries, position)
20
+ super(parent, *@entries&.flat_map(&:to_a), position)
21
+ end
22
+
23
+ attr_reader :entries
24
+
25
+ def evaluate(_context = nil)
26
+ self
27
+ end
28
+
29
+ def to_ruby(context = nil)
30
+ entries&.to_h { |e| e.map { _1.to_ruby(context) } } || {}
31
+ end
32
+
33
+ def to_pkl_string(context = nil)
34
+ to_string(context)
35
+ end
36
+
37
+ def to_string(context = nil)
38
+ entry_string =
39
+ entries
40
+ &.flat_map { |entry| entry.map { _1.to_pkl_string(context) } }
41
+ &.join(', ')
42
+ "Map(#{entry_string})"
43
+ end
44
+
45
+ def b_op_add(r_operand, position)
46
+ result =
47
+ if entries && r_operand.entries
48
+ entries + r_operand.entries
49
+ else
50
+ entries || r_operand.entries
51
+ end
52
+ self.class.new(nil, result&.flat_map(&:to_a), position)
53
+ end
54
+
55
+ def ==(other)
56
+ return false unless other.is_a?(self.class)
57
+ return false unless entries&.size == other.entries&.size
58
+ return true unless entries
59
+
60
+ entries
61
+ .all? { |entry| entry.value == other.find_by_key(entry.key) }
62
+ end
63
+
64
+ def find_by_key(key)
65
+ find_entry(key)&.value
66
+ end
67
+
68
+ define_builtin_property(:isEmpty) do
69
+ result = entries.nil? || entries.empty?
70
+ Boolean.new(self, result, position)
71
+ end
72
+
73
+ define_builtin_property(:length) do
74
+ result = entries&.length || 0
75
+ Int.new(self, result, position)
76
+ end
77
+
78
+ define_builtin_property(:keys) do
79
+ keys = entries&.map(&:key)
80
+ Set.new(self, keys, position)
81
+ end
82
+
83
+ define_builtin_property(:values) do
84
+ values = entries&.map(&:value)
85
+ List.new(self, values, position)
86
+ end
87
+
88
+ define_builtin_property(:entries) do
89
+ key_value_pairs =
90
+ entries&.map { |e| Pair.new(nil, e.key, e.value, position) }
91
+ List.new(self, key_value_pairs, position)
92
+ end
93
+
94
+ private
95
+
96
+ def compose_entries(entries, position)
97
+ return if entries.nil? || entries.empty?
98
+
99
+ check_arity(entries, position)
100
+ entries.each_slice(2)&.with_object([]) do |(k, v), result|
101
+ index =
102
+ result.find_index { _1.match_key?(k) } ||
103
+ result.size
104
+ result[index] = MapEntry.new(k, v)
105
+ end
106
+ end
107
+
108
+ def check_arity(entries, position)
109
+ return if ((entries&.size || 0) % 2).zero?
110
+
111
+ m = 'number of arguments must be a multiple of two'
112
+ raise EvaluationError.new(m, position)
113
+ end
114
+
115
+ def defined_operator?(operator)
116
+ [:[], :+].any?(operator)
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Mapping < Any
6
+ include Operatable
7
+ include StructCommon
8
+
9
+ def entries
10
+ @body&.entries
11
+ end
12
+
13
+ def to_ruby(context = nil)
14
+ to_ruby_object(context) do |_properties, entries, _elements|
15
+ replace_self_hash(entries, entries) || {}
16
+ end
17
+ end
18
+
19
+ def ==(other)
20
+ other.instance_of?(self.class) &&
21
+ match_members?(entries, other.entries, false)
22
+ end
23
+
24
+ def find_by_key(key)
25
+ find_entry(key)&.value
26
+ end
27
+
28
+ define_builtin_property(:isEmpty) do
29
+ result = entries.nil? || entries.empty?
30
+ Boolean.new(self, result, position)
31
+ end
32
+
33
+ define_builtin_property(:length) do
34
+ result = entries&.size || 0
35
+ Int.new(self, result, position)
36
+ end
37
+
38
+ define_builtin_method(:containsKey, key: Any) do |args, parent, position|
39
+ result = find_entry(args[:key]) && true || false
40
+ Boolean.new(parent, result, position)
41
+ end
42
+
43
+ define_builtin_method(:getOrNull, key: Any) do |args, parent, position|
44
+ find_entry(args[:key])&.value || Null.new(parent, position)
45
+ end
46
+
47
+ private
48
+
49
+ def entries_not_allowed?
50
+ false
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ module MemberFinder
6
+ def property(name)
7
+ value = find_property(name)&.value
8
+ return value if value
9
+
10
+ super if self.class <= Any
11
+ end
12
+
13
+ def pkl_method(name)
14
+ method = find_pkl_method(name)
15
+ return method if method
16
+
17
+ super if self.class <= Any
18
+ end
19
+
20
+ private
21
+
22
+ def find_property(name)
23
+ return unless respond_to?(:properties)
24
+
25
+ properties&.find { _1.name == name }
26
+ end
27
+
28
+ def find_entry(key)
29
+ return unless respond_to?(:entries)
30
+
31
+ entries&.find { _1.key == key }
32
+ end
33
+
34
+ def find_element(index)
35
+ return unless respond_to?(:elements)
36
+ return unless elements
37
+ return unless index.value.is_a?(::Integer)
38
+
39
+ elements.find.with_index { |_, i| i == index.value }
40
+ end
41
+
42
+ def find_pkl_method(name)
43
+ return unless respond_to?(:pkl_methods)
44
+
45
+ pkl_methods&.find { _1.name == name }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,43 +3,68 @@
3
3
  module RuPkl
4
4
  module Node
5
5
  class MemberReference
6
- def initialize(receiver, member, position)
6
+ include NodeCommon
7
+ include ReferenceResolver
8
+
9
+ def initialize(parent, receiver, member, nullable, position)
10
+ super(parent, receiver, member, position)
7
11
  @receiver = receiver
8
12
  @member = member
9
- @position = position
13
+ @nullable = nullable
10
14
  end
11
15
 
12
16
  attr_reader :receiver
13
17
  attr_reader :member
14
- attr_reader :position
15
18
 
16
- def evaluate(scopes)
17
- member_node =
18
- if receiver
19
- find_member([receiver.evaluate(scopes)])
20
- else
21
- find_member(scopes)
22
- end
23
- member_node.evaluate(scopes).value
19
+ def nullable?
20
+ @nullable
24
21
  end
25
22
 
26
- def to_string(scopes)
27
- evaluate(scopes).to_string(nil)
23
+ def evaluate(context = nil)
24
+ do_evaluate do
25
+ result = resolve_member_reference(context, member, receiver, nullable?)
26
+ result.evaluate
27
+ end
28
28
  end
29
29
 
30
- def to_pkl_string(scopes)
31
- evaluate(scopes).to_pkl_string(nil)
30
+ def resolve_reference(context = nil)
31
+ do_evaluate do
32
+ result = resolve_member_reference(context, member, receiver, nullable?)
33
+ result.resolve_reference
34
+ end
35
+ end
36
+
37
+ def copy(parent = nil, position = @position)
38
+ self
39
+ .class.new(parent, receiver&.copy, member&.copy, nullable?, position)
40
+ .tap { copy_scope_index(_1) }
32
41
  end
33
42
 
34
43
  private
35
44
 
36
- def find_member(scopes)
37
- scopes.reverse_each do |scope|
38
- node = scope&.properties&.find { _1.name.id == member.id }
39
- return node if node
40
- end
45
+ def do_evaluate
46
+ @evaluating &&
47
+ (raise EvaluationError.new('circular reference is detected', position))
48
+
49
+ @evaluating = true
50
+ result = yield
51
+ @evaluating = false
52
+
53
+ result
54
+ end
55
+
56
+ def ifnone_value(receiver)
57
+ receiver
58
+ end
59
+
60
+ def get_member_node(scope, target)
61
+ return unless scope.respond_to?(:property)
62
+
63
+ scope.property(target)
64
+ end
41
65
 
42
- raise EvaluationError.new("cannot find property '#{member.id}'", position)
66
+ def unresolve_reference_error(target)
67
+ EvaluationError.new("cannot find property '#{target.id}'", position)
43
68
  end
44
69
  end
45
70
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class MethodCall
6
+ include NodeCommon
7
+ include ReferenceResolver
8
+
9
+ def initialize(parent, receiver, method_name, arguments, nullable, position)
10
+ super(parent, receiver, method_name, *arguments, position)
11
+ @receiver = receiver
12
+ @method_name = method_name
13
+ @arguments = arguments
14
+ @nullable = nullable
15
+ end
16
+
17
+ attr_reader :receiver
18
+ attr_reader :method_name
19
+ attr_reader :arguments
20
+
21
+ def nullable?
22
+ @nullable
23
+ end
24
+
25
+ def evaluate(context = nil)
26
+ exec_on(context) do |c|
27
+ r = evaluate_receiver(receiver, c)
28
+ m = resolve_member_reference(c, method_name, r, nullable?)
29
+ m && execute_method(m, r, c) || r
30
+ end
31
+ end
32
+
33
+ def copy(parent = nil, position = @position)
34
+ copied_args = arguments&.map(&:copy)
35
+ self.class
36
+ .new(parent, receiver&.copy, method_name, copied_args, nullable?, position)
37
+ .tap { copy_scope_index(_1) }
38
+ end
39
+
40
+ private
41
+
42
+ def ifnone_value(_)
43
+ nil
44
+ end
45
+
46
+ def get_member_node(scope, target)
47
+ return unless scope.respond_to?(:pkl_method)
48
+
49
+ scope.pkl_method(target)
50
+ end
51
+
52
+ def unresolve_reference_error(target)
53
+ m = "cannot find method '#{target.id}'"
54
+ raise EvaluationError.new(m, position)
55
+ end
56
+
57
+ def execute_method(pkl_method, receiver, context)
58
+ pkl_method
59
+ .call(receiver, arguments, context, parent, position)
60
+ end
61
+ end
62
+ end
63
+ end