forthic 0.3.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: edd1101930e744ff0289d9a71f7449645852a558c7f12b4f4b0fe8bb211278cd
4
- data.tar.gz: ea53ba5d3f04fca67b2088debe870eba40e9c2b1bca04061095420d0b92de8cd
3
+ metadata.gz: ecff72dd063577e62dffecb04a0e67b8419f77eef928fce884448b067209cca9
4
+ data.tar.gz: 11e0fdec3cf805e4b893b27035288de08f6ff1e825e4aff08b0781ac1627d544
5
5
  SHA512:
6
- metadata.gz: 8f193bcb169fe8b7a7f59c27978b3c296b61e791199b6e4c9797eb10d671ff091416ac5e7fc6ecd42f7bf70955dbbbf237fa7c97e8434ea3abd41e13a4c861e9
7
- data.tar.gz: 9bea9f3a03156019a3e3e23bf634c8988bd13180ec28faa4cb91d4256851c3cefbcd9928341e494e8f281a3f331bd4262d358c7fd1bb61db99c50e0983dcc13c
6
+ metadata.gz: c74d61d76a7244e8cecd9de1f816320aa2d0c99e9789d03558dc098d381207b3cc7ed2147b87cf7f4aea322a4de5a959c5579a34d57292acf4fdba4f93bad31a
7
+ data.tar.gz: 81fb605d6781127d0e6583a6ee936445ff16a81b1cbf1336b5bf789ce902d52c7a5f76f6695ed5f9584ae270b2bce0e616528ab928441cd62502dcf93d008041
@@ -133,7 +133,12 @@ module Forthic
133
133
  end
134
134
 
135
135
  # Create a zoned datetime literal handler with timezone support
136
- # Parses: 2025-05-24T10:15:00Z, 2025-05-24T10:15:00-05:00, 2025-05-24T10:15:00
136
+ # Parses ISO 8601 datetime with IANA timezone bracket notation:
137
+ # - 2025-05-20T08:00:00[America/Los_Angeles] (IANA timezone)
138
+ # - 2025-05-20T08:00:00-07:00[America/Los_Angeles] (offset + IANA)
139
+ # - 2025-05-24T10:15:00Z (UTC)
140
+ # - 2025-05-24T10:15:00-05:00 (offset only)
141
+ # - 2025-05-24T10:15:00 (uses default timezone)
137
142
  #
138
143
  # @param timezone [String] The default timezone to use
139
144
  # @return [Proc] A literal handler proc
@@ -142,6 +147,29 @@ module Forthic
142
147
  return nil unless str.include?("T")
143
148
 
144
149
  begin
150
+ # Extract IANA timezone from brackets if present
151
+ bracket_match = str.match(/\[([^\]]+)\]$/)
152
+
153
+ if bracket_match
154
+ # Extract IANA timezone name from brackets
155
+ tz_name = bracket_match[1]
156
+
157
+ # Extract datetime string (before bracket)
158
+ datetime_str = str[0...bracket_match.begin(0)]
159
+
160
+ # Parse datetime (may have offset)
161
+ # Handle Z suffix
162
+ datetime_str = datetime_str.sub(/Z$/, "+00:00")
163
+ time = Time.parse(datetime_str)
164
+
165
+ # Convert to specified timezone using TZInfo
166
+ require 'tzinfo'
167
+ tz = TZInfo::Timezone.get(tz_name)
168
+ return tz.to_local(time)
169
+ end
170
+
171
+ # No brackets - handle as before
172
+
145
173
  # Handle explicit UTC (Z suffix)
146
174
  if str.end_with?("Z")
147
175
  return Time.parse(str).utc
@@ -162,7 +190,9 @@ module Forthic
162
190
  ensure
163
191
  ENV['TZ'] = old_tz
164
192
  end
165
- rescue ArgumentError
193
+ rescue ArgumentError, TZInfo::InvalidTimezoneIdentifier
194
+ # ArgumentError: invalid time format
195
+ # TZInfo::InvalidTimezoneIdentifier: invalid timezone name
166
196
  nil
167
197
  end
168
198
  end
@@ -57,6 +57,7 @@ module Forthic
57
57
  @name = name
58
58
  @string = name
59
59
  @location = nil
60
+ @error_handlers = []
60
61
  end
61
62
 
62
63
  def set_location(location)
@@ -67,6 +68,34 @@ module Forthic
67
68
  @location
68
69
  end
69
70
 
71
+ def add_error_handler(&handler)
72
+ @error_handlers << handler
73
+ end
74
+
75
+ def remove_error_handler(handler)
76
+ @error_handlers.delete(handler)
77
+ end
78
+
79
+ def clear_error_handlers
80
+ @error_handlers.clear
81
+ end
82
+
83
+ def error_handlers
84
+ @error_handlers.dup
85
+ end
86
+
87
+ def try_error_handlers(error, interp)
88
+ @error_handlers.each do |handler|
89
+ begin
90
+ handler.call(error, self, interp)
91
+ return true # Handler succeeded
92
+ rescue => e
93
+ next # Try next handler
94
+ end
95
+ end
96
+ false # No handler succeeded
97
+ end
98
+
70
99
  def execute(_interp)
71
100
  raise NotImplementedError, "Must override Word#execute"
72
101
  end
@@ -205,6 +234,31 @@ module Forthic
205
234
  end
206
235
  end
207
236
 
237
+ # ModuleWord - Word that executes a handler function with error handling support
238
+ #
239
+ # Used for module words created via decorators or add_module_word().
240
+ # Integrates per-word error handler functionality. Ruby version is synchronous.
241
+ class ModuleWord < Word
242
+ attr_reader :handler
243
+
244
+ def initialize(name, &handler)
245
+ super(name)
246
+ @handler = handler
247
+ end
248
+
249
+ def execute(interp)
250
+ @handler.call(interp)
251
+ rescue IntentionalStopError => e
252
+ # Never handle intentional flow control errors
253
+ raise
254
+ rescue => e
255
+ # Try error handlers
256
+ handled = try_error_handlers(e, interp)
257
+ raise unless handled # Re-raise if not handled
258
+ # If handled, execution continues (error suppressed)
259
+ end
260
+ end
261
+
208
262
  # -------------------------------------
209
263
  # Module
210
264
 
@@ -337,13 +391,16 @@ module Forthic
337
391
  @exportable << word.name
338
392
  end
339
393
 
340
- def add_module_word(word_name, word_func)
341
- word = Word.new(word_name)
342
- # Define the execute method for this specific instance
343
- word.define_singleton_method(:execute) do |interp|
344
- word_func.call(interp)
345
- end
394
+ def add_module_word(word_name, word_func = nil, &block)
395
+ # Support both calling styles:
396
+ # 1. add_module_word("NAME", proc) - old style
397
+ # 2. add_module_word("NAME") { |interp| ... } - new style (block)
398
+ handler = block || word_func
399
+ raise ArgumentError, "Must provide either a proc or block" unless handler
400
+
401
+ word = ModuleWord.new(word_name, &handler)
346
402
  add_exportable_word(word)
403
+ word
347
404
  end
348
405
 
349
406
  def exportable_words
@@ -412,7 +412,24 @@ module Forthic
412
412
  advance_position(1)
413
413
  break if whitespace?(char)
414
414
 
415
- if [";", "[", "]", "{", "}", "#"].include?(char)
415
+ if char == "["
416
+ # Special case: if token contains 'T', this is likely a zoned datetime
417
+ # Include the bracketed timezone as part of the token
418
+ if @token_string.include?("T")
419
+ @token_string += char
420
+ # Continue gathering until closing bracket
421
+ while @input_pos < @input_string.length
422
+ char = @input_string[@input_pos]
423
+ advance_position(1)
424
+ @token_string += char
425
+ break if char == "]"
426
+ end
427
+ else
428
+ # Otherwise, '[' is a delimiter (for arrays)
429
+ advance_position(-1)
430
+ break
431
+ end
432
+ elsif [";", "]", "{", "}", "#"].include?(char)
416
433
  advance_position(-1)
417
434
  break
418
435
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forthic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rino Jose
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-09 00:00:00.000000000 Z
11
+ date: 2025-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec