forthic 0.2.4 → 0.3.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +311 -21
  3. data/Rakefile +36 -7
  4. data/lib/forthic/decorators/docs.rb +69 -0
  5. data/lib/forthic/decorators/word.rb +331 -0
  6. data/lib/forthic/errors.rb +270 -0
  7. data/lib/forthic/grpc/client.rb +223 -0
  8. data/lib/forthic/grpc/errors.rb +149 -0
  9. data/lib/forthic/grpc/forthic_runtime_pb.rb +32 -0
  10. data/lib/forthic/grpc/forthic_runtime_services_pb.rb +31 -0
  11. data/lib/forthic/grpc/remote_module.rb +120 -0
  12. data/lib/forthic/grpc/remote_runtime_module.rb +148 -0
  13. data/lib/forthic/grpc/remote_word.rb +91 -0
  14. data/lib/forthic/grpc/runtime_manager.rb +60 -0
  15. data/lib/forthic/grpc/serializer.rb +184 -0
  16. data/lib/forthic/grpc/server.rb +361 -0
  17. data/lib/forthic/interpreter.rb +677 -318
  18. data/lib/forthic/literals.rb +170 -0
  19. data/lib/forthic/module.rb +383 -0
  20. data/lib/forthic/modules/standard/array_module.rb +940 -0
  21. data/lib/forthic/modules/standard/boolean_module.rb +176 -0
  22. data/lib/forthic/modules/standard/core_module.rb +362 -0
  23. data/lib/forthic/modules/standard/datetime_module.rb +349 -0
  24. data/lib/forthic/modules/standard/json_module.rb +55 -0
  25. data/lib/forthic/modules/standard/math_module.rb +365 -0
  26. data/lib/forthic/modules/standard/record_module.rb +203 -0
  27. data/lib/forthic/modules/standard/string_module.rb +170 -0
  28. data/lib/forthic/tokenizer.rb +224 -77
  29. data/lib/forthic/utils.rb +35 -0
  30. data/lib/forthic/websocket/handler.rb +548 -0
  31. data/lib/forthic/websocket/serializer.rb +160 -0
  32. data/lib/forthic/word_options.rb +141 -0
  33. data/lib/forthic.rb +30 -24
  34. data/protos/README.md +43 -0
  35. data/protos/v1/forthic_runtime.proto +200 -0
  36. metadata +73 -46
  37. data/.standard.yml +0 -3
  38. data/CHANGELOG.md +0 -11
  39. data/CLAUDE.md +0 -74
  40. data/Guardfile +0 -42
  41. data/lib/forthic/code_location.rb +0 -35
  42. data/lib/forthic/errors/unknown_word_error.rb +0 -39
  43. data/lib/forthic/forthic_error.rb +0 -65
  44. data/lib/forthic/forthic_module.rb +0 -146
  45. data/lib/forthic/global_module.rb +0 -2328
  46. data/lib/forthic/positioned_string.rb +0 -19
  47. data/lib/forthic/token.rb +0 -37
  48. data/lib/forthic/variable.rb +0 -34
  49. data/lib/forthic/version.rb +0 -5
  50. data/lib/forthic/words/definition_word.rb +0 -38
  51. data/lib/forthic/words/end_array_word.rb +0 -28
  52. data/lib/forthic/words/end_module_word.rb +0 -16
  53. data/lib/forthic/words/imported_word.rb +0 -27
  54. data/lib/forthic/words/map_word.rb +0 -169
  55. data/lib/forthic/words/module_memo_bang_at_word.rb +0 -22
  56. data/lib/forthic/words/module_memo_bang_word.rb +0 -21
  57. data/lib/forthic/words/module_memo_word.rb +0 -35
  58. data/lib/forthic/words/module_word.rb +0 -21
  59. data/lib/forthic/words/push_value_word.rb +0 -21
  60. data/lib/forthic/words/start_module_word.rb +0 -31
  61. data/lib/forthic/words/word.rb +0 -30
  62. data/sig/forthic.rbs +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ebffd3b7bf74a96a5bdbb6d178996eeea1c2d35edc22743e4dba9154e1e40f9
4
- data.tar.gz: a34907460efd7db068214dff1410b512a9a9ad10e3eee149e120eb5b414ebc2f
3
+ metadata.gz: edd1101930e744ff0289d9a71f7449645852a558c7f12b4f4b0fe8bb211278cd
4
+ data.tar.gz: ea53ba5d3f04fca67b2088debe870eba40e9c2b1bca04061095420d0b92de8cd
5
5
  SHA512:
6
- metadata.gz: 7013c8908441bc6aaee014af8e6ac124bf0e60245474adacdcf4a8633c686ef781dc022e0f163a93222dfd3435e5e21c875ac273f13ae029909f51d354fbfdeb
7
- data.tar.gz: 759a725d11e735ab283d99e076cf314f72ff98c086cf2dcd0ff93077f2031fa0bb27d814008f63ced3c179b6803f566a10d6b79a5e0082d9c91c30f9159b3d03
6
+ metadata.gz: 8f193bcb169fe8b7a7f59c27978b3c296b61e791199b6e4c9797eb10d671ff091416ac5e7fc6ecd42f7bf70955dbbbf237fa7c97e8434ea3abd41e13a4c861e9
7
+ data.tar.gz: 9bea9f3a03156019a3e3e23bf634c8988bd13180ec28faa4cb91d4256851c3cefbcd9928341e494e8f281a3f331bd4262d358c7fd1bb61db99c50e0983dcc13c
data/README.md CHANGED
@@ -1,47 +1,337 @@
1
- > **⚠️ DEPRECATED PACKAGE**
2
- >
3
- > This package is from an archived repository and will not receive updates.
4
- >
5
- > **New repository:** https://github.com/forthix/forthic-rb
6
- >
7
- > Watch the new repository for announcements about updated packages.
1
+ # Forthic Ruby Runtime
2
+
3
+ **A Ruby runtime for [Forthic](https://github.com/forthix/forthic)** - *the* stack-based, concatenative language for composable transformations.
4
+
5
+ Use Forthic to wrap your Ruby code within composable words, leveraging categorical principles for clean, powerful abstractions.
6
+
7
+ **[Forthic Parent Documentation](https://github.com/forthix/forthic)** | **[Getting Started](#getting-started)** | **[Examples](examples/)** | **[API Docs](docs/)**
8
+
9
+ ---
10
+
11
+ ## What is Forthic?
12
+
13
+ Forthic enables **categorical coding** - a way to solve problems by viewing them in terms of transformation rather than computation. This Ruby runtime lets you:
14
+
15
+ 1. **Wrap existing code** with simple decorators
16
+ 2. **Compose transformations** cleanly using stack-based operations
17
+ 3. **Build powerful abstractions** from simple primitives
18
+
19
+ See the [Forthic repository](https://github.com/forthix/forthic) for philosophy, core concepts, and why categorical coding matters.
8
20
 
9
21
  ---
10
22
 
11
- # Forthic
23
+ ## Quick Example
24
+
25
+ ### Create a Module
26
+
27
+ ```ruby
28
+ require 'forthic'
29
+
30
+ class AnalyticsModule < Forthic::Decorators::DecoratedModule
31
+ def initialize
32
+ super("analytics")
33
+ end
34
+
35
+ word :AVERAGE, "( numbers -- avg )", "Calculate average"
36
+ def AVERAGE(numbers)
37
+ numbers.sum.to_f / numbers.length
38
+ end
39
+
40
+ word :FILTER_OUTLIERS, "( numbers stdDevs -- filtered )", "Filter outliers beyond N std devs"
41
+ def FILTER_OUTLIERS(numbers, std_devs)
42
+ mean = AVERAGE(numbers)
43
+ std_dev = Math.sqrt(numbers.map { |n| (n - mean) ** 2 }.sum / numbers.length)
44
+ threshold = std_dev * std_devs
45
+ numbers.select { |n| (n - mean).abs <= threshold }
46
+ end
47
+ end
48
+ ```
49
+
50
+ ### Use It
51
+
52
+ ```ruby
53
+ require 'forthic'
54
+
55
+ interp = Forthic::Interpreter.new
56
+ interp.register_module(AnalyticsModule.new)
57
+
58
+ interp.run(%{
59
+ ["analytics"] USE-MODULES
12
60
 
13
- A Forthic interpreter that runs within Ruby.
61
+ [1 2 3 100 4 5] 2 FILTER-OUTLIERS AVERAGE
62
+ })
63
+
64
+ result = interp.stack_pop # Clean average without outliers
65
+ ```
66
+
67
+ ---
14
68
 
15
69
  ## Installation
16
70
 
17
- Install the gem and add to the application's Gemfile by executing:
71
+ Add to your Gemfile:
72
+
73
+ ```ruby
74
+ gem 'forthic'
75
+ ```
76
+
77
+ Then install:
18
78
 
19
79
  ```bash
20
- bundle add forthic
80
+ bundle install
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Getting Started
86
+
87
+ ### Basic Usage
88
+
89
+ ```ruby
90
+ require 'forthic'
91
+
92
+ interp = Forthic::Interpreter.new
93
+
94
+ # Execute Forthic code
95
+ interp.run(%{
96
+ [1 2 3 4 5] "2 *" MAP # Double each element
97
+ })
98
+
99
+ result = interp.stack_pop # [2, 4, 6, 8, 10]
100
+ ```
101
+
102
+ ### Creating Your First Module
103
+
104
+ ```ruby
105
+ require 'forthic'
106
+
107
+ class MyModule < Forthic::Decorators::DecoratedModule
108
+ def initialize
109
+ super("mymodule")
110
+ end
111
+
112
+ word :PROCESS, "( data -- result )", "Process data your way"
113
+ def PROCESS(data)
114
+ # Wrap your existing Ruby code
115
+ my_existing_function(data)
116
+ end
117
+ end
118
+
119
+ # Register and use
120
+ interp = Forthic::Interpreter.new
121
+ interp.register_module(MyModule.new)
122
+
123
+ interp.run(%{
124
+ ["mymodule"] USE-MODULES
125
+ SOME-DATA PROCESS
126
+ })
127
+ ```
128
+
129
+ See [examples/README.md](examples/README.md) for detailed tutorials and examples.
130
+
131
+ ---
132
+
133
+ ## Features
134
+
135
+ ### Standard Library
136
+
137
+ The Ruby runtime includes comprehensive standard modules:
138
+
139
+ - **array** - MAP, SELECT, SORT, GROUP-BY, ZIP, REDUCE, FLATTEN (30+ operations)
140
+ - **record** - REC@, <REC, MERGE, KEYS, VALUES, INVERT-KEYS
141
+ - **string** - SPLIT, JOIN, UPPERCASE, LOWERCASE, TRIM, REPLACE
142
+ - **math** - +, -, *, /, ROUND, ABS, MIN, MAX, AVERAGE
143
+ - **datetime** - >DATE, >DATETIME, ADD-DAYS, FORMAT, DIFF-DAYS
144
+ - **json** - >JSON, JSON>, JSON-PRETTIFY
145
+ - **boolean** - ==, <, >, AND, OR, NOT, IN
146
+
147
+ See [docs/modules/](docs/modules/) for complete reference.
148
+
149
+ ### Easy Module Creation
150
+
151
+ The decorator system makes wrapping code trivial:
152
+
153
+ ```ruby
154
+ word :MY_WORD, "( input -- output )", "Description"
155
+ def MY_WORD(input)
156
+ your_logic(input)
157
+ end
21
158
  ```
22
159
 
23
- If bundler is not being used to manage dependencies, install the gem by executing:
160
+ ### Ruby Integration
161
+
162
+ - Full Ruby compatibility (Ruby 2.7+)
163
+ - Works with Rails applications
164
+ - Synchronous execution model
165
+ - Native Ruby error handling
166
+
167
+ ---
168
+
169
+ ## Documentation
170
+
171
+ ### This Runtime
172
+ - **[Module API Reference](docs/modules/)** - Standard library documentation
173
+ - **[Examples](examples/)** - Working code samples
174
+
175
+ ### Core Forthic Concepts
176
+ - **[Main Forthic Docs](https://github.com/forthix/forthic)** - Philosophy, language guide
177
+ - **[Why Forthic?](https://github.com/forthix/forthic/blob/main/docs/why-forthic.md)** - Motivation and core principles
178
+ - **[Category Theory](https://github.com/forthix/forthic/blob/main/docs/language/category-theory.md)** - Mathematical foundations
179
+ - **[Building Modules](https://github.com/forthix/forthic/blob/main/docs/tutorials/building-modules.md)** - Module creation patterns
180
+
181
+ ---
182
+
183
+ ## Examples
184
+
185
+ See the [examples/](examples/) directory for working code samples including:
186
+ - Basic usage patterns
187
+ - Custom module creation
188
+ - Multi-runtime execution
189
+ - Rails integration
190
+
191
+ ---
192
+
193
+ ## Building
24
194
 
25
195
  ```bash
26
- gem install forthic
196
+ # Install dependencies
197
+ bundle install
198
+
199
+ # Run all tests
200
+ bundle exec rspec
201
+
202
+ # Run only unit tests
203
+ bundle exec rspec spec/unit/
204
+
205
+ # Run only integration tests
206
+ bundle exec rspec spec/integration/
207
+
208
+ # Generate documentation
209
+ bundle exec rake docs
27
210
  ```
28
211
 
29
- ## Usage
212
+ ---
213
+
214
+ ## Multi-Runtime Execution
215
+
216
+ Call code from other language runtimes seamlessly - use Python's pandas from Ruby, or TypeScript's JavaScript libraries from Ruby.
30
217
 
31
- Here's a basic example of how to use the Forthic interpreter:
218
+ ### Quick Example
32
219
 
33
220
  ```ruby
34
221
  require 'forthic'
222
+ require 'forthic/grpc/client'
223
+ require 'forthic/grpc/remote_runtime_module'
35
224
 
36
225
  interp = Forthic::Interpreter.new
37
- interp.run("[1 2 3] '8 *' MAP")
38
- puts interp.stack_pop
39
226
 
40
- # Output:
41
- #
42
- # [ 8, 16, 24 ]
227
+ # Register the remote runtime module
228
+ remote_runtime = Forthic::Grpc::RemoteRuntimeModule.new
229
+ interp.register_module(remote_runtime)
230
+
231
+ interp.run(%{
232
+ # Connect to Python runtime
233
+ "localhost:50051" CONNECT-RUNTIME
234
+
235
+ # Load Python pandas module
236
+ ["pandas"] USE-PY-MODULES
237
+
238
+ # Now use Python pandas from Ruby!
239
+ [
240
+ [ [.name "Alice"] [.age 30] ] REC
241
+ [ [.name "Bob"] [.age 25] ] REC
242
+ ] DF-FROM-RECORDS
243
+ })
43
244
  ```
44
245
 
246
+ ### Approaches
247
+
248
+ - **gRPC** - Ruby ↔ Python ↔ TypeScript (fast, server-to-server)
249
+ - **WebSocket** - Browser ↔ Rails (ActionCable, client-server)
250
+
251
+ ### Learn More
252
+
253
+ 📖 **[Complete Multi-Runtime Documentation](docs/multi-runtime/)**
254
+
255
+ - **[Overview](docs/multi-runtime/)** - When and how to use multi-runtime
256
+ - **[gRPC Setup](docs/multi-runtime/grpc.md)** - Server and client configuration
257
+ - **[WebSocket Setup](docs/multi-runtime/websocket.md)** - Browser-compatible communication
258
+ - **[Configuration](docs/multi-runtime/configuration.md)** - YAML config and connection management
259
+ - **[Examples](examples/)** - Working code samples
260
+
261
+ **Runtime Status:** ✅ TypeScript, Python, Ruby | 🚧 Rust | 📋 Java, .NET
262
+
263
+ ---
264
+
265
+ ## Project Structure
266
+
267
+ ```
268
+ forthic-rb/
269
+ ├── lib/forthic/ # Core library code
270
+ │ ├── decorators/ # Decorator system for modules and words
271
+ │ ├── modules/ # Standard library modules
272
+ │ │ └── standard/ # Standard modules (array, string, math, etc.)
273
+ │ ├── grpc/ # gRPC client/server for multi-runtime
274
+ │ ├── websocket/ # WebSocket/ActionCable support
275
+ │ ├── interpreter.rb # Main interpreter implementation
276
+ │ ├── module.rb # Module and word classes
277
+ │ ├── tokenizer.rb # Lexical analysis
278
+ │ └── errors.rb # Error classes
279
+ ├── spec/ # Test suite
280
+ │ ├── unit/ # Unit tests
281
+ │ └── integration/ # Integration tests
282
+ ├── scripts/ # Utility scripts
283
+ │ └── generate_docs.rb # Documentation generator
284
+ ├── protos/ # Protocol buffer definitions
285
+ │ └── v1/ # Version 1 of gRPC protocol
286
+ └── docs/ # Generated documentation (created by rake docs)
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Cross-Runtime Compatibility
292
+
293
+ This Ruby implementation maintains compatibility with:
294
+ - **forthic-ts** (TypeScript/JavaScript)
295
+ - **forthic-py** (Python)
296
+ - **forthic-rs** (Rust, in progress)
297
+
298
+ All runtimes share the same test suite and language semantics to ensure consistent behavior across platforms.
299
+
300
+ ---
301
+
45
302
  ## Contributing
46
303
 
47
- Bug reports and pull requests are welcome on GitHub at https://github.com/linkedin/forthic.
304
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines, or refer to the main [Forthic contributing guide](https://github.com/forthix/forthic/blob/main/CONTRIBUTING.md).
305
+
306
+ When adding new words or modules:
307
+
308
+ 1. Use the decorator system (`word` or `direct_word`)
309
+ 2. Include stack effect notation: `( input -- output )`
310
+ 3. Provide clear descriptions
311
+ 4. Add corresponding tests in `spec/`
312
+ 5. Regenerate documentation: `bundle exec rake docs`
313
+
314
+ ---
315
+
316
+ ## Community
317
+
318
+ - **Main Repository:** [forthix/forthic](https://github.com/forthix/forthic)
319
+ - **Issues:** [Report issues](https://github.com/forthix/forthic-rb/issues)
320
+ - **Discussions:** [GitHub Discussions](https://github.com/forthix/forthic/discussions)
321
+ - **Examples:** [Real-world applications](examples/)
322
+
323
+ ---
324
+
325
+ ## License
326
+
327
+ [BSD-2-Clause License](LICENSE) - Copyright 2024 LinkedIn Corporation. Copyright 2025 Forthix LLC.
328
+
329
+ ---
330
+
331
+ ## Related
332
+
333
+ - **[Forthic (main repo)](https://github.com/forthix/forthic)** - Core documentation and concepts
334
+
335
+ ---
336
+
337
+ **Forthic**: Wrap. Compose. Abstract.
data/Rakefile CHANGED
@@ -1,14 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "minitest/test_task"
3
+ require 'rspec/core/rake_task'
5
4
 
6
- Minitest::TestTask.create
5
+ RSpec::Core::RakeTask.new(:spec)
7
6
 
8
- require "standard/rake"
7
+ task default: :spec
9
8
 
10
- task default: %i[test standard]
9
+ desc 'Generate documentation from module decorators'
10
+ task :docs do
11
+ ruby 'scripts/generate_docs.rb'
12
+ end
13
+
14
+ desc 'Run all tests'
15
+ task test: :spec
16
+
17
+ desc 'Run unit tests only'
18
+ task :test_unit do
19
+ sh 'bundle exec rspec spec/unit'
20
+ end
21
+
22
+ desc 'Run integration tests only'
23
+ task :test_integration do
24
+ sh 'bundle exec rspec spec/integration'
25
+ end
26
+
27
+ desc 'Generate gRPC protobuf code from .proto file'
28
+ task :generate_grpc do
29
+ proto_file = 'protos/v1/forthic_runtime.proto'
30
+ proto_dir = 'protos/v1'
31
+ output_dir = 'lib/forthic/grpc'
32
+
33
+ # Create output directory if it doesn't exist
34
+ FileUtils.mkdir_p(output_dir)
35
+
36
+ # Generate Ruby protobuf code from v1 proto
37
+ sh "grpc_tools_ruby_protoc -I #{proto_dir} " \
38
+ "--ruby_out=#{output_dir} " \
39
+ "--grpc_out=#{output_dir} " \
40
+ "#{proto_file}"
11
41
 
12
- task :guard do
13
- sh "bundle exec guard"
42
+ puts "✓ Generated protobuf code from #{proto_file}"
14
43
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'word'
4
+ require 'json'
5
+
6
+ module Forthic
7
+ module Decorators
8
+ # Generate markdown documentation for a module
9
+ #
10
+ # @param mod [DecoratedModule] The module to document
11
+ # @return [String] Markdown-formatted documentation
12
+ def self.generate_module_docs(mod)
13
+ docs = mod.get_word_docs
14
+ md = "# #{mod.get_name} Module\n\n"
15
+
16
+ if docs.empty?
17
+ md += "*No documented words*\n"
18
+ return md
19
+ end
20
+
21
+ # Sort alphabetically by name
22
+ docs.sort_by! { |doc| doc[:name] }
23
+
24
+ docs.each do |doc|
25
+ md += "## #{doc[:name]}\n\n"
26
+ md += "**Stack Effect**: `#{doc[:stack_effect]}`\n\n"
27
+ md += "#{doc[:description]}\n\n" if doc[:description] && !doc[:description].empty?
28
+ end
29
+
30
+ md
31
+ end
32
+
33
+ # Generate JSON documentation for multiple modules
34
+ #
35
+ # @param modules [Array<DecoratedModule>] The modules to document
36
+ # @return [Array<Hash>] Array of module documentation hashes
37
+ def self.generate_docs_json(modules)
38
+ modules.map do |mod|
39
+ {
40
+ name: mod.get_name,
41
+ words: mod.get_word_docs
42
+ }
43
+ end
44
+ end
45
+
46
+ # Generate full stdlib documentation
47
+ #
48
+ # @param interp [StandardInterpreter] The interpreter with stdlib loaded
49
+ # @return [String] Markdown-formatted stdlib documentation
50
+ def self.generate_stdlib_docs(interp)
51
+ module_names = [
52
+ "core", "array", "record", "string", "datetime",
53
+ "math", "boolean", "json", "utility"
54
+ ]
55
+
56
+ md = "# Forthic Standard Library\n\n"
57
+
58
+ module_names.each do |module_name|
59
+ mod = interp.get_app_module.find_module(module_name)
60
+ if mod && mod.is_a?(DecoratedModule)
61
+ md += generate_module_docs(mod)
62
+ md += "\n---\n\n"
63
+ end
64
+ end
65
+
66
+ md
67
+ end
68
+ end
69
+ end