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/format.rb
CHANGED
|
@@ -214,6 +214,7 @@ class Code
|
|
|
214
214
|
literal = %("#{content}")
|
|
215
215
|
return literal if literal.length <= string_inline_limit(indent)
|
|
216
216
|
return literal unless allow_split
|
|
217
|
+
return literal if content.include?("\\")
|
|
217
218
|
|
|
218
219
|
split_string_literal(components, indent: indent)
|
|
219
220
|
end
|
|
@@ -331,7 +332,7 @@ class Code
|
|
|
331
332
|
def format_dictionary_statement_code(statement_code)
|
|
332
333
|
key =
|
|
333
334
|
format_dictionary_statement_key(statement_code[:statement]) ||
|
|
334
|
-
|
|
335
|
+
format_nested_statement(statement_code[:statement], indent: 0)
|
|
335
336
|
return key unless statement_code.key?(:code)
|
|
336
337
|
|
|
337
338
|
value = format_code_inline(statement_code[:code], indent: 0)
|
|
@@ -380,6 +381,14 @@ class Code
|
|
|
380
381
|
return format_code_inline(Array(argument), indent: 0)
|
|
381
382
|
end
|
|
382
383
|
|
|
384
|
+
if argument.key?(:operator)
|
|
385
|
+
return argument[:operator].to_s unless argument.key?(:value)
|
|
386
|
+
|
|
387
|
+
value = format_code_inline(argument[:value], indent: 0)
|
|
388
|
+
|
|
389
|
+
return "#{argument[:operator]}#{value}"
|
|
390
|
+
end
|
|
391
|
+
|
|
383
392
|
value = format_code_inline(argument[:value], indent: 0)
|
|
384
393
|
return value unless argument.key?(:name)
|
|
385
394
|
|
|
@@ -423,6 +432,8 @@ class Code
|
|
|
423
432
|
parameter[:spread]
|
|
424
433
|
elsif parameter.key?(:block)
|
|
425
434
|
parameter[:block]
|
|
435
|
+
elsif parameter.key?(:blocks)
|
|
436
|
+
parameter[:blocks]
|
|
426
437
|
else
|
|
427
438
|
""
|
|
428
439
|
end
|
|
@@ -463,14 +474,17 @@ class Code
|
|
|
463
474
|
first_line, *rest = right.lines(chomp: true)
|
|
464
475
|
if multiline_operand_statement?(other[:statement]) ||
|
|
465
476
|
!BOOLEAN_WORD_OPERATORS.include?(operator)
|
|
466
|
-
["#{expression} #{operator} #{first_line.lstrip}", *rest].join(
|
|
477
|
+
["#{expression} #{operator} #{first_line.lstrip}", *rest].join(
|
|
478
|
+
"\n"
|
|
479
|
+
)
|
|
467
480
|
else
|
|
468
481
|
[
|
|
469
482
|
"#{expression}\n#{INDENT * (indent + 1)}#{operator} #{first_line.lstrip}",
|
|
470
483
|
*rest
|
|
471
484
|
].join("\n")
|
|
472
485
|
end
|
|
473
|
-
elsif expression.include?("\n") ||
|
|
486
|
+
elsif expression.include?("\n") ||
|
|
487
|
+
candidate.length > MAX_LINE_LENGTH
|
|
474
488
|
right_lines =
|
|
475
489
|
if right.include?("\n")
|
|
476
490
|
right.lines(chomp: true).map(&:lstrip)
|
|
@@ -507,10 +521,9 @@ class Code
|
|
|
507
521
|
return false if expression.lstrip.start_with?("(")
|
|
508
522
|
|
|
509
523
|
continuation_lines =
|
|
510
|
-
expression
|
|
511
|
-
.
|
|
512
|
-
|
|
513
|
-
.reject { |line| line.strip.empty? || line.strip.match?(/\A[\])}]+\z/) }
|
|
524
|
+
expression.lines(chomp: true)[1..].to_a.reject do |line|
|
|
525
|
+
line.strip.empty? || line.strip.match?(/\A[\])}]+\z/)
|
|
526
|
+
end
|
|
514
527
|
|
|
515
528
|
return false if continuation_lines.empty?
|
|
516
529
|
|
|
@@ -564,10 +577,7 @@ class Code
|
|
|
564
577
|
first_line = first_line.lstrip
|
|
565
578
|
return "#{left} #{operator} #{first_line}" if rest.empty?
|
|
566
579
|
|
|
567
|
-
return [
|
|
568
|
-
"#{left} #{operator} #{first_line}",
|
|
569
|
-
*rest
|
|
570
|
-
].join("\n")
|
|
580
|
+
return ["#{left} #{operator} #{first_line}", *rest].join("\n")
|
|
571
581
|
end
|
|
572
582
|
|
|
573
583
|
"#{left} #{operator} #{right}"
|
|
@@ -580,7 +590,9 @@ class Code
|
|
|
580
590
|
if nested_operator == "="
|
|
581
591
|
left = format_nested_statement(nested[:left], indent: indent)
|
|
582
592
|
right = format_nested_statement(nested[:right], indent: indent)
|
|
583
|
-
return
|
|
593
|
+
return(
|
|
594
|
+
"#{left} #{nested_operator} #{group_multiline_expression(right, indent: indent)}"
|
|
595
|
+
)
|
|
584
596
|
end
|
|
585
597
|
end
|
|
586
598
|
|
|
@@ -710,7 +722,8 @@ class Code
|
|
|
710
722
|
Array(while_statement[:parameters]).map do |parameter|
|
|
711
723
|
format_parameter(parameter, indent: indent)
|
|
712
724
|
end
|
|
713
|
-
header =
|
|
725
|
+
header =
|
|
726
|
+
parameters.empty? ? "loop {" : "loop { |#{parameters.join(", ")}|"
|
|
714
727
|
|
|
715
728
|
return "#{INDENT * indent}#{header}\n#{body}\n#{INDENT * indent}}"
|
|
716
729
|
end
|
|
@@ -743,7 +756,9 @@ class Code
|
|
|
743
756
|
others = Array(operation[:others])
|
|
744
757
|
|
|
745
758
|
return false if others.empty?
|
|
746
|
-
|
|
759
|
+
unless others.all? { |other| compact_operator?(other[:operator]) }
|
|
760
|
+
return false
|
|
761
|
+
end
|
|
747
762
|
|
|
748
763
|
return multiline_operand_statement?(operation[:first])
|
|
749
764
|
end
|
|
@@ -771,7 +786,10 @@ class Code
|
|
|
771
786
|
|
|
772
787
|
def indent_lines(value, indent)
|
|
773
788
|
prefix = INDENT * indent
|
|
774
|
-
value
|
|
789
|
+
value
|
|
790
|
+
.split("\n")
|
|
791
|
+
.map { |line| line.empty? ? "" : "#{prefix}#{line}" }
|
|
792
|
+
.join("\n")
|
|
775
793
|
end
|
|
776
794
|
|
|
777
795
|
def statement_separator(inline:, indent: nil)
|
data/lib/code/network.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Code
|
|
4
|
+
module Network
|
|
5
|
+
BLOCKED_HOSTS = %w[localhost localhost.localdomain].freeze
|
|
6
|
+
BLOCKED_HOST_SUFFIXES = %w[.local .localhost].freeze
|
|
7
|
+
BLOCKED_IP_RANGES =
|
|
8
|
+
%w[
|
|
9
|
+
0.0.0.0/8
|
|
10
|
+
10.0.0.0/8
|
|
11
|
+
100.64.0.0/10
|
|
12
|
+
127.0.0.0/8
|
|
13
|
+
169.254.0.0/16
|
|
14
|
+
172.16.0.0/12
|
|
15
|
+
192.0.0.0/24
|
|
16
|
+
192.168.0.0/16
|
|
17
|
+
224.0.0.0/4
|
|
18
|
+
240.0.0.0/4
|
|
19
|
+
::/128
|
|
20
|
+
::1/128
|
|
21
|
+
64:ff9b::/96
|
|
22
|
+
64:ff9b:1::/48
|
|
23
|
+
2002::/16
|
|
24
|
+
fc00::/7
|
|
25
|
+
fe80::/10
|
|
26
|
+
ff00::/8
|
|
27
|
+
].map { |range| IPAddr.new(range) }.freeze
|
|
28
|
+
|
|
29
|
+
def self.validate_public_uri!(uri, service:)
|
|
30
|
+
unless %w[http https].include?(uri.scheme)
|
|
31
|
+
raise Error, "#{service}: unsupported url scheme"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
validate_public_host!(uri.hostname, service: service).first
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.validate_public_host!(host, service:)
|
|
38
|
+
normalized_host = normalize_host(host)
|
|
39
|
+
|
|
40
|
+
if blocked_hostname?(normalized_host)
|
|
41
|
+
raise Error, "#{service}: local network requests are not allowed"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
addresses = resolved_addresses(normalized_host)
|
|
45
|
+
raise Error, "#{service}: could not resolve host" if addresses.empty?
|
|
46
|
+
|
|
47
|
+
if addresses.any? { |address| blocked_address?(address) }
|
|
48
|
+
raise Error, "#{service}: local network requests are not allowed"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
addresses
|
|
52
|
+
rescue Resolv::ResolvError, ArgumentError
|
|
53
|
+
raise Error, "#{service}: could not resolve host"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.blocked_address?(address)
|
|
57
|
+
ip = IPAddr.new(address)
|
|
58
|
+
ip = ip.native if ip.respond_to?(:ipv4_mapped?) && ip.ipv4_mapped?
|
|
59
|
+
ip = ip.native if ip.respond_to?(:ipv4_compat?) && ip.ipv4_compat?
|
|
60
|
+
|
|
61
|
+
BLOCKED_IP_RANGES.any? { |range| range.include?(ip) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.resolved_addresses(host)
|
|
65
|
+
[IPAddr.new(host).to_s]
|
|
66
|
+
rescue ArgumentError
|
|
67
|
+
Resolv.getaddresses(host)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.normalize_host(host)
|
|
71
|
+
normalized_host = host.to_s.downcase.delete_suffix(".")
|
|
72
|
+
raise Error, "network: invalid host" if normalized_host.blank?
|
|
73
|
+
|
|
74
|
+
normalized_host
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.blocked_hostname?(host)
|
|
78
|
+
BLOCKED_HOSTS.include?(host) ||
|
|
79
|
+
BLOCKED_HOST_SUFFIXES.any? { |suffix| host.end_with?(suffix) }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/code/node/call.rb
CHANGED
|
@@ -36,7 +36,9 @@ class Code
|
|
|
36
36
|
arguments = []
|
|
37
37
|
|
|
38
38
|
(@arguments || []).each do |argument|
|
|
39
|
-
if argument.
|
|
39
|
+
if argument.expansion?
|
|
40
|
+
append_expanded_argument(arguments, argument, **args)
|
|
41
|
+
elsif argument.keyword?
|
|
40
42
|
if arguments.last.is_a?(Object::Dictionary)
|
|
41
43
|
arguments.last.code_merge!(argument.evaluate(**args))
|
|
42
44
|
else
|
|
@@ -51,7 +53,18 @@ class Code
|
|
|
51
53
|
|
|
52
54
|
name = Object::String.new(@name)
|
|
53
55
|
|
|
54
|
-
args.fetch(:object)
|
|
56
|
+
object = args.fetch(:object)
|
|
57
|
+
dynamic_result =
|
|
58
|
+
object.code_dynamic_call(
|
|
59
|
+
name,
|
|
60
|
+
operator: name,
|
|
61
|
+
arguments: Object::List.new(arguments),
|
|
62
|
+
explicit_arguments: @explicit_arguments,
|
|
63
|
+
**args
|
|
64
|
+
)
|
|
65
|
+
return dynamic_result if dynamic_result
|
|
66
|
+
|
|
67
|
+
object.call(
|
|
55
68
|
operator: name,
|
|
56
69
|
arguments: Object::List.new(arguments),
|
|
57
70
|
explicit_arguments: @explicit_arguments,
|
|
@@ -62,6 +75,71 @@ class Code
|
|
|
62
75
|
def resolve(**_args)
|
|
63
76
|
Object::String.new(@name)
|
|
64
77
|
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def append_expanded_argument(arguments, argument, **args)
|
|
82
|
+
value =
|
|
83
|
+
if argument.value?
|
|
84
|
+
argument.evaluate(**args)
|
|
85
|
+
else
|
|
86
|
+
default_forwarded_argument(argument.operator, args.fetch(:context))
|
|
87
|
+
end
|
|
88
|
+
return if value.nil?
|
|
89
|
+
|
|
90
|
+
case argument.operator
|
|
91
|
+
when "*", "&&"
|
|
92
|
+
value.to_code.code_to_list.raw.each { |item| arguments << item }
|
|
93
|
+
when "**"
|
|
94
|
+
dictionary = value.to_code.code_to_dictionary
|
|
95
|
+
if arguments.last.is_a?(Object::Dictionary)
|
|
96
|
+
arguments.last.code_merge!(dictionary)
|
|
97
|
+
else
|
|
98
|
+
arguments << dictionary
|
|
99
|
+
end
|
|
100
|
+
when "&"
|
|
101
|
+
code_value = value.to_code
|
|
102
|
+
arguments << if code_value.is_a?(Object::Function) ||
|
|
103
|
+
code_value.nothing?
|
|
104
|
+
code_value
|
|
105
|
+
else
|
|
106
|
+
code_value.call(operator: "&", **args)
|
|
107
|
+
end
|
|
108
|
+
when "..."
|
|
109
|
+
expanded_arguments(value, args.fetch(:context)).each do |item|
|
|
110
|
+
arguments << item
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def default_forwarded_argument(operator, context)
|
|
116
|
+
case operator
|
|
117
|
+
when "*"
|
|
118
|
+
context_fetch(context, "arguments") || Object::List.new
|
|
119
|
+
when "**"
|
|
120
|
+
context_fetch(context, "keyword_arguments")
|
|
121
|
+
when "&"
|
|
122
|
+
context_fetch(context, "block")
|
|
123
|
+
when "&&"
|
|
124
|
+
context_fetch(context, "blocks") || Object::List.new
|
|
125
|
+
when "..."
|
|
126
|
+
context_fetch(context, "rest") || Object::List.new
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def context_fetch(context, name)
|
|
131
|
+
context.code_lookup!(name).code_fetch(name)
|
|
132
|
+
rescue Error
|
|
133
|
+
nil
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def expanded_arguments(value, context)
|
|
137
|
+
if value.nothing?
|
|
138
|
+
context.code_lookup!("rest").code_fetch("rest").code_to_list.raw
|
|
139
|
+
else
|
|
140
|
+
value.to_code.code_to_list.raw
|
|
141
|
+
end
|
|
142
|
+
end
|
|
65
143
|
end
|
|
66
144
|
end
|
|
67
145
|
end
|
|
@@ -6,8 +6,10 @@ class Code
|
|
|
6
6
|
def initialize(parsed)
|
|
7
7
|
return if parsed.blank?
|
|
8
8
|
|
|
9
|
+
@has_value = parsed[:value].present?
|
|
9
10
|
@value = Node::Code.new(parsed.delete(:value).presence)
|
|
10
11
|
@name = parsed.delete(:name).presence
|
|
12
|
+
@operator = parsed.delete(:operator).presence
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def evaluate(**args)
|
|
@@ -20,10 +22,22 @@ class Code
|
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
def operator
|
|
26
|
+
@operator.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def expansion?
|
|
30
|
+
operator.present?
|
|
31
|
+
end
|
|
32
|
+
|
|
23
33
|
def keyword?
|
|
24
34
|
!!@name
|
|
25
35
|
end
|
|
26
36
|
|
|
37
|
+
def value?
|
|
38
|
+
!!@has_value
|
|
39
|
+
end
|
|
40
|
+
|
|
27
41
|
def regular?
|
|
28
42
|
!keyword?
|
|
29
43
|
end
|
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
|
+
root_object = args.fetch(:root_object, args.fetch(:object))
|
|
24
25
|
|
|
25
26
|
begin
|
|
26
27
|
(@statements || []).each do |statement|
|
|
27
|
-
last =
|
|
28
|
-
statement.evaluate(**statement_args, object: Object::Global.new)
|
|
28
|
+
last = statement.evaluate(**statement_args, object: root_object)
|
|
29
29
|
end
|
|
30
30
|
rescue Error::Retry
|
|
31
31
|
retry if control_flow_scope == :group
|
|
@@ -48,9 +48,10 @@ class Code
|
|
|
48
48
|
|
|
49
49
|
def resolve(**args)
|
|
50
50
|
last = Object::Nothing.new
|
|
51
|
+
root_object = args.fetch(:root_object, args.fetch(:object))
|
|
51
52
|
|
|
52
53
|
(@statements || []).each do |statement|
|
|
53
|
-
last = statement.resolve(**args, object:
|
|
54
|
+
last = statement.resolve(**args, object: root_object)
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
last
|
|
@@ -8,15 +8,13 @@ class Code
|
|
|
8
8
|
def initialize(parsed)
|
|
9
9
|
return if parsed.blank?
|
|
10
10
|
|
|
11
|
-
@name =
|
|
12
|
-
parsed.delete(:name).presence || parsed[:regular_splat].presence ||
|
|
13
|
-
parsed[:keyword_splat].presence || parsed[:spread].presence ||
|
|
14
|
-
parsed[:block].presence
|
|
11
|
+
@name = parsed.delete(:name).presence
|
|
15
12
|
@keyword = parsed.delete(:keyword).present?
|
|
16
13
|
@regular_splat = parsed.delete(:regular_splat).present?
|
|
17
14
|
@keyword_splat = parsed.delete(:keyword_splat).present?
|
|
18
15
|
@spread = parsed.delete(:spread).present?
|
|
19
16
|
@block = parsed.delete(:block).present?
|
|
17
|
+
@blocks = parsed.delete(:blocks).present?
|
|
20
18
|
@default = Code.new(parsed.delete(:default)) if parsed.key?(:default)
|
|
21
19
|
end
|
|
22
20
|
|
|
@@ -48,6 +46,10 @@ class Code
|
|
|
48
46
|
!!@block
|
|
49
47
|
end
|
|
50
48
|
|
|
49
|
+
def blocks?
|
|
50
|
+
!!@blocks
|
|
51
|
+
end
|
|
52
|
+
|
|
51
53
|
def to_h
|
|
52
54
|
{
|
|
53
55
|
name: name,
|
|
@@ -57,6 +59,7 @@ class Code
|
|
|
57
59
|
keyword_splat?: keyword_splat?,
|
|
58
60
|
spread?: spread?,
|
|
59
61
|
block?: block?,
|
|
62
|
+
blocks?: blocks?,
|
|
60
63
|
default: default
|
|
61
64
|
}
|
|
62
65
|
end
|
data/lib/code/node/list.rb
CHANGED
|
@@ -11,10 +11,40 @@ class Code
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def evaluate(**args)
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
list =
|
|
15
|
+
::Code::Object::List.new(
|
|
16
|
+
(@elements || []).map { |element| element.evaluate(**args) }
|
|
17
|
+
)
|
|
18
|
+
constructor = literal_constructor(args.fetch(:context), "List")
|
|
19
|
+
return list unless constructor
|
|
20
|
+
if Array(args[:constructing_literal_classes]).include?(
|
|
21
|
+
::Code::Object::List
|
|
22
|
+
)
|
|
23
|
+
return list
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
constructor.call(
|
|
27
|
+
**args,
|
|
28
|
+
constructing_literal_classes:
|
|
29
|
+
Array(args[:constructing_literal_classes]) + [::Code::Object::List],
|
|
30
|
+
operator: nil,
|
|
31
|
+
arguments: ::Code::Object::List.new([list]),
|
|
32
|
+
explicit_arguments: true
|
|
16
33
|
)
|
|
17
34
|
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def literal_constructor(context, name)
|
|
39
|
+
return unless context.code_has_key?(name).truthy?
|
|
40
|
+
|
|
41
|
+
constructor = context.code_fetch(name)
|
|
42
|
+
return unless constructor.is_a?(::Code::Object::Function)
|
|
43
|
+
return unless constructor.parent.is_a?(::Code::Object::Class)
|
|
44
|
+
return unless constructor.parent.raw == ::Code::Object::List
|
|
45
|
+
|
|
46
|
+
constructor
|
|
47
|
+
end
|
|
18
48
|
end
|
|
19
49
|
end
|
|
20
50
|
end
|
|
@@ -13,7 +13,8 @@ class Code
|
|
|
13
13
|
|
|
14
14
|
def evaluate(**args)
|
|
15
15
|
left = @left&.evaluate(**args) || Object::Nothing.new
|
|
16
|
-
index_args =
|
|
16
|
+
index_args =
|
|
17
|
+
args.merge(object: args.fetch(:previous_object, args.fetch(:object)))
|
|
17
18
|
|
|
18
19
|
(@statements || []).reduce(left) do |object, statement|
|
|
19
20
|
object.code_fetch(statement.evaluate(**index_args))
|
|
@@ -30,7 +31,8 @@ class Code
|
|
|
30
31
|
Object::IdentifierList.new([left])
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
index_args =
|
|
34
|
+
index_args =
|
|
35
|
+
args.merge(object: args.fetch(:previous_object, args.fetch(:object)))
|
|
34
36
|
|
|
35
37
|
(@statements || []).each do |statement|
|
|
36
38
|
list.code_append(statement.evaluate(**index_args))
|
data/lib/code/object/base_64.rb
CHANGED
|
@@ -3,28 +3,154 @@
|
|
|
3
3
|
class Code
|
|
4
4
|
class Object
|
|
5
5
|
class Base64 < Object
|
|
6
|
+
CLASS_DOCUMENTATION = {
|
|
7
|
+
name: "Base64",
|
|
8
|
+
description: "encodes and decodes strings with base64 text formats.",
|
|
9
|
+
examples: [
|
|
10
|
+
"Base64.encode(:hello)",
|
|
11
|
+
"Base64.decode(Base64.encode(:hello))",
|
|
12
|
+
"Base64.urlsafe_decode(Base64.urlsafe_encode(\"???\"))"
|
|
13
|
+
]
|
|
14
|
+
}.freeze
|
|
15
|
+
CLASS_FUNCTIONS = {
|
|
16
|
+
"encode" => {
|
|
17
|
+
name: "encode",
|
|
18
|
+
description: "returns base64 text with line breaks for a string.",
|
|
19
|
+
examples: [
|
|
20
|
+
"Base64.encode(:hello)",
|
|
21
|
+
"Base64.encode(:hello_world)",
|
|
22
|
+
"Base64.encode(\"123\")"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"encode_64" => {
|
|
26
|
+
name: "encode_64",
|
|
27
|
+
description: "returns base64 text with line breaks for a string.",
|
|
28
|
+
examples: [
|
|
29
|
+
"Base64.encode_64(:hello)",
|
|
30
|
+
"Base64.encode_64(:hello_world)",
|
|
31
|
+
"Base64.encode_64(\"123\")"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"decode" => {
|
|
35
|
+
name: "decode",
|
|
36
|
+
description: "returns a string decoded from base64 text.",
|
|
37
|
+
examples: [
|
|
38
|
+
"Base64.decode(\"aGVsbG8=\")",
|
|
39
|
+
"Base64.decode(Base64.encode(:hello))",
|
|
40
|
+
"Base64.decode(\"MTIz\")"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"decode_64" => {
|
|
44
|
+
name: "decode_64",
|
|
45
|
+
description: "returns a string decoded from base64 text.",
|
|
46
|
+
examples: [
|
|
47
|
+
"Base64.decode_64(\"aGVsbG8=\")",
|
|
48
|
+
"Base64.decode_64(Base64.encode(:hello))",
|
|
49
|
+
"Base64.decode_64(\"MTIz\")"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"strict_encode" => {
|
|
53
|
+
name: "strict_encode",
|
|
54
|
+
description: "returns base64 text without line breaks for a string.",
|
|
55
|
+
examples: [
|
|
56
|
+
"Base64.strict_encode(:hello)",
|
|
57
|
+
"Base64.strict_encode(:hello_world)",
|
|
58
|
+
"Base64.strict_encode(\"123\")"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"strict_encode_64" => {
|
|
62
|
+
name: "strict_encode_64",
|
|
63
|
+
description: "returns base64 text without line breaks for a string.",
|
|
64
|
+
examples: [
|
|
65
|
+
"Base64.strict_encode_64(:hello)",
|
|
66
|
+
"Base64.strict_encode_64(:hello_world)",
|
|
67
|
+
"Base64.strict_encode_64(\"123\")"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"strict_decode" => {
|
|
71
|
+
name: "strict_decode",
|
|
72
|
+
description: "returns a string decoded from strict base64 text.",
|
|
73
|
+
examples: [
|
|
74
|
+
"Base64.strict_decode(\"aGVsbG8=\")",
|
|
75
|
+
"Base64.strict_decode(Base64.strict_encode(:hello))",
|
|
76
|
+
"Base64.strict_decode(\"MTIz\")"
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
"strict_decode_64" => {
|
|
80
|
+
name: "strict_decode_64",
|
|
81
|
+
description: "returns a string decoded from strict base64 text.",
|
|
82
|
+
examples: [
|
|
83
|
+
"Base64.strict_decode_64(\"aGVsbG8=\")",
|
|
84
|
+
"Base64.strict_decode_64(Base64.strict_encode(:hello))",
|
|
85
|
+
"Base64.strict_decode_64(\"MTIz\")"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"urlsafe_encode" => {
|
|
89
|
+
name: "urlsafe_encode",
|
|
90
|
+
description: "returns url-safe base64 text for a string.",
|
|
91
|
+
examples: [
|
|
92
|
+
"Base64.urlsafe_encode(:hello)",
|
|
93
|
+
"Base64.urlsafe_encode(\">>>\")",
|
|
94
|
+
"Base64.urlsafe_encode(\"???\")"
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
"url_safe_encode_64" => {
|
|
98
|
+
name: "url_safe_encode_64",
|
|
99
|
+
description: "returns url-safe base64 text for a string.",
|
|
100
|
+
examples: [
|
|
101
|
+
"Base64.url_safe_encode_64(:hello)",
|
|
102
|
+
"Base64.url_safe_encode_64(\">>>\")",
|
|
103
|
+
"Base64.url_safe_encode_64(\"???\")"
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
"urlsafe_decode" => {
|
|
107
|
+
name: "urlsafe_decode",
|
|
108
|
+
description: "returns a string decoded from url-safe base64 text.",
|
|
109
|
+
examples: [
|
|
110
|
+
"Base64.urlsafe_decode(\"aGVsbG8=\")",
|
|
111
|
+
"Base64.urlsafe_decode(\"Pj4-\")",
|
|
112
|
+
"Base64.urlsafe_decode(\"Pz8_\")"
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
"url_safe_decode_64" => {
|
|
116
|
+
name: "url_safe_decode_64",
|
|
117
|
+
description: "returns a string decoded from url-safe base64 text.",
|
|
118
|
+
examples: [
|
|
119
|
+
"Base64.url_safe_decode_64(\"aGVsbG8=\")",
|
|
120
|
+
"Base64.url_safe_decode_64(\"Pj4-\")",
|
|
121
|
+
"Base64.url_safe_decode_64(\"Pz8_\")"
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}.freeze
|
|
125
|
+
|
|
126
|
+
def self.function_documentation(scope)
|
|
127
|
+
return CLASS_FUNCTIONS if scope == :class
|
|
128
|
+
|
|
129
|
+
{}
|
|
130
|
+
end
|
|
131
|
+
|
|
6
132
|
def self.call(**args)
|
|
7
133
|
code_operator = args.fetch(:operator, nil).to_code
|
|
8
134
|
code_arguments = args.fetch(:arguments, []).to_code
|
|
9
135
|
code_value = code_arguments.code_first
|
|
10
136
|
|
|
11
137
|
case code_operator.to_s
|
|
12
|
-
when "encode"
|
|
138
|
+
when "encode", "encode_64"
|
|
13
139
|
sig(args) { String }
|
|
14
140
|
code_encode(code_value)
|
|
15
|
-
when "decode"
|
|
141
|
+
when "decode", "decode_64"
|
|
16
142
|
sig(args) { String }
|
|
17
143
|
code_decode(code_value)
|
|
18
|
-
when "strict_encode"
|
|
144
|
+
when "strict_encode", "strict_encode_64"
|
|
19
145
|
sig(args) { String }
|
|
20
146
|
code_strict_encode(code_value)
|
|
21
|
-
when "strict_decode"
|
|
147
|
+
when "strict_decode", "strict_decode_64"
|
|
22
148
|
sig(args) { String }
|
|
23
149
|
code_strict_decode(code_value)
|
|
24
|
-
when "urlsafe_encode"
|
|
150
|
+
when "urlsafe_encode", "url_safe_encode_64"
|
|
25
151
|
sig(args) { String }
|
|
26
152
|
code_urlsafe_encode(code_value)
|
|
27
|
-
when "urlsafe_decode"
|
|
153
|
+
when "urlsafe_decode", "url_safe_decode_64"
|
|
28
154
|
sig(args) { String }
|
|
29
155
|
code_urlsafe_decode(code_value)
|
|
30
156
|
else
|