forthic 0.1.0 → 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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +314 -14
  3. data/Rakefile +37 -8
  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 +682 -133
  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 +225 -78
  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 -20
  34. data/protos/README.md +43 -0
  35. data/protos/v1/forthic_runtime.proto +200 -0
  36. metadata +76 -39
  37. data/.standard.yml +0 -3
  38. data/CHANGELOG.md +0 -5
  39. data/Guardfile +0 -42
  40. data/lib/forthic/code_location.rb +0 -20
  41. data/lib/forthic/forthic_error.rb +0 -51
  42. data/lib/forthic/forthic_module.rb +0 -145
  43. data/lib/forthic/global_module.rb +0 -2341
  44. data/lib/forthic/positioned_string.rb +0 -19
  45. data/lib/forthic/token.rb +0 -38
  46. data/lib/forthic/variable.rb +0 -34
  47. data/lib/forthic/version.rb +0 -5
  48. data/lib/forthic/words/definition_word.rb +0 -40
  49. data/lib/forthic/words/end_array_word.rb +0 -28
  50. data/lib/forthic/words/end_module_word.rb +0 -16
  51. data/lib/forthic/words/imported_word.rb +0 -27
  52. data/lib/forthic/words/map_word.rb +0 -169
  53. data/lib/forthic/words/module_memo_bang_at_word.rb +0 -22
  54. data/lib/forthic/words/module_memo_bang_word.rb +0 -21
  55. data/lib/forthic/words/module_memo_word.rb +0 -35
  56. data/lib/forthic/words/module_word.rb +0 -21
  57. data/lib/forthic/words/push_value_word.rb +0 -21
  58. data/lib/forthic/words/start_module_word.rb +0 -31
  59. data/lib/forthic/words/word.rb +0 -30
  60. data/sig/forthic.rbs +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e71f6368dc2bcc817f0095a389740a6676005ab8ddb862cdef02f0e0910d09d
4
- data.tar.gz: b408177ce893d5353aa00ce2a73698005c07e22e3d68a8ec6cade6032887a591
3
+ metadata.gz: edd1101930e744ff0289d9a71f7449645852a558c7f12b4f4b0fe8bb211278cd
4
+ data.tar.gz: ea53ba5d3f04fca67b2088debe870eba40e9c2b1bca04061095420d0b92de8cd
5
5
  SHA512:
6
- metadata.gz: 2ffd692800bdf839d25a106c79bea485addaeabb7d2eafd5366a9d034927a642fed7b4ecb498e5f081016f5079077d3a4db4ba96eae113a8ce547024b4164736
7
- data.tar.gz: b4ab0aec5f7b788873bbbc71761f5382f565864057101125919338c24453bdccdedbcf26f281dc5c46dee4a16fc7824a08516a52c9c318af91ab0f932a329f42
6
+ metadata.gz: 8f193bcb169fe8b7a7f59c27978b3c296b61e791199b6e4c9797eb10d671ff091416ac5e7fc6ecd42f7bf70955dbbbf237fa7c97e8434ea3abd41e13a4c861e9
7
+ data.tar.gz: 9bea9f3a03156019a3e3e23bf634c8988bd13180ec28faa4cb91d4256851c3cefbcd9928341e494e8f281a3f331bd4262d358c7fd1bb61db99c50e0983dcc13c
data/README.md CHANGED
@@ -1,37 +1,337 @@
1
- # Forthic
1
+ # Forthic Ruby Runtime
2
2
 
3
- A Forthic interpreter that runs within Ruby.
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.
20
+
21
+ ---
22
+
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
60
+
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
+ ---
4
68
 
5
69
  ## Installation
6
70
 
7
- 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:
8
78
 
9
79
  ```bash
10
- 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]
11
100
  ```
12
101
 
13
- If bundler is not being used to manage dependencies, install the gem by executing:
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
158
+ ```
159
+
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
14
194
 
15
195
  ```bash
16
- 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
17
210
  ```
18
211
 
19
- ## Usage
212
+ ---
213
+
214
+ ## Multi-Runtime Execution
20
215
 
21
- Here's a basic example of how to use the Forthic interpreter:
216
+ Call code from other language runtimes seamlessly - use Python's pandas from Ruby, or TypeScript's JavaScript libraries from Ruby.
217
+
218
+ ### Quick Example
22
219
 
23
220
  ```ruby
24
221
  require 'forthic'
222
+ require 'forthic/grpc/client'
223
+ require 'forthic/grpc/remote_runtime_module'
25
224
 
26
225
  interp = Forthic::Interpreter.new
27
- interp.run("[1 2 3] '8 *' MAP")
28
- puts interp.stack_pop
29
226
 
30
- # Output:
31
- #
32
- # [ 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
+ })
244
+ ```
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
+
33
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
+ ---
34
301
 
35
302
  ## Contributing
36
303
 
37
- 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
11
13
 
12
- task :guard do
13
- sh "bundle exec guard"
14
- end
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}"
41
+
42
+ puts "✓ Generated protobuf code from #{proto_file}"
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