rupkl 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) 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 +99 -0
  5. data/lib/rupkl/node/base.rb +45 -0
  6. data/lib/rupkl/node/boolean.rb +12 -4
  7. data/lib/rupkl/node/context.rb +27 -0
  8. data/lib/rupkl/node/declared_type.rb +32 -0
  9. data/lib/rupkl/node/dynamic.rb +28 -59
  10. data/lib/rupkl/node/identifier.rb +16 -6
  11. data/lib/rupkl/node/listing.rb +61 -0
  12. data/lib/rupkl/node/mapping.rb +47 -0
  13. data/lib/rupkl/node/member_finder.rb +55 -0
  14. data/lib/rupkl/node/member_reference.rb +44 -21
  15. data/lib/rupkl/node/method_call.rb +64 -0
  16. data/lib/rupkl/node/method_definition.rb +143 -0
  17. data/lib/rupkl/node/node_common.rb +76 -0
  18. data/lib/rupkl/node/null.rb +27 -0
  19. data/lib/rupkl/node/number.rb +136 -6
  20. data/lib/rupkl/node/object.rb +369 -73
  21. data/lib/rupkl/node/operation.rb +97 -60
  22. data/lib/rupkl/node/pkl_module.rb +16 -28
  23. data/lib/rupkl/node/reference_resolver.rb +79 -0
  24. data/lib/rupkl/node/string.rb +388 -18
  25. data/lib/rupkl/node/struct_common.rb +119 -57
  26. data/lib/rupkl/node/this.rb +17 -0
  27. data/lib/rupkl/node/type_common.rb +34 -0
  28. data/lib/rupkl/node/value_common.rb +18 -9
  29. data/lib/rupkl/parser/expression.rb +158 -41
  30. data/lib/rupkl/parser/identifier.rb +2 -2
  31. data/lib/rupkl/parser/literal.rb +18 -12
  32. data/lib/rupkl/parser/method.rb +41 -0
  33. data/lib/rupkl/parser/misc.rb +4 -0
  34. data/lib/rupkl/parser/object.rb +57 -25
  35. data/lib/rupkl/parser/pkl_class.rb +28 -8
  36. data/lib/rupkl/parser/pkl_module.rb +5 -3
  37. data/lib/rupkl/parser/type.rb +28 -0
  38. data/lib/rupkl/parser.rb +8 -0
  39. data/lib/rupkl/pkl_object.rb +26 -6
  40. data/lib/rupkl/version.rb +1 -1
  41. data/lib/rupkl.rb +25 -4
  42. metadata +33 -3
  43. data/lib/rupkl/node/pkl_class.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8b6a7346123ff0c771a201f1218f0c2d92e3c34a16e4fe0f668724eb53726ee
4
- data.tar.gz: 169990d7f909f37f72ea97ba2ff44c9634137fead23791ab6a80fddd9b3af109
3
+ metadata.gz: d7731a56bb62b392639860c5e0f82c2175e8bca4ecd439e8ccffd05d9b223b0f
4
+ data.tar.gz: 18487cb107c5a47212d185d782f4866d5fc5c0734b893986e4388c003728687d
5
5
  SHA512:
6
- metadata.gz: 50e6f855bec47026a3a6f4b8b61b9fd61ceafc7686bcd0a51f459a42ae88a97ee954fb362f3450c89d6499fe6e1d97f2b2350350a2820607c007bc7a8e8d8190
7
- data.tar.gz: ad63c41fa79cc9b487d6d3c7f4b129febbf914e564f15d9921072cf4680442dcc77c605d8d7cc283aa15662f15247f427ccbd218c2a1856681ca402500e2810f
6
+ metadata.gz: e9d8c2d0d8d9f4976ba804810285344b8f5a6cd308ce1eeff23eb3503feb365c2415d1876c8c879fdf38ca016b01dc849aaeb2e01ae502b9c0e0a44a1a2123ce
7
+ data.tar.gz: 2bc34ee5b570519a913299dbe5415fe9a7e3dd406b5912f729127ce152c18fd6821f769f9c6df6e677c0c114de935f401584e67e5ae385133aef318810f57ce0
data/README.md CHANGED
@@ -116,7 +116,7 @@ Pkl code snippets used for RSpec examples are originaly from:
116
116
  ## Copyright & License
117
117
 
118
118
  Copyright © 2024 Taichi Ishitani.
119
- RuPkl is licensed under the terms of the [MIT License](https://opensource.org/licenses/MIT), see [LICENSE](LICENSE) for further details.
119
+ RuPkl is licensed under the terms of the [MIT License](https://opensource.org/licenses/MIT), see [LICENSE.txt](LICENSE.txt) for further details.
120
120
 
121
121
  ## Code of Conduct
122
122
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class AmendExpression
6
+ include NodeCommon
7
+
8
+ def initialize(parent, target, bodies, position)
9
+ super(parent, target, *bodies, position)
10
+ @target = target
11
+ @bodies = bodies
12
+ end
13
+
14
+ attr_reader :target
15
+ attr_reader :bodies
16
+
17
+ def evaluate(context = nil)
18
+ resolve_structure(context).evaluate(context)
19
+ end
20
+
21
+ def resolve_structure(context = nil)
22
+ t =
23
+ target
24
+ .resolve_reference(context)
25
+ .resolve_structure(context)
26
+ t.respond_to?(:body) ||
27
+ begin
28
+ message = "cannot amend the target type #{t.class.basename}"
29
+ raise EvaluationError.new(message, position)
30
+ end
31
+ do_amend(t.copy(parent))
32
+ end
33
+
34
+ def copy(parent = nil)
35
+ self.class.new(parent, target.copy, bodies.each(&:copy), position)
36
+ end
37
+
38
+ private
39
+
40
+ def do_amend(target)
41
+ bodies
42
+ .map { _1.copy(target).resolve_structure }
43
+ .then { target.merge!(*_1) }
44
+ target
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Any
6
+ include NodeCommon
7
+
8
+ class << self
9
+ def abstract?
10
+ @abstract || false
11
+ end
12
+
13
+ def instantiable?
14
+ !@uninstantiable
15
+ end
16
+
17
+ def class_name
18
+ @class_name || basename.to_sym
19
+ end
20
+
21
+ def builtin_property(name)
22
+ @builtin_properties&.[](name.id)
23
+ end
24
+
25
+ def buildin_method(name)
26
+ @builtin_methods&.[](name.id)
27
+ end
28
+
29
+ private
30
+
31
+ def abstract_class
32
+ @abstract = true
33
+ end
34
+
35
+ def uninstantiable_class
36
+ @uninstantiable = true
37
+ end
38
+
39
+ def klass_name(name)
40
+ @class_name = name
41
+ end
42
+
43
+ def define_builtin_property(name, &body)
44
+ (@builtin_properties ||= {})[name] = body
45
+ end
46
+
47
+ def define_builtin_method(name, ...)
48
+ (@builtin_methods ||= {})[name] = BuiltinMethodDefinition.new(name, ...)
49
+ end
50
+ end
51
+
52
+ abstract_class
53
+ uninstantiable_class
54
+
55
+ def property(name)
56
+ builtin_property(name)
57
+ end
58
+
59
+ def pkl_method(name)
60
+ buildin_method(name)
61
+ end
62
+
63
+ def null?
64
+ false
65
+ end
66
+
67
+ private
68
+
69
+ def builtin_property(name)
70
+ self.class.ancestors.each do |klass|
71
+ next unless klass.respond_to?(:builtin_property)
72
+
73
+ body = klass.builtin_property(name)
74
+ return instance_exec(&body) if body
75
+ end
76
+
77
+ nil
78
+ end
79
+
80
+ def buildin_method(name)
81
+ self.class.ancestors.each do |klass|
82
+ next unless klass.respond_to?(:buildin_method)
83
+
84
+ method = klass.buildin_method(name)
85
+ return method if method
86
+ end
87
+
88
+ nil
89
+ end
90
+
91
+ def check_positive_number(number)
92
+ return unless number.value.negative?
93
+
94
+ m = "expected a positive number, but got '#{number.value}'"
95
+ raise EvaluationError.new(m, position)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Base < PklModule
6
+ include Singleton
7
+
8
+ def initialize
9
+ super(nil, nil, nil)
10
+ end
11
+
12
+ attr_reader :pkl_classes
13
+
14
+ class << self
15
+ private
16
+
17
+ def add_builtin_class(klass)
18
+ instance.instance_eval do
19
+ name = klass.class_name
20
+ (@pkl_classes ||= {})[name] = klass
21
+ end
22
+ end
23
+ end
24
+
25
+ add_builtin_class Any
26
+ add_builtin_class Boolean
27
+ add_builtin_class Number
28
+ add_builtin_class Int
29
+ add_builtin_class Float
30
+ add_builtin_class String
31
+ add_builtin_class Dynamic
32
+ add_builtin_class Mapping
33
+ add_builtin_class Listing
34
+ add_builtin_class PklModule
35
+
36
+ define_builtin_property(:NaN) do
37
+ Float.new(parent, ::Float::NAN, position)
38
+ end
39
+
40
+ define_builtin_property(:Infinity) do
41
+ Float.new(parent, ::Float::INFINITY, position)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -2,12 +2,10 @@
2
2
 
3
3
  module RuPkl
4
4
  module Node
5
- class Boolean
5
+ class Boolean < Any
6
6
  include ValueCommon
7
7
 
8
- def evaluate(_scopes)
9
- self
10
- end
8
+ uninstantiable_class
11
9
 
12
10
  def undefined_operator?(operator)
13
11
  [:!, :==, :'!=', :'&&', :'||'].none?(operator)
@@ -16,6 +14,16 @@ module RuPkl
16
14
  def short_circuit?(operator)
17
15
  [operator, value] in [:'&&', false] | [:'||', true]
18
16
  end
17
+
18
+ define_builtin_method(:xor, other: Boolean) do |other|
19
+ result = value ^ other.value
20
+ Boolean.new(nil, result, position)
21
+ end
22
+
23
+ define_builtin_method(:implies, other: Boolean) do |other|
24
+ result = !value || other.value
25
+ Boolean.new(nil, result, position)
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Context
6
+ def initialize(scopes, objects)
7
+ @scopes = scopes
8
+ @objects = objects
9
+ end
10
+
11
+ attr_reader :scopes
12
+ attr_reader :objects
13
+
14
+ def push_scope(scope)
15
+ Context.new([*scopes, scope], objects)
16
+ end
17
+
18
+ def push_object(object)
19
+ Context.new(scopes, [*objects, object])
20
+ end
21
+
22
+ def pop
23
+ Context.new(scopes&.slice(..-2), objects&.slice(..-2))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class DeclaredType
6
+ include TypeCommon
7
+
8
+ def initialize(parent, type, position)
9
+ super(parent, *type, position)
10
+ @type = type
11
+ end
12
+
13
+ attr_reader :type
14
+
15
+ def find_class(context)
16
+ find_type(type, context)
17
+ end
18
+
19
+ def to_s
20
+ type.last.id.to_s
21
+ end
22
+
23
+ private
24
+
25
+ def match_type?(klass, context)
26
+ rhs = klass
27
+ lhs = find_class(context)
28
+ rhs <= lhs
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,36 +2,19 @@
2
2
 
3
3
  module RuPkl
4
4
  module Node
5
- class Dynamic
5
+ class Dynamic < Any
6
6
  include StructCommon
7
7
 
8
- def initialize(members, scopes, position)
9
- @position = position
10
- add_members(members, scopes)
8
+ def properties
9
+ @body&.properties(visibility: :object)
11
10
  end
12
11
 
13
- attr_reader :properties
14
- attr_reader :entries
15
- attr_reader :elements
16
- attr_reader :position
17
-
18
- def evaluate(_scopes)
19
- self
20
- end
21
-
22
- def to_ruby(_scopes)
23
- create_pkl_object(nil, properties, entries, elements)
12
+ def entries
13
+ @body&.entries
24
14
  end
25
15
 
26
- def to_pkl_string(_scopes)
27
- to_pkl_string_object(*properties, *entries, *elements)
28
- end
29
-
30
- def merge!(other)
31
- @properties = merge_hash_members(properties, other.properties, :name)
32
- @entries = merge_hash_members(entries, other.entries, :key)
33
- @elements = merge_array_members(elements, other.elements)
34
- self
16
+ def elements
17
+ @body&.elements
35
18
  end
36
19
 
37
20
  def ==(other)
@@ -41,58 +24,44 @@ module RuPkl
41
24
  match_members?(elements, other.elements, true)
42
25
  end
43
26
 
44
- def undefined_operator?(operator)
45
- [:[], :==, :'!='].none?(operator)
46
- end
47
-
48
27
  def find_by_key(key)
49
28
  find_entry(key) || find_element(key)
50
29
  end
51
30
 
52
- private
53
-
54
- def add_members(members, scopes)
55
- return unless members
31
+ define_builtin_method(:length) do
32
+ result = elements&.size || 0
33
+ Int.new(nil, result, position)
34
+ end
56
35
 
57
- push_scope(scopes) do |s|
58
- members.each { |m| add_member(m, s) }
59
- end
36
+ define_builtin_method(:hasProperty, name: String) do |name|
37
+ result = find_property(name.value.to_sym) && true || false
38
+ Boolean.new(nil, result, position)
60
39
  end
61
40
 
62
- def add_member(member, scopes)
63
- member.evaluate(scopes).then do |m|
64
- case member
65
- when ObjectProperty then add_property(m)
66
- when ObjectEntry then add_entry(m)
67
- else add_element(m)
41
+ define_builtin_method(:getProperty, name: String) do |name|
42
+ find_property(name.value.to_sym) ||
43
+ begin
44
+ m = "cannot find property '#{name.value}'"
45
+ raise EvaluationError.new(m, position)
68
46
  end
69
- end
70
47
  end
71
48
 
72
- def add_property(member)
73
- add_hash_member((@properties ||= []), member, :name)
49
+ define_builtin_method(:getPropertyOrNull, name: String) do |name|
50
+ find_property(name.value.to_sym) || Null.new(nil, position)
74
51
  end
75
52
 
76
- def add_entry(member)
77
- add_hash_member((@entries ||= []), member, :key)
78
- end
53
+ private
79
54
 
80
- def add_element(member)
81
- add_array_member((@elements ||= []), member)
55
+ def properties_not_allowed?
56
+ false
82
57
  end
83
58
 
84
- def find_entry(key)
85
- entries
86
- &.find { _1.key == key }
87
- &.then(&:value)
59
+ def entries_not_allowed?
60
+ false
88
61
  end
89
62
 
90
- def find_element(index)
91
- return nil unless elements
92
- return nil unless index.value.is_a?(::Integer)
93
-
94
- elements
95
- .find.with_index { |_, i| i == index.value }
63
+ def elements_not_allowed?
64
+ false
96
65
  end
97
66
  end
98
67
  end
@@ -3,17 +3,27 @@
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
9
11
  end
10
12
 
11
- def ==(other)
12
- other.instance_of?(self.class) && id == other.id
13
+ attr_reader :id
14
+
15
+ def copy(parent = nil)
16
+ self.class.new(parent, id, position)
13
17
  end
14
18
 
15
- attr_reader :id
16
- attr_reader :position
19
+ def ==(other)
20
+ id ==
21
+ if other.respond_to?(:id)
22
+ other.id
23
+ else
24
+ other
25
+ end
26
+ end
17
27
  end
18
28
  end
19
29
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Listing < Any
6
+ include StructCommon
7
+
8
+ def elements
9
+ @body&.elements
10
+ end
11
+
12
+ def ==(other)
13
+ other.instance_of?(self.class) &&
14
+ match_members?(elements, other.elements, true)
15
+ end
16
+
17
+ def find_by_key(key)
18
+ find_element(key)
19
+ end
20
+
21
+ define_builtin_property(:isEmpty) do
22
+ result = elements.nil? || elements.empty?
23
+ Boolean.new(self, result, position)
24
+ end
25
+
26
+ define_builtin_property(:length) do
27
+ result = elements&.size || 0
28
+ Int.new(self, result, position)
29
+ end
30
+
31
+ define_builtin_property(:isDistinct) do
32
+ result =
33
+ elements.nil? ||
34
+ elements.all? { |lhs| elements.one? { |rhs| rhs == lhs } }
35
+ Boolean.new(self, result || elements.nil?, position)
36
+ end
37
+
38
+ define_builtin_property(:distinct) do
39
+ result =
40
+ elements
41
+ &.each_with_object([]) { |e, l| l << e unless l.include?(e) }
42
+ body = ObjectBody.new(nil, result, position)
43
+ Listing.new(self, body, position)
44
+ end
45
+
46
+ define_builtin_method(:join, separator: String) do |separator|
47
+ result =
48
+ elements
49
+ &.map { _1.value.to_string }
50
+ &.join(separator.value)
51
+ String.new(nil, result || '', nil, position)
52
+ end
53
+
54
+ private
55
+
56
+ def elements_not_allowed?
57
+ false
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Mapping < Any
6
+ include StructCommon
7
+
8
+ def entries
9
+ @body&.entries
10
+ end
11
+
12
+ def ==(other)
13
+ other.instance_of?(self.class) &&
14
+ match_members?(entries, other.entries, false)
15
+ end
16
+
17
+ def find_by_key(key)
18
+ find_entry(key)
19
+ end
20
+
21
+ define_builtin_property(:isEmpty) do
22
+ result = entries.nil? || entries.empty?
23
+ Boolean.new(self, result, position)
24
+ end
25
+
26
+ define_builtin_property(:length) do
27
+ result = entries&.size || 0
28
+ Int.new(self, result, position)
29
+ end
30
+
31
+ define_builtin_method(:containsKey, key: Any) do |key|
32
+ result = find_entry(key) && true || false
33
+ Boolean.new(nil, result, position)
34
+ end
35
+
36
+ define_builtin_method(:getOrNull, key: Any) do |key|
37
+ find_entry(key) || Null.new(nil, position)
38
+ end
39
+
40
+ private
41
+
42
+ def entries_not_allowed?
43
+ false
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ module MemberFinder
6
+ def property(name)
7
+ value = find_property(name)
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
26
+ &.find { _1.name == name }
27
+ &.value
28
+ end
29
+
30
+ def find_entry(key)
31
+ return unless respond_to?(:entries)
32
+
33
+ entries
34
+ &.find { _1.key == key }
35
+ &.value
36
+ end
37
+
38
+ def find_element(index)
39
+ return unless respond_to?(:elements)
40
+ return unless elements
41
+ return unless index.value.is_a?(::Integer)
42
+
43
+ elements
44
+ .find.with_index { |_, i| i == index.value }
45
+ &.value
46
+ end
47
+
48
+ def find_pkl_method(name)
49
+ return unless respond_to?(:pkl_methods)
50
+
51
+ pkl_methods&.find { _1.name == name }
52
+ end
53
+ end
54
+ end
55
+ end