forthic 0.2.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +314 -14
  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 +694 -245
  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 -20
  34. data/protos/README.md +43 -0
  35. data/protos/v1/forthic_runtime.proto +200 -0
  36. metadata +72 -39
  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 -20
  42. data/lib/forthic/forthic_error.rb +0 -50
  43. data/lib/forthic/forthic_module.rb +0 -146
  44. data/lib/forthic/global_module.rb +0 -2328
  45. data/lib/forthic/positioned_string.rb +0 -19
  46. data/lib/forthic/token.rb +0 -37
  47. data/lib/forthic/variable.rb +0 -34
  48. data/lib/forthic/version.rb +0 -5
  49. data/lib/forthic/words/definition_word.rb +0 -38
  50. data/lib/forthic/words/end_array_word.rb +0 -28
  51. data/lib/forthic/words/end_module_word.rb +0 -16
  52. data/lib/forthic/words/imported_word.rb +0 -27
  53. data/lib/forthic/words/map_word.rb +0 -169
  54. data/lib/forthic/words/module_memo_bang_at_word.rb +0 -22
  55. data/lib/forthic/words/module_memo_bang_word.rb +0 -21
  56. data/lib/forthic/words/module_memo_word.rb +0 -35
  57. data/lib/forthic/words/module_word.rb +0 -21
  58. data/lib/forthic/words/push_value_word.rb +0 -21
  59. data/lib/forthic/words/start_module_word.rb +0 -31
  60. data/lib/forthic/words/word.rb +0 -30
  61. data/sig/forthic.rbs +0 -4
metadata CHANGED
@@ -1,59 +1,92 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forthic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rino Jose
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-13 00:00:00.000000000 Z
12
- dependencies: []
13
- description: This package provides a Forthic interpreter that allows you to execute
14
- Forthic code within your Ruby projects. Forthic is a stack-based programming language
15
- inspired by Forth.
11
+ date: 2025-11-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ description: Forthic is a stack-based, concatenative programming language designed
42
+ for data manipulation and processing. This is the Ruby implementation with WebSocket
43
+ support for remote word execution.
16
44
  email:
17
- - rjose@forthix.com
45
+ - rjose@gmail.com
18
46
  executables: []
19
47
  extensions: []
20
48
  extra_rdoc_files: []
21
49
  files:
22
- - ".standard.yml"
23
- - CHANGELOG.md
24
- - CLAUDE.md
25
- - Guardfile
26
50
  - README.md
27
51
  - Rakefile
28
52
  - lib/forthic.rb
29
- - lib/forthic/code_location.rb
30
- - lib/forthic/forthic_error.rb
31
- - lib/forthic/forthic_module.rb
32
- - lib/forthic/global_module.rb
53
+ - lib/forthic/decorators/docs.rb
54
+ - lib/forthic/decorators/word.rb
55
+ - lib/forthic/errors.rb
56
+ - lib/forthic/grpc/client.rb
57
+ - lib/forthic/grpc/errors.rb
58
+ - lib/forthic/grpc/forthic_runtime_pb.rb
59
+ - lib/forthic/grpc/forthic_runtime_services_pb.rb
60
+ - lib/forthic/grpc/remote_module.rb
61
+ - lib/forthic/grpc/remote_runtime_module.rb
62
+ - lib/forthic/grpc/remote_word.rb
63
+ - lib/forthic/grpc/runtime_manager.rb
64
+ - lib/forthic/grpc/serializer.rb
65
+ - lib/forthic/grpc/server.rb
33
66
  - lib/forthic/interpreter.rb
34
- - lib/forthic/positioned_string.rb
35
- - lib/forthic/token.rb
67
+ - lib/forthic/literals.rb
68
+ - lib/forthic/module.rb
69
+ - lib/forthic/modules/standard/array_module.rb
70
+ - lib/forthic/modules/standard/boolean_module.rb
71
+ - lib/forthic/modules/standard/core_module.rb
72
+ - lib/forthic/modules/standard/datetime_module.rb
73
+ - lib/forthic/modules/standard/json_module.rb
74
+ - lib/forthic/modules/standard/math_module.rb
75
+ - lib/forthic/modules/standard/record_module.rb
76
+ - lib/forthic/modules/standard/string_module.rb
36
77
  - lib/forthic/tokenizer.rb
37
- - lib/forthic/variable.rb
38
- - lib/forthic/version.rb
39
- - lib/forthic/words/definition_word.rb
40
- - lib/forthic/words/end_array_word.rb
41
- - lib/forthic/words/end_module_word.rb
42
- - lib/forthic/words/imported_word.rb
43
- - lib/forthic/words/map_word.rb
44
- - lib/forthic/words/module_memo_bang_at_word.rb
45
- - lib/forthic/words/module_memo_bang_word.rb
46
- - lib/forthic/words/module_memo_word.rb
47
- - lib/forthic/words/module_word.rb
48
- - lib/forthic/words/push_value_word.rb
49
- - lib/forthic/words/start_module_word.rb
50
- - lib/forthic/words/word.rb
51
- - sig/forthic.rbs
52
- homepage: https://github.com/linkedin/forthic
53
- licenses: []
78
+ - lib/forthic/utils.rb
79
+ - lib/forthic/websocket/handler.rb
80
+ - lib/forthic/websocket/serializer.rb
81
+ - lib/forthic/word_options.rb
82
+ - protos/README.md
83
+ - protos/v1/forthic_runtime.proto
84
+ homepage: https://github.com/forthix/forthic
85
+ licenses:
86
+ - MIT
54
87
  metadata:
55
- homepage_uri: https://github.com/linkedin/forthic
56
- source_code_uri: https://github.com/linkedin/forthic
88
+ homepage_uri: https://github.com/forthix/forthic
89
+ source_code_uri: https://github.com/forthix/forthic
57
90
  post_install_message:
58
91
  rdoc_options: []
59
92
  require_paths:
@@ -62,7 +95,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
62
95
  requirements:
63
96
  - - ">="
64
97
  - !ruby/object:Gem::Version
65
- version: 3.1.0
98
+ version: 3.0.0
66
99
  required_rubygems_version: !ruby/object:Gem::Requirement
67
100
  requirements:
68
101
  - - ">="
@@ -72,5 +105,5 @@ requirements: []
72
105
  rubygems_version: 3.3.26
73
106
  signing_key:
74
107
  specification_version: 4
75
- summary: A Forthic interpreter that runs within Ruby.
108
+ summary: Ruby implementation of the Forthic programming language
76
109
  test_files: []
data/.standard.yml DELETED
@@ -1,3 +0,0 @@
1
- # For available configuration options, see:
2
- # https://github.com/standardrb/standard
3
- ruby_version: 3.1
data/CHANGELOG.md DELETED
@@ -1,11 +0,0 @@
1
- ## [Unreleased]
2
-
3
- ## [0.2.0] - 2025-01-13
4
-
5
- - Add `import_module` method to Interpreter class
6
- - Add test coverage for module importing functionality
7
- - Implement module importing with prefix support matching forthic-ts behavior
8
-
9
- ## [0.1.0] - 2024-12-27
10
-
11
- - Initial release
data/CLAUDE.md DELETED
@@ -1,74 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Development Commands
6
-
7
- ### Testing
8
- - `rake test` - Run all tests using Minitest
9
- - `rake test TEST=test/test_specific_file.rb` - Run a specific test file
10
- - `bundle exec guard` - Start Guard for continuous testing (watches file changes)
11
- - `rake guard` - Alternative command to start Guard
12
-
13
- ### Code Quality
14
- - `rake standard` - Run StandardRB linter for code formatting and style
15
- - `rake standard:fix` - Auto-fix StandardRB issues where possible
16
- - `rake` - Run default task (both tests and linting)
17
-
18
- ### Development
19
- - `bundle install` - Install gem dependencies
20
- - `bundle exec rake build` - Build the gem
21
- - `bundle exec rake install` - Install the gem locally
22
- - `bundle exec rake release` - Release the gem (requires proper credentials)
23
-
24
- ## Architecture Overview
25
-
26
- This is a Ruby implementation of the Forthic programming language - a stack-based language inspired by Forth. The codebase follows a clean modular architecture:
27
-
28
- ### Core Components
29
-
30
- **Interpreter (`lib/forthic/interpreter.rb`)**: The main execution engine that:
31
- - Manages the execution stack and module stack
32
- - Handles tokenization and parsing of Forthic code
33
- - Provides word lookup and execution
34
- - Supports profiling and debugging capabilities
35
- - Manages compilation of word definitions
36
-
37
- **Tokenizer (`lib/forthic/tokenizer.rb`)**: Lexical analyzer that breaks Forthic code into tokens (strings, words, arrays, modules, definitions, comments).
38
-
39
- **Word System (`lib/forthic/words/`)**: Polymorphic word execution system where each word type implements an `execute` method:
40
- - `Word` - Base class for all executable words
41
- - `PushValueWord` - Pushes values onto the stack
42
- - `DefinitionWord` - User-defined words compiled from Forthic code
43
- - `EndArrayWord` - Handles array construction
44
- - `ModuleWord` variants - Handle module operations and memoization
45
- - `StartModuleWord`/`EndModuleWord` - Module boundary management
46
-
47
- **Module System**:
48
- - `GlobalModule` - Built-in words and core functionality
49
- - `ForthicModule` - User-defined modules with isolated namespaces
50
- - Module stack for hierarchical word resolution
51
-
52
- ### Key Design Patterns
53
-
54
- - **Stack-based execution**: All operations work with a central execution stack
55
- - **Autoloading**: Classes are autoloaded for better startup performance
56
- - **Error handling**: Custom `ForthicError` with location tracking for debugging
57
- - **Compilation**: Words can be compiled into definition words for reuse
58
- - **Memoization**: Special memo words for caching expensive computations
59
-
60
- ### Testing Strategy
61
-
62
- Uses Minitest for testing with:
63
- - `test_helper.rb` sets up Minitest with SpecReporter for better output
64
- - Guard for continuous testing during development
65
- - Tests cover tokenizer, interpreter, and module functionality
66
- - StandardRB enforces consistent code style
67
-
68
- ### File Organization
69
-
70
- - `lib/forthic.rb` - Main entry point with autoload declarations
71
- - `lib/forthic/` - Core implementation files
72
- - `lib/forthic/words/` - Word implementation classes
73
- - `test/` - Test files following Minitest conventions
74
- - `sig/` - RBS type signatures for static analysis
data/Guardfile DELETED
@@ -1,42 +0,0 @@
1
- # A sample Guardfile
2
- # More info at https://github.com/guard/guard#readme
3
-
4
- ## Uncomment and set this to only include directories you want to watch
5
- # directories %w(app lib config test spec features) \
6
- # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
-
8
- ## Note: if you are using the `directories` clause above and you are not
9
- ## watching the project directory ('.'), then you will want to move
10
- ## the Guardfile to a watched dir and symlink it back, e.g.
11
- #
12
- # $ mkdir config
13
- # $ mv Guardfile config/
14
- # $ ln -s config/Guardfile .
15
- #
16
- # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
-
18
- guard :minitest do
19
- # with Minitest::Unit
20
- watch(%r{^test/(.*)/?test_(.*)\.rb$})
21
- watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
22
- watch(%r{^test/test_helper\.rb$}) { "test" }
23
-
24
- # with Minitest::Spec
25
- # watch(%r{^spec/(.*)_spec\.rb$})
26
- # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
27
- # watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
28
-
29
- # Rails 4
30
- # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
31
- # watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
32
- # watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
33
- # watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
34
- # watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
35
- # watch(%r{^test/.+_test\.rb$})
36
- # watch(%r{^test/test_helper\.rb$}) { 'test' }
37
-
38
- # Rails < 4
39
- # watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
40
- # watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
41
- # watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
42
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Forthic
4
- class CodeLocation
5
- attr_accessor :screen_name, :line, :column, :start_pos, :end_pos
6
-
7
- # @param [String] screen_name
8
- # @param [Integer] line
9
- # @param [Integer] column
10
- # @param [Integer] start_pos
11
- # @param [Integer] end_pos
12
- def initialize(screen_name: "<ad-hoc>", line: 1, column: 1, start_pos: 0, end_pos: 0)
13
- @screen_name = screen_name
14
- @line = line
15
- @column = column
16
- @start_pos = start_pos
17
- @end_pos = end_pos
18
- end
19
- end
20
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Forthic
4
- class ForthicError < StandardError
5
- attr_accessor :error_key, :title, :description, :location, :caught_error
6
-
7
- # @param [String] error_key
8
- # @param [String] title
9
- # @param [String] description
10
- # @param [CodeLocation, nil] location
11
- def initialize(error_key, title, description, location = nil)
12
- @error_key = error_key
13
- @title = title
14
- @description = description
15
- @location = location
16
- @caught_error = nil
17
- end
18
-
19
- # @param [ForthicError] error
20
- def set_caught_error(error)
21
- @caught_error = error
22
- end
23
-
24
- # @return [String]
25
- def get_title
26
- @title
27
- end
28
-
29
- # @return [String]
30
- def get_description
31
- @description
32
- end
33
-
34
- # @return [Array<ForthicError>]
35
- def get_error_stack
36
- max_depth = 100
37
- cur_error = self
38
- result = [cur_error]
39
-
40
- max_depth.times do
41
- break unless cur_error.caught_error
42
-
43
- result << cur_error.caught_error
44
- cur_error = cur_error.caught_error
45
- end
46
-
47
- result.reverse
48
- end
49
- end
50
- end
@@ -1,146 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "set"
4
- require_relative "words/word"
5
- require_relative "words/module_word"
6
- require_relative "words/module_memo_word"
7
- require_relative "words/module_memo_bang_word"
8
- require_relative "words/module_memo_bang_at_word"
9
- require_relative "words/imported_word"
10
- require_relative "variable"
11
-
12
- module Forthic
13
- class ForthicModule
14
- attr_accessor :words, :exportable, :variables, :modules, :module_prefixes, :required_modules, :name, :forthic_code, :module_id
15
-
16
- # @param [String] name
17
- # @param [Interpreter, nil] interp
18
- # @param [String] forthic_code
19
- def initialize(name, interp = nil, forthic_code = "")
20
- @words = []
21
- @exportable = []
22
- @variables = {}
23
- @modules = {}
24
- @module_prefixes = {}
25
- @required_modules = []
26
- @name = name
27
- @forthic_code = forthic_code
28
- @module_id = "#{name}-#{rand(1_000_000)}"
29
- end
30
-
31
- # @return [ForthicModule]
32
- def dup
33
- result = ForthicModule.new(@name, @interp, @forthic_code)
34
- result.words = @words.dup
35
- result.exportable = @exportable.dup
36
- @variables.each { |key, var| result.variables[key] = var.dup }
37
- @modules.each { |key, mod| result.modules[key] = mod }
38
- result.required_modules = @required_modules.dup
39
- result.forthic_code = @forthic_code
40
- result
41
- end
42
-
43
- # @param [String] prefix
44
- # @param [ForthicModule] mod
45
- def require_module(prefix, mod)
46
- @required_modules << {prefix: prefix, module: mod}
47
- end
48
-
49
- # @param [String] name
50
- # @return [ForthicModule, nil]
51
- def find_module(name)
52
- @modules[name]
53
- end
54
-
55
- # @param [String] word_name
56
- # @param [Proc] word_func
57
- def add_module_word(word_name, word_func)
58
- add_exportable_word(ModuleWord.new(word_name, word_func))
59
- end
60
-
61
- # @param [Word] word
62
- def add_word(word)
63
- @words << word
64
- end
65
-
66
- # @param [Word] word
67
- # @return [ModuleMemoWord]
68
- def add_memo_words(word)
69
- memo_word = ModuleMemoWord.new(word)
70
- @words << memo_word
71
- @words << ModuleMemoBangWord.new(memo_word)
72
- @words << ModuleMemoBangAtWord.new(memo_word)
73
- memo_word
74
- end
75
-
76
- # @param [Array<String>] names
77
- def add_exportable(names)
78
- @exportable.concat(names)
79
- end
80
-
81
- # @return [Array<Word>]
82
- def exportable_words
83
- @words.select { |word| @exportable.include?(word.name) }
84
- end
85
-
86
- # @param [Word] word
87
- def add_exportable_word(word)
88
- @words << word
89
- @exportable << word.name
90
- end
91
-
92
- # @param [String] name
93
- # @param [Object, nil] value
94
- def add_variable(name, value = nil)
95
- @variables[name] ||= Variable.new(name, value)
96
- end
97
-
98
- # @param [Interpreter] interp
99
- def initialize_modules(interp)
100
- @required_modules.each do |rec|
101
- import_module(rec[:prefix], rec[:module], interp)
102
- end
103
- end
104
-
105
- # @param [String] module_name
106
- # @param [String] prefix
107
- # @param [ForthicModule] mod
108
- def register_module(module_name, prefix, mod)
109
- @modules[module_name] = mod
110
- @module_prefixes[module_name] ||= Set.new
111
- @module_prefixes[module_name] << prefix
112
- end
113
-
114
- # @param [String] prefix
115
- # @param [ForthicModule] mod
116
- # @param [Interpreter] interp
117
- def import_module(prefix, mod, interp)
118
- new_module = mod.dup
119
- new_module.initialize_modules(interp)
120
-
121
- new_module.exportable_words.each do |word|
122
- add_word(ImportedWord.new(word, prefix, new_module))
123
- end
124
- register_module(mod.name, prefix, new_module)
125
- end
126
-
127
- # @param [String] name
128
- # @return [Word, nil]
129
- def find_word(name)
130
- find_dictionary_word(name) || find_variable(name)
131
- end
132
-
133
- # @param [String] word_name
134
- # @return [Word, nil]
135
- def find_dictionary_word(word_name)
136
- @words.reverse.find { |w| w.name == word_name }
137
- end
138
-
139
- # @param [String] varname
140
- # @return [PushValueWord, nil]
141
- def find_variable(varname)
142
- var_result = @variables[varname]
143
- var_result ? PushValueWord.new(varname, var_result) : nil
144
- end
145
- end
146
- end