code-ruby 4.0.1 → 4.0.2
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/VERSION +1 -1
- data/bin/code +19 -35
- data/lib/code/concerns/shared.rb +1 -1
- data/lib/code/node/call.rb +6 -1
- data/lib/code/node/code.rb +4 -4
- data/lib/code/node/function.rb +6 -1
- data/lib/code/node/left_operation.rb +3 -3
- data/lib/code/node/square_bracket.rb +2 -4
- data/lib/code/object/class.rb +1 -0
- data/lib/code/object/code.rb +4 -40
- data/lib/code/object/context.rb +4 -9
- data/lib/code/object/dictionary.rb +3 -7
- data/lib/code/object/function.rb +34 -16
- data/lib/code/object/global.rb +1 -1
- data/lib/code/object/html.rb +3 -11
- data/lib/code/object/http.rb +0 -20
- data/lib/code/object/ics.rb +4 -12
- data/lib/code/object/json.rb +0 -25
- data/lib/code/object/list.rb +2 -6
- data/lib/code/object/smtp.rb +0 -3
- data/lib/code/object/string.rb +3 -1
- data/lib/code/parser.rb +0 -63
- data/lib/code.rb +3 -11
- metadata +3 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2296ee1cc187e2213bbefbb8306efe2de92e79e21b12d8ff5f3472d5ce18eb30
|
|
4
|
+
data.tar.gz: ca3868cf9a4c2c58560a2c81130f633aced9c16a47af11d2386ea5c79a8ba6ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9efaa6dd86c9e6484184ab83e2d4df371f32e737e507e90e60e9151acac473ec029eeecc26e123b577343286712197430c6828de4f340ea88effa592958f451b
|
|
7
|
+
data.tar.gz: f16dba556195a93d2b564511a020cf6bca95c24a86534ddb063573619c214b9fbc0439842621d9ef1244cd59cbb3e3fff1544dfd9c4074e96adc5c7541e1c4ee
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.0.
|
|
1
|
+
4.0.2
|
data/bin/code
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require_relative "../lib/code-ruby"
|
|
5
5
|
require "dorian/arguments"
|
|
6
|
+
require "pp"
|
|
6
7
|
|
|
7
8
|
parsed =
|
|
8
9
|
Dorian::Arguments.parse(
|
|
@@ -37,8 +38,6 @@ parsed =
|
|
|
37
38
|
abort Code::Version.to_s if parsed.options.version
|
|
38
39
|
abort parsed.help if parsed.options.help
|
|
39
40
|
|
|
40
|
-
MAX_ARGUMENT_FILES = 1000
|
|
41
|
-
|
|
42
41
|
def glob_pattern?(value)
|
|
43
42
|
value.match?(/[*?\[\]{]/)
|
|
44
43
|
end
|
|
@@ -47,35 +46,22 @@ def timeout
|
|
|
47
46
|
@timeout ||= @parsed_timeout
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
def with_timeout(&)
|
|
51
|
-
Timeout.timeout(timeout, &)
|
|
49
|
+
def with_timeout(&block)
|
|
50
|
+
Timeout.timeout(timeout, &block)
|
|
52
51
|
rescue Timeout::Error
|
|
53
52
|
raise Code::Error, "timeout"
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
def append_limited(buffer, value, label: "input")
|
|
57
|
-
value = value.to_s
|
|
58
|
-
if buffer.bytesize + value.bytesize > Code::MAX_INPUT_BYTES
|
|
59
|
-
raise Code::Error, "#{label} is too large"
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
buffer << value
|
|
63
|
-
end
|
|
64
|
-
|
|
65
55
|
def read_file(path)
|
|
66
56
|
raise Code::Error, "#{path} is not a regular file" unless File.file?(path)
|
|
67
57
|
|
|
68
58
|
File.open(path, "rb") do |file|
|
|
69
|
-
|
|
70
|
-
Code.ensure_input_size!(data, label: path)
|
|
71
|
-
data
|
|
59
|
+
file.read.to_s
|
|
72
60
|
end
|
|
73
61
|
end
|
|
74
62
|
|
|
75
63
|
def read_stdin
|
|
76
|
-
|
|
77
|
-
Code.ensure_input_size!(data, label: "stdin")
|
|
78
|
-
data
|
|
64
|
+
$stdin.read.to_s
|
|
79
65
|
end
|
|
80
66
|
|
|
81
67
|
def expand_arguments(arguments)
|
|
@@ -94,10 +80,6 @@ def expand_arguments(arguments)
|
|
|
94
80
|
else
|
|
95
81
|
remaining_arguments << argument
|
|
96
82
|
end
|
|
97
|
-
|
|
98
|
-
if expanded_files.size > MAX_ARGUMENT_FILES
|
|
99
|
-
raise Code::Error, "too many input files"
|
|
100
|
-
end
|
|
101
83
|
end
|
|
102
84
|
|
|
103
85
|
[expanded_files, remaining_arguments]
|
|
@@ -118,22 +100,20 @@ begin
|
|
|
118
100
|
expanded_argument_files, remaining_arguments =
|
|
119
101
|
expand_arguments(parsed.arguments)
|
|
120
102
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
103
|
+
buffer <<
|
|
104
|
+
if option_input.present? && File.exist?(option_input)
|
|
105
|
+
read_file(option_input)
|
|
106
|
+
else
|
|
107
|
+
option_input
|
|
108
|
+
end
|
|
126
109
|
|
|
127
|
-
|
|
110
|
+
buffer << remaining_arguments.join(" ")
|
|
128
111
|
|
|
129
112
|
input_files = parsed.files + expanded_argument_files
|
|
130
|
-
if input_files.size > MAX_ARGUMENT_FILES
|
|
131
|
-
raise Code::Error, "too many input files"
|
|
132
|
-
end
|
|
133
113
|
|
|
134
114
|
input_files.each.with_index do |file, index|
|
|
135
|
-
|
|
136
|
-
|
|
115
|
+
buffer << "\n\n" if index.positive?
|
|
116
|
+
buffer << read_file(file)
|
|
137
117
|
end
|
|
138
118
|
|
|
139
119
|
buffer
|
|
@@ -155,7 +135,11 @@ end
|
|
|
155
135
|
|
|
156
136
|
if parsed.options.parse
|
|
157
137
|
begin
|
|
158
|
-
|
|
138
|
+
output = StringIO.new
|
|
139
|
+
with_timeout { PP.pp(Code.parse(input, timeout: timeout), output) }
|
|
140
|
+
print output.string
|
|
141
|
+
rescue SystemStackError
|
|
142
|
+
warn "timeout"
|
|
159
143
|
rescue StandardError => e
|
|
160
144
|
warn e.message
|
|
161
145
|
end
|
data/lib/code/concerns/shared.rb
CHANGED
data/lib/code/node/call.rb
CHANGED
data/lib/code/node/code.rb
CHANGED
|
@@ -21,11 +21,11 @@ class Code
|
|
|
21
21
|
args
|
|
22
22
|
end
|
|
23
23
|
last = Object::Nothing.new
|
|
24
|
-
|
|
24
|
+
object = args.fetch(:previous_object)
|
|
25
25
|
|
|
26
26
|
begin
|
|
27
27
|
(@statements || []).each do |statement|
|
|
28
|
-
last = statement.evaluate(**statement_args, object:
|
|
28
|
+
last = statement.evaluate(**statement_args, object: object)
|
|
29
29
|
end
|
|
30
30
|
rescue Error::Retry
|
|
31
31
|
retry if control_flow_scope == :group
|
|
@@ -48,10 +48,10 @@ class Code
|
|
|
48
48
|
|
|
49
49
|
def resolve(**args)
|
|
50
50
|
last = Object::Nothing.new
|
|
51
|
-
|
|
51
|
+
object = args.fetch(:previous_object)
|
|
52
52
|
|
|
53
53
|
(@statements || []).each do |statement|
|
|
54
|
-
last = statement.resolve(**args, object:
|
|
54
|
+
last = statement.resolve(**args, object: object)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
last
|
data/lib/code/node/function.rb
CHANGED
|
@@ -54,7 +54,7 @@ class Code
|
|
|
54
54
|
right.statement.evaluate(
|
|
55
55
|
**args,
|
|
56
56
|
object: left,
|
|
57
|
-
previous_object: args.fetch(:
|
|
57
|
+
previous_object: args.fetch(:previous_object)
|
|
58
58
|
)
|
|
59
59
|
elsif right.safe_call?
|
|
60
60
|
if left.is_an?(Object::Nothing)
|
|
@@ -63,7 +63,7 @@ class Code
|
|
|
63
63
|
right.statement.evaluate(
|
|
64
64
|
**args,
|
|
65
65
|
object: left,
|
|
66
|
-
previous_object: args.fetch(:
|
|
66
|
+
previous_object: args.fetch(:previous_object)
|
|
67
67
|
)
|
|
68
68
|
end
|
|
69
69
|
elsif (right.or? && left.truthy?) || (right.and? && left.falsy?)
|
|
@@ -88,7 +88,7 @@ class Code
|
|
|
88
88
|
other.statement.resolve(
|
|
89
89
|
**args,
|
|
90
90
|
object: list.code_last,
|
|
91
|
-
previous_object: args.fetch(:
|
|
91
|
+
previous_object: args.fetch(:previous_object)
|
|
92
92
|
)
|
|
93
93
|
|
|
94
94
|
if resolved.is_a?(Object::IdentifierList)
|
|
@@ -13,8 +13,7 @@ class Code
|
|
|
13
13
|
|
|
14
14
|
def evaluate(**args)
|
|
15
15
|
left = @left&.evaluate(**args) || Object::Nothing.new
|
|
16
|
-
index_args =
|
|
17
|
-
args.merge(object: args.fetch(:previous_object, args.fetch(:object)))
|
|
16
|
+
index_args = args.merge(object: args.fetch(:previous_object))
|
|
18
17
|
|
|
19
18
|
(@statements || []).reduce(left) do |object, statement|
|
|
20
19
|
object.code_fetch(statement.evaluate(**index_args))
|
|
@@ -31,8 +30,7 @@ class Code
|
|
|
31
30
|
Object::IdentifierList.new([left])
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
index_args =
|
|
35
|
-
args.merge(object: args.fetch(:previous_object, args.fetch(:object)))
|
|
33
|
+
index_args = args.merge(object: args.fetch(:previous_object))
|
|
36
34
|
|
|
37
35
|
(@statements || []).each do |statement|
|
|
38
36
|
list.code_append(statement.evaluate(**index_args))
|
data/lib/code/object/class.rb
CHANGED
data/lib/code/object/code.rb
CHANGED
|
@@ -42,33 +42,9 @@ class Code
|
|
|
42
42
|
{}
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
class ScopedObject < Object
|
|
46
|
-
def call(**args)
|
|
47
|
-
code_operator = args.fetch(:operator, nil).to_code
|
|
48
|
-
code_context = args.fetch(:context).to_code
|
|
49
|
-
|
|
50
|
-
code_context = code_context.code_lookup!(code_operator)
|
|
51
|
-
code_result = code_context.code_fetch(code_operator)
|
|
52
|
-
|
|
53
|
-
if code_result.is_a?(Super) ||
|
|
54
|
-
(
|
|
55
|
-
code_result.is_a?(Function) &&
|
|
56
|
-
args.fetch(:explicit_arguments, false)
|
|
57
|
-
)
|
|
58
|
-
code_result.call(**args, operator: nil)
|
|
59
|
-
else
|
|
60
|
-
sig(args)
|
|
61
|
-
code_result
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
attr_reader :trusted
|
|
67
|
-
|
|
68
45
|
def initialize(*args, **_kargs, &_block)
|
|
69
|
-
@trusted = args.first.is_a?(Node::Code)
|
|
70
46
|
self.raw =
|
|
71
|
-
(
|
|
47
|
+
(args.first.is_a?(Node::Code) ? args.first : Node::Code.new(::Code.parse(args.first.to_s)))
|
|
72
48
|
end
|
|
73
49
|
|
|
74
50
|
def self.code_evaluate(*args, **globals)
|
|
@@ -103,25 +79,13 @@ class Code
|
|
|
103
79
|
end
|
|
104
80
|
end
|
|
105
81
|
|
|
106
|
-
def code_evaluate(
|
|
107
|
-
raw.evaluate(
|
|
108
|
-
**evaluation_globals(globals, trusted_evaluation: trusted_evaluation)
|
|
109
|
-
)
|
|
82
|
+
def code_evaluate(**globals)
|
|
83
|
+
raw.evaluate(**globals)
|
|
110
84
|
end
|
|
111
85
|
|
|
112
|
-
def code_deep_duplicate
|
|
86
|
+
def code_deep_duplicate
|
|
113
87
|
self.class.new(raw)
|
|
114
88
|
end
|
|
115
|
-
|
|
116
|
-
private
|
|
117
|
-
|
|
118
|
-
def evaluation_globals(globals, trusted_evaluation:)
|
|
119
|
-
return globals if trusted && trusted_evaluation
|
|
120
|
-
|
|
121
|
-
object = ScopedObject.new
|
|
122
|
-
|
|
123
|
-
globals.merge(context: Context.new, object: object, root_object: object)
|
|
124
|
-
end
|
|
125
89
|
end
|
|
126
90
|
end
|
|
127
91
|
end
|
data/lib/code/object/context.rb
CHANGED
|
@@ -67,20 +67,15 @@ class Code
|
|
|
67
67
|
Context.new(raw.merge(other.raw), parent || other.parent)
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
def code_deep_duplicate
|
|
71
|
-
seen.compare_by_identity unless seen.compare_by_identity?
|
|
72
|
-
return seen[self] if seen.key?(self)
|
|
73
|
-
|
|
70
|
+
def code_deep_duplicate
|
|
74
71
|
duplicate = Context.new
|
|
75
|
-
seen[self] = duplicate
|
|
76
|
-
|
|
77
72
|
raw.each do |key, value|
|
|
78
73
|
duplicate.code_set(
|
|
79
|
-
key.code_deep_duplicate
|
|
80
|
-
value.code_deep_duplicate
|
|
74
|
+
key.code_deep_duplicate,
|
|
75
|
+
value.code_deep_duplicate
|
|
81
76
|
)
|
|
82
77
|
end
|
|
83
|
-
duplicate.parent = parent
|
|
78
|
+
duplicate.parent = parent
|
|
84
79
|
duplicate
|
|
85
80
|
end
|
|
86
81
|
|
|
@@ -2934,17 +2934,13 @@ class Code
|
|
|
2934
2934
|
pair ? List.new(pair) : Nothing.new
|
|
2935
2935
|
end
|
|
2936
2936
|
|
|
2937
|
-
def code_deep_duplicate
|
|
2938
|
-
seen.compare_by_identity unless seen.compare_by_identity?
|
|
2939
|
-
return seen[self] if seen.key?(self)
|
|
2940
|
-
|
|
2937
|
+
def code_deep_duplicate
|
|
2941
2938
|
duplicate = Dictionary.new
|
|
2942
|
-
seen[self] = duplicate
|
|
2943
2939
|
|
|
2944
2940
|
raw.each do |key, value|
|
|
2945
2941
|
duplicate.code_set(
|
|
2946
|
-
key.code_deep_duplicate
|
|
2947
|
-
value.code_deep_duplicate
|
|
2942
|
+
key.code_deep_duplicate,
|
|
2943
|
+
value.code_deep_duplicate
|
|
2948
2944
|
)
|
|
2949
2945
|
end
|
|
2950
2946
|
|
data/lib/code/object/function.rb
CHANGED
|
@@ -24,6 +24,16 @@ class Code
|
|
|
24
24
|
"((a, b = 2) => { a + b }).call(3)"
|
|
25
25
|
]
|
|
26
26
|
},
|
|
27
|
+
"new" => {
|
|
28
|
+
name: "new",
|
|
29
|
+
description:
|
|
30
|
+
"evaluates the function body as a constructor with the provided arguments.",
|
|
31
|
+
examples: [
|
|
32
|
+
"Widget = () => { self.name = :widget return(self) } Widget.new.name",
|
|
33
|
+
"User = (name:) => { self.name = name return(self) } User.new(name: :ada).name",
|
|
34
|
+
"Counter = () => { self.count = 0 return(self) } Counter.new.count"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
27
37
|
"extend" => {
|
|
28
38
|
name: "extend",
|
|
29
39
|
description:
|
|
@@ -84,6 +94,7 @@ class Code
|
|
|
84
94
|
attr_reader :code_parameters,
|
|
85
95
|
:code_body,
|
|
86
96
|
:definition_context,
|
|
97
|
+
:definition_object,
|
|
87
98
|
:instance_functions,
|
|
88
99
|
:parent
|
|
89
100
|
|
|
@@ -97,6 +108,7 @@ class Code
|
|
|
97
108
|
|
|
98
109
|
@code_body = Code.new(args.second.presence)
|
|
99
110
|
@definition_context = args.third if args.third.is_a?(Context)
|
|
111
|
+
@definition_object = args.fourth if args.fourth.is_a?(Object)
|
|
100
112
|
@parent = parent.to_code
|
|
101
113
|
self.functions = functions.to_code
|
|
102
114
|
self.functions = Dictionary.new if self.functions.nothing?
|
|
@@ -116,8 +128,11 @@ class Code
|
|
|
116
128
|
code_arguments = args.fetch(:arguments, List.new).to_code
|
|
117
129
|
code_value = code_arguments.code_first
|
|
118
130
|
globals = multi_fetch(args, *GLOBALS)
|
|
131
|
+
operator_name = code_operator.to_s
|
|
132
|
+
operator_name = "" if operator_name == "new" &&
|
|
133
|
+
code_has_key?(code_operator).falsy?
|
|
119
134
|
|
|
120
|
-
case
|
|
135
|
+
case operator_name
|
|
121
136
|
when "", "call"
|
|
122
137
|
sig(args) { signature_for_call }
|
|
123
138
|
code_call(
|
|
@@ -171,6 +186,7 @@ class Code
|
|
|
171
186
|
)
|
|
172
187
|
code_arguments = arguments.to_code
|
|
173
188
|
code_context = Context.new({}, definition_context || globals[:context])
|
|
189
|
+
evaluation_object = definition_object || globals.fetch(:previous_object)
|
|
174
190
|
code_self = bound_self.to_code
|
|
175
191
|
if (code_self.nil? || code_self.nothing?) && parent.is_a?(Class)
|
|
176
192
|
code_self = parent.code_call(*arguments, **globals)
|
|
@@ -254,7 +270,8 @@ class Code
|
|
|
254
270
|
code_default.code_evaluate(
|
|
255
271
|
**globals,
|
|
256
272
|
context: code_context,
|
|
257
|
-
|
|
273
|
+
object: evaluation_object,
|
|
274
|
+
previous_object: evaluation_object
|
|
258
275
|
)
|
|
259
276
|
else
|
|
260
277
|
code_default
|
|
@@ -272,7 +289,8 @@ class Code
|
|
|
272
289
|
**globals,
|
|
273
290
|
constructing_literal_classes: constructing_literal_classes,
|
|
274
291
|
context: code_context,
|
|
275
|
-
|
|
292
|
+
object: evaluation_object,
|
|
293
|
+
previous_object: evaluation_object
|
|
276
294
|
)
|
|
277
295
|
.tap { persist_instance_functions(code_self) }
|
|
278
296
|
rescue Error::Return => e
|
|
@@ -340,6 +358,7 @@ class Code
|
|
|
340
358
|
code_function.code_parameters,
|
|
341
359
|
code_function.code_body.raw,
|
|
342
360
|
code_function.definition_context,
|
|
361
|
+
code_function.definition_object,
|
|
343
362
|
parent: self,
|
|
344
363
|
functions: functions.code_deep_duplicate
|
|
345
364
|
)
|
|
@@ -348,22 +367,19 @@ class Code
|
|
|
348
367
|
end
|
|
349
368
|
end
|
|
350
369
|
|
|
351
|
-
def code_deep_duplicate
|
|
352
|
-
seen.compare_by_identity unless seen.compare_by_identity?
|
|
353
|
-
return seen[self] if seen.key?(self)
|
|
354
|
-
|
|
370
|
+
def code_deep_duplicate
|
|
355
371
|
duplicate = Function.new
|
|
356
|
-
seen[self] = duplicate
|
|
357
372
|
|
|
358
373
|
duplicate.code_replace(
|
|
359
|
-
code_parameters: code_parameters.code_deep_duplicate
|
|
360
|
-
code_body: code_body.code_deep_duplicate
|
|
361
|
-
definition_context: definition_context&.code_deep_duplicate
|
|
374
|
+
code_parameters: code_parameters.code_deep_duplicate,
|
|
375
|
+
code_body: code_body.code_deep_duplicate,
|
|
376
|
+
definition_context: definition_context&.code_deep_duplicate,
|
|
377
|
+
definition_object: definition_object,
|
|
362
378
|
parent:
|
|
363
|
-
parent.is_a?(Function) ? parent.code_deep_duplicate
|
|
364
|
-
functions: functions.code_deep_duplicate
|
|
365
|
-
instance_functions: instance_functions.code_deep_duplicate
|
|
366
|
-
documentation: documentation.code_deep_duplicate
|
|
379
|
+
parent.is_a?(Function) ? parent.code_deep_duplicate : parent,
|
|
380
|
+
functions: functions.code_deep_duplicate,
|
|
381
|
+
instance_functions: instance_functions.code_deep_duplicate,
|
|
382
|
+
documentation: documentation.code_deep_duplicate
|
|
367
383
|
)
|
|
368
384
|
duplicate
|
|
369
385
|
end
|
|
@@ -375,11 +391,13 @@ class Code
|
|
|
375
391
|
parent:,
|
|
376
392
|
functions:,
|
|
377
393
|
instance_functions:,
|
|
378
|
-
documentation
|
|
394
|
+
documentation:,
|
|
395
|
+
definition_object: nil
|
|
379
396
|
)
|
|
380
397
|
@code_parameters = code_parameters
|
|
381
398
|
@code_body = code_body
|
|
382
399
|
@definition_context = definition_context
|
|
400
|
+
@definition_object = definition_object
|
|
383
401
|
@parent = parent
|
|
384
402
|
self.functions = functions
|
|
385
403
|
@instance_functions = instance_functions
|
data/lib/code/object/global.rb
CHANGED
data/lib/code/object/html.rb
CHANGED
|
@@ -265,7 +265,6 @@ class Code
|
|
|
265
265
|
args.first
|
|
266
266
|
else
|
|
267
267
|
source = args.first.to_s
|
|
268
|
-
::Code.ensure_input_size!(source, label: "html")
|
|
269
268
|
Nokogiri.HTML(source)
|
|
270
269
|
end
|
|
271
270
|
end
|
|
@@ -385,10 +384,7 @@ class Code
|
|
|
385
384
|
value_or_function.to_code
|
|
386
385
|
end
|
|
387
386
|
|
|
388
|
-
|
|
389
|
-
::Code.ensure_input_size!(source, label: "html")
|
|
390
|
-
|
|
391
|
-
String.new(Nokogiri::HTML.fragment(source).text)
|
|
387
|
+
String.new(Nokogiri::HTML.fragment(code_value.to_s).text)
|
|
392
388
|
rescue Error::Break => e
|
|
393
389
|
e.code_value
|
|
394
390
|
end
|
|
@@ -473,18 +469,14 @@ class Code
|
|
|
473
469
|
if code_value.is_an?(Html)
|
|
474
470
|
Html.new(fragment_from_html(code_value))
|
|
475
471
|
else
|
|
476
|
-
|
|
477
|
-
::Code.ensure_input_size!(source, label: "html")
|
|
478
|
-
Html.new(Nokogiri::HTML::DocumentFragment.parse(source))
|
|
472
|
+
Html.new(Nokogiri::HTML::DocumentFragment.parse(code_value.to_s))
|
|
479
473
|
end
|
|
480
474
|
rescue Error::Break => e
|
|
481
475
|
e.code_value
|
|
482
476
|
end
|
|
483
477
|
|
|
484
478
|
def self.fragment_from_html(html)
|
|
485
|
-
|
|
486
|
-
::Code.ensure_input_size!(source, label: "html")
|
|
487
|
-
Nokogiri::HTML::DocumentFragment.parse(source)
|
|
479
|
+
Nokogiri::HTML::DocumentFragment.parse(html.to_html)
|
|
488
480
|
end
|
|
489
481
|
|
|
490
482
|
def call(**args)
|
data/lib/code/object/http.rb
CHANGED
|
@@ -191,8 +191,6 @@ class Code
|
|
|
191
191
|
network_authentication_required: 511
|
|
192
192
|
}.freeze
|
|
193
193
|
DEFAULT_TIMEOUT = 1.hour.to_f
|
|
194
|
-
MAX_REQUEST_BYTES = ::Code::MAX_INPUT_BYTES
|
|
195
|
-
MAX_RESPONSE_BYTES = ::Code::MAX_INPUT_BYTES
|
|
196
194
|
MAX_HEADER_BYTES = 32.kilobytes
|
|
197
195
|
HEADER_NAME = /\A[A-Za-z0-9!#$%&'*+\-.^_`|~]+\z/
|
|
198
196
|
RESTRICTED_HEADERS = %w[
|
|
@@ -291,17 +289,9 @@ class Code
|
|
|
291
289
|
write_timeout = options.code_get("write_timeout")
|
|
292
290
|
query = options.code_get("query").raw || {}
|
|
293
291
|
query = query.to_a.flatten.map(&:to_s).each_slice(2).to_h.to_query
|
|
294
|
-
validate_payload_size!(username, label: "http username")
|
|
295
|
-
validate_payload_size!(password, label: "http password")
|
|
296
|
-
validate_payload_size!(query, label: "http query")
|
|
297
|
-
validate_payload_size!(body, label: "http request body")
|
|
298
|
-
if data.present?
|
|
299
|
-
validate_payload_size!(data.as_json.to_query, label: "http form data")
|
|
300
|
-
end
|
|
301
292
|
|
|
302
293
|
url = original_url
|
|
303
294
|
url = "#{url}?#{query}" if query.present?
|
|
304
|
-
validate_payload_size!(url, label: "http url")
|
|
305
295
|
|
|
306
296
|
if username.present? || password.present?
|
|
307
297
|
authorization = ::Base64.strict_encode64("#{username}:#{password}")
|
|
@@ -356,10 +346,6 @@ class Code
|
|
|
356
346
|
validate_response_headers!(http_response)
|
|
357
347
|
|
|
358
348
|
http_response.read_body do |chunk|
|
|
359
|
-
if response_body.bytesize + chunk.bytesize > MAX_RESPONSE_BYTES
|
|
360
|
-
raise ::Code::Error, "http response is too large"
|
|
361
|
-
end
|
|
362
|
-
|
|
363
349
|
response_body << chunk
|
|
364
350
|
end
|
|
365
351
|
end
|
|
@@ -478,12 +464,6 @@ class Code
|
|
|
478
464
|
raise ::Code::Error, "http response headers are too large"
|
|
479
465
|
end
|
|
480
466
|
|
|
481
|
-
def self.validate_payload_size!(value, label:)
|
|
482
|
-
return if value.to_s.bytesize <= MAX_REQUEST_BYTES
|
|
483
|
-
|
|
484
|
-
raise ::Code::Error, "#{label} is too large"
|
|
485
|
-
end
|
|
486
|
-
|
|
487
467
|
def self.http_timeout(value, default)
|
|
488
468
|
::Code.normalize_timeout!(value.nothing? ? default : value)
|
|
489
469
|
end
|
data/lib/code/object/ics.rb
CHANGED
|
@@ -44,8 +44,6 @@ class Code
|
|
|
44
44
|
geo
|
|
45
45
|
].freeze
|
|
46
46
|
MISSING_ATTRIBUTE = Object.new.freeze
|
|
47
|
-
MAX_EVENTS = 10_000
|
|
48
|
-
MAX_NESTING = 50
|
|
49
47
|
|
|
50
48
|
def self.call(**args)
|
|
51
49
|
code_operator = args.fetch(:operator, nil).to_code
|
|
@@ -63,10 +61,8 @@ class Code
|
|
|
63
61
|
|
|
64
62
|
def self.code_parse(value)
|
|
65
63
|
source = value.to_code.raw
|
|
66
|
-
::Code.ensure_input_size!(source, label: "ics")
|
|
67
64
|
calendars = ::Icalendar::Calendar.parse(source)
|
|
68
65
|
events = calendars.flat_map(&:events)
|
|
69
|
-
raise Error, "ics has too many events" if events.size > MAX_EVENTS
|
|
70
66
|
|
|
71
67
|
events.map { |event| serialize_event(event) }.to_code
|
|
72
68
|
rescue ::Code::Error
|
|
@@ -128,9 +124,7 @@ class Code
|
|
|
128
124
|
MISSING_ATTRIBUTE
|
|
129
125
|
end
|
|
130
126
|
|
|
131
|
-
def self.serialize_value(value
|
|
132
|
-
raise Error, "ics is too deeply nested" if depth > MAX_NESTING
|
|
133
|
-
|
|
127
|
+
def self.serialize_value(value)
|
|
134
128
|
case value
|
|
135
129
|
when nil
|
|
136
130
|
nil
|
|
@@ -139,17 +133,15 @@ class Code
|
|
|
139
133
|
when ::Symbol, ::Integer, ::Float, ::BigDecimal, true, false
|
|
140
134
|
value
|
|
141
135
|
when ::Array
|
|
142
|
-
value.map { |item| serialize_value(item
|
|
136
|
+
value.map { |item| serialize_value(item) }
|
|
143
137
|
when ::Hash
|
|
144
|
-
value.transform_values
|
|
145
|
-
serialize_value(item, depth: depth + 1)
|
|
146
|
-
end
|
|
138
|
+
value.transform_values { |item| serialize_value(item) }
|
|
147
139
|
else
|
|
148
140
|
serialized_date = serialize_date_like(value)
|
|
149
141
|
return serialized_date unless serialized_date.nil?
|
|
150
142
|
|
|
151
143
|
if value.is_a?(::Icalendar::Value)
|
|
152
|
-
serialize_value(value.value
|
|
144
|
+
serialize_value(value.value)
|
|
153
145
|
else
|
|
154
146
|
normalize_string(value.to_s)
|
|
155
147
|
end
|
data/lib/code/object/json.rb
CHANGED
|
@@ -41,9 +41,6 @@ class Code
|
|
|
41
41
|
{}
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
MAX_NESTING = 100
|
|
45
|
-
MAX_ITEMS = 10_000
|
|
46
|
-
|
|
47
44
|
def self.call(**args)
|
|
48
45
|
code_operator = args.fetch(:operator, nil).to_code
|
|
49
46
|
code_arguments = args.fetch(:arguments, []).to_code
|
|
@@ -71,10 +68,8 @@ class Code
|
|
|
71
68
|
|
|
72
69
|
def self.code_parse(value)
|
|
73
70
|
code_value = value.to_code
|
|
74
|
-
::Code.ensure_input_size!(code_value.raw, label: "json")
|
|
75
71
|
|
|
76
72
|
parsed = ::JSON.parse(code_value.raw)
|
|
77
|
-
validate_shape!(parsed)
|
|
78
73
|
parsed.to_code
|
|
79
74
|
rescue JSON::ParserError
|
|
80
75
|
Nothing.new
|
|
@@ -83,26 +78,6 @@ class Code
|
|
|
83
78
|
def self.code_generate(value, pretty: nil)
|
|
84
79
|
value.to_code.code_to_json(pretty: pretty)
|
|
85
80
|
end
|
|
86
|
-
|
|
87
|
-
def self.validate_shape!(value, depth: 0, count: 0)
|
|
88
|
-
raise Error, "json is too deeply nested" if depth > MAX_NESTING
|
|
89
|
-
raise Error, "json has too many items" if count > MAX_ITEMS
|
|
90
|
-
|
|
91
|
-
case value
|
|
92
|
-
when ::Array
|
|
93
|
-
count += value.size
|
|
94
|
-
value.each do |item|
|
|
95
|
-
count = validate_shape!(item, depth: depth + 1, count: count)
|
|
96
|
-
end
|
|
97
|
-
when ::Hash
|
|
98
|
-
count += value.size
|
|
99
|
-
value.each_value do |item|
|
|
100
|
-
count = validate_shape!(item, depth: depth + 1, count: count)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
count
|
|
105
|
-
end
|
|
106
81
|
end
|
|
107
82
|
end
|
|
108
83
|
end
|
data/lib/code/object/list.rb
CHANGED
|
@@ -5060,14 +5060,10 @@ class Code
|
|
|
5060
5060
|
raw.inject(&:code_plus) || Nothing.new
|
|
5061
5061
|
end
|
|
5062
5062
|
|
|
5063
|
-
def code_deep_duplicate
|
|
5064
|
-
seen.compare_by_identity unless seen.compare_by_identity?
|
|
5065
|
-
return seen[self] if seen.key?(self)
|
|
5066
|
-
|
|
5063
|
+
def code_deep_duplicate
|
|
5067
5064
|
duplicate = List.new
|
|
5068
|
-
seen[self] = duplicate
|
|
5069
5065
|
duplicate.raw.concat(
|
|
5070
|
-
raw.map { |value| value.code_deep_duplicate
|
|
5066
|
+
raw.map { |value| value.code_deep_duplicate }
|
|
5071
5067
|
)
|
|
5072
5068
|
duplicate
|
|
5073
5069
|
end
|
data/lib/code/object/smtp.rb
CHANGED
data/lib/code/object/string.rb
CHANGED
data/lib/code/parser.rb
CHANGED
|
@@ -73,7 +73,6 @@ class Code
|
|
|
73
73
|
SUFFIX_PUNCTUATION = %w[! ?].freeze
|
|
74
74
|
|
|
75
75
|
ASSIGNMENT_RHS_MIN_BP = 20
|
|
76
|
-
MAX_NESTING = 200
|
|
77
76
|
INFIX_PRECEDENCE = {
|
|
78
77
|
"if" => [10, 9],
|
|
79
78
|
"unless" => [10, 9],
|
|
@@ -129,7 +128,6 @@ class Code
|
|
|
129
128
|
|
|
130
129
|
def initialize(input)
|
|
131
130
|
@input = input.to_s
|
|
132
|
-
ensure_source_nesting_limit!(@input)
|
|
133
131
|
@tokens = lex(@input)
|
|
134
132
|
@index = 0
|
|
135
133
|
end
|
|
@@ -976,67 +974,6 @@ class Code
|
|
|
976
974
|
raise Error, "#{message} at #{token.position}"
|
|
977
975
|
end
|
|
978
976
|
|
|
979
|
-
def ensure_source_nesting_limit!(source)
|
|
980
|
-
depth = 0
|
|
981
|
-
quote = nil
|
|
982
|
-
escaped = false
|
|
983
|
-
index = 0
|
|
984
|
-
|
|
985
|
-
while index < source.length
|
|
986
|
-
char = source[index]
|
|
987
|
-
if quote
|
|
988
|
-
if escaped
|
|
989
|
-
escaped = false
|
|
990
|
-
elsif char == "\\"
|
|
991
|
-
escaped = true
|
|
992
|
-
elsif char == quote
|
|
993
|
-
quote = nil
|
|
994
|
-
end
|
|
995
|
-
index += 1
|
|
996
|
-
next
|
|
997
|
-
end
|
|
998
|
-
|
|
999
|
-
if %w[' "].include?(char)
|
|
1000
|
-
quote = char
|
|
1001
|
-
elsif char == "#"
|
|
1002
|
-
index += 1
|
|
1003
|
-
index += 1 while index < source.length &&
|
|
1004
|
-
!NEWLINE_CHARACTERS.include?(source[index])
|
|
1005
|
-
next
|
|
1006
|
-
elsif source[index, 2] == "//"
|
|
1007
|
-
index += 2
|
|
1008
|
-
index += 1 while index < source.length &&
|
|
1009
|
-
!NEWLINE_CHARACTERS.include?(source[index])
|
|
1010
|
-
next
|
|
1011
|
-
elsif source[index, 2] == "/*"
|
|
1012
|
-
index += 2
|
|
1013
|
-
index += 1 while index < source.length && source[index, 2] != "*/"
|
|
1014
|
-
index += 2 if source[index, 2] == "*/"
|
|
1015
|
-
next
|
|
1016
|
-
elsif "([{".include?(char)
|
|
1017
|
-
depth += 1
|
|
1018
|
-
if depth > MAX_NESTING
|
|
1019
|
-
raise_parse_error_at("source is too deeply nested", index)
|
|
1020
|
-
end
|
|
1021
|
-
elsif ")]}".include?(char)
|
|
1022
|
-
depth -= 1 if depth.positive?
|
|
1023
|
-
end
|
|
1024
|
-
index += 1
|
|
1025
|
-
end
|
|
1026
|
-
end
|
|
1027
|
-
|
|
1028
|
-
def raise_parse_error_at(message, position)
|
|
1029
|
-
token =
|
|
1030
|
-
Token.new(
|
|
1031
|
-
type: :unknown,
|
|
1032
|
-
value: "",
|
|
1033
|
-
position: position,
|
|
1034
|
-
newline_before: false,
|
|
1035
|
-
space_before: false
|
|
1036
|
-
)
|
|
1037
|
-
raise_parse_error(message, token)
|
|
1038
|
-
end
|
|
1039
|
-
|
|
1040
977
|
def lex(source)
|
|
1041
978
|
tokens = []
|
|
1042
979
|
index = 0
|
data/lib/code.rb
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Code
|
|
4
|
-
GLOBALS = %i[context error input object output
|
|
4
|
+
GLOBALS = %i[context error input object output previous_object source].freeze
|
|
5
5
|
DEFAULT_TIMEOUT = 1.hour.to_f
|
|
6
|
-
MAX_INPUT_BYTES = 10.megabytes
|
|
7
6
|
LOCALES = %w[en fr].freeze
|
|
8
7
|
|
|
9
8
|
def initialize(
|
|
@@ -27,7 +26,6 @@ class Code
|
|
|
27
26
|
def self.parse(source, timeout: DEFAULT_TIMEOUT)
|
|
28
27
|
timeout = normalize_timeout!(timeout)
|
|
29
28
|
|
|
30
|
-
ensure_input_size!(source, label: "source")
|
|
31
29
|
Timeout.timeout(timeout) { Parser.parse(source).to_raw }
|
|
32
30
|
rescue Timeout::Error
|
|
33
31
|
raise Error, "timeout"
|
|
@@ -50,16 +48,10 @@ class Code
|
|
|
50
48
|
Format.format(parse_tree)
|
|
51
49
|
end
|
|
52
50
|
|
|
53
|
-
def self.ensure_input_size!(source, limit: MAX_INPUT_BYTES, label: "input")
|
|
54
|
-
return if source.to_s.bytesize <= limit
|
|
55
|
-
|
|
56
|
-
raise Error, "#{label} is too large"
|
|
57
|
-
end
|
|
58
|
-
|
|
59
51
|
def self.normalize_timeout!(timeout)
|
|
60
52
|
timeout = DEFAULT_TIMEOUT if timeout.nil?
|
|
61
53
|
timeout = timeout.to_f
|
|
62
|
-
raise Error, "timeout must be
|
|
54
|
+
raise Error, "timeout must be non-negative" unless timeout >= 0
|
|
63
55
|
|
|
64
56
|
timeout
|
|
65
57
|
end
|
|
@@ -75,7 +67,7 @@ class Code
|
|
|
75
67
|
input: input,
|
|
76
68
|
object: object,
|
|
77
69
|
output: output,
|
|
78
|
-
|
|
70
|
+
previous_object: object,
|
|
79
71
|
source: source,
|
|
80
72
|
timeout: timeout
|
|
81
73
|
)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: code-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.0.
|
|
4
|
+
version: 4.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dorian Marié
|
|
@@ -249,26 +249,6 @@ dependencies:
|
|
|
249
249
|
- - "<"
|
|
250
250
|
- !ruby/object:Gem::Version
|
|
251
251
|
version: '2'
|
|
252
|
-
- !ruby/object:Gem::Dependency
|
|
253
|
-
name: ruby-prof
|
|
254
|
-
requirement: !ruby/object:Gem::Requirement
|
|
255
|
-
requirements:
|
|
256
|
-
- - ">="
|
|
257
|
-
- !ruby/object:Gem::Version
|
|
258
|
-
version: '2.0'
|
|
259
|
-
- - "<"
|
|
260
|
-
- !ruby/object:Gem::Version
|
|
261
|
-
version: '3'
|
|
262
|
-
type: :runtime
|
|
263
|
-
prerelease: false
|
|
264
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
265
|
-
requirements:
|
|
266
|
-
- - ">="
|
|
267
|
-
- !ruby/object:Gem::Version
|
|
268
|
-
version: '2.0'
|
|
269
|
-
- - "<"
|
|
270
|
-
- !ruby/object:Gem::Version
|
|
271
|
-
version: '3'
|
|
272
252
|
- !ruby/object:Gem::Dependency
|
|
273
253
|
name: uri
|
|
274
254
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -309,7 +289,7 @@ dependencies:
|
|
|
309
289
|
- - "<"
|
|
310
290
|
- !ruby/object:Gem::Version
|
|
311
291
|
version: '3'
|
|
312
|
-
description: a
|
|
292
|
+
description: a compact, embeddable scripting language for the internet
|
|
313
293
|
email: dorian@dorianmarie.com
|
|
314
294
|
executables:
|
|
315
295
|
- code
|
|
@@ -420,7 +400,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
420
400
|
requirements:
|
|
421
401
|
- - ">="
|
|
422
402
|
- !ruby/object:Gem::Version
|
|
423
|
-
version: '3.
|
|
403
|
+
version: '3.0'
|
|
424
404
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
425
405
|
requirements:
|
|
426
406
|
- - ">="
|