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
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'time'
5
+ require 'json'
6
+
7
+ module Forthic
8
+ module WebSocket
9
+ # Serializer for converting Ruby values to/from JSON StackValue format
10
+ #
11
+ # Mirrors the gRPC serializer but outputs JSON instead of protobuf.
12
+ # Maintains the same structure as the gRPC StackValue messages for consistency.
13
+ #
14
+ # Handles all Forthic types:
15
+ # - Primitives: Integer, Float, String, Boolean, Nil
16
+ # - Collections: Array, Hash
17
+ # - Temporal: Date, Time, DateTime
18
+ #
19
+ # JSON format matches the WebSocket protocol specification in
20
+ # BROWSER_SERVER_WEBSOCKET_PLAN.md
21
+ module Serializer
22
+ module_function
23
+
24
+ # Serialize a Ruby value to a JSON StackValue hash
25
+ #
26
+ # @param value [Object] Ruby value to serialize
27
+ # @return [Hash] JSON-serializable hash with {type:, value:} structure
28
+ def serialize_value(value)
29
+ case value
30
+ when Integer
31
+ { 'type' => 'int', 'value' => value }
32
+ when Float
33
+ { 'type' => 'float', 'value' => value }
34
+ when String
35
+ { 'type' => 'string', 'value' => value }
36
+ when TrueClass, FalseClass
37
+ { 'type' => 'bool', 'value' => value }
38
+ when NilClass
39
+ { 'type' => 'null', 'value' => nil }
40
+ when Array
41
+ { 'type' => 'array', 'value' => value.map { |item| serialize_value(item) } }
42
+ when Hash
43
+ { 'type' => 'record', 'value' => serialize_record(value) }
44
+ when Time, DateTime
45
+ # Handle Time and DateTime before Date (DateTime is a subclass of Date)
46
+ { 'type' => 'instant', 'value' => serialize_instant(value) }
47
+ when Date
48
+ { 'type' => 'plain_date', 'value' => value.iso8601 }
49
+ else
50
+ raise ArgumentError, "Cannot serialize type #{value.class}: #{value.inspect}"
51
+ end
52
+ end
53
+
54
+ # Deserialize a JSON StackValue hash to a Ruby value
55
+ #
56
+ # @param stack_value [Hash] JSON hash with {type:, value:} structure
57
+ # @return [Object] Ruby value
58
+ def deserialize_value(stack_value)
59
+ type = stack_value['type']
60
+ value = stack_value['value']
61
+
62
+ case type
63
+ when 'int'
64
+ value.to_i
65
+ when 'float'
66
+ value.to_f
67
+ when 'string'
68
+ value.to_s
69
+ when 'bool'
70
+ value
71
+ when 'null'
72
+ nil
73
+ when 'array'
74
+ value.map { |item| deserialize_value(item) }
75
+ when 'record'
76
+ deserialize_record(value)
77
+ when 'plain_date'
78
+ Date.iso8601(value)
79
+ when 'instant'
80
+ Time.iso8601(value)
81
+ when 'zoned_datetime'
82
+ Time.iso8601(value)
83
+ else
84
+ raise ArgumentError, "Unknown StackValue type: #{type}"
85
+ end
86
+ end
87
+
88
+ # Serialize a Ruby array to JSON array
89
+ #
90
+ # @param array [Array] Ruby array
91
+ # @return [Array<Hash>] Array of serialized StackValues
92
+ def serialize_array(array)
93
+ array.map { |item| serialize_value(item) }
94
+ end
95
+
96
+ # Deserialize a JSON array to Ruby array
97
+ #
98
+ # @param array_value [Array<Hash>] Array of JSON StackValues
99
+ # @return [Array] Ruby array
100
+ def deserialize_array(array_value)
101
+ array_value.map { |item| deserialize_value(item) }
102
+ end
103
+
104
+ # Serialize a Ruby hash to JSON record
105
+ #
106
+ # @param hash [Hash] Ruby hash with string keys
107
+ # @return [Hash] Hash with serialized StackValue values
108
+ def serialize_record(hash)
109
+ result = {}
110
+ hash.each do |key, value|
111
+ result[key.to_s] = serialize_value(value)
112
+ end
113
+ result
114
+ end
115
+
116
+ # Deserialize a JSON record to Ruby hash
117
+ #
118
+ # @param record_value [Hash] JSON record with StackValue values
119
+ # @return [Hash] Ruby hash with string keys
120
+ def deserialize_record(record_value)
121
+ result = {}
122
+ record_value.each do |key, value|
123
+ result[key] = deserialize_value(value)
124
+ end
125
+ result
126
+ end
127
+
128
+ # Serialize a Ruby Time/DateTime to ISO 8601 UTC string
129
+ #
130
+ # @param time [Time, DateTime] Ruby Time or DateTime
131
+ # @return [String] ISO 8601 UTC string
132
+ def serialize_instant(time)
133
+ if time.is_a?(DateTime)
134
+ # DateTime: convert to Time first
135
+ utc_time = time.to_time.utc
136
+ else
137
+ # Time: call utc directly
138
+ utc_time = time.utc
139
+ end
140
+ utc_time.iso8601
141
+ end
142
+
143
+ # Serialize a complete stack to JSON
144
+ #
145
+ # @param stack [Array] Array of Ruby values
146
+ # @return [Array<Hash>] Array of serialized StackValues
147
+ def serialize_stack(stack)
148
+ stack.map { |value| serialize_value(value) }
149
+ end
150
+
151
+ # Deserialize a JSON stack to Ruby array
152
+ #
153
+ # @param json_stack [Array<Hash>] Array of JSON StackValues
154
+ # @return [Array] Array of Ruby values
155
+ def deserialize_stack(json_stack)
156
+ json_stack.map { |stack_value| deserialize_value(stack_value) }
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Forthic
6
+ # WordOptions - Type-safe options container for module words
7
+ #
8
+ # Overview:
9
+ # WordOptions provides a structured way for Forthic words to accept optional
10
+ # configuration parameters without requiring fixed parameter positions. This
11
+ # enables flexible, extensible APIs similar to keyword arguments in other languages.
12
+ #
13
+ # Usage in Forthic:
14
+ # [.option_name value ...] ~> WORD
15
+ #
16
+ # The ~> operator takes an options array and a word, passing the options as
17
+ # an additional parameter to words that support them.
18
+ #
19
+ # Example in Forthic code:
20
+ # [1 2 3] '2 *' [.with_key TRUE] ~> MAP
21
+ # [10 20 30] [.comparator "-1 *"] ~> SORT
22
+ # [[[1 2]]] [.depth 1] ~> FLATTEN
23
+ #
24
+ # Implementation in Module Words:
25
+ # Words declare options support by adding an options parameter:
26
+ #
27
+ # def MAP(items, forthic, options = {})
28
+ # with_key = options[:with_key] || options['with_key']
29
+ # push_error = options[:push_error] || options['push_error']
30
+ # # ... use options to modify behavior
31
+ # end
32
+ #
33
+ # The word decorator/registration automatically:
34
+ # 1. Checks if the top stack item is a WordOptions instance
35
+ # 2. Converts it to a plain Hash if present
36
+ # 3. Passes an empty {} if no options provided
37
+ #
38
+ # Common Patterns:
39
+ # - Boolean flags: options[:with_key]
40
+ # - Numeric values: options[:depth]
41
+ # - String values: options[:comparator]
42
+ # - Multiple options: All accessed from same options hash
43
+ #
44
+ # Internal Representation:
45
+ # Created from flat array: [.key1 val1 .key2 val2]
46
+ # Stored as Hash internally for efficient lookup
47
+ # Converted to Hash when passed to words
48
+ #
49
+ # Note: Dot-symbols in Forthic have the leading '.' already stripped,
50
+ # so keys come in as "key1", "key2", etc.
51
+ class WordOptions
52
+ # Create a new WordOptions from a flat array
53
+ #
54
+ # @param flat_array [Array] Array of alternating keys and values
55
+ # @raise [ArgumentError] if array is not even length or keys are not strings
56
+ def initialize(flat_array)
57
+ unless flat_array.is_a?(Array)
58
+ raise ArgumentError, "Options must be an array"
59
+ end
60
+
61
+ unless flat_array.length.even?
62
+ raise ArgumentError,
63
+ "Options must be key-value pairs (even length). Got #{flat_array.length} elements"
64
+ end
65
+
66
+ @options = {}
67
+
68
+ flat_array.each_slice(2) do |key, value|
69
+ # Key should be a string (dot-symbol with . already stripped)
70
+ unless key.is_a?(String)
71
+ raise ArgumentError,
72
+ "Option key must be a string (dot-symbol). Got: #{key.class}"
73
+ end
74
+
75
+ @options[key] = value
76
+ end
77
+ end
78
+
79
+ # Get option value with optional default
80
+ #
81
+ # @param key [String] The option key
82
+ # @param default_value [Object, nil] The default value if key not found
83
+ # @return [Object, nil] The option value or default
84
+ def get(key, default_value = nil)
85
+ @options.key?(key) ? @options[key] : default_value
86
+ end
87
+
88
+ # Check if option exists
89
+ #
90
+ # @param key [String] The option key
91
+ # @return [Boolean] true if option exists
92
+ def has?(key)
93
+ @options.key?(key)
94
+ end
95
+
96
+ # Get all options as plain hash
97
+ #
98
+ # @return [Hash] All options as a hash
99
+ def to_hash
100
+ @options.dup
101
+ end
102
+
103
+ # Alias for compatibility
104
+ alias to_record to_hash
105
+
106
+ # Get all option keys
107
+ #
108
+ # @return [Array<String>] All option keys
109
+ def keys
110
+ @options.keys
111
+ end
112
+
113
+ # For debugging/display
114
+ #
115
+ # @return [String] String representation of options
116
+ def to_s
117
+ pairs = @options.map { |k, v| ".#{k} #{v.to_json}" }.join(" ")
118
+ "<WordOptions: #{pairs}>"
119
+ end
120
+
121
+ # Inspect method for better debugging
122
+ alias inspect to_s
123
+ end
124
+
125
+ # Helper for words to check if top of stack is WordOptions
126
+ # and pop it if present. Returns empty hash if not present.
127
+ #
128
+ # @param interp [Interpreter] The interpreter instance
129
+ # @return [Hash] The options hash or empty hash
130
+ def self.pop_options_if_present(interp)
131
+ return {} if interp.get_stack.length.zero?
132
+
133
+ top = interp.stack_peek
134
+ if top.is_a?(WordOptions)
135
+ opts = interp.stack_pop
136
+ return opts.to_hash
137
+ end
138
+
139
+ {}
140
+ end
141
+ end
data/lib/forthic.rb CHANGED
@@ -1,25 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "forthic/version"
3
+ # Forthic - A stack-based programming language
4
+ #
5
+ # Main entry point for the Forthic Ruby runtime.
6
+ # Requires all core components and standard library modules.
4
7
 
8
+ # Core components
9
+ require_relative 'forthic/errors'
10
+ require_relative 'forthic/utils'
11
+ require_relative 'forthic/literals'
12
+ require_relative 'forthic/tokenizer'
13
+ require_relative 'forthic/word_options'
14
+ require_relative 'forthic/module'
15
+ require_relative 'forthic/interpreter'
16
+
17
+ # Decorators
18
+ require_relative 'forthic/decorators/word'
19
+ require_relative 'forthic/decorators/docs'
20
+
21
+ # Standard library modules
22
+ require_relative 'forthic/modules/standard/core_module'
23
+ require_relative 'forthic/modules/standard/array_module'
24
+ require_relative 'forthic/modules/standard/record_module'
25
+ require_relative 'forthic/modules/standard/string_module'
26
+ require_relative 'forthic/modules/standard/math_module'
27
+ require_relative 'forthic/modules/standard/boolean_module'
28
+ require_relative 'forthic/modules/standard/json_module'
29
+ require_relative 'forthic/modules/standard/datetime_module'
30
+
31
+ # Main module namespace
5
32
  module Forthic
6
- autoload :Tokenizer, 'forthic/tokenizer'
7
- autoload :CodeLocation, 'forthic/code_location'
8
- autoload :Token, 'forthic/token'
9
- autoload :PositionedString, 'forthic/positioned_string'
10
- autoload :ForthicError, 'forthic/forthic_error'
11
- autoload :Word, 'forthic/words/word'
12
- autoload :PushValueWord, 'forthic/words/push_value_word'
13
- autoload :DefinitionWord, 'forthic/words/definition_word'
14
- autoload :ModuleMemoWord, 'forthic/words/module_memo_word'
15
- autoload :ModuleMemoBangAtWord, 'forthic/words/module_memo_bang_at_word'
16
- autoload :ModuleMemoBangWord, 'forthic/words/module_memo_bang_word'
17
- autoload :ModuleMemoBangAtWord, 'forthic/words/module_memo_bang_at_word'
18
- autoload :EndArrayWord, 'forthic/words/end_array_word'
19
- autoload :StartModuleWord, 'forthic/words/start_module_word'
20
- autoload :EndModuleWord, 'forthic/words/end_module_word'
21
- autoload :MapWord, 'forthic/words/map_word'
22
- autoload :ForthicModule, 'forthic/forthic_module'
23
- autoload :GlobalModule, 'forthic/global_module'
24
- autoload :Interpreter, 'forthic/interpreter'
33
+ # Version of the Forthic Ruby runtime
34
+ VERSION = '0.3.0'
25
35
  end
data/protos/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Forthic gRPC Protocol Definitions
2
+
3
+ This directory contains versioned Protocol Buffer definitions for Forthic's multi-runtime communication.
4
+
5
+ ## Versioning
6
+
7
+ Protocol definitions are organized by version to ensure compatibility:
8
+
9
+ - **v1/** - Initial gRPC protocol version
10
+ - Basic types: int, string, bool, float, null, array, record
11
+ - Temporal types: instant, plain_date, zoned_datetime
12
+ - Operations: ExecuteWord, ExecuteSequence, ListModules, GetModuleInfo
13
+ - Error handling with rich context
14
+
15
+ ## Version History
16
+
17
+ ### v1 (Current)
18
+ - Initial release supporting TypeScript ↔ Python ↔ Ruby ↔ Rust cross-runtime execution
19
+ - Full type serialization for all Forthic standard types
20
+ - Module discovery and introspection
21
+ - Enhanced error reporting with stack traces and context
22
+
23
+ ## Usage
24
+
25
+ The proto files are used to generate Ruby gRPC code:
26
+
27
+ ```bash
28
+ # Generate protobuf code
29
+ bundle exec rake generate_grpc
30
+ ```
31
+
32
+ The generated files are in `lib/forthic/grpc/`:
33
+ - `forthic_runtime_pb.rb` - Message definitions
34
+ - `forthic_runtime_services_pb.rb` - Service definitions
35
+
36
+ ## Future Versions
37
+
38
+ When protocol changes are needed:
39
+ 1. Copy current version to new directory (e.g., `v2/`)
40
+ 2. Make changes in new version
41
+ 3. Regenerate Ruby code from new version
42
+ 4. Update client/server to support multiple versions if needed
43
+ 5. Deprecate old versions with migration timeline
@@ -0,0 +1,200 @@
1
+ syntax = "proto3";
2
+
3
+ package forthic;
4
+
5
+ // Service for executing Forthic words across runtime boundaries
6
+ service ForthicRuntime {
7
+ // Execute a single word in the remote runtime
8
+ rpc ExecuteWord(ExecuteWordRequest) returns (ExecuteWordResponse);
9
+
10
+ // Execute a sequence of words in one remote call (batched execution optimization)
11
+ rpc ExecuteSequence(ExecuteSequenceRequest) returns (ExecuteSequenceResponse);
12
+
13
+ // Phase 3: Module discovery
14
+ // List available runtime-specific modules (excludes standard library)
15
+ rpc ListModules(ListModulesRequest) returns (ListModulesResponse);
16
+
17
+ // Get detailed information about a specific module
18
+ rpc GetModuleInfo(GetModuleInfoRequest) returns (GetModuleInfoResponse);
19
+ }
20
+
21
+ // Request to execute a word in a remote runtime
22
+ message ExecuteWordRequest {
23
+ // Name of the word to execute
24
+ string word_name = 1;
25
+
26
+ // Stack state before execution (simple types only for Phase 1)
27
+ repeated StackValue stack = 2;
28
+ }
29
+
30
+ // Response from executing a word
31
+ message ExecuteWordResponse {
32
+ // Stack state after execution
33
+ repeated StackValue result_stack = 1;
34
+
35
+ // Error information if execution failed
36
+ optional ErrorInfo error = 2;
37
+ }
38
+
39
+ // Request to execute a sequence of words in one batch
40
+ // Optimization: Reduces RPC round-trips for consecutive remote words
41
+ message ExecuteSequenceRequest {
42
+ // Names of words to execute in order
43
+ repeated string word_names = 1;
44
+
45
+ // Initial stack state before execution
46
+ repeated StackValue stack = 2;
47
+ }
48
+
49
+ // Response from executing a word sequence
50
+ message ExecuteSequenceResponse {
51
+ // Stack state after executing all words
52
+ repeated StackValue result_stack = 1;
53
+
54
+ // Error information if any word failed
55
+ optional ErrorInfo error = 2;
56
+ }
57
+
58
+ // Represents a value on the Forthic stack
59
+ // Phase 2: Supports all basic Forthic types including arrays and records
60
+ // Phase 8: Added temporal types (instant, plain_date, zoned_datetime)
61
+ message StackValue {
62
+ oneof value {
63
+ int64 int_value = 1;
64
+ string string_value = 2;
65
+ bool bool_value = 3;
66
+ double float_value = 4;
67
+ NullValue null_value = 5;
68
+ ArrayValue array_value = 6;
69
+ RecordValue record_value = 7;
70
+ InstantValue instant_value = 8;
71
+ PlainDateValue plain_date_value = 9;
72
+ ZonedDateTimeValue zoned_datetime_value = 10;
73
+ }
74
+ }
75
+
76
+ // Represents null/None value
77
+ message NullValue {
78
+ // Empty message, presence indicates null
79
+ }
80
+
81
+ // Represents an array (list) of values
82
+ message ArrayValue {
83
+ repeated StackValue items = 1;
84
+ }
85
+
86
+ // Represents a record (object/dict) with string keys
87
+ message RecordValue {
88
+ map<string, StackValue> fields = 1;
89
+ }
90
+
91
+ // Phase 8: Temporal Types
92
+
93
+ // Represents an instant in time (UTC timestamp)
94
+ // Serialized as ISO 8601 string with timezone (e.g., "2025-01-15T10:30:00Z")
95
+ // Maps to:
96
+ // - TypeScript: Temporal.Instant
97
+ // - Python: datetime.datetime (timezone-aware)
98
+ message InstantValue {
99
+ string iso8601 = 1;
100
+ }
101
+
102
+ // Represents a calendar date without time or timezone
103
+ // Serialized as ISO 8601 date string (e.g., "2025-01-15")
104
+ // Maps to:
105
+ // - TypeScript: Temporal.PlainDate
106
+ // - Python: datetime.date
107
+ message PlainDateValue {
108
+ string iso8601_date = 1;
109
+ }
110
+
111
+ // Represents a date-time with timezone
112
+ // Serialized as ISO 8601 string with timezone (e.g., "2025-01-15T10:30:00-05:00[America/New_York]")
113
+ // Maps to:
114
+ // - TypeScript: Temporal.ZonedDateTime
115
+ // - Python: datetime.datetime (timezone-aware with tzinfo)
116
+ message ZonedDateTimeValue {
117
+ string iso8601 = 1;
118
+ string timezone = 2; // IANA timezone name (e.g., "America/New_York")
119
+ }
120
+
121
+ // Phase 9: Enhanced error information from remote execution
122
+ message ErrorInfo {
123
+ // Error message
124
+ string message = 1;
125
+
126
+ // Runtime where error occurred (e.g., "python", "ruby", "typescript")
127
+ string runtime = 2;
128
+
129
+ // Stack trace from the remote runtime
130
+ repeated string stack_trace = 3;
131
+
132
+ // Error type/class name (e.g., "ValueError", "TypeError")
133
+ string error_type = 4;
134
+
135
+ // Word location where error occurred (if available)
136
+ optional string word_location = 5;
137
+
138
+ // Module name where error occurred (if available)
139
+ optional string module_name = 6;
140
+
141
+ // Additional context information
142
+ map<string, string> context = 7;
143
+ }
144
+
145
+ // Phase 3: Module Discovery Messages
146
+
147
+ // Request to list available runtime-specific modules
148
+ message ListModulesRequest {
149
+ // Empty for now - future: could add filters
150
+ }
151
+
152
+ // Response with list of available modules
153
+ message ListModulesResponse {
154
+ repeated ModuleSummary modules = 1;
155
+ }
156
+
157
+ // Summary information about a module
158
+ message ModuleSummary {
159
+ // Module name
160
+ string name = 1;
161
+
162
+ // Brief description
163
+ string description = 2;
164
+
165
+ // Number of words in the module
166
+ int32 word_count = 3;
167
+
168
+ // Whether this is a runtime-specific module (e.g., pandas)
169
+ bool runtime_specific = 4;
170
+ }
171
+
172
+ // Request detailed information about a specific module
173
+ message GetModuleInfoRequest {
174
+ // Name of the module
175
+ string module_name = 1;
176
+ }
177
+
178
+ // Response with detailed module information
179
+ message GetModuleInfoResponse {
180
+ // Module name
181
+ string name = 1;
182
+
183
+ // Module description
184
+ string description = 2;
185
+
186
+ // List of words in the module
187
+ repeated WordInfo words = 3;
188
+ }
189
+
190
+ // Information about a single word
191
+ message WordInfo {
192
+ // Word name
193
+ string name = 1;
194
+
195
+ // Stack effect notation (e.g., "( a b -- c )")
196
+ string stack_effect = 2;
197
+
198
+ // Description of what the word does
199
+ string description = 3;
200
+ }