rupkl 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/rupkl/node/amend_expression.rb +48 -0
- data/lib/rupkl/node/any.rb +99 -0
- data/lib/rupkl/node/base.rb +45 -0
- data/lib/rupkl/node/boolean.rb +12 -4
- data/lib/rupkl/node/context.rb +27 -0
- data/lib/rupkl/node/declared_type.rb +32 -0
- data/lib/rupkl/node/dynamic.rb +28 -59
- data/lib/rupkl/node/identifier.rb +16 -6
- data/lib/rupkl/node/listing.rb +61 -0
- data/lib/rupkl/node/mapping.rb +47 -0
- data/lib/rupkl/node/member_finder.rb +55 -0
- data/lib/rupkl/node/member_reference.rb +44 -21
- data/lib/rupkl/node/method_call.rb +64 -0
- data/lib/rupkl/node/method_definition.rb +143 -0
- data/lib/rupkl/node/node_common.rb +76 -0
- data/lib/rupkl/node/null.rb +27 -0
- data/lib/rupkl/node/number.rb +136 -6
- data/lib/rupkl/node/object.rb +369 -73
- data/lib/rupkl/node/operation.rb +97 -60
- data/lib/rupkl/node/pkl_module.rb +16 -28
- data/lib/rupkl/node/reference_resolver.rb +79 -0
- data/lib/rupkl/node/string.rb +388 -18
- data/lib/rupkl/node/struct_common.rb +119 -57
- data/lib/rupkl/node/this.rb +17 -0
- data/lib/rupkl/node/type_common.rb +34 -0
- data/lib/rupkl/node/value_common.rb +18 -9
- data/lib/rupkl/parser/expression.rb +158 -41
- data/lib/rupkl/parser/identifier.rb +2 -2
- data/lib/rupkl/parser/literal.rb +18 -12
- data/lib/rupkl/parser/method.rb +41 -0
- data/lib/rupkl/parser/misc.rb +4 -0
- data/lib/rupkl/parser/object.rb +57 -25
- data/lib/rupkl/parser/pkl_class.rb +28 -8
- data/lib/rupkl/parser/pkl_module.rb +5 -3
- data/lib/rupkl/parser/type.rb +28 -0
- data/lib/rupkl/parser.rb +8 -0
- data/lib/rupkl/pkl_object.rb +26 -6
- data/lib/rupkl/version.rb +1 -1
- data/lib/rupkl.rb +25 -4
- metadata +33 -3
- data/lib/rupkl/node/pkl_class.rb +0 -30
@@ -3,43 +3,66 @@
|
|
3
3
|
module RuPkl
|
4
4
|
module Node
|
5
5
|
class MemberReference
|
6
|
-
|
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
|
-
@
|
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
|
17
|
-
|
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
|
27
|
-
|
23
|
+
def evaluate(context = nil)
|
24
|
+
do_evaluate do
|
25
|
+
resolve_member_reference(context, member, receiver, nullable?).evaluate
|
26
|
+
end
|
28
27
|
end
|
29
28
|
|
30
|
-
def
|
31
|
-
|
29
|
+
def resolve_reference(context = nil)
|
30
|
+
do_evaluate do
|
31
|
+
resolve_member_reference(context, member, receiver, nullable?).resolve_reference
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def copy(parent = nil)
|
36
|
+
self
|
37
|
+
.class.new(parent, receiver&.copy, member&.copy, nullable?, position)
|
38
|
+
.tap { copy_scope_index(_1) }
|
32
39
|
end
|
33
40
|
|
34
41
|
private
|
35
42
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
43
|
+
def do_evaluate
|
44
|
+
@evaluating &&
|
45
|
+
(raise EvaluationError.new('circular reference is detected', position))
|
46
|
+
|
47
|
+
@evaluating = true
|
48
|
+
result = yield
|
49
|
+
@evaluating = false
|
50
|
+
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def ifnone_value(receiver)
|
55
|
+
receiver
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_member_node(scope, target)
|
59
|
+
return unless scope.respond_to?(:property)
|
60
|
+
|
61
|
+
scope.property(target)
|
62
|
+
end
|
41
63
|
|
42
|
-
|
64
|
+
def unresolve_reference_error(target)
|
65
|
+
EvaluationError.new("cannot find property '#{target.id}'", position)
|
43
66
|
end
|
44
67
|
end
|
45
68
|
end
|
@@ -0,0 +1,64 @@
|
|
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)
|
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, position)
|
60
|
+
.tap { parent&.add_child(_1) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
class MethodParam
|
6
|
+
include NodeCommon
|
7
|
+
|
8
|
+
def initialize(parent, name, type, position)
|
9
|
+
super
|
10
|
+
@name = name
|
11
|
+
@type = type
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :name
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
def check_type(value, context, position)
|
18
|
+
type&.check_type(value, context, position)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class MethodDefinition
|
23
|
+
include NodeCommon
|
24
|
+
|
25
|
+
def initialize(parent, name, params, type, body, position)
|
26
|
+
super(parent, name, *params, type, body, position)
|
27
|
+
@name = name
|
28
|
+
@params = params
|
29
|
+
@type = type
|
30
|
+
@body = body&.copy # reset `#parent` handle
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :name
|
34
|
+
attr_reader :params
|
35
|
+
attr_reader :type
|
36
|
+
attr_reader :body
|
37
|
+
|
38
|
+
def call(receiver, arguments, context, position)
|
39
|
+
args = evaluate_arguments(arguments, context, position)
|
40
|
+
execute_method(receiver, args)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def evaluate_arguments(arguments, context, position)
|
46
|
+
check_arity(arguments, position)
|
47
|
+
|
48
|
+
arguments&.zip(params)&.map do |arg, param|
|
49
|
+
evaluate_argument(arg, param, context)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def evaluate_argument(arg, param, context)
|
54
|
+
value = arg.evaluate(context)
|
55
|
+
param.check_type(value, context, position)
|
56
|
+
[param.name, value]
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_arity(arguments, position)
|
60
|
+
n_args = arguments&.size || 0
|
61
|
+
n_params = params&.size || 0
|
62
|
+
return if n_args == n_params
|
63
|
+
|
64
|
+
m = "expected #{n_params} method arguments but got #{n_args}"
|
65
|
+
raise EvaluationError.new(m, position)
|
66
|
+
end
|
67
|
+
|
68
|
+
def execute_method(receiver, arguments)
|
69
|
+
context = create_call_context(receiver, arguments)
|
70
|
+
execute_body(context)
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_call_context(receiver, arguments)
|
74
|
+
local_context = MethodCallContext.new(arguments)
|
75
|
+
method_context = current_context
|
76
|
+
if receiver
|
77
|
+
Context.new([*method_context.scopes, local_context], [receiver])
|
78
|
+
else
|
79
|
+
method_context.push_scope(local_context)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def execute_body(context)
|
84
|
+
body
|
85
|
+
.evaluate(context)
|
86
|
+
.tap { type&.check_type(_1, context, position) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class MethodCallContext
|
91
|
+
include NodeCommon
|
92
|
+
include MemberFinder
|
93
|
+
|
94
|
+
def initialize(arguments)
|
95
|
+
@properties =
|
96
|
+
arguments&.map do |(name, value)|
|
97
|
+
ObjectProperty.new(nil, name, value, nil)
|
98
|
+
end
|
99
|
+
super(nil, *@properties, nil)
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_reader :properties
|
103
|
+
end
|
104
|
+
|
105
|
+
class BuiltinMethodTypeChecker
|
106
|
+
include TypeCommon
|
107
|
+
|
108
|
+
def initialize(klass)
|
109
|
+
super(nil, nil)
|
110
|
+
@klass = klass
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def match_type?(klass, _context)
|
116
|
+
klass <= @klass
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class BuiltinMethodParams < MethodParam
|
121
|
+
def initialize(name, klass)
|
122
|
+
id = Identifier.new(nil, name, nil)
|
123
|
+
type = BuiltinMethodTypeChecker.new(klass)
|
124
|
+
super(nil, id, type, nil)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class BuiltinMethodDefinition < MethodDefinition
|
129
|
+
def initialize(name, **params, &body)
|
130
|
+
param_list = params.map { |n, t| BuiltinMethodParams.new(n, t) }
|
131
|
+
id = Identifier.new(nil, name, nil)
|
132
|
+
super(nil, id, param_list, nil, nil, nil)
|
133
|
+
@body = body
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def execute_method(receiver, arguments)
|
139
|
+
receiver.instance_exec(*arguments&.map(&:last), &body)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
module NodeCommon
|
6
|
+
def initialize(parent, *children, position)
|
7
|
+
@position = position
|
8
|
+
parent&.add_child(self)
|
9
|
+
children.each { add_child(_1) }
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :parent
|
13
|
+
attr_reader :children
|
14
|
+
attr_reader :position
|
15
|
+
|
16
|
+
def resolve_reference(_context = nil)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def resolve_structure(_context = nil)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_ruby(context = nil)
|
25
|
+
evaluate(context).to_ruby(context)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_string(context = nil)
|
29
|
+
evaluate(context).to_string(context)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_pkl_string(context = nil)
|
33
|
+
evaluate(context).to_pkl_string(context)
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_child(child)
|
37
|
+
return unless child
|
38
|
+
|
39
|
+
child.parent ||
|
40
|
+
child.instance_exec(self) { @parent = _1 }
|
41
|
+
|
42
|
+
@children&.any? { _1.equal?(child) } ||
|
43
|
+
(@children ||= []) << child
|
44
|
+
end
|
45
|
+
|
46
|
+
def copy(parent = nil)
|
47
|
+
copied_children = children&.map(&:copy)
|
48
|
+
self.class.new(parent, *copied_children, position)
|
49
|
+
end
|
50
|
+
|
51
|
+
def current_context
|
52
|
+
parent&.current_context
|
53
|
+
end
|
54
|
+
|
55
|
+
def structure?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
INVALID_STRING = String.new('?').freeze
|
62
|
+
|
63
|
+
def invalid_string
|
64
|
+
INVALID_STRING
|
65
|
+
end
|
66
|
+
|
67
|
+
def invalid_string?(string)
|
68
|
+
string.equal?(INVALID_STRING)
|
69
|
+
end
|
70
|
+
|
71
|
+
def exec_on(context)
|
72
|
+
yield(context || current_context)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
class Null < Any
|
6
|
+
include ValueCommon
|
7
|
+
|
8
|
+
uninstantiable_class
|
9
|
+
|
10
|
+
def initialize(parent, position)
|
11
|
+
super(parent, nil, position)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_string(_context = nil)
|
15
|
+
'null'
|
16
|
+
end
|
17
|
+
|
18
|
+
def undefined_operator?(operator)
|
19
|
+
[:==, :'!='].none?(operator)
|
20
|
+
end
|
21
|
+
|
22
|
+
def null?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/rupkl/node/number.rb
CHANGED
@@ -2,13 +2,9 @@
|
|
2
2
|
|
3
3
|
module RuPkl
|
4
4
|
module Node
|
5
|
-
class Number
|
5
|
+
class Number < Any
|
6
6
|
include ValueCommon
|
7
7
|
|
8
|
-
def evaluate(_scopes)
|
9
|
-
self
|
10
|
-
end
|
11
|
-
|
12
8
|
def undefined_operator?(operator)
|
13
9
|
[:[], :!, :'&&', :'||'].include?(operator)
|
14
10
|
end
|
@@ -29,12 +25,146 @@ module RuPkl
|
|
29
25
|
operator == :/ ||
|
30
26
|
operator != :'~/' && [self, r_operand].any?(Float)
|
31
27
|
end
|
28
|
+
|
29
|
+
abstract_class
|
30
|
+
uninstantiable_class
|
31
|
+
|
32
|
+
define_builtin_property(:sign) do
|
33
|
+
result =
|
34
|
+
case value
|
35
|
+
when :positive?.to_proc then 1
|
36
|
+
when :negative?.to_proc then -1
|
37
|
+
else value
|
38
|
+
end
|
39
|
+
self.class.new(self, result, position)
|
40
|
+
end
|
41
|
+
|
42
|
+
define_builtin_property(:abs) do
|
43
|
+
self.class.new(self, value.abs, position)
|
44
|
+
end
|
45
|
+
|
46
|
+
define_builtin_property(:ceil) do
|
47
|
+
result = value.finite? && value.ceil || value
|
48
|
+
self.class.new(self, result, position)
|
49
|
+
end
|
50
|
+
|
51
|
+
define_builtin_property(:floor) do
|
52
|
+
result = value.finite? && value.floor || value
|
53
|
+
self.class.new(self, result, position)
|
54
|
+
end
|
55
|
+
|
56
|
+
define_builtin_property(:isPositive) do
|
57
|
+
result = value.zero? || value.positive?
|
58
|
+
Boolean.new(self, result, position)
|
59
|
+
end
|
60
|
+
|
61
|
+
define_builtin_property(:isFinite) do
|
62
|
+
Boolean.new(self, value.finite?, position)
|
63
|
+
end
|
64
|
+
|
65
|
+
define_builtin_property(:isInfinite) do
|
66
|
+
result = value.infinite? && true || false
|
67
|
+
Boolean.new(self, result, position)
|
68
|
+
end
|
69
|
+
|
70
|
+
define_builtin_property(:isNaN) do
|
71
|
+
result = !value.integer? && value.nan?
|
72
|
+
Boolean.new(self, result, position)
|
73
|
+
end
|
74
|
+
|
75
|
+
define_builtin_property(:isNonZero) do
|
76
|
+
Boolean.new(self, !value.zero?, position)
|
77
|
+
end
|
78
|
+
|
79
|
+
define_builtin_method(:toString) do
|
80
|
+
String.new(nil, value.to_s, nil, position)
|
81
|
+
end
|
82
|
+
|
83
|
+
define_builtin_method(:toInt) do
|
84
|
+
Int.new(nil, value.to_i, position)
|
85
|
+
end
|
86
|
+
|
87
|
+
define_builtin_method(:toFloat) do
|
88
|
+
Float.new(nil, value.to_f, position)
|
89
|
+
end
|
90
|
+
|
91
|
+
define_builtin_method(:round) do
|
92
|
+
result = value.finite? && value.round || value
|
93
|
+
self.class.new(nil, result, position)
|
94
|
+
end
|
95
|
+
|
96
|
+
define_builtin_method(:truncate) do
|
97
|
+
result = value.finite? && value.truncate || value
|
98
|
+
self.class.new(nil, result, position)
|
99
|
+
end
|
100
|
+
|
101
|
+
define_builtin_method(:isBetween, first: Number, last: Number) do |f, l|
|
102
|
+
result =
|
103
|
+
[f.value, l.value, value].all? { _1.finite? || _1.infinite? } &&
|
104
|
+
(f.value..l.value).include?(value)
|
105
|
+
Boolean.new(nil, result, position)
|
106
|
+
end
|
32
107
|
end
|
33
108
|
|
34
|
-
class
|
109
|
+
class Int < Number
|
110
|
+
def initialize(parent, value, position)
|
111
|
+
super(parent, value.to_i, position)
|
112
|
+
end
|
113
|
+
|
114
|
+
uninstantiable_class
|
115
|
+
|
116
|
+
define_builtin_property(:isEven) do
|
117
|
+
Boolean.new(self, value.even?, position)
|
118
|
+
end
|
119
|
+
|
120
|
+
define_builtin_property(:isOdd) do
|
121
|
+
Boolean.new(self, value.odd?, position)
|
122
|
+
end
|
123
|
+
|
124
|
+
define_builtin_property(:inv) do
|
125
|
+
self.class.new(self, ~value, position)
|
126
|
+
end
|
127
|
+
|
128
|
+
define_builtin_method(:shl, n: Int) do |n|
|
129
|
+
result = value << n.value
|
130
|
+
self.class.new(self, result, position)
|
131
|
+
end
|
132
|
+
|
133
|
+
define_builtin_method(:shr, n: Int) do |n|
|
134
|
+
result = value >> n.value
|
135
|
+
self.class.new(self, result, position)
|
136
|
+
end
|
137
|
+
|
138
|
+
define_builtin_method(:ushr, n: Int) do |n|
|
139
|
+
result =
|
140
|
+
if value.negative?
|
141
|
+
mask = (1 << 63) - 1
|
142
|
+
n.value.times.inject(value) { |v, _| (v >> 1) & mask }
|
143
|
+
else
|
144
|
+
value >> n.value
|
145
|
+
end
|
146
|
+
self.class.new(self, result, position)
|
147
|
+
end
|
148
|
+
|
149
|
+
define_builtin_method(:and, n: Int) do |n|
|
150
|
+
self.class.new(nil, value & n.value, position)
|
151
|
+
end
|
152
|
+
|
153
|
+
define_builtin_method(:or, n: Int) do |n|
|
154
|
+
self.class.new(nil, value | n.value, position)
|
155
|
+
end
|
156
|
+
|
157
|
+
define_builtin_method(:xor, n: Int) do |n|
|
158
|
+
self.class.new(nil, value ^ n.value, position)
|
159
|
+
end
|
35
160
|
end
|
36
161
|
|
37
162
|
class Float < Number
|
163
|
+
def initialize(parent, value, position)
|
164
|
+
super(parent, value.to_f, position)
|
165
|
+
end
|
166
|
+
|
167
|
+
uninstantiable_class
|
38
168
|
end
|
39
169
|
end
|
40
170
|
end
|