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 +4 -4
- data/lib/forthic/literals.rb +32 -2
- data/lib/forthic/module.rb +63 -6
- data/lib/forthic/tokenizer.rb +18 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ecff72dd063577e62dffecb04a0e67b8419f77eef928fce884448b067209cca9
|
|
4
|
+
data.tar.gz: 11e0fdec3cf805e4b893b27035288de08f6ff1e825e4aff08b0781ac1627d544
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c74d61d76a7244e8cecd9de1f816320aa2d0c99e9789d03558dc098d381207b3cc7ed2147b87cf7f4aea322a4de5a959c5579a34d57292acf4fdba4f93bad31a
|
|
7
|
+
data.tar.gz: 81fb605d6781127d0e6583a6ee936445ff16a81b1cbf1336b5bf789ce902d52c7a5f76f6695ed5f9584ae270b2bce0e616528ab928441cd62502dcf93d008041
|
data/lib/forthic/literals.rb
CHANGED
|
@@ -133,7 +133,12 @@ module Forthic
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
# Create a zoned datetime literal handler with timezone support
|
|
136
|
-
# Parses
|
|
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
|
data/lib/forthic/module.rb
CHANGED
|
@@ -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
|
-
|
|
342
|
-
#
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
data/lib/forthic/tokenizer.rb
CHANGED
|
@@ -412,7 +412,24 @@ module Forthic
|
|
|
412
412
|
advance_position(1)
|
|
413
413
|
break if whitespace?(char)
|
|
414
414
|
|
|
415
|
-
if
|
|
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.
|
|
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
|
+
date: 2025-12-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|