code-ruby 3.1.2 → 4.0.1
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 +100 -20
- data/lib/code/concerns/shared.rb +335 -15
- data/lib/code/format.rb +33 -15
- data/lib/code/network.rb +82 -0
- data/lib/code/node/call.rb +80 -2
- data/lib/code/node/call_argument.rb +14 -0
- data/lib/code/node/code.rb +4 -3
- data/lib/code/node/function_parameter.rb +7 -4
- data/lib/code/node/list.rb +32 -2
- data/lib/code/node/square_bracket.rb +4 -2
- data/lib/code/object/base_64.rb +132 -6
- data/lib/code/object/boolean.rb +56 -0
- data/lib/code/object/class.rb +143 -2
- data/lib/code/object/code.rb +108 -7
- data/lib/code/object/context.rb +59 -1
- data/lib/code/object/cryptography.rb +69 -0
- data/lib/code/object/date.rb +13800 -462
- data/lib/code/object/decimal.rb +1098 -0
- data/lib/code/object/dictionary.rb +1861 -11
- data/lib/code/object/duration.rb +24 -0
- data/lib/code/object/function.rb +289 -27
- data/lib/code/object/global.rb +447 -1
- data/lib/code/object/html.rb +181 -7
- data/lib/code/object/http.rb +253 -17
- data/lib/code/object/ics.rb +76 -13
- data/lib/code/object/identifier_list.rb +30 -10
- data/lib/code/object/integer.rb +1265 -2
- data/lib/code/object/json.rb +80 -1
- data/lib/code/object/list.rb +3371 -10
- data/lib/code/object/nothing.rb +53 -0
- data/lib/code/object/number.rb +120 -0
- data/lib/code/object/parameter.rb +149 -0
- data/lib/code/object/range.rb +530 -14
- data/lib/code/object/smtp.rb +103 -12
- data/lib/code/object/string.rb +968 -3
- data/lib/code/object/super.rb +11 -1
- data/lib/code/object/time.rb +13932 -498
- data/lib/code/object/url.rb +67 -0
- data/lib/code/object.rb +582 -0
- data/lib/code/parser.rb +194 -55
- data/lib/code-ruby.rb +3 -0
- data/lib/code.rb +30 -3
- metadata +135 -84
- data/.github/dependabot.yml +0 -15
- data/.github/workflows/ci.yml +0 -38
- data/.gitignore +0 -30
- data/.node-version +0 -1
- data/.npm-version +0 -1
- data/.prettierignore +0 -2
- data/.rspec +0 -1
- data/.rubocop.yml +0 -140
- data/.ruby-version +0 -1
- data/.tool-versions +0 -3
- data/AGENTS.md +0 -43
- data/Gemfile +0 -22
- data/Gemfile.lock +0 -292
- data/Rakefile +0 -5
- data/bin/bundle +0 -123
- data/bin/bundle-audit +0 -31
- data/bin/bundler-audit +0 -31
- data/bin/dorian +0 -31
- data/bin/rspec +0 -31
- data/bin/rubocop +0 -31
- data/bin/test +0 -5
- data/code-ruby.gemspec +0 -34
- data/docs/precedence.txt +0 -36
- data/package-lock.json +0 -14
- data/package.json +0 -7
- data/spec/bin/code_spec.rb +0 -48
- data/spec/code/format_spec.rb +0 -153
- data/spec/code/node/call_spec.rb +0 -11
- data/spec/code/object/boolean_spec.rb +0 -18
- data/spec/code/object/cryptography_spec.rb +0 -25
- data/spec/code/object/decimal_spec.rb +0 -50
- data/spec/code/object/dictionary_spec.rb +0 -98
- data/spec/code/object/function_spec.rb +0 -268
- data/spec/code/object/http_spec.rb +0 -33
- data/spec/code/object/ics_spec.rb +0 -50
- data/spec/code/object/integer_spec.rb +0 -42
- data/spec/code/object/list_spec.rb +0 -22
- data/spec/code/object/nothing_spec.rb +0 -14
- data/spec/code/object/range_spec.rb +0 -23
- data/spec/code/object/string_spec.rb +0 -26
- data/spec/code/parser/boolean_spec.rb +0 -11
- data/spec/code/parser/chained_call_spec.rb +0 -16
- data/spec/code/parser/dictionary_spec.rb +0 -18
- data/spec/code/parser/function_spec.rb +0 -16
- data/spec/code/parser/group_spec.rb +0 -11
- data/spec/code/parser/if_modifier_spec.rb +0 -18
- data/spec/code/parser/list_spec.rb +0 -17
- data/spec/code/parser/number_spec.rb +0 -11
- data/spec/code/parser/string_spec.rb +0 -20
- data/spec/code/parser_spec.rb +0 -52
- data/spec/code/type_spec.rb +0 -21
- data/spec/code_spec.rb +0 -717
- data/spec/spec_helper.rb +0 -21
- data/spec/zeitwerk/loader_spec.rb +0 -7
data/lib/code/parser.rb
CHANGED
|
@@ -32,37 +32,37 @@ class Code
|
|
|
32
32
|
while
|
|
33
33
|
].freeze
|
|
34
34
|
|
|
35
|
-
MULTI_CHAR_OPERATORS = [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
35
|
+
MULTI_CHAR_OPERATORS = %w[
|
|
36
|
+
&.
|
|
37
|
+
&&
|
|
38
|
+
&&=
|
|
39
|
+
**
|
|
40
|
+
*=
|
|
41
|
+
+=
|
|
42
|
+
-=
|
|
43
|
+
..
|
|
44
|
+
...
|
|
45
|
+
/=
|
|
46
|
+
::
|
|
47
|
+
<<=
|
|
48
|
+
<<
|
|
49
|
+
<=>
|
|
50
|
+
<=
|
|
51
|
+
===
|
|
52
|
+
==
|
|
53
|
+
=~
|
|
54
|
+
>=
|
|
55
|
+
>>=
|
|
56
|
+
>>
|
|
57
|
+
||=
|
|
58
|
+
||
|
|
59
|
+
|=
|
|
60
|
+
!==
|
|
61
|
+
!=
|
|
62
|
+
!~
|
|
63
|
+
%=
|
|
64
|
+
^=
|
|
65
|
+
=>
|
|
66
66
|
].sort_by(&:length).reverse.freeze
|
|
67
67
|
CONTINUATION_KEYWORDS = %w[or and rescue].freeze
|
|
68
68
|
POSTFIX_CONTINUATIONS = %w[. :: &.].freeze
|
|
@@ -73,7 +73,7 @@ class Code
|
|
|
73
73
|
SUFFIX_PUNCTUATION = %w[! ?].freeze
|
|
74
74
|
|
|
75
75
|
ASSIGNMENT_RHS_MIN_BP = 20
|
|
76
|
-
|
|
76
|
+
MAX_NESTING = 200
|
|
77
77
|
INFIX_PRECEDENCE = {
|
|
78
78
|
"if" => [10, 9],
|
|
79
79
|
"unless" => [10, 9],
|
|
@@ -129,6 +129,7 @@ class Code
|
|
|
129
129
|
|
|
130
130
|
def initialize(input)
|
|
131
131
|
@input = input.to_s
|
|
132
|
+
ensure_source_nesting_limit!(@input)
|
|
132
133
|
@tokens = lex(@input)
|
|
133
134
|
@index = 0
|
|
134
135
|
end
|
|
@@ -141,6 +142,10 @@ class Code
|
|
|
141
142
|
Node::Code.new(parse_code)
|
|
142
143
|
end
|
|
143
144
|
|
|
145
|
+
def lex_source(source)
|
|
146
|
+
lex(source.to_s)
|
|
147
|
+
end
|
|
148
|
+
|
|
144
149
|
private
|
|
145
150
|
|
|
146
151
|
attr_reader :input, :tokens
|
|
@@ -284,7 +289,12 @@ class Code
|
|
|
284
289
|
case current.value
|
|
285
290
|
when ".", "::", "&."
|
|
286
291
|
operator = advance.value
|
|
287
|
-
statement =
|
|
292
|
+
statement =
|
|
293
|
+
if current.type == :keyword
|
|
294
|
+
{ call: { name: advance.value } }
|
|
295
|
+
else
|
|
296
|
+
parse_expression(151)
|
|
297
|
+
end
|
|
288
298
|
append_left_operation(left, operator, statement)
|
|
289
299
|
when "["
|
|
290
300
|
advance
|
|
@@ -565,6 +575,19 @@ class Code
|
|
|
565
575
|
end
|
|
566
576
|
|
|
567
577
|
def parse_argument
|
|
578
|
+
if current.type == :operator && %w[* ** & && ...].include?(current.value)
|
|
579
|
+
operator = advance.value
|
|
580
|
+
value =
|
|
581
|
+
if %w[, )].include?(next_significant_token.value)
|
|
582
|
+
skip_newlines
|
|
583
|
+
nil
|
|
584
|
+
else
|
|
585
|
+
parse_code(stop_values: %w[, )])
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
return { operator: operator, value: value }.compact
|
|
589
|
+
end
|
|
590
|
+
|
|
568
591
|
if label_name_start?(current) && next_token_value == ":"
|
|
569
592
|
name = advance.value
|
|
570
593
|
advance
|
|
@@ -653,10 +676,11 @@ class Code
|
|
|
653
676
|
|
|
654
677
|
{
|
|
655
678
|
name: name,
|
|
656
|
-
regular_splat: (
|
|
657
|
-
keyword_splat: (
|
|
658
|
-
block: (
|
|
659
|
-
|
|
679
|
+
regular_splat: (prefix if prefix == "*"),
|
|
680
|
+
keyword_splat: (prefix if prefix == "**"),
|
|
681
|
+
block: (prefix if prefix == "&"),
|
|
682
|
+
blocks: (prefix if prefix == "&&"),
|
|
683
|
+
spread: (prefix if %w[. .. ...].include?(prefix)),
|
|
660
684
|
default: default
|
|
661
685
|
}.compact
|
|
662
686
|
end
|
|
@@ -664,7 +688,7 @@ class Code
|
|
|
664
688
|
def parse_parameter_prefix
|
|
665
689
|
return unless current.type == :operator
|
|
666
690
|
|
|
667
|
-
advance.value if %w[* ** & .. ... .].include?(current.value)
|
|
691
|
+
advance.value if %w[* ** & && .. ... .].include?(current.value)
|
|
668
692
|
end
|
|
669
693
|
|
|
670
694
|
def parse_optional_code(stop_values)
|
|
@@ -952,6 +976,67 @@ class Code
|
|
|
952
976
|
raise Error, "#{message} at #{token.position}"
|
|
953
977
|
end
|
|
954
978
|
|
|
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
|
+
|
|
955
1040
|
def lex(source)
|
|
956
1041
|
tokens = []
|
|
957
1042
|
index = 0
|
|
@@ -1200,10 +1285,16 @@ class Code
|
|
|
1200
1285
|
end
|
|
1201
1286
|
|
|
1202
1287
|
if char == "{"
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1288
|
+
code, i, closed = extract_braced(source, i)
|
|
1289
|
+
|
|
1290
|
+
if closed
|
|
1291
|
+
parts << { type: :text, value: text } unless text.empty?
|
|
1292
|
+
text = +""
|
|
1293
|
+
parts << { type: :code, value: code }
|
|
1294
|
+
else
|
|
1295
|
+
text << "{" << code
|
|
1296
|
+
text << "}" if closed
|
|
1297
|
+
end
|
|
1207
1298
|
next
|
|
1208
1299
|
end
|
|
1209
1300
|
|
|
@@ -1219,28 +1310,74 @@ class Code
|
|
|
1219
1310
|
depth = 1
|
|
1220
1311
|
i = index + 1
|
|
1221
1312
|
body = +""
|
|
1313
|
+
quote = nil
|
|
1314
|
+
escaped = false
|
|
1222
1315
|
|
|
1223
1316
|
while i < source.length
|
|
1224
1317
|
char = source[i]
|
|
1225
1318
|
|
|
1226
|
-
if
|
|
1319
|
+
if quote
|
|
1320
|
+
body << char
|
|
1321
|
+
if escaped
|
|
1322
|
+
escaped = false
|
|
1323
|
+
elsif char == "\\"
|
|
1324
|
+
escaped = true
|
|
1325
|
+
elsif char == quote
|
|
1326
|
+
quote = nil
|
|
1327
|
+
end
|
|
1328
|
+
i += 1
|
|
1329
|
+
next
|
|
1330
|
+
end
|
|
1331
|
+
|
|
1332
|
+
if %w[' "].include?(char)
|
|
1333
|
+
quote = char
|
|
1334
|
+
elsif char == "#"
|
|
1335
|
+
while i < source.length && !NEWLINE_CHARACTERS.include?(source[i])
|
|
1336
|
+
body << source[i]
|
|
1337
|
+
i += 1
|
|
1338
|
+
end
|
|
1339
|
+
next
|
|
1340
|
+
elsif source[i, 2] == "//"
|
|
1341
|
+
2.times do
|
|
1342
|
+
body << source[i]
|
|
1343
|
+
i += 1
|
|
1344
|
+
end
|
|
1345
|
+
while i < source.length && !NEWLINE_CHARACTERS.include?(source[i])
|
|
1346
|
+
body << source[i]
|
|
1347
|
+
i += 1
|
|
1348
|
+
end
|
|
1349
|
+
next
|
|
1350
|
+
elsif source[i, 2] == "/*"
|
|
1351
|
+
2.times do
|
|
1352
|
+
body << source[i]
|
|
1353
|
+
i += 1
|
|
1354
|
+
end
|
|
1355
|
+
while i < source.length && source[i, 2] != "*/"
|
|
1356
|
+
body << source[i]
|
|
1357
|
+
i += 1
|
|
1358
|
+
end
|
|
1359
|
+
if source[i, 2] == "*/"
|
|
1360
|
+
2.times do
|
|
1361
|
+
body << source[i]
|
|
1362
|
+
i += 1
|
|
1363
|
+
end
|
|
1364
|
+
end
|
|
1365
|
+
next
|
|
1366
|
+
elsif char == "{"
|
|
1227
1367
|
depth += 1
|
|
1228
1368
|
elsif char == "}"
|
|
1229
1369
|
depth -= 1
|
|
1230
|
-
return body, i + 1 if depth.zero?
|
|
1370
|
+
return body, i + 1, true if depth.zero?
|
|
1231
1371
|
end
|
|
1232
1372
|
body << char
|
|
1233
1373
|
i += 1
|
|
1234
1374
|
end
|
|
1235
1375
|
|
|
1236
|
-
[body, i]
|
|
1376
|
+
[body, i, false]
|
|
1237
1377
|
end
|
|
1238
1378
|
|
|
1239
1379
|
def scan_number(source, index)
|
|
1240
|
-
|
|
1241
|
-
return unless rest
|
|
1242
|
-
|
|
1243
|
-
if (match = /\A0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*/.match(rest))
|
|
1380
|
+
if (match = /\G0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*/.match(source, index))
|
|
1244
1381
|
return(
|
|
1245
1382
|
{
|
|
1246
1383
|
raw: {
|
|
@@ -1253,7 +1390,7 @@ class Code
|
|
|
1253
1390
|
)
|
|
1254
1391
|
end
|
|
1255
1392
|
|
|
1256
|
-
if (match = /\
|
|
1393
|
+
if (match = /\G0[oO][0-7](?:_?[0-7])*/.match(source, index))
|
|
1257
1394
|
return(
|
|
1258
1395
|
{
|
|
1259
1396
|
raw: {
|
|
@@ -1266,7 +1403,7 @@ class Code
|
|
|
1266
1403
|
)
|
|
1267
1404
|
end
|
|
1268
1405
|
|
|
1269
|
-
if (match = /\
|
|
1406
|
+
if (match = /\G0[bB][01](?:_?[01])*/.match(source, index))
|
|
1270
1407
|
return(
|
|
1271
1408
|
{
|
|
1272
1409
|
raw: {
|
|
@@ -1281,8 +1418,9 @@ class Code
|
|
|
1281
1418
|
|
|
1282
1419
|
if (
|
|
1283
1420
|
match =
|
|
1284
|
-
/\
|
|
1285
|
-
|
|
1421
|
+
/\G[0-9](?:_?[0-9])*\.[0-9](?:_?[0-9])*(?:[eE][0-9](?:_?[0-9])*(?:\.[0-9](?:_?[0-9])*)?)?/.match(
|
|
1422
|
+
source,
|
|
1423
|
+
index
|
|
1286
1424
|
)
|
|
1287
1425
|
)
|
|
1288
1426
|
decimal, exponent = match[0].split(/[eE]/, 2)
|
|
@@ -1295,8 +1433,9 @@ class Code
|
|
|
1295
1433
|
|
|
1296
1434
|
if (
|
|
1297
1435
|
match =
|
|
1298
|
-
/\
|
|
1299
|
-
|
|
1436
|
+
/\G[0-9](?:_?[0-9])*(?:[eE][0-9](?:_?[0-9])*(?:\.[0-9](?:_?[0-9])*)?)?/.match(
|
|
1437
|
+
source,
|
|
1438
|
+
index
|
|
1300
1439
|
)
|
|
1301
1440
|
)
|
|
1302
1441
|
whole, exponent = match[0].split(/[eE]/, 2)
|
data/lib/code-ruby.rb
CHANGED
|
@@ -8,10 +8,13 @@ require "date"
|
|
|
8
8
|
require "did_you_mean"
|
|
9
9
|
require "digest"
|
|
10
10
|
require "icalendar"
|
|
11
|
+
require "ipaddr"
|
|
11
12
|
require "json"
|
|
12
13
|
require "mail"
|
|
13
14
|
require "net/http"
|
|
14
15
|
require "nokogiri"
|
|
16
|
+
require "openssl"
|
|
17
|
+
require "resolv"
|
|
15
18
|
require "stringio"
|
|
16
19
|
require "timeout"
|
|
17
20
|
require "uri"
|
data/lib/code.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Code
|
|
4
|
-
GLOBALS = %i[context error input object output source].freeze
|
|
5
|
-
DEFAULT_TIMEOUT =
|
|
4
|
+
GLOBALS = %i[context error input object output root_object source].freeze
|
|
5
|
+
DEFAULT_TIMEOUT = 1.hour.to_f
|
|
6
|
+
MAX_INPUT_BYTES = 10.megabytes
|
|
6
7
|
LOCALES = %w[en fr].freeze
|
|
7
8
|
|
|
8
9
|
def initialize(
|
|
@@ -20,13 +21,18 @@ class Code
|
|
|
20
21
|
@object = object
|
|
21
22
|
@output = output
|
|
22
23
|
@source = source
|
|
23
|
-
@timeout = timeout
|
|
24
|
+
@timeout = self.class.normalize_timeout!(timeout)
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def self.parse(source, timeout: DEFAULT_TIMEOUT)
|
|
28
|
+
timeout = normalize_timeout!(timeout)
|
|
29
|
+
|
|
30
|
+
ensure_input_size!(source, label: "source")
|
|
27
31
|
Timeout.timeout(timeout) { Parser.parse(source).to_raw }
|
|
28
32
|
rescue Timeout::Error
|
|
29
33
|
raise Error, "timeout"
|
|
34
|
+
rescue SystemStackError
|
|
35
|
+
raise Error, "source is too deeply nested"
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
def self.evaluate(...)
|
|
@@ -44,7 +50,23 @@ class Code
|
|
|
44
50
|
Format.format(parse_tree)
|
|
45
51
|
end
|
|
46
52
|
|
|
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
|
+
def self.normalize_timeout!(timeout)
|
|
60
|
+
timeout = DEFAULT_TIMEOUT if timeout.nil?
|
|
61
|
+
timeout = timeout.to_f
|
|
62
|
+
raise Error, "timeout must be positive" unless timeout.positive?
|
|
63
|
+
|
|
64
|
+
timeout
|
|
65
|
+
end
|
|
66
|
+
|
|
47
67
|
def evaluate
|
|
68
|
+
time_zone = ::Time.zone
|
|
69
|
+
|
|
48
70
|
Timeout.timeout(timeout) do
|
|
49
71
|
Node::Code.new(Code.parse(source)).evaluate(
|
|
50
72
|
context: context,
|
|
@@ -53,6 +75,7 @@ class Code
|
|
|
53
75
|
input: input,
|
|
54
76
|
object: object,
|
|
55
77
|
output: output,
|
|
78
|
+
root_object: object,
|
|
56
79
|
source: source,
|
|
57
80
|
timeout: timeout
|
|
58
81
|
)
|
|
@@ -61,6 +84,10 @@ class Code
|
|
|
61
84
|
raise Error, "timeout"
|
|
62
85
|
rescue Interrupt
|
|
63
86
|
raise Error, "interrupt"
|
|
87
|
+
rescue SystemStackError
|
|
88
|
+
raise Error, "source is too deeply nested"
|
|
89
|
+
ensure
|
|
90
|
+
::Time.zone = time_zone
|
|
64
91
|
end
|
|
65
92
|
|
|
66
93
|
private
|