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,428 @@
3
3
  class Code
4
4
  class Object
5
5
  class Global < Object
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Global",
8
+ description:
9
+ "dispatches top-level functions, constructors, and control flow helpers.",
10
+ examples: [
11
+ "globals.keys.include?(:puts)",
12
+ "classes.keys.include?(:List)",
13
+ "evaluate(\"1 + 2\")"
14
+ ]
15
+ }.freeze
16
+ GETTING_STARTED_EXAMPLES = [
17
+ {
18
+ name: "expression",
19
+ description: "evaluate an expression and print the result.",
20
+ source: "1 + 2",
21
+ expected: "3",
22
+ command: "bin/code '1 + 2'"
23
+ },
24
+ {
25
+ name: "list",
26
+ description: "transform every item in a list with a block.",
27
+ source: "[1, 2, 3].map { |number| number * 2 }",
28
+ expected: "[2, 4, 6]",
29
+ command: "bin/code '[1, 2, 3].map { |number| number * 2 }'"
30
+ },
31
+ {
32
+ name: "dictionary",
33
+ description:
34
+ "store named values in a dictionary and read them by key.",
35
+ source: "user = { name: :Dorian, age: 31 } user.name",
36
+ expected: ":Dorian",
37
+ command: "bin/code 'user = { name: :Dorian, age: 31 } user.name'"
38
+ },
39
+ {
40
+ name: "function",
41
+ description: "define a function and call it with an argument.",
42
+ source: "double = (number) => { number * 2 } double(21)",
43
+ expected: "42",
44
+ command: "bin/code 'double = (number) => { number * 2 } double(21)'"
45
+ },
46
+ {
47
+ name: "documentation",
48
+ description: "query built-in documentation from the language.",
49
+ source: "globals.fetch(:puts).examples.first",
50
+ expected: "\"puts(:hello)\"",
51
+ command: "bin/code 'globals.fetch(:puts).examples.first'"
52
+ }
53
+ ].freeze
54
+ GETTING_STARTED = {
55
+ title: "Getting started with Code",
56
+ description: "a short guide with steps and runnable examples.",
57
+ steps: [
58
+ {
59
+ title: "Install dependencies",
60
+ action: "run bundle install from the repository root.",
61
+ command: "bundle install"
62
+ },
63
+ {
64
+ title: "Run one expression",
65
+ action: "pass Code source to bin/code and read the printed result.",
66
+ example: GETTING_STARTED_EXAMPLES.fetch(0)
67
+ },
68
+ {
69
+ title: "Format source",
70
+ action: "use -f before a snippet or file to print formatted Code.",
71
+ command: "bin/code -f '{a:1}'",
72
+ expected: "{ a: 1 }"
73
+ },
74
+ {
75
+ title: "Work with data",
76
+ action: "use lists, dictionaries, and functions together.",
77
+ examples: GETTING_STARTED_EXAMPLES[1, 3]
78
+ },
79
+ {
80
+ title: "Find built-in functions",
81
+ action:
82
+ "use globals, functions, instance_functions, and class_functions from inside Code.",
83
+ example: GETTING_STARTED_EXAMPLES.fetch(4)
84
+ }
85
+ ],
86
+ examples: GETTING_STARTED_EXAMPLES,
87
+ next_steps: [
88
+ "run bin/code 'getting_started'",
89
+ "run bin/code 'globals.keys'",
90
+ "run bin/code 'List.instance_functions.keys'"
91
+ ]
92
+ }.freeze
93
+ FUNCTIONS = {
94
+ "Base64" => {
95
+ name: "Base64",
96
+ description:
97
+ "returns the Base64 class when called without arguments, otherwise calls Base64.new.",
98
+ examples: %w[Base64 Base64.new Base64.new(nothing)]
99
+ },
100
+ "Boolean" => {
101
+ name: "Boolean",
102
+ description:
103
+ "returns the Boolean class when called without arguments, otherwise calls Boolean.new.",
104
+ examples: %w[Boolean Boolean.new Boolean.new(nothing)]
105
+ },
106
+ "Class" => {
107
+ name: "Class",
108
+ description:
109
+ "returns the Class class when called without arguments, otherwise calls Class.new.",
110
+ examples: %w[Class Class.new Class.new(nothing)]
111
+ },
112
+ "Code" => {
113
+ name: "Code",
114
+ description:
115
+ "returns the Code class when called without arguments, otherwise calls Code.new.",
116
+ examples: %w[Code Code.new Code.new(nothing)]
117
+ },
118
+ "Context" => {
119
+ name: "Context",
120
+ description:
121
+ "returns the Context class when called without arguments, otherwise calls Context.new.",
122
+ examples: %w[Context Context.new Context.new(nothing)]
123
+ },
124
+ "Cryptography" => {
125
+ name: "Cryptography",
126
+ description:
127
+ "returns the Cryptography class when called without arguments, otherwise calls Cryptography.new.",
128
+ examples: %w[Cryptography Cryptography.new Cryptography.new(nothing)]
129
+ },
130
+ "Date" => {
131
+ name: "Date",
132
+ description:
133
+ "returns the Date class when called without arguments, otherwise calls Date.new.",
134
+ examples: %w[Date Date.new Date.new(nothing)]
135
+ },
136
+ "Decimal" => {
137
+ name: "Decimal",
138
+ description:
139
+ "returns the Decimal class when called without arguments, otherwise calls Decimal.new.",
140
+ examples: %w[Decimal Decimal.new Decimal.new(nothing)]
141
+ },
142
+ "Dictionary" => {
143
+ name: "Dictionary",
144
+ description:
145
+ "returns the Dictionary class when called without arguments, otherwise calls Dictionary.new.",
146
+ examples: %w[Dictionary Dictionary.new Dictionary.new(nothing)]
147
+ },
148
+ "Duration" => {
149
+ name: "Duration",
150
+ description:
151
+ "returns the Duration class when called without arguments, otherwise calls Duration.new.",
152
+ examples: %w[Duration Duration.new Duration.new(nothing)]
153
+ },
154
+ "Function" => {
155
+ name: "Function",
156
+ description:
157
+ "returns the Function class when called without arguments, otherwise calls Function.new.",
158
+ examples: %w[Function Function.new Function.new(nothing)]
159
+ },
160
+ "Html" => {
161
+ name: "Html",
162
+ description:
163
+ "returns the Html class when called without arguments, otherwise calls Html.new.",
164
+ examples: %w[Html Html.new Html.new(nothing)]
165
+ },
166
+ "Http" => {
167
+ name: "Http",
168
+ description:
169
+ "returns the Http class when called without arguments, otherwise calls Http.new.",
170
+ examples: %w[Http Http.new Http.new(nothing)]
171
+ },
172
+ "Ics" => {
173
+ name: "Ics",
174
+ description:
175
+ "returns the Ics class when called without arguments, otherwise calls Ics.new.",
176
+ examples: %w[Ics Ics.new Ics.new(nothing)]
177
+ },
178
+ "IdentifierList" => {
179
+ name: "IdentifierList",
180
+ description:
181
+ "returns the IdentifierList class when called without arguments, otherwise calls IdentifierList.new.",
182
+ examples: %w[
183
+ IdentifierList
184
+ IdentifierList.new
185
+ IdentifierList.new(nothing)
186
+ ]
187
+ },
188
+ "Integer" => {
189
+ name: "Integer",
190
+ description:
191
+ "returns the Integer class when called without arguments, otherwise calls Integer.new.",
192
+ examples: %w[Integer Integer.new Integer.new(nothing)]
193
+ },
194
+ "Json" => {
195
+ name: "Json",
196
+ description:
197
+ "returns the Json class when called without arguments, otherwise calls Json.new.",
198
+ examples: %w[Json Json.new Json.new(nothing)]
199
+ },
200
+ "List" => {
201
+ name: "List",
202
+ description:
203
+ "returns the List class when called without arguments, otherwise calls List.new.",
204
+ examples: %w[List List.new List.new(nothing)]
205
+ },
206
+ "Nothing" => {
207
+ name: "Nothing",
208
+ description:
209
+ "returns the Nothing class when called without arguments, otherwise calls Nothing.new.",
210
+ examples: %w[Nothing Nothing.new Nothing.new(nothing)]
211
+ },
212
+ "Number" => {
213
+ name: "Number",
214
+ description:
215
+ "returns the Number class when called without arguments, otherwise calls Number.new.",
216
+ examples: %w[Number Number.new Number.new(nothing)]
217
+ },
218
+ "Object" => {
219
+ name: "Object",
220
+ description:
221
+ "returns the Object class when called without arguments, otherwise calls Object.new.",
222
+ examples: %w[Object Object.new Object.new(nothing)]
223
+ },
224
+ "Parameter" => {
225
+ name: "Parameter",
226
+ description:
227
+ "returns the Parameter class when called without arguments, otherwise calls Parameter.new.",
228
+ examples: %w[Parameter Parameter.new Parameter.new(nothing)]
229
+ },
230
+ "Range" => {
231
+ name: "Range",
232
+ description:
233
+ "returns the Range class when called without arguments, otherwise calls Range.new.",
234
+ examples: %w[Range Range.new Range.new(nothing)]
235
+ },
236
+ "Smtp" => {
237
+ name: "Smtp",
238
+ description:
239
+ "returns the Smtp class when called without arguments, otherwise calls Smtp.new.",
240
+ examples: %w[Smtp Smtp.new Smtp.new(nothing)]
241
+ },
242
+ "String" => {
243
+ name: "String",
244
+ description:
245
+ "returns the String class when called without arguments, otherwise calls String.new.",
246
+ examples: %w[String String.new String.new(nothing)]
247
+ },
248
+ "Time" => {
249
+ name: "Time",
250
+ description:
251
+ "returns the Time class when called without arguments, otherwise calls Time.new.",
252
+ examples: %w[Time Time.new Time.new(nothing)]
253
+ },
254
+ "Url" => {
255
+ name: "Url",
256
+ description:
257
+ "returns the Url class when called without arguments, otherwise calls Url.new.",
258
+ examples: %w[Url Url.new Url.new(nothing)]
259
+ },
260
+ "classes" => {
261
+ name: "classes",
262
+ description:
263
+ "returns documentation for built-in classes exposed by globals.",
264
+ examples: %w[
265
+ classes
266
+ classes.keys.include?(:List)
267
+ classes.fetch(:String).description
268
+ ]
269
+ },
270
+ "break" => {
271
+ name: "break",
272
+ description: "exits the current loop and returns an optional value.",
273
+ examples: [
274
+ "[1].each { break }",
275
+ "[1].each { break 1 }",
276
+ "while true { break :done }"
277
+ ]
278
+ },
279
+ "continue" => {
280
+ name: "continue",
281
+ description: "skips the rest of the current loop iteration.",
282
+ examples: [
283
+ "[1].each { continue }",
284
+ "[1, 2].each { |value| continue if value == 1 }",
285
+ "while false { continue }"
286
+ ]
287
+ },
288
+ "context" => {
289
+ name: "context",
290
+ description: "returns the current evaluation context dictionary.",
291
+ examples: %w[context context.keys context.has_key?(:context)]
292
+ },
293
+ "evaluate" => {
294
+ name: "evaluate",
295
+ description:
296
+ "evaluates a string as Code source and returns its result.",
297
+ examples: [
298
+ "evaluate(\"1 + 2\")",
299
+ "evaluate(\":hello\")",
300
+ "evaluate(\"[1, 2].size\")"
301
+ ]
302
+ },
303
+ "getting_started" => {
304
+ name: "getting_started",
305
+ description:
306
+ "returns a short guide with runnable examples and commands.",
307
+ examples: %w[
308
+ getting_started.title
309
+ getting_started.steps.first.command
310
+ getting_started.examples.map(&:name)
311
+ ]
312
+ },
313
+ "globals" => {
314
+ name: "globals",
315
+ description:
316
+ "returns documentation for top-level functions and constructors.",
317
+ examples: %w[
318
+ globals
319
+ globals.keys.include?(:List)
320
+ globals.fetch(:puts).description
321
+ ]
322
+ },
323
+ "next" => {
324
+ name: "next",
325
+ description:
326
+ "exits the current block iteration and returns an optional value.",
327
+ examples: [
328
+ "[1].map { next }",
329
+ "[1].map { next 2 }",
330
+ "[1, 2].map { next it if it > 1 }"
331
+ ]
332
+ },
333
+ "p" => {
334
+ name: "p",
335
+ description: "writes inspected values to output and returns nothing.",
336
+ examples: ["p(:hello)", "p(1, 2)", "p([1, 2])"]
337
+ },
338
+ "print" => {
339
+ name: "print",
340
+ description: "writes values to output without adding a newline.",
341
+ examples: ["print(:hello)", "print(1, 2)", "print(\"hello\")"]
342
+ },
343
+ "puts" => {
344
+ name: "puts",
345
+ description:
346
+ "writes values to output with newlines and returns nothing.",
347
+ examples: ["puts(:hello)", "puts(1, 2)", "puts(\"hello\")"]
348
+ },
349
+ "read" => {
350
+ name: "read",
351
+ description: "reads one line from input.",
352
+ examples: ["read", "name = read", "read.to_string"]
353
+ },
354
+ "redo" => {
355
+ name: "redo",
356
+ description: "retries the current loop iteration.",
357
+ examples: [
358
+ "[1].each { redo if false }",
359
+ "while false { redo }",
360
+ "[1, 2].each { |value| redo if value == 0 }"
361
+ ]
362
+ },
363
+ "retry" => {
364
+ name: "retry",
365
+ description: "retries the current loop iteration.",
366
+ examples: [
367
+ "[1].each { retry if false }",
368
+ "while false { retry }",
369
+ "[1, 2].each { |value| retry if value == 0 }"
370
+ ]
371
+ },
372
+ "return" => {
373
+ name: "return",
374
+ description:
375
+ "exits the current function and returns an optional value.",
376
+ examples: [
377
+ "() => { return }",
378
+ "() => { return 1 }",
379
+ "f = () => { return :done } f()"
380
+ ]
381
+ }
382
+ }.freeze
383
+
384
+ def self.functions
385
+ Object.sorted_dictionary(FUNCTIONS)
386
+ end
387
+
388
+ def self.classes
389
+ Object.sorted_dictionary(
390
+ documented_classes.to_h do |name, klass|
391
+ [name, Object.documentation_for(klass)]
392
+ end
393
+ )
394
+ end
395
+
396
+ def self.documented_classes
397
+ {
398
+ "Base64" => Object::Base64,
399
+ "Boolean" => Boolean,
400
+ "Class" => Class,
401
+ "Code" => Code,
402
+ "Context" => Context,
403
+ "Cryptography" => Cryptography,
404
+ "Date" => Date,
405
+ "Decimal" => Decimal,
406
+ "Dictionary" => Dictionary,
407
+ "Duration" => Duration,
408
+ "Function" => Function,
409
+ "Html" => Html,
410
+ "Http" => Http,
411
+ "Ics" => Ics,
412
+ "IdentifierList" => IdentifierList,
413
+ "Integer" => Integer,
414
+ "Json" => Json,
415
+ "List" => List,
416
+ "Nothing" => Nothing,
417
+ "Number" => Number,
418
+ "Object" => Object,
419
+ "Parameter" => Parameter,
420
+ "Range" => Range,
421
+ "Smtp" => Smtp,
422
+ "String" => String,
423
+ "Time" => Time,
424
+ "Url" => Url
425
+ }
426
+ end
427
+
6
428
  def call(**args)
7
429
  code_operator = args.fetch(:operator, nil).to_code
8
430
  code_arguments = args.fetch(:arguments, []).to_code
@@ -12,6 +434,21 @@ class Code
12
434
  code_value = code_arguments.code_first
13
435
  globals = multi_fetch(args, *GLOBALS)
14
436
 
437
+ if code_context.code_has_key?(code_operator).truthy?
438
+ code_result = code_context.code_fetch(code_operator)
439
+
440
+ if code_result.is_a?(Super) ||
441
+ (
442
+ code_result.is_a?(Function) &&
443
+ args.fetch(:explicit_arguments, false)
444
+ )
445
+ return code_result.call(**args, operator: nil)
446
+ end
447
+
448
+ sig(args)
449
+ return code_result
450
+ end
451
+
15
452
  case code_operator.to_s
16
453
  when "Boolean"
17
454
  sig(args) { Object.repeat }
@@ -73,6 +510,12 @@ class Code
73
510
  else
74
511
  Class.new(Function)
75
512
  end
513
+ when "globals"
514
+ sig(args)
515
+ self.class.functions
516
+ when "classes"
517
+ sig(args)
518
+ self.class.classes
76
519
  when "Integer"
77
520
  sig(args) { Object.repeat }
78
521
  if code_arguments.any?
@@ -92,7 +535,7 @@ class Code
92
535
  end
93
536
  when "context"
94
537
  sig(args)
95
- code_context
538
+ code_context.code_deep_duplicate
96
539
  when "Object"
97
540
  sig(args)
98
541
  if code_arguments.any?
@@ -173,6 +616,9 @@ class Code
173
616
  when "evaluate"
174
617
  sig(args) { Object }
175
618
  Code.code_evaluate(code_value.code_to_string, **globals)
619
+ when "getting_started"
620
+ sig(args)
621
+ GETTING_STARTED.to_code
176
622
  when "p"
177
623
  sig(args) { Object.repeat }
178
624
  output.puts(*code_arguments.raw.map(&:inspect))