code-ruby 3.1.2 → 4.0.0

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