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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/code +100 -20
  4. data/lib/code/concerns/shared.rb +335 -15
  5. data/lib/code/format.rb +33 -15
  6. data/lib/code/network.rb +82 -0
  7. data/lib/code/node/call.rb +80 -2
  8. data/lib/code/node/call_argument.rb +14 -0
  9. data/lib/code/node/code.rb +4 -3
  10. data/lib/code/node/function_parameter.rb +7 -4
  11. data/lib/code/node/list.rb +32 -2
  12. data/lib/code/node/square_bracket.rb +4 -2
  13. data/lib/code/object/base_64.rb +132 -6
  14. data/lib/code/object/boolean.rb +56 -0
  15. data/lib/code/object/class.rb +143 -2
  16. data/lib/code/object/code.rb +108 -7
  17. data/lib/code/object/context.rb +59 -1
  18. data/lib/code/object/cryptography.rb +69 -0
  19. data/lib/code/object/date.rb +13800 -462
  20. data/lib/code/object/decimal.rb +1098 -0
  21. data/lib/code/object/dictionary.rb +1861 -11
  22. data/lib/code/object/duration.rb +24 -0
  23. data/lib/code/object/function.rb +289 -27
  24. data/lib/code/object/global.rb +447 -1
  25. data/lib/code/object/html.rb +181 -7
  26. data/lib/code/object/http.rb +253 -17
  27. data/lib/code/object/ics.rb +76 -13
  28. data/lib/code/object/identifier_list.rb +30 -10
  29. data/lib/code/object/integer.rb +1265 -2
  30. data/lib/code/object/json.rb +80 -1
  31. data/lib/code/object/list.rb +3371 -10
  32. data/lib/code/object/nothing.rb +53 -0
  33. data/lib/code/object/number.rb +120 -0
  34. data/lib/code/object/parameter.rb +149 -0
  35. data/lib/code/object/range.rb +530 -14
  36. data/lib/code/object/smtp.rb +103 -12
  37. data/lib/code/object/string.rb +968 -3
  38. data/lib/code/object/super.rb +11 -1
  39. data/lib/code/object/time.rb +13932 -498
  40. data/lib/code/object/url.rb +67 -0
  41. data/lib/code/object.rb +582 -0
  42. data/lib/code/parser.rb +194 -55
  43. data/lib/code-ruby.rb +3 -0
  44. data/lib/code.rb +30 -3
  45. metadata +135 -84
  46. data/.github/dependabot.yml +0 -15
  47. data/.github/workflows/ci.yml +0 -38
  48. data/.gitignore +0 -30
  49. data/.node-version +0 -1
  50. data/.npm-version +0 -1
  51. data/.prettierignore +0 -2
  52. data/.rspec +0 -1
  53. data/.rubocop.yml +0 -140
  54. data/.ruby-version +0 -1
  55. data/.tool-versions +0 -3
  56. data/AGENTS.md +0 -43
  57. data/Gemfile +0 -22
  58. data/Gemfile.lock +0 -292
  59. data/Rakefile +0 -5
  60. data/bin/bundle +0 -123
  61. data/bin/bundle-audit +0 -31
  62. data/bin/bundler-audit +0 -31
  63. data/bin/dorian +0 -31
  64. data/bin/rspec +0 -31
  65. data/bin/rubocop +0 -31
  66. data/bin/test +0 -5
  67. data/code-ruby.gemspec +0 -34
  68. data/docs/precedence.txt +0 -36
  69. data/package-lock.json +0 -14
  70. data/package.json +0 -7
  71. data/spec/bin/code_spec.rb +0 -48
  72. data/spec/code/format_spec.rb +0 -153
  73. data/spec/code/node/call_spec.rb +0 -11
  74. data/spec/code/object/boolean_spec.rb +0 -18
  75. data/spec/code/object/cryptography_spec.rb +0 -25
  76. data/spec/code/object/decimal_spec.rb +0 -50
  77. data/spec/code/object/dictionary_spec.rb +0 -98
  78. data/spec/code/object/function_spec.rb +0 -268
  79. data/spec/code/object/http_spec.rb +0 -33
  80. data/spec/code/object/ics_spec.rb +0 -50
  81. data/spec/code/object/integer_spec.rb +0 -42
  82. data/spec/code/object/list_spec.rb +0 -22
  83. data/spec/code/object/nothing_spec.rb +0 -14
  84. data/spec/code/object/range_spec.rb +0 -23
  85. data/spec/code/object/string_spec.rb +0 -26
  86. data/spec/code/parser/boolean_spec.rb +0 -11
  87. data/spec/code/parser/chained_call_spec.rb +0 -16
  88. data/spec/code/parser/dictionary_spec.rb +0 -18
  89. data/spec/code/parser/function_spec.rb +0 -16
  90. data/spec/code/parser/group_spec.rb +0 -11
  91. data/spec/code/parser/if_modifier_spec.rb +0 -18
  92. data/spec/code/parser/list_spec.rb +0 -17
  93. data/spec/code/parser/number_spec.rb +0 -11
  94. data/spec/code/parser/string_spec.rb +0 -20
  95. data/spec/code/parser_spec.rb +0 -52
  96. data/spec/code/type_spec.rb +0 -21
  97. data/spec/code_spec.rb +0 -717
  98. data/spec/spec_helper.rb +0 -21
  99. data/spec/zeitwerk/loader_spec.rb +0 -7
@@ -3,6 +3,62 @@
3
3
  class Code
4
4
  class Object
5
5
  class Boolean < ::Code::Object
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Boolean",
8
+ description: "represents true or false.",
9
+ examples: %w[Boolean Boolean.new(true) Boolean.new(false)]
10
+ }.freeze
11
+ INSTANCE_FUNCTIONS = {
12
+ "&" => {
13
+ name: "&",
14
+ description: "returns true when both booleans are true.",
15
+ examples: ["true & true", "true & false", "false & false"]
16
+ },
17
+ "bitwise_and" => {
18
+ name: "bitwise_and",
19
+ description: "returns true when both booleans are true.",
20
+ examples: %w[
21
+ true.bitwise_and(true)
22
+ true.bitwise_and(false)
23
+ false.bitwise_and(false)
24
+ ]
25
+ },
26
+ "|" => {
27
+ name: "|",
28
+ description: "returns true when either boolean is true.",
29
+ examples: ["true | false", "false | true", "false | false"]
30
+ },
31
+ "bitwise_or" => {
32
+ name: "bitwise_or",
33
+ description: "returns true when either boolean is true.",
34
+ examples: %w[
35
+ true.bitwise_or(false)
36
+ false.bitwise_or(true)
37
+ false.bitwise_or(false)
38
+ ]
39
+ },
40
+ "^" => {
41
+ name: "^",
42
+ description: "returns true when exactly one boolean is true.",
43
+ examples: ["true ^ false", "true ^ true", "false ^ false"]
44
+ },
45
+ "bitwise_xor" => {
46
+ name: "bitwise_xor",
47
+ description: "returns true when exactly one boolean is true.",
48
+ examples: %w[
49
+ true.bitwise_xor(false)
50
+ true.bitwise_xor(true)
51
+ false.bitwise_xor(false)
52
+ ]
53
+ }
54
+ }.freeze
55
+
56
+ def self.function_documentation(scope)
57
+ return INSTANCE_FUNCTIONS if scope == :instance
58
+
59
+ {}
60
+ end
61
+
6
62
  def initialize(*args, **_kargs, &_block)
7
63
  self.raw =
8
64
  (args.first.is_an?(Object) ? args.first.truthy? : !!args.first)
@@ -3,6 +3,79 @@
3
3
  class Code
4
4
  class Object
5
5
  class Class < Object
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Class",
8
+ description:
9
+ "wraps a value constructor and documents its class and instance functions.",
10
+ examples: %w[Class Class.new(String) Class.documentation.name]
11
+ }.freeze
12
+ INSTANCE_FUNCTIONS = {
13
+ "documentation" => {
14
+ name: "documentation",
15
+ description: "returns documentation for this class.",
16
+ examples: %w[
17
+ Class.documentation.description
18
+ List.documentation.name
19
+ Dictionary.documentation.name
20
+ ]
21
+ },
22
+ "functions" => {
23
+ name: "functions",
24
+ description:
25
+ "returns documented class functions available on this class.",
26
+ examples: %w[
27
+ Class.functions.keys.include?(:new)
28
+ List.functions.keys.include?(:new)
29
+ Dictionary.functions.keys.include?(:from_entries)
30
+ ]
31
+ },
32
+ "instance_functions" => {
33
+ name: "instance_functions",
34
+ description:
35
+ "returns documented functions available on values built by this class.",
36
+ examples: %w[
37
+ Class.instance_functions.keys.include?(:documentation)
38
+ List.instance_functions.keys.include?(:map)
39
+ String.instance_functions.keys.include?(:upcase)
40
+ ]
41
+ },
42
+ "class_functions" => {
43
+ name: "class_functions",
44
+ description:
45
+ "returns documented class functions available on this class.",
46
+ examples: %w[
47
+ Class.class_functions.keys.include?(:new)
48
+ List.class_functions.keys.include?(:new)
49
+ Dictionary.class_functions.keys.include?(:from_entries)
50
+ ]
51
+ },
52
+ "call" => {
53
+ name: "call",
54
+ description: "returns a new value by calling this class constructor.",
55
+ examples: [
56
+ "Class.call(String)",
57
+ "List.call([1, 2])",
58
+ "String.call(:hello)"
59
+ ]
60
+ },
61
+ "extend" => {
62
+ name: "extend",
63
+ description:
64
+ "returns a function that builds a value from this class before running the body.",
65
+ examples: [
66
+ "Widget = Dictionary.extend(() => { self.name = :widget self }) Widget().fetch(:name)",
67
+ "Person = Dictionary.extend((name) => { self.name = name self }) Person(:Ada).fetch(:name)",
68
+ "Counter = Dictionary.extend(() => { self.count = 0 self }) Counter().fetch(:count)"
69
+ ]
70
+ }
71
+ }.freeze
72
+
73
+ def self.function_documentation(scope)
74
+ return INSTANCE_FUNCTIONS if scope == :instance
75
+
76
+ {}
77
+ end
78
+
6
79
  def initialize(*args, **_kargs, &_block)
7
80
  self.raw =
8
81
  if args.first.is_a?(Class)
@@ -16,13 +89,81 @@ class Code
16
89
  end
17
90
  end
18
91
 
19
- def call(...)
20
- raw.call(...)
92
+ def call(**args)
93
+ code_operator = args.fetch(:operator, nil).to_code
94
+ dynamic_result = code_dynamic_call(code_operator, **args)
95
+ return dynamic_result if dynamic_result
96
+
97
+ case code_operator.to_s
98
+ when "documentation"
99
+ sig(args)
100
+ code_documentation
101
+ when "functions"
102
+ sig(args)
103
+ code_functions
104
+ when "instance_functions"
105
+ sig(args)
106
+ code_instance_functions
107
+ when "class_functions"
108
+ sig(args)
109
+ code_class_functions
110
+ when "extend"
111
+ sig(args) { Function }
112
+ code_extend(args.fetch(:arguments).code_first)
113
+ when "call"
114
+ sig(args) { Object.repeat }
115
+ code_call(*args.fetch(:arguments, []).to_code.raw)
116
+ when /=$/
117
+ if raw_class_functions.code_has_key?(code_operator).truthy?
118
+ raw.call(**args)
119
+ else
120
+ super
121
+ end
122
+ else
123
+ raw.call(**args)
124
+ end
125
+ end
126
+
127
+ def code_call(*arguments, **_globals)
128
+ raw.code_new(*arguments)
129
+ end
130
+
131
+ def code_extend(function)
132
+ code_function = function.to_code
133
+
134
+ Function.new(
135
+ code_function.code_parameters,
136
+ code_function.code_body.raw,
137
+ code_function.definition_context,
138
+ parent: self
139
+ )
140
+ end
141
+
142
+ def code_functions
143
+ code_class_functions
144
+ end
145
+
146
+ def code_instance_functions
147
+ Object.documented_functions_for(raw, :instance)
148
+ end
149
+
150
+ def code_class_functions
151
+ raw_class_functions
152
+ end
153
+
154
+ def code_documentation
155
+ Object.documentation_for(raw)
21
156
  end
22
157
 
23
158
  def code_to_string
24
159
  String.new(raw.name.to_s.split("::")[2..].join("::"))
25
160
  end
161
+
162
+ private
163
+
164
+ def raw_class_functions
165
+ Object.documented_functions_for(raw, :class)
166
+ end
26
167
  end
27
168
  end
28
169
  end
@@ -3,13 +3,72 @@
3
3
  class Code
4
4
  class Object
5
5
  class Code < Object
6
- def initialize(*args, **_kargs, &_block)
7
- self.raw =
8
- if args.first.is_a?(Node::Code)
9
- args.first
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Code",
8
+ description: "stores parsed source code for later evaluation.",
9
+ examples: [
10
+ "Code",
11
+ "Code.evaluate(\"1 + 2\")",
12
+ "Code.new(\"1 + 2\").evaluate"
13
+ ]
14
+ }.freeze
15
+ CLASS_FUNCTIONS = {
16
+ "evaluate" => {
17
+ name: "evaluate",
18
+ description: "evaluates source code and returns the result.",
19
+ examples: [
20
+ "Code.evaluate(\"1\")",
21
+ "Code.evaluate(\"1 + 2\")",
22
+ "Code.evaluate(\"[1, 2].size\")"
23
+ ]
24
+ }
25
+ }.freeze
26
+ INSTANCE_FUNCTIONS = {
27
+ "evaluate" => {
28
+ name: "evaluate",
29
+ description: "evaluates stored source code and returns the result.",
30
+ examples: [
31
+ "Code(\"1\").evaluate",
32
+ "Code(\"1 + 2\").evaluate",
33
+ "Code(\"[1, 2].size\").evaluate"
34
+ ]
35
+ }
36
+ }.freeze
37
+
38
+ def self.function_documentation(scope)
39
+ return INSTANCE_FUNCTIONS if scope == :instance
40
+ return CLASS_FUNCTIONS if scope == :class
41
+
42
+ {}
43
+ end
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)
10
59
  else
11
- Node::Code.new(::Code.parse(args.first.to_s))
60
+ sig(args)
61
+ code_result
12
62
  end
63
+ end
64
+ end
65
+
66
+ attr_reader :trusted
67
+
68
+ def initialize(*args, **_kargs, &_block)
69
+ @trusted = args.first.is_a?(Node::Code)
70
+ self.raw =
71
+ (trusted ? args.first : Node::Code.new(::Code.parse(args.first.to_s)))
13
72
  end
14
73
 
15
74
  def self.code_evaluate(*args, **globals)
@@ -18,8 +77,50 @@ class Code
18
77
  new(*code_args.raw).code_evaluate(**globals)
19
78
  end
20
79
 
21
- def code_evaluate(...)
22
- raw.evaluate(...)
80
+ def self.call(**args)
81
+ code_operator = args.fetch(:operator, nil).to_code
82
+ globals = multi_fetch(args, *GLOBALS)
83
+
84
+ case code_operator.to_s
85
+ when "evaluate"
86
+ sig(args) { Object }
87
+ code_evaluate(*args.fetch(:arguments, []).to_code.raw, **globals)
88
+ else
89
+ super
90
+ end
91
+ end
92
+
93
+ def call(**args)
94
+ code_operator = args.fetch(:operator, nil).to_code
95
+ globals = multi_fetch(args, *GLOBALS)
96
+
97
+ case code_operator.to_s
98
+ when "evaluate"
99
+ sig(args)
100
+ code_evaluate(**globals)
101
+ else
102
+ super
103
+ end
104
+ end
105
+
106
+ def code_evaluate(trusted_evaluation: false, **globals)
107
+ raw.evaluate(
108
+ **evaluation_globals(globals, trusted_evaluation: trusted_evaluation)
109
+ )
110
+ end
111
+
112
+ def code_deep_duplicate(_seen = {})
113
+ self.class.new(raw)
114
+ 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)
23
124
  end
24
125
  end
25
126
  end
@@ -3,13 +3,54 @@
3
3
  class Code
4
4
  class Object
5
5
  class Context < Dictionary
6
- attr_reader :parent
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Context",
8
+ description:
9
+ "stores scoped identifier values used while evaluating code.",
10
+ examples: [
11
+ "context",
12
+ "Context.new(a: 1)",
13
+ "Context.new({ a: 1 }, Context.new(b: 2)).lookup!(:b)"
14
+ ]
15
+ }.freeze
16
+ INSTANCE_FUNCTIONS = {
17
+ "lookup!" => {
18
+ name: "lookup!",
19
+ description:
20
+ "returns the context that defines an identifier or raises when it is missing.",
21
+ examples: [
22
+ "Context.new(a: 1).lookup!(:a)",
23
+ "Context.new({ a: 1 }, Context.new(b: 2)).lookup!(:b)",
24
+ "Context.new(a: 1).lookup!(:missing) rescue nothing"
25
+ ]
26
+ }
27
+ }.freeze
28
+
29
+ def self.function_documentation(scope)
30
+ return INSTANCE_FUNCTIONS if scope == :instance
31
+
32
+ {}
33
+ end
34
+
35
+ attr_accessor :parent
7
36
 
8
37
  def initialize(*args, **_kargs, &_block)
9
38
  super(args.first)
10
39
  @parent = args.second if args.second.is_a?(Dictionary)
11
40
  end
12
41
 
42
+ def call(**args)
43
+ code_operator = args.fetch(:operator, nil).to_code
44
+
45
+ case code_operator.to_s
46
+ when "lookup!"
47
+ sig(args) { Object }
48
+ code_lookup!(args.fetch(:arguments, []).to_code.code_first)
49
+ else
50
+ super
51
+ end
52
+ end
53
+
13
54
  def code_lookup!(identifier)
14
55
  code_identifier = identifier.to_code
15
56
 
@@ -26,6 +67,23 @@ class Code
26
67
  Context.new(raw.merge(other.raw), parent || other.parent)
27
68
  end
28
69
 
70
+ def code_deep_duplicate(seen = {})
71
+ seen.compare_by_identity unless seen.compare_by_identity?
72
+ return seen[self] if seen.key?(self)
73
+
74
+ duplicate = Context.new
75
+ seen[self] = duplicate
76
+
77
+ raw.each do |key, value|
78
+ duplicate.code_set(
79
+ key.code_deep_duplicate(seen),
80
+ value.code_deep_duplicate(seen)
81
+ )
82
+ end
83
+ duplicate.parent = parent&.code_deep_duplicate(seen)
84
+ duplicate
85
+ end
86
+
29
87
  def parent?
30
88
  !!parent
31
89
  end
@@ -3,6 +3,75 @@
3
3
  class Code
4
4
  class Object
5
5
  class Cryptography < Object
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Cryptography",
8
+ description:
9
+ "computes md5, sha1, sha256, sha384, and sha512 message digests in hex, digest, or base64 format.",
10
+ examples: [
11
+ "Cryptography.md5(:hello)",
12
+ "Cryptography.sha256(:hello, format: :hex)",
13
+ "Cryptography.sha512(:hello, format: :base64)"
14
+ ]
15
+ }.freeze
16
+ CLASS_FUNCTIONS = {
17
+ "md5" => {
18
+ name: "md5",
19
+ description:
20
+ "returns an md5 message digest as hex text by default, with digest and base64 formats available.",
21
+ examples: [
22
+ "Cryptography.md5(:hello)",
23
+ "Cryptography.md5(:hello, format: :hex)",
24
+ "Cryptography.md5(:hello, format: :base64)"
25
+ ]
26
+ },
27
+ "sha1" => {
28
+ name: "sha1",
29
+ description:
30
+ "returns a sha1 message digest as hex text by default, with digest and base64 formats available.",
31
+ examples: [
32
+ "Cryptography.sha1(:hello)",
33
+ "Cryptography.sha1(:hello, format: :hex)",
34
+ "Cryptography.sha1(:hello, format: :base64)"
35
+ ]
36
+ },
37
+ "sha256" => {
38
+ name: "sha256",
39
+ description:
40
+ "returns a sha256 message digest as hex text by default, with digest and base64 formats available.",
41
+ examples: [
42
+ "Cryptography.sha256(:hello)",
43
+ "Cryptography.sha256(:hello, format: :hex)",
44
+ "Cryptography.sha256(:hello, format: :base64)"
45
+ ]
46
+ },
47
+ "sha384" => {
48
+ name: "sha384",
49
+ description:
50
+ "returns a sha384 message digest as hex text by default, with digest and base64 formats available.",
51
+ examples: [
52
+ "Cryptography.sha384(:hello)",
53
+ "Cryptography.sha384(:hello, format: :hex)",
54
+ "Cryptography.sha384(:hello, format: :base64)"
55
+ ]
56
+ },
57
+ "sha512" => {
58
+ name: "sha512",
59
+ description:
60
+ "returns a sha512 message digest as hex text by default, with digest and base64 formats available.",
61
+ examples: [
62
+ "Cryptography.sha512(:hello)",
63
+ "Cryptography.sha512(:hello, format: :hex)",
64
+ "Cryptography.sha512(:hello, format: :base64)"
65
+ ]
66
+ }
67
+ }.freeze
68
+
69
+ def self.function_documentation(scope)
70
+ return CLASS_FUNCTIONS if scope == :class
71
+
72
+ {}
73
+ end
74
+
6
75
  def self.call(**args)
7
76
  code_operator = args.fetch(:operator, nil).to_code
8
77
  code_arguments = args.fetch(:arguments, []).to_code