interscript 2.4.5 → 3.0.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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -0
  3. data/docs/demo/20191118-interscript-demo-cast.gif +0 -0
  4. data/exe/codemod-imp-to-isc +5 -0
  5. data/exe/diagnose_parse_failures +47 -0
  6. data/exe/interscript +2 -1
  7. data/exe/verify_isc_deep +211 -0
  8. data/exe/verify_isc_equivalence +128 -0
  9. data/interscript.gemspec +27 -20
  10. data/lib/interscript/command.rb +21 -12
  11. data/lib/interscript/compiler/javascript.rb +34 -35
  12. data/lib/interscript/compiler/json_ir.rb +214 -0
  13. data/lib/interscript/compiler/python.rb +354 -0
  14. data/lib/interscript/compiler/ruby.rb +23 -22
  15. data/lib/interscript/compiler.rb +20 -3
  16. data/lib/interscript/detector.rb +14 -7
  17. data/lib/interscript/dsl/aliases.rb +1 -1
  18. data/lib/interscript/dsl/document.rb +6 -3
  19. data/lib/interscript/dsl/group/parallel.rb +1 -1
  20. data/lib/interscript/dsl/group.rb +18 -9
  21. data/lib/interscript/dsl/items.rb +28 -16
  22. data/lib/interscript/dsl/metadata.rb +19 -18
  23. data/lib/interscript/dsl/stage.rb +1 -1
  24. data/lib/interscript/dsl/symbol_mm.rb +4 -2
  25. data/lib/interscript/dsl/tests.rb +1 -1
  26. data/lib/interscript/dsl.rb +21 -13
  27. data/lib/interscript/interpreter.rb +32 -21
  28. data/lib/interscript/isc/codemod.rb +791 -0
  29. data/lib/interscript/isc/document_builder.rb +354 -0
  30. data/lib/interscript/isc/generator.rb +191 -0
  31. data/lib/interscript/isc/grammar/concerns/aliases.rb +29 -0
  32. data/lib/interscript/isc/grammar/concerns/dependencies.rb +23 -0
  33. data/lib/interscript/isc/grammar/concerns/items.rb +176 -0
  34. data/lib/interscript/isc/grammar/concerns/metadata.rb +142 -0
  35. data/lib/interscript/isc/grammar/concerns/primitives.rb +112 -0
  36. data/lib/interscript/isc/grammar/concerns/stages.rb +129 -0
  37. data/lib/interscript/isc/grammar/concerns/system.rb +37 -0
  38. data/lib/interscript/isc/grammar/concerns/tests.rb +31 -0
  39. data/lib/interscript/isc/grammar/concerns.rb +18 -0
  40. data/lib/interscript/isc/grammar/core.rb +23 -0
  41. data/lib/interscript/isc/grammar/isc.artifact.json +1 -0
  42. data/lib/interscript/isc/grammar/isc.parg +196 -0
  43. data/lib/interscript/isc/grammar.rb +12 -0
  44. data/lib/interscript/isc/items.rb +185 -0
  45. data/lib/interscript/isc/model/alias.rb +20 -0
  46. data/lib/interscript/isc/model/constraint.rb +20 -0
  47. data/lib/interscript/isc/model/dependency.rb +19 -0
  48. data/lib/interscript/isc/model/document.rb +31 -0
  49. data/lib/interscript/isc/model/item.rb +50 -0
  50. data/lib/interscript/isc/model/rule.rb +23 -0
  51. data/lib/interscript/isc/model/stage.rb +20 -0
  52. data/lib/interscript/isc/model/stage_item.rb +33 -0
  53. data/lib/interscript/isc/model/test.rb +21 -0
  54. data/lib/interscript/isc/model.rb +19 -0
  55. data/lib/interscript/isc/node_adapter.rb +310 -0
  56. data/lib/interscript/isc/normalizer.rb +127 -0
  57. data/lib/interscript/isc/parser.rb +78 -0
  58. data/lib/interscript/isc/serializer.rb +248 -0
  59. data/lib/interscript/isc/transform.rb +138 -0
  60. data/lib/interscript/isc/yaml_bridge.rb +236 -0
  61. data/lib/interscript/isc.rb +30 -0
  62. data/lib/interscript/ml/byt5_onnx.rb +102 -0
  63. data/lib/interscript/ml/imf.rb +182 -0
  64. data/lib/interscript/ml/model.rb +52 -0
  65. data/lib/interscript/ml/provisioning.rb +189 -0
  66. data/lib/interscript/ml/translator.rb +38 -0
  67. data/lib/interscript/ml/vocab.rb +48 -0
  68. data/lib/interscript/ml.rb +25 -0
  69. data/lib/interscript/node/alias_def.rb +5 -5
  70. data/lib/interscript/node/dependency.rb +7 -7
  71. data/lib/interscript/node/document.rb +10 -10
  72. data/lib/interscript/node/group.rb +11 -9
  73. data/lib/interscript/node/item/alias.rb +14 -10
  74. data/lib/interscript/node/item/any.rb +14 -7
  75. data/lib/interscript/node/item/capture.rb +14 -9
  76. data/lib/interscript/node/item/group.rb +22 -17
  77. data/lib/interscript/node/item/repeat.rb +3 -3
  78. data/lib/interscript/node/item/stage.rb +4 -5
  79. data/lib/interscript/node/item/string.rb +18 -13
  80. data/lib/interscript/node/item.rb +27 -17
  81. data/lib/interscript/node/metadata.rb +6 -5
  82. data/lib/interscript/node/rule/funcall.rb +4 -5
  83. data/lib/interscript/node/rule/run.rb +4 -5
  84. data/lib/interscript/node/rule/sub.rb +55 -55
  85. data/lib/interscript/node/rule.rb +6 -5
  86. data/lib/interscript/node/stage.rb +11 -14
  87. data/lib/interscript/node/tests.rb +6 -6
  88. data/lib/interscript/node.rb +14 -17
  89. data/lib/interscript/stdlib/functions/rababa_adapter.rb +56 -0
  90. data/lib/interscript/stdlib/functions/secryst_adapter.rb +37 -0
  91. data/lib/interscript/stdlib/functions.rb +49 -0
  92. data/lib/interscript/stdlib.rb +25 -95
  93. data/lib/interscript/utils/helpers.rb +8 -5
  94. data/lib/interscript/utils/regexp_converter.rb +143 -144
  95. data/lib/interscript/version.rb +1 -1
  96. data/lib/interscript/visualize/json.rb +12 -13
  97. data/lib/interscript/visualize/nodes.rb +13 -13
  98. data/lib/interscript/visualize.rb +18 -18
  99. data/lib/interscript.rb +59 -45
  100. metadata +88 -31
  101. data/.github/workflows/assets.yml +0 -91
  102. data/.github/workflows/rake.yml +0 -52
  103. data/.github/workflows/release.yml +0 -68
  104. data/.gitignore +0 -79
  105. data/.rspec +0 -3
  106. data/Gemfile +0 -38
  107. data/Rakefile +0 -139
  108. data/bin/console +0 -10
  109. data/bin/interscript +0 -5
  110. data/bin/maps_analyze_staging +0 -168
  111. data/bin/maps_debug_compilers +0 -58
  112. data/bin/maps_debug_ordering +0 -88
  113. data/bin/maps_debug_ruby_compile +0 -24
  114. data/bin/maps_debug_step_by_step +0 -44
  115. data/bin/maps_optimize_order +0 -112
  116. data/bin/maps_v1_analyze_regexps +0 -45
  117. data/bin/maps_v1_to_v2 +0 -426
  118. data/bin/set_version +0 -16
  119. data/bin/setup +0 -8
  120. data/requirements.txt +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d70ba0bcf8f6cf72951983b6aacd3a0089e2d79ceb62b441bd4549a7731bfcb
4
- data.tar.gz: 1fd552a2803d7c67cd12ec7ed3f2b956c1fc603d624673e3cace9f2db3bc2adb
3
+ metadata.gz: a04dd91ecaf1ba6e202f6d61ddae77a1fa97afe3ae2f40874dac43142e09baa3
4
+ data.tar.gz: 7315cdfb66bfae1620920ae97f3cc363ace436d8506c227dbb449a48cb7fac10
5
5
  SHA512:
6
- metadata.gz: e0ed290794bc0724ece2c4e9ce547fe7a716566b119061c3ee6ec65240b92d81b2b41be833a257ca130aba9f440294ad9626accc4fd905d96a21a644fdaa6c00
7
- data.tar.gz: ba5ed46ba6dddf8c422fbc671492e41bc8898ed0cb1622e63310a77c6972352ee6285f9eb01d038f576dbd926a120ea41f7aa41fb60df66318c2274144551634
6
+ metadata.gz: df34de55722a54f9942b0d24eef322bc1b2e54fa4cb587249e98da56fb6bad21e839038922ca9555edbe0ccdb0f8a278dcbd15026dff3160e91ce5fd334c3220
7
+ data.tar.gz: a88b209689466f418cd6af6c31cca544e656c4df98215120cdc2d1f5131aa8781c67803c3729da1aa3c7e7d8da47f438ccfee84203e3568f6c44c88843def7c2
data/README.md CHANGED
@@ -1,3 +1,18 @@
1
+ image:https://github.com/interscript/interscript-ruby/actions/workflows/rake.yml/badge.svg["CI status", link="https://github.com/interscript/interscript-ruby/actions/workflows/rake.yml"]
1
2
  # Interscript
2
3
 
3
4
  Documentation is available directory higher in file README.adoc.
5
+
6
+ ## Phonological layer (optional)
7
+
8
+ Some transliteration systems need their input vocalized before the map
9
+ can apply (undiacritized Arabic, nikud-less Hebrew, unsegmented Thai).
10
+ The stdlib ships adapters for this: the `secryst` function dispatches
11
+ to a [secryst crystal](https://www.secryst.org) —
12
+ `gem 'secryst'` (Ruby), `pip install secryst`, or `npm i secryst` —
13
+ which resolves a model id through the
14
+ [interscript-ml `models.yaml` index](https://github.com/interscript/interscript-ml)
15
+ and runs sha256-verified ONNX inference locally. The gem has no
16
+ interscript dependency: a crystal can be used standalone for any G2P
17
+ need, and an engine without a crystal simply cannot execute maps that
18
+ declare a vocalization step.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "interscript/isc"
5
+ Interscript::Isc::Codemod.run(ARGV)
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ # Find first failing line in each failing .isc file
3
+ # Usage: diagnose_parse_failures.rb [N] # show first N failures with context
4
+
5
+ require "interscript"
6
+ require "interscript/isc"
7
+
8
+ n = (ARGV.shift || "5").to_i
9
+ fails = []
10
+ Dir.glob("/tmp/isc-verify/*.isc").sort.each do |path|
11
+ src = File.read(path)
12
+ begin
13
+ Interscript::Isc::Parser.parse(src, filename: File.basename(path))
14
+ rescue Interscript::Isc::ParseError
15
+ fails << path
16
+ end
17
+ end
18
+
19
+ puts "Total fails: #{fails.size}"
20
+ puts
21
+
22
+ fails.first(n).each do |path|
23
+ src = File.read(path)
24
+ lines = src.lines
25
+ # Binary search for first failing prefix
26
+ lo = 1
27
+ hi = lines.size
28
+ while lo < hi
29
+ mid = (lo + hi) / 2
30
+ snippet = lines[0...mid].join
31
+ open = snippet.count("{") - snippet.count("}")
32
+ snippet += "}" * open
33
+ begin
34
+ Interscript::Isc::Parser.parse(snippet, filename: path)
35
+ lo = mid + 1
36
+ rescue
37
+ hi = mid
38
+ end
39
+ end
40
+ puts "=== #{File.basename(path)} (first fail at line #{lo}) ==="
41
+ start = [1, lo - 2].max
42
+ lines[(start - 1)...(lo + 1)].each_with_index do |line, i|
43
+ marker = (start + i == lo) ? ">>" : " "
44
+ puts " #{marker} #{start + i}: #{line.chomp[0..100]}"
45
+ end
46
+ puts
47
+ end
data/exe/interscript CHANGED
@@ -1,4 +1,5 @@
1
- require 'interscript/command'
1
+ #!/usr/bin/env ruby
2
+ require "interscript/command"
2
3
 
3
4
  if ARGV.any? && !Interscript::Command.all_tasks.key?(ARGV.first)
4
5
  ARGV.unshift :translit
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env ruby
2
+ # Deep equivalence checker: compares ISC parser output against Ruby DSL
3
+ # at the rule level (from/to/constraints), not just test counts.
4
+
5
+ require "interscript"
6
+ require "interscript/isc"
7
+ require "json"
8
+
9
+ class DeepVerifier
10
+ Result = Struct.new(:status, :details, :imp_data, :isc_data)
11
+
12
+ def initialize
13
+ @results = []
14
+ end
15
+
16
+ def verify(imp_path, isc_path)
17
+ imp_data = extract_imp(imp_path)
18
+ isc_data = extract_isc(isc_path)
19
+
20
+ return Result.new(:both_fail, "Both parsers failed", nil, nil) if imp_data.nil? && isc_data.nil?
21
+ return Result.new(:imp_fail, "Ruby DSL parse failed", nil, isc_data) if imp_data.nil?
22
+ return Result.new(:isc_fail, "ISC parse failed", imp_data, nil) if isc_data.nil?
23
+
24
+ details = []
25
+
26
+ # Compare metadata — skip DSL-internal fields only.
27
+ # STANDARD_ARRAY_KEYS bug is fixed, so url/notes/etc. are now stored.
28
+ skip_fields = [:nonstandard, :tests, :stages, :aliases, :dependencies]
29
+ imp_meta = imp_data[:metadata].reject { |k, _| skip_fields.include?(k.to_sym) }
30
+ isc_meta = isc_data[:metadata].reject { |k, _| skip_fields.include?(k.to_sym) }
31
+ (imp_meta.keys | isc_meta.keys).each do |key|
32
+ imp_val = normalize_meta(imp_meta[key])
33
+ isc_val = normalize_meta(isc_meta[key])
34
+ next if imp_val == isc_val
35
+ next if imp_val.nil? && isc_val.to_s.strip == ""
36
+ next if isc_val.nil? && imp_val.to_s.strip == ""
37
+ details << "metadata[#{key}]: imp=#{imp_val.inspect[0..60]} isc=#{isc_val.inspect[0..60]}"
38
+ end
39
+
40
+ # Compare tests
41
+ Set.new(imp_data[:tests])
42
+ isc_tests = Set.new(isc_data[:tests])
43
+ missing = imp_data[:tests].reject { |t| isc_tests.include?(t) }
44
+ details << "missing #{missing.size} tests from ISC" if missing.any?
45
+
46
+ # Compare aliases
47
+ imp_aliases = imp_data[:aliases].to_h
48
+ isc_aliases = isc_data[:aliases].to_h
49
+ if imp_aliases.keys.sort != isc_aliases.keys.sort
50
+ details << "alias names differ: imp=#{imp_aliases.keys.sort} isc=#{isc_aliases.keys.sort}"
51
+ end
52
+
53
+ # Compare rule counts per stage
54
+ imp_rules = imp_data[:rule_counts]
55
+ isc_rules = isc_data[:rule_counts]
56
+ if imp_rules != isc_rules
57
+ details << "rule counts: imp=#{imp_rules} isc=#{isc_rules}"
58
+ end
59
+
60
+ status = details.empty? ? :equivalent : :differ
61
+ Result.new(status, details.join("; "), imp_data, isc_data)
62
+ rescue => e
63
+ Result.new(:error, "#{e.class}: #{e.message[0..100]}", nil, nil)
64
+ end
65
+
66
+ private
67
+
68
+ def extract_imp(path)
69
+ dsl = Interscript::DSL.parse(File.basename(path, ".imp"))
70
+ hash = dsl.to_hash
71
+
72
+ metadata = hash[:metadata]&.fetch(:data, {}) || {}
73
+ tests = (dsl.tests&.data || []).map { |t| [t[0], t[1]] }
74
+ aliases = (hash[:aliases] || {}).map { |k, v| [k.to_s, rule_to_s(v)] }
75
+
76
+ rule_counts = {}
77
+ hash[:stages]&.each do |name, stage|
78
+ rule_counts[name] = count_rules(stage[:children])
79
+ end
80
+
81
+ {
82
+ metadata: metadata,
83
+ tests: tests,
84
+ aliases: aliases,
85
+ rule_counts: rule_counts
86
+ }
87
+ rescue => e
88
+ warn "IMP fail #{path}: #{e.message[0..80]}"
89
+ nil
90
+ end
91
+
92
+ def extract_isc(path)
93
+ src = File.read(path)
94
+ tree = Interscript::Isc::Parser.parse(src, filename: File.basename(path))
95
+ doc = Interscript::Isc::DocumentBuilder.build(tree, filename: File.basename(path))
96
+
97
+ metadata = doc[:metadata] || {}
98
+ tests = doc[:tests].map { |t| [t[:input], t[:expected]] }
99
+ aliases = (doc[:aliases] || []).map { |a| [a[:name], item_to_s(a[:value])] }
100
+
101
+ rule_counts = {}
102
+ doc[:stages].each do |stage|
103
+ rule_counts[stage[:name].to_sym] = count_stage_rules(stage[:body])
104
+ end
105
+
106
+ {
107
+ metadata: metadata,
108
+ tests: tests,
109
+ aliases: aliases,
110
+ rule_counts: rule_counts
111
+ }
112
+ rescue => e
113
+ warn "ISC fail #{path}: #{e.message[0..80]}"
114
+ nil
115
+ end
116
+
117
+ def normalize_meta(val)
118
+ case val
119
+ when String then val.gsub(/\s+/, " ").strip
120
+ when Array then val.map { |v| normalize_meta(v) }.reject { |v| v == "" }
121
+ when Hash then val.transform_values { |v| normalize_meta(v) }
122
+ else val
123
+ end
124
+ end
125
+
126
+ def count_rules(children)
127
+ return 0 unless children.is_a?(Array)
128
+ count = 0
129
+ children.each do |child|
130
+ case child[:class].to_s
131
+ when /Group/
132
+ count += count_rules(child[:children])
133
+ when /Rule/
134
+ count += 1
135
+ end
136
+ end
137
+ count
138
+ end
139
+
140
+ def count_stage_rules(body)
141
+ return 0 unless body.is_a?(Array)
142
+ count = 0
143
+ body.each do |item|
144
+ case item[:kind]
145
+ when :sequence, :parallel
146
+ count += item[:rules].size
147
+ when :bare_rule, :sub, :run, :separate, :compose, :string_case
148
+ count += 1
149
+ end
150
+ end
151
+ count
152
+ end
153
+
154
+ def rule_to_s(node)
155
+ return "" unless node.is_a?(Hash)
156
+ data = node[:data]
157
+ data ? data.to_s : node.to_s
158
+ end
159
+
160
+ def item_to_s(item)
161
+ item.respond_to?(:value) ? item.value : item.to_s
162
+ end
163
+ end
164
+
165
+ if $PROGRAM_NAME == __FILE__
166
+ maps_dir = "/Users/mulgogi/src/interscript/maps/maps"
167
+ isc_dir = "/tmp/isc-verify"
168
+
169
+ v = DeepVerifier.new
170
+ results = {}
171
+ counts = Hash.new(0)
172
+
173
+ Dir.glob("#{maps_dir}/*.imp").sort.each do |imp_path|
174
+ base = File.basename(imp_path, ".imp")
175
+ isc_path = "#{isc_dir}/#{base}.isc"
176
+ next unless File.exist?(isc_path)
177
+
178
+ r = v.verify(imp_path, isc_path)
179
+ results[base] = r
180
+ counts[r.status] += 1
181
+ end
182
+
183
+ puts "=" * 60
184
+ puts "DEEP EQUIVALENCE REPORT"
185
+ puts "=" * 60
186
+ puts "Total maps: #{results.size}"
187
+ puts "Equivalent: #{counts[:equivalent]}"
188
+ puts "Differ: #{counts[:differ]}"
189
+ puts "IMP fail: #{counts[:imp_fail]}"
190
+ puts "ISC fail: #{counts[:isc_fail]}"
191
+ puts "Both fail: #{counts[:both_fail]}"
192
+ puts "Errors: #{counts[:error]}"
193
+ puts
194
+
195
+ if counts[:differ] > 0
196
+ puts "Differences:"
197
+ results.select { |_, r| r.status == :differ }.each do |base, r|
198
+ puts " #{base}: #{r.details}"
199
+ end
200
+ end
201
+
202
+ File.write("/tmp/deep_report.json", JSON.pretty_generate(
203
+ results.transform_values do |r|
204
+ {
205
+ status: r.status,
206
+ details: r.details
207
+ }
208
+ end
209
+ ))
210
+ puts "Full report: /tmp/deep_report.json"
211
+ end
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env ruby
2
+ # Verification harness: for each .imp file, compare Ruby DSL parse vs ISC parse.
3
+ # Reports per-file equivalence status.
4
+
5
+ require "interscript"
6
+ require "interscript/isc"
7
+ require "json"
8
+
9
+ class Verifier
10
+ Result = Struct.new(:status, :details, :imp_data, :isc_data)
11
+
12
+ def initialize
13
+ @results = []
14
+ end
15
+
16
+ def verify(imp_path, isc_path)
17
+ imp_data = parse_imp(imp_path)
18
+ isc_data = parse_isc(isc_path)
19
+
20
+ return Result.new(:both_fail, "Both parsers failed", nil, nil) if imp_data.nil? && isc_data.nil?
21
+ return Result.new(:imp_fail, "Ruby DSL parse failed", nil, isc_data) if imp_data.nil?
22
+ return Result.new(:isc_fail, "ISC parse failed", imp_data, nil) if isc_data.nil?
23
+
24
+ # Compare semantic equivalence on the dimensions we can compare:
25
+ # - If ISC tests are a SUPERSET of IMP tests, count as equivalent
26
+ # (ISC parser is more capable; extra tests are legitimately parsed)
27
+ # - If counts differ and tests don't match, it's a real difference
28
+ details = []
29
+ imp_set = Set.new(imp_data[:tests])
30
+ isc_set = Set.new(isc_data[:tests])
31
+
32
+ missing_from_isc = imp_data[:tests].reject { |t| isc_set.include?(t) }
33
+ isc_data[:tests].reject { |t| imp_set.include?(t) }
34
+
35
+ if missing_from_isc.any?
36
+ details << "missing #{missing_from_isc.size} tests from ISC"
37
+ end
38
+ if imp_data[:stages_count] != isc_data[:stages_count]
39
+ details << "stages: imp=#{imp_data[:stages_count]} isc=#{isc_data[:stages_count]}"
40
+ end
41
+
42
+ status = details.empty? ? :equivalent : :differ
43
+ Result.new(status, details.join("; "), imp_data, isc_data)
44
+ rescue => e
45
+ Result.new(:error, "#{e.class}: #{e.message[0..100]}", nil, nil)
46
+ end
47
+
48
+ private
49
+
50
+ def parse_imp(path)
51
+ dsl = Interscript::DSL.parse(File.basename(path, ".imp"))
52
+ tests_data = (dsl.tests&.data if dsl.tests.respond_to?(:data)) || []
53
+ tests = tests_data.map { |t| [t[0], t[1]] }
54
+ {
55
+ tests_count: tests.size,
56
+ tests: tests,
57
+ stages_count: dsl.stages&.size || 0
58
+ }
59
+ rescue => e
60
+ warn "IMP parse fail #{path}: #{e.message[0..80]}"
61
+ nil
62
+ end
63
+
64
+ def parse_isc(path)
65
+ src = File.read(path)
66
+ tree = Interscript::Isc::Parser.parse(src, filename: File.basename(path))
67
+ doc = Interscript::Isc::DocumentBuilder.build(tree, filename: File.basename(path))
68
+ {
69
+ tests_count: doc[:tests].size,
70
+ tests: doc[:tests].map { |t| [t[:input], t[:expected]] },
71
+ stages_count: doc[:stages].size
72
+ }
73
+ rescue => e
74
+ warn "ISC parse fail #{path}: #{e.message[0..80]}"
75
+ nil
76
+ end
77
+ end
78
+
79
+ if $PROGRAM_NAME == __FILE__
80
+ maps_dir = "/Users/mulgogi/src/interscript/maps/maps"
81
+ isc_dir = "/tmp/isc-verify"
82
+
83
+ v = Verifier.new
84
+ results = {}
85
+ counts = Hash.new(0)
86
+
87
+ Dir.glob("#{maps_dir}/*.imp").sort.each do |imp_path|
88
+ base = File.basename(imp_path, ".imp")
89
+ isc_path = "#{isc_dir}/#{base}.isc"
90
+ next unless File.exist?(isc_path)
91
+
92
+ r = v.verify(imp_path, isc_path)
93
+ results[base] = r
94
+ counts[r.status] += 1
95
+ end
96
+
97
+ puts "=" * 60
98
+ puts "VERIFICATION REPORT"
99
+ puts "=" * 60
100
+ puts "Total maps: #{results.size}"
101
+ puts "Equivalent: #{counts[:equivalent]}"
102
+ puts "Differ: #{counts[:differ]}"
103
+ puts "IMP fail: #{counts[:imp_fail]}"
104
+ puts "ISC fail: #{counts[:isc_fail]}"
105
+ puts "Both fail: #{counts[:both_fail]}"
106
+ puts "Errors: #{counts[:error]}"
107
+ puts
108
+
109
+ if counts[:differ] > 0
110
+ puts "First 10 differences:"
111
+ results.select { |_, r| r.status == :differ }.first(10).each do |base, r|
112
+ puts " #{base}: #{r.details}"
113
+ end
114
+ end
115
+
116
+ # Save full report
117
+ File.write("/tmp/verification_report.json", JSON.pretty_generate(
118
+ results.transform_values do |r|
119
+ {
120
+ status: r.status,
121
+ details: r.details,
122
+ imp: r.imp_data,
123
+ isc: r.isc_data
124
+ }
125
+ end
126
+ ))
127
+ puts "Full report: /tmp/verification_report.json"
128
+ end
data/interscript.gemspec CHANGED
@@ -1,32 +1,39 @@
1
- require_relative 'lib/interscript/version'
1
+ require_relative "lib/interscript/version"
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.name = "interscript"
5
- spec.version = Interscript::VERSION
6
- spec.summary = %q{Interoperable script conversion systems}
7
- spec.description = %q{Interoperable script conversion systems}
8
- spec.authors = ["Ribose Inc."]
9
- spec.email = ["open.source@ribose.com"]
4
+ spec.name = "interscript"
5
+ spec.version = Interscript::VERSION
6
+ spec.summary = "Interoperable script conversion systems"
7
+ spec.description = "Interoperable script conversion systems."
8
+ spec.authors = ["Ribose Inc."]
9
+ spec.email = ["open.source@ribose.com"]
10
10
 
11
- spec.date = %q{2019-11-17}
12
- spec.homepage = "https://www.interscript.com"
13
- spec.license = "BSD-2-Clause"
14
-
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
11
+ spec.homepage = "https://www.interscript.com"
12
+ spec.license = "BSD-2-Clause"
13
+ spec.required_ruby_version = ">= 3.3.0"
16
14
 
17
15
  spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = "https://github.com/interscript/interscript"
16
+ spec.metadata["source_code_uri"] = "https://github.com/interscript/interscript-ruby"
17
+ spec.metadata["changelog_uri"] = "https://github.com/interscript/interscript-ruby/releases"
18
+ spec.metadata["bug_tracker_uri"] = "https://github.com/interscript/interscript-ruby/issues"
19
+ spec.metadata["rubygems_mfa_required"] = "true"
19
20
 
20
- # Specify which files should be added to the gem when it is released.
21
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ spec.files = Dir.chdir(__dir__) do
22
+ Dir[
23
+ "lib/**/*",
24
+ "exe/**/*",
25
+ "docs/**/*",
26
+ "README*",
27
+ "LICENSE*",
28
+ "*.gemspec"
29
+ ].select { |f| File.file?(f) }
24
30
  end
25
- spec.bindir = "exe"
26
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
33
  spec.require_paths = ["lib"]
28
34
 
29
35
  spec.add_dependency "thor"
30
- spec.add_dependency "interscript-maps", "~> #{Interscript::VERSION.split('.')[0,2].join(".")}.0a"
36
+ spec.add_dependency "interscript-maps", "~> 2.5"
31
37
  spec.add_dependency "text"
38
+ spec.add_dependency "parsanol", ">= 1.3.61", "< 2"
32
39
  end
@@ -1,32 +1,41 @@
1
- require 'thor'
2
- require 'interscript'
3
- require 'json'
1
+ require "thor"
2
+ require "interscript"
3
+ require "json"
4
4
 
5
5
  module Interscript
6
6
  # Command line interface
7
7
  class Command < Thor
8
- desc '<file>', 'Transliterate text'
9
- option :system, aliases: '-s', required: true, desc: 'Transliteration system'
10
- option :output, aliases: '-o', required: false, desc: 'Output file'
8
+ desc "<file>", "Transliterate text"
9
+ option :system, aliases: "-s", required: true, desc: "Transliteration system"
10
+ option :output, aliases: "-o", required: false, desc: "Output file"
11
+ option :compiler, aliases: "-c", required: false, desc: "Compiler (eg. Interscript::Compiler::Python)"
11
12
  # Was this option really well thought out? The last parameter is a cache, isn't it?
12
- #option :map, aliases: '-m', required: false, default: "{}", desc: 'Transliteration mapping json'
13
+ # option :map, aliases: '-m', required: false, default: "{}", desc: 'Transliteration mapping json'
13
14
 
14
15
  def translit(input)
16
+ compiler = if options[:compiler]
17
+ compiler = options[:compiler].split("::").last.downcase
18
+ require "interscript/compiler/#{compiler}"
19
+ Object.const_get(options[:compiler])
20
+ else
21
+ Interscript::Interpreter
22
+ end
23
+
15
24
  if options[:output]
16
- Interscript.transliterate_file(options[:system], input, options[:output]) #, JSON.parse(options[:map]))
25
+ Interscript.transliterate_file(options[:system], input, options[:output], compiler: compiler)
17
26
  else
18
- puts Interscript.transliterate(options[:system], IO.read(input))
27
+ puts Interscript.transliterate(options[:system], IO.read(input), compiler: compiler)
19
28
  end
20
29
  end
21
30
 
22
- desc 'list', 'Prints allowed transliteration systems'
31
+ desc "list", "Prints allowed transliteration systems"
23
32
  def list
24
33
  Interscript.maps(load_path: true).each do |path|
25
34
  puts path
26
35
  end
27
36
  end
28
37
 
29
- desc 'stats', 'Prints statistics about the maps we have'
38
+ desc "stats", "Prints statistics about the maps we have"
30
39
  def stats
31
40
  maps = Interscript.maps(load_path: true)
32
41
  parsed_maps = maps.map { |i| [i, Interscript.parse(i)] }.to_h
@@ -35,7 +44,7 @@ module Interscript
35
44
  end
36
45
 
37
46
  authorities, languages, source_scripts, target_scripts = 4.times.map do |i|
38
- maps.group_by { |map| map.split('-')[i] }
47
+ maps.group_by { |map| map.split("-")[i] }
39
48
  end
40
49
 
41
50
  puts <<~END