interscript 0.1.6 → 2.1.0a9

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 (226) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +29 -0
  5. data/LICENSE.adoc +31 -0
  6. data/README.md +3 -0
  7. data/Rakefile +53 -0
  8. data/bin/console +14 -0
  9. data/bin/interscript +3 -39
  10. data/bin/maps_analyze_staging +168 -0
  11. data/bin/maps_debug_compilers +58 -0
  12. data/bin/maps_debug_ordering +88 -0
  13. data/bin/maps_debug_ruby_compile +24 -0
  14. data/bin/maps_debug_step_by_step +44 -0
  15. data/bin/maps_optimize_order +112 -0
  16. data/bin/maps_v1_analyze_regexps +45 -0
  17. data/bin/maps_v1_to_v2 +426 -0
  18. data/exe/interscript +6 -0
  19. data/interscript.gemspec +31 -0
  20. data/lib/interscript.rb +81 -127
  21. data/lib/interscript/command.rb +5 -5
  22. data/lib/interscript/compiler.rb +22 -0
  23. data/lib/interscript/compiler/javascript.rb +292 -0
  24. data/lib/interscript/compiler/ruby.rb +262 -0
  25. data/lib/interscript/dsl.rb +67 -0
  26. data/lib/interscript/dsl/aliases.rb +23 -0
  27. data/lib/interscript/dsl/document.rb +46 -0
  28. data/lib/interscript/dsl/group.rb +45 -0
  29. data/lib/interscript/dsl/group/parallel.rb +6 -0
  30. data/lib/interscript/dsl/items.rb +89 -0
  31. data/lib/interscript/dsl/metadata.rb +26 -0
  32. data/lib/interscript/dsl/stage.rb +6 -0
  33. data/lib/interscript/dsl/symbol_mm.rb +11 -0
  34. data/lib/interscript/dsl/tests.rb +12 -0
  35. data/lib/interscript/interpreter.rb +251 -0
  36. data/lib/interscript/node.rb +25 -0
  37. data/lib/interscript/node/alias_def.rb +15 -0
  38. data/lib/interscript/node/dependency.rb +13 -0
  39. data/lib/interscript/node/document.rb +45 -0
  40. data/lib/interscript/node/group.rb +34 -0
  41. data/lib/interscript/node/group/parallel.rb +9 -0
  42. data/lib/interscript/node/group/sequential.rb +2 -0
  43. data/lib/interscript/node/item.rb +52 -0
  44. data/lib/interscript/node/item/alias.rb +42 -0
  45. data/lib/interscript/node/item/any.rb +61 -0
  46. data/lib/interscript/node/item/capture.rb +50 -0
  47. data/lib/interscript/node/item/group.rb +51 -0
  48. data/lib/interscript/node/item/repeat.rb +40 -0
  49. data/lib/interscript/node/item/stage.rb +23 -0
  50. data/lib/interscript/node/item/string.rb +51 -0
  51. data/lib/interscript/node/metadata.rb +18 -0
  52. data/lib/interscript/node/rule.rb +6 -0
  53. data/lib/interscript/node/rule/funcall.rb +18 -0
  54. data/lib/interscript/node/rule/run.rb +15 -0
  55. data/lib/interscript/node/rule/sub.rb +65 -0
  56. data/lib/interscript/node/stage.rb +19 -0
  57. data/lib/interscript/node/tests.rb +15 -0
  58. data/lib/interscript/stdlib.rb +211 -0
  59. data/lib/interscript/utils/regexp_converter.rb +283 -0
  60. data/lib/interscript/version.rb +1 -1
  61. data/requirements.txt +1 -0
  62. metadata +75 -339
  63. data/README.adoc +0 -298
  64. data/bin/rspec +0 -29
  65. data/lib/__pycache__/g2pwrapper.cpython-38.pyc +0 -0
  66. data/lib/g2pwrapper.py +0 -34
  67. data/lib/interscript-opal.rb +0 -2
  68. data/lib/interscript/fs.rb +0 -71
  69. data/lib/interscript/mapping.rb +0 -142
  70. data/lib/interscript/opal.rb +0 -27
  71. data/lib/interscript/opal/maps.js.erb +0 -10
  72. data/lib/interscript/opal_map_translate.rb +0 -12
  73. data/lib/model-7 +0 -0
  74. data/lib/tha-pt-b-7 +0 -0
  75. data/maps/acadsin-zho-Hani-Latn-2002.yaml +0 -38912
  76. data/maps/alalc-amh-Ethi-Latn-1997.yaml +0 -509
  77. data/maps/alalc-amh-Ethi-Latn-2011.yaml +0 -138
  78. data/maps/alalc-ara-Arab-Latn-1997.yaml +0 -1283
  79. data/maps/alalc-asm-Deva-Latn-1997.yaml +0 -159
  80. data/maps/alalc-aze-Cyrl-Latn-1997.yaml +0 -141
  81. data/maps/alalc-bel-Cyrl-Latn-1997.yaml +0 -125
  82. data/maps/alalc-ben-Beng-Latn-2017.yaml +0 -130
  83. data/maps/alalc-bul-Cyrl-Latn-1997.yaml +0 -94
  84. data/maps/alalc-ell-Grek-Latn-1997.yaml +0 -624
  85. data/maps/alalc-ell-Grek-Latn-2010.yaml +0 -627
  86. data/maps/alalc-hin-Deva-Latn-2020.yaml +0 -159
  87. data/maps/alalc-kat-Geok-Latn-1997.yaml +0 -111
  88. data/maps/alalc-kat-Geor-Latn-1997.yaml +0 -146
  89. data/maps/alalc-kor-Hang-Latn-1997.yaml +0 -94
  90. data/maps/alalc-mar-Deva-Latn-1997.yaml +0 -170
  91. data/maps/alalc-mkd-Cyrl-Latn-1997.yaml +0 -114
  92. data/maps/alalc-mkd-Cyrl-Latn-2013.yaml +0 -103
  93. data/maps/alalc-pan-Deva-Latn-1997.yaml +0 -237
  94. data/maps/alalc-rus-Cyrl-Latn-1997.yaml +0 -221
  95. data/maps/alalc-rus-Cyrl-Latn-2012.yaml +0 -162
  96. data/maps/alalc-srp-Cyrl-Latn-1997.yaml +0 -114
  97. data/maps/alalc-srp-Cyrl-Latn-2013.yaml +0 -135
  98. data/maps/alalc-ukr-Cyrl-Latn-1997.yaml +0 -141
  99. data/maps/alalc-ukr-Cyrl-Latn-2011.yaml +0 -16
  100. data/maps/apcbg-bul-Cyrl-Latn-1995.yaml +0 -283
  101. data/maps/bas-rus-Cyrl-Latn-2017-bss.yaml +0 -174
  102. data/maps/bas-rus-Cyrl-Latn-2017-oss.yaml +0 -169
  103. data/maps/bgn-jpn-Hrkt-Latn-1962.yaml +0 -292
  104. data/maps/bgn-kor-Hang-Latn-1943.yaml +0 -31
  105. data/maps/bgn-kor-Kore-Latn-1943.yaml +0 -31
  106. data/maps/bgna-bul-Cyrl-Latn-2006.yaml +0 -208
  107. data/maps/bgna-bul-Cyrl-Latn-2009.yaml +0 -208
  108. data/maps/bgnpcgn-amh-Ethi-Latn-1967.yaml +0 -528
  109. data/maps/bgnpcgn-ara-Arab-Latn-1956.yaml +0 -592
  110. data/maps/bgnpcgn-arm-Armn-Latn-1981.yaml +0 -108
  111. data/maps/bgnpcgn-aze-Cyrl-Latn-1993.yaml +0 -104
  112. data/maps/bgnpcgn-bak-Cyrl-Latn-2007.yaml +0 -184
  113. data/maps/bgnpcgn-bel-Cyrl-Latn-1979.yaml +0 -285
  114. data/maps/bgnpcgn-bul-Cyrl-Latn-1952.yaml +0 -115
  115. data/maps/bgnpcgn-bul-Cyrl-Latn-2013.yaml +0 -38
  116. data/maps/bgnpcgn-ell-Grek-Latn-1962.yaml +0 -701
  117. data/maps/bgnpcgn-ell-Grek-Latn-1996.yaml +0 -19
  118. data/maps/bgnpcgn-jpn-Hrkt-Latn-1976.yaml +0 -257
  119. data/maps/bgnpcgn-kat-Geor-Latn-1981.yaml +0 -127
  120. data/maps/bgnpcgn-kat-Geor-Latn-2009.yaml +0 -42
  121. data/maps/bgnpcgn-kor-Hang-Latn-kn-1945.yaml +0 -253
  122. data/maps/bgnpcgn-kor-Hang-Latn-rok-2011.yaml +0 -48
  123. data/maps/bgnpcgn-kor-Kore-Latn-rok-2011.yaml +0 -48
  124. data/maps/bgnpcgn-mkd-Cyrl-Latn-1981.yaml +0 -159
  125. data/maps/bgnpcgn-mkd-Cyrl-Latn-2013.yaml +0 -190
  126. data/maps/bgnpcgn-nep-Deva-Latn-2011.yaml +0 -200
  127. data/maps/bgnpcgn-per-Arab-Latn-1956.yaml +0 -92
  128. data/maps/bgnpcgn-rus-Cyrl-Latn-1947.yaml +0 -314
  129. data/maps/bgnpcgn-srp-Cyrl-Latn-2005.yaml +0 -166
  130. data/maps/bgnpcgn-ukr-Cyrl-Latn-1965.yaml +0 -162
  131. data/maps/bgnpcgn-ukr-Cyrl-Latn-2019.yaml +0 -208
  132. data/maps/bgnpcgn-zho-Hans-Latn-1979.yaml +0 -7456
  133. data/maps/bis-asm-Beng-Latn-13194-1991.yaml +0 -159
  134. data/maps/bis-ben-Beng-Latn-13194-1991.yaml +0 -156
  135. data/maps/bis-dev-Deva-Latn-13194-1991.yaml +0 -184
  136. data/maps/bis-gjr-Gujr-Latn-13194-1991.yaml +0 -166
  137. data/maps/bis-knd-Knda-Latn-13194-1991.yaml +0 -173
  138. data/maps/bis-mlm-Mlym-Latn-13194-1991.yaml +0 -176
  139. data/maps/bis-ori-Orya-Latn-13194-1991.yaml +0 -160
  140. data/maps/bis-pnj-Guru-Latn-13194-1991.yaml +0 -175
  141. data/maps/bis-tel-Telu-Latn-13194-1991.yaml +0 -170
  142. data/maps/bis-tml-Taml-Latn-13194-1991.yaml +0 -155
  143. data/maps/by-bel-Cyrl-Latn-1998.yaml +0 -168
  144. data/maps/by-bel-Cyrl-Latn-2007.yaml +0 -115
  145. data/maps/dos-nep-Deva-Latn-1997.yaml +0 -33
  146. data/maps/elot-ell-Grek-Latn-743-1982-tl.yaml +0 -684
  147. data/maps/elot-ell-Grek-Latn-743-1982-ts.yaml +0 -680
  148. data/maps/elot-ell-Grek-Latn-743-2001-tl.yaml +0 -19
  149. data/maps/elot-ell-Grek-Latn-743-2001-ts.yaml +0 -31
  150. data/maps/ggg-kat-Geor-Latn-2002.yaml +0 -88
  151. data/maps/gki-bel-Cyrl-Latn-1992.yaml +0 -33
  152. data/maps/gki-bel-Cyrl-Latn-2000.yaml +0 -201
  153. data/maps/gost-rus-Cyrl-Latn-16876-71-1983.yaml +0 -186
  154. data/maps/hk-yue-Hani-Latn-1888.yaml +0 -38497
  155. data/maps/icao-bel-Cyrl-Latn-9303.yaml +0 -136
  156. data/maps/icao-bul-Cyrl-Latn-9303.yaml +0 -118
  157. data/maps/icao-heb-Hebr-Latn-9303.yaml +0 -151
  158. data/maps/icao-mkd-Cyrl-Latn-9303.yaml +0 -117
  159. data/maps/icao-per-Arab-Latn-9303.yaml +0 -103
  160. data/maps/icao-rus-Cyrl-Latn-9303.yaml +0 -117
  161. data/maps/icao-srp-Cyrl-Latn-9303.yaml +0 -117
  162. data/maps/icao-ukr-Cyrl-Latn-9303.yaml +0 -119
  163. data/maps/iso-ara-Arab-Latn-233-1984.yaml +0 -323
  164. data/maps/iso-ell-Grek-Latn-843-1997-t1.yaml +0 -609
  165. data/maps/iso-ell-Grek-Latn-843-1997-t2.yaml +0 -40
  166. data/maps/iso-jpn-Hrkt-Latn-3602-1989.yaml +0 -62
  167. data/maps/iso-rus-Cyrl-Latn-9-1995.yaml +0 -271
  168. data/maps/iso-tha-Thai-Latn-11940-1998.yaml +0 -109
  169. data/maps/kp-kor-Hang-Latn-2002.yaml +0 -901
  170. data/maps/lshk-yue-Hani-Latn-jyutping-1993.yaml +0 -44820
  171. data/maps/mext-jpn-Hrkt-Latn-1954.yaml +0 -411
  172. data/maps/moct-kor-Hang-Latn-2000.yaml +0 -803
  173. data/maps/mofa-jpn-Hrkt-Latn-1989.yaml +0 -541
  174. data/maps/mvd-bel-Cyrl-Latn-2008.yaml +0 -225
  175. data/maps/mvd-bel-Cyrl-Latn-2010.yaml +0 -63
  176. data/maps/mvd-rus-Cyrl-Latn-2008.yaml +0 -109
  177. data/maps/mvd-rus-Cyrl-Latn-2010.yaml +0 -37
  178. data/maps/nil-kor-Hang-Hang-jamo.yaml +0 -11193
  179. data/maps/odni-aze-Cyrl-Latn-2015.yaml +0 -144
  180. data/maps/odni-bel-Cyrl-Latn-2015.yaml +0 -148
  181. data/maps/odni-bul-Cyrl-Latn-2015.yaml +0 -96
  182. data/maps/odni-hin-Deva-Latn-2015.yaml +0 -258
  183. data/maps/odni-kat-Geor-Latn-2015.yaml +0 -87
  184. data/maps/odni-kaz-Cyrl-Latn-2015.yaml +0 -148
  185. data/maps/odni-kir-Cyrl-Latn-2015.yaml +0 -136
  186. data/maps/odni-mkd-Cyrl-Latn-2015.yaml +0 -122
  187. data/maps/odni-rus-Cyrl-Latn-2015.yaml +0 -77
  188. data/maps/odni-srp-Cyrl-Latn-2015.yaml +0 -129
  189. data/maps/odni-tat-Cyrl-Latn-2015.yaml +0 -142
  190. data/maps/odni-tgk-Cyrl-Latn-2015.yaml +0 -148
  191. data/maps/odni-uig-Cyrl-Latn-2015.yaml +0 -138
  192. data/maps/odni-ukr-Cyrl-Latn-2015.yaml +0 -157
  193. data/maps/odni-urd-Arab-Latn-2015.yaml +0 -221
  194. data/maps/odni-uzb-Cyrl-Latn-2015.yaml +0 -166
  195. data/maps/royin-tha-Thai-Latn-1939-generic.yaml +0 -90
  196. data/maps/royin-tha-Thai-Latn-1968.yaml +0 -179
  197. data/maps/royin-tha-Thai-Latn-1999-chained.yaml +0 -180
  198. data/maps/royin-tha-Thai-Latn-1999.yaml +0 -76
  199. data/maps/sac-zho-Hans-Latn-1979.yaml +0 -24759
  200. data/maps/ses-ara-Arab-Latn-1930.yaml +0 -279
  201. data/maps/stategeocadastre-ukr-Cyrl-Latn-1993.yaml +0 -222
  202. data/maps/ua-ukr-Cyrl-Latn-1996.yaml +0 -193
  203. data/maps/un-ara-Arab-Latn-1971.yaml +0 -139
  204. data/maps/un-ara-Arab-Latn-1972.yaml +0 -159
  205. data/maps/un-ara-Arab-Latn-2017.yaml +0 -420
  206. data/maps/un-bel-Cyrl-Latn-2007.yaml +0 -114
  207. data/maps/un-ben-Beng-Latn-2016.yaml +0 -534
  208. data/maps/un-ell-Grek-Latn-1987-tl.yaml +0 -31
  209. data/maps/un-ell-Grek-Latn-1987-ts.yaml +0 -19
  210. data/maps/un-ell-Grek-Latn-phonetic-1987.yaml +0 -780
  211. data/maps/un-mon-Mong-Latn-2013.yaml +0 -99
  212. data/maps/un-nep-Deva-Latn-1972.yaml +0 -163
  213. data/maps/un-rus-Cyrl-Latn-1987.yaml +0 -166
  214. data/maps/un-ukr-Cyrl-Latn-1998.yaml +0 -30
  215. data/maps/ungegn-amh-Ethi-Latn-2016.yaml +0 -575
  216. data/maps/var-jpn-Hrkt-Latn-hepburn-1886.yaml +0 -406
  217. data/maps/var-jpn-Hrkt-Latn-hepburn-1954.yaml +0 -386
  218. data/maps/var-kor-Hang-Latn-mr-1939.yaml +0 -1054
  219. data/maps/var-kor-Kore-Hang-2013.yaml +0 -59754
  220. data/maps/var-kor-Kore-Latn-mr-1939.yaml +0 -36
  221. data/maps/var-tha-Thai-Thai-phonemic.yaml +0 -59
  222. data/maps/var-tha-Thai-Zsym-ipa.yaml +0 -301
  223. data/maps/var-zho-Hani-Latn-1979.yaml +0 -38908
  224. data/spec/interscript/mapping_spec.rb +0 -42
  225. data/spec/interscript_spec.rb +0 -26
  226. data/spec/spec_helper.rb +0 -3
data/exe/interscript ADDED
@@ -0,0 +1,6 @@
1
+ require 'interscript/command'
2
+
3
+ if ARGV.any? && !Interscript::Command.all_tasks.key?(ARGV.first)
4
+ ARGV.unshift :translit
5
+ end
6
+ Interscript::Command.start ARGV
@@ -0,0 +1,31 @@
1
+ require_relative 'lib/interscript/version'
2
+
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"]
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")
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/interscript/interscript"
19
+
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)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "thor"
30
+ spec.add_dependency "interscript-maps"
31
+ end
data/lib/interscript.rb CHANGED
@@ -1,157 +1,111 @@
1
- # frozen_string_literal: true
1
+ require "interscript/version"
2
+ require "yaml"
2
3
 
3
- require "interscript/opal/maps" if RUBY_ENGINE == "opal"
4
- require "interscript/mapping"
5
-
6
- # Transliteration
7
4
  module Interscript
8
-
9
- class InvalidSystemError < StandardError; end
10
- class ExternalProcessNotRecognizedError < StandardError; end
11
- class ExternalProcessUnavailableError < StandardError; end
12
-
13
- if RUBY_ENGINE == 'opal'
14
- require "interscript/opal"
15
- extend Opal
16
- else
17
- require "interscript/fs"
18
- extend Fs
19
- end
5
+ class MapNotFoundError < StandardError; end
20
6
 
21
7
  class << self
8
+ def load_path
9
+ @load_path ||= ['.', *Interscript.map_locations]
10
+ end
22
11
 
23
- def transliterate(system_code, string, maps={})
24
- unless maps.has_key? system_code
25
- maps[system_code] = Interscript::Mapping.for(system_code)
26
- end
27
- # mapping = Interscript::Mapping.for(system_code)
28
- mapping = maps[system_code]
12
+ def locate map_name
13
+ map_name = map_aliases[map_name] if map_aliases.include? map_name
29
14
 
30
- # First, apply chained transliteration as specified in the list `chain`
31
- chain = mapping.chain.dup
32
- while chain.length > 0
33
- string = transliterate(chain.shift, string, maps)
15
+ load_path.each do |i|
16
+ # iml is an extension for a library, imp for a map
17
+ ["iml", "imp"].each do |ext|
18
+ f = File.expand_path("#{map_name}.#{ext}", i)
19
+ return f if File.exist?(f)
20
+ end
34
21
  end
22
+ raise MapNotFoundError, "Couldn't locate #{map_name}"
23
+ end
35
24
 
36
- # Then, apply the rest of the map
37
- separator = mapping.character_separator || ""
38
- word_separator = mapping.word_separator || ""
39
- title_case = mapping.title_case
40
- downcase = mapping.downcase
41
-
42
- # charmap = mapping.characters&.sort_by { |k, _v| k.size }&.reverse&.to_h
43
- # dictmap = mapping.dictionary&.sort_by { |k, _v| k.size }&.reverse&.to_h
44
- charmap = mapping.characters_hash
45
- dictmap = mapping.dictionary_hash
46
- trie = mapping.dictionary_trie
47
-
48
- string = external_processing(mapping, string)
49
-
50
- pos = 0
51
- while pos < string.to_s.size
52
- m = 0
53
- wordmatch = ""
54
-
55
- # Using Trie, find the longest matching substring
56
- while (pos + m < string.to_s.size) && (trie.partial_word?string[pos..pos+m])
57
- wordmatch = string[pos..pos+m] if trie.word?string[pos..pos+m]
58
- m += 1
59
- end
25
+ def parse(map_name)
26
+ Interscript::DSL.parse(map_name)
27
+ end
60
28
 
61
- m = wordmatch.length
62
- if m > 0
63
- repl = dictmap[string[pos..pos+m-1]]
64
- string = sub_replace(string, pos, m, repl)
65
- pos += repl.length
66
- else
67
- pos += 1
68
- end
69
- end
29
+ def load(system_code, maps={}, compiler: Interscript::Interpreter)
30
+ maps[[system_code, compiler.name]] ||= compiler.(system_code)
31
+ end
70
32
 
71
- output = string.clone
72
- offsets = Array.new string.to_s.size, 1
73
-
74
- # mapping.rules.each do |r|
75
- # string.to_s.scan(/#{r['pattern']}/) do |matches|
76
- # match = Regexp.last_match
77
- # pos = match.offset(0).first
78
- # result = r['result'].clone
79
- # matches.each.with_index { |v, i| result.sub!(/\\#{i + 1}/, v) } if matches.is_a? Array
80
- # result.upcase! if up_case_around?(string, pos)
81
- # output[offsets[0...pos].sum, match[0].size] = result
82
- # offsets[pos] += result.size - match[0].size
83
- # end
84
- # end
85
-
86
- mapping.rules.each do |r|
87
- next unless output
88
- re = mkregexp(r["pattern"])
89
- output = output.gsub(re, r["result"])
90
- end
33
+ # Transliterates the string.
34
+ def transliterate(system_code, string, maps={}, compiler: Interscript::Interpreter)
35
+ # The current best implementation is Interpreter
36
+ load(system_code, maps, compiler: compiler).(string)
37
+ end
91
38
 
92
- charmap.each do |k, v|
93
- while (match = output&.match(/#{k}/))
94
- pos = match.offset(0).first
95
- result = !downcase && up_case_around?(output, pos) ? v.upcase : v
39
+ # Gives each possible value of the transliteration.
40
+ def transliterate_each(system_code, string, maps={}, &block)
41
+ load(system_code, maps).(string, each: true, &block)
42
+ end
96
43
 
97
- # if more than one, choose the first one
98
- result = result[0] if result.is_a?(Array)
44
+ def transliterate_file(system_code, input_file, output_file, maps={})
45
+ input = File.read(input_file)
46
+ output = transliterate(system_code, input, maps)
99
47
 
100
- output = sub_replace(
101
- output,
102
- pos,
103
- match[0].size,
104
- add_separator(separator, pos, result)
105
- )
106
- end
48
+ File.open(output_file, 'w') do |f|
49
+ f.puts(output)
107
50
  end
108
51
 
109
- mapping.postrules.each do |r|
110
- next unless output
111
- re = mkregexp(r["pattern"])
112
- output = if r["result"] == "upcase"
113
- output.gsub(re, &:upcase)
114
- else
115
- output.gsub(re, r["result"])
116
- end
117
- end
52
+ puts "Output written to: #{output_file}"
53
+ output_file
54
+ end
118
55
 
119
- return unless output
56
+ def map_gems
57
+ @map_gems ||= Gem.find_latest_files('interscript-maps.yaml').map do |i|
58
+ [i, YAML.load_file(i)]
59
+ end.to_h
60
+ end
120
61
 
121
- output = output.sub(/^(.)/, &:upcase) if title_case
122
- if word_separator != ''
123
- output = output.gsub(/#{word_separator}#{separator}/u, word_separator)
62
+ def map_locations
63
+ @map_locations ||= map_gems.map do |i,v|
64
+ paths = v["paths"].dup
65
+ paths += v["staging"] if ENV["INTERSCRIPT_STAGING"] && v["staging"]
124
66
 
125
- if title_case
126
- output = output.gsub(/#{word_separator}(.)/u, &:upcase)
67
+ paths.map do |j|
68
+ File.expand_path(j, File.dirname(i))
127
69
  end
128
- end
70
+ end.flatten
71
+ end
129
72
 
130
- output.unicode_normalize
73
+ def secryst_index_locations
74
+ @secryst_index_locations ||= map_gems.map do |i,v|
75
+ v["secryst-models"]
76
+ end.compact.flatten
131
77
  end
132
78
 
133
- private
79
+ def map_aliases
80
+ return @map_aliases if @map_aliases
134
81
 
135
- def add_separator(separator, pos, result)
136
- pos == 0 ? result : separator + result
82
+ @map_aliases = {}
83
+ map_gems.each do |i,v|
84
+ (v["aliases"] || {}).each do |code, value|
85
+ value.each do |al, map|
86
+ @map_aliases[al] = map["alias_to"]
87
+ end
88
+ end
89
+ end
90
+ @map_aliases
137
91
  end
138
92
 
139
- def up_case_around?(string, pos)
140
- return false if string[pos] == string[pos].downcase
93
+ # List all possible maps to use
94
+ def maps(basename: true, load_path: false, select: "*", libraries: false)
95
+ paths = load_path ? Interscript.load_path : Interscript.map_locations
96
+ ext = libraries ? "iml" : "imp"
141
97
 
142
- i = pos - 1
143
- i -= 1 while i.positive? && string[i] !~ Regexp.new(ALPHA_REGEXP)
144
- before = i >= 0 && i < pos ? string[i].to_s.strip : ''
98
+ imps = paths.map { |i| Dir["#{i}/#{select}.#{ext}"] }.flatten
145
99
 
146
- i = pos + 1
147
- i += 1 while i < string.size - 1 && string[i] !~ Regexp.new(ALPHA_REGEXP)
148
- after = i > pos ? string[i].to_s.strip : ''
149
-
150
- before_uc = !before.empty? && before == before.upcase
151
- after_uc = !after.empty? && after == after.upcase
152
- # before_uc && (after.empty? || after_uc) || after_uc && (before.empty? || before_uc)
153
- before_uc || after_uc
100
+ basename ? imps.map { |j| File.basename(j, ".#{ext}") } : imps
154
101
  end
155
-
156
102
  end
157
103
  end
104
+
105
+ require 'interscript/stdlib'
106
+
107
+ require "interscript/compiler"
108
+ require "interscript/interpreter"
109
+
110
+ require 'interscript/dsl'
111
+ require 'interscript/node'
@@ -7,11 +7,12 @@ module Interscript
7
7
  desc '<file>', 'Transliterate text'
8
8
  option :system, aliases: '-s', required: true, desc: 'Transliteration system'
9
9
  option :output, aliases: '-o', required: false, desc: 'Output file'
10
- option :map, aliases: '-m', required: false, default: "{}", desc: 'Transliteration mapping json'
10
+ # Was this option really well thought out? The last parameter is a cache, isn't it?
11
+ #option :map, aliases: '-m', required: false, default: "{}", desc: 'Transliteration mapping json'
11
12
 
12
13
  def translit(input)
13
14
  if options[:output]
14
- Interscript.transliterate_file(options[:system], input, options[:output], JSON.parse(options[:map]))
15
+ Interscript.transliterate_file(options[:system], input, options[:output]) #, JSON.parse(options[:map]))
15
16
  else
16
17
  puts Interscript.transliterate(options[:system], IO.read(input))
17
18
  end
@@ -19,9 +20,8 @@ module Interscript
19
20
 
20
21
  desc 'list', 'Prints allowed transliteration systems'
21
22
  def list
22
- dir = File.expand_path '../../maps/*.yaml', __dir__
23
- Dir[dir].each do |path|
24
- puts File.basename path, '.yaml'
23
+ Interscript.maps(load_path: true).each do |path|
24
+ puts path
25
25
  end
26
26
  end
27
27
  end
@@ -0,0 +1,22 @@
1
+ # An Interscript compiler interface
2
+ class Interscript::Compiler
3
+ attr_accessor :code
4
+
5
+ def self.call(map, **kwargs)
6
+ if String === map
7
+ map = Interscript::DSL.parse(map)
8
+ end
9
+ compiler = new
10
+ compiler.compile(map, **kwargs)
11
+ compiler
12
+ end
13
+
14
+ def compile(map)
15
+ raise NotImplementedError, "Compile method on #{self.class} is not implemented"
16
+ end
17
+
18
+ # Execute a map
19
+ def call
20
+ raise NotImplementedError, "Call class on #{self.class} is not implemented"
21
+ end
22
+ end
@@ -0,0 +1,292 @@
1
+ begin
2
+ require 'mini_racer'
3
+ rescue LoadError
4
+ # Ignore loading error
5
+ end
6
+ require 'json'
7
+
8
+ class Interscript::Compiler::Javascript < Interscript::Compiler
9
+ def compile(map, debug: false)
10
+ @map = map
11
+ @parallel_trees = {}
12
+ @parallel_regexps = {}
13
+ @debug = debug
14
+ c = "var map = function(Interscript) {"
15
+ c << "Interscript.define_map(#{map.name.inspect}, function(Interscript, map) {\n";
16
+ c << "map.dependencies = #{map.dependencies.map(&:full_name).to_json};\n"
17
+ c
18
+
19
+ map.aliases.each do |name, value|
20
+ val = compile_item(value.data, map, :str)
21
+ c << "map.aliases.#{name} = #{val};\n"
22
+ val = '"'+compile_item(value.data, map, :re)+'"'
23
+ c << "map.aliases_re.#{name} = #{val};\n"
24
+ end
25
+
26
+ map.stages.each do |_, stage|
27
+ c << compile_rule(stage, @map, true)
28
+ end
29
+ @parallel_trees.each do |k,v|
30
+ c << "map.cache.PTREE_#{k} = #{v.to_json};\n"
31
+ end
32
+ @parallel_regexps.each do |k,v|
33
+ v = "[\"#{v[0]}\", #{v[1].to_json}]"
34
+ c << "map.cache.PRE_#{k} = #{v};\n"
35
+ end
36
+
37
+ c << "});"
38
+ c << "};"
39
+ c << "if (typeof module !== 'undefined') { module.exports = map; }"
40
+ c << "else if (typeof Interscript !== 'undefined') { map(Interscript); }"
41
+ c << 'else { throw "We couldn\'t dispatch Interscript from a map!"; }'
42
+ @code = c
43
+ end
44
+
45
+ def parallel_regexp_compile(subs_hash)
46
+ # puts subs_hash.inspect
47
+ regexp = subs_hash.each_with_index.map do |p,i|
48
+ "(?<_%d>%s)" % [i,p[0]]
49
+ end.join("|")
50
+ subs_regexp = regexp
51
+ # puts subs_regexp.inspect
52
+ end
53
+
54
+ def compile_rule(r, map = @map, wrapper = false)
55
+ c = ""
56
+ case r
57
+ when Interscript::Node::Stage
58
+ c += "map.stages.#{r.name} = function(s) {\n"
59
+ c += "globalThis.map_debug = globalThis.map_debug || [];\n" if @debug
60
+ r.children.each do |t|
61
+ comp = compile_rule(t, map)
62
+ c += comp
63
+ c += %{globalThis.map_debug.push([s, #{@map.name.to_s.to_json}, #{r.name.to_s.to_json}, #{t.inspect.to_json}, #{comp.to_json}]);\n} if @debug
64
+ end
65
+ c += "return s;\n"
66
+ c += "};\n"
67
+ when Interscript::Node::Group::Parallel
68
+ begin
69
+ # Try to build a tree
70
+ a = []
71
+ r.children.each do |i|
72
+ raise ArgumentError, "Can't parallelize #{i.class}" unless Interscript::Node::Rule::Sub === i
73
+ raise ArgumentError, "Can't parallelize rules with :before" if i.before
74
+ raise ArgumentError, "Can't parallelize rules with :after" if i.after
75
+ raise ArgumentError, "Can't parallelize rules with :not_before" if i.not_before
76
+ raise ArgumentError, "Can't parallelize rules with :not_after" if i.not_after
77
+
78
+ a << [compile_item(i.from, map, :par), compile_item(i.to, map, :parstr)]
79
+ end
80
+ ah = a.hash.abs
81
+ unless @parallel_trees.include? ah
82
+ tree = Interscript::Stdlib.parallel_replace_compile_tree(a)
83
+ @parallel_trees[ah] = tree
84
+ end
85
+ c += "s = Interscript.parallel_replace_tree(s, map.cache.PTREE_#{ah});\n"
86
+ rescue
87
+ # Otherwise let's build a megaregexp
88
+ a = []
89
+ Interscript::Stdlib.deterministic_sort_by_max_length(r.children).each do |i|
90
+ raise ArgumentError, "Can't parallelize #{i.class}" unless Interscript::Node::Rule::Sub === i
91
+
92
+ a << [build_regexp(i, map), compile_item(i.to, map, :parstr)]
93
+ end
94
+ ah = a.hash.abs
95
+ unless @parallel_regexps.include? ah
96
+ re = parallel_regexp_compile(a)
97
+ @parallel_regexps[ah] = [re, a.map(&:last)]
98
+ end
99
+ c += "s = Interscript.parallel_regexp_gsub(s, map.cache.PRE_#{ah});\n"
100
+ end
101
+ when Interscript::Node::Rule::Sub
102
+ from = %{"#{build_regexp(r, map).gsub("/", "\\\\/")}"}
103
+ if r.to == :upcase
104
+ to = 'function(a){return a.toUpperCase();}'
105
+ else
106
+ to = compile_item(r.to, map, :str)
107
+ end
108
+ c += "s = Interscript.gsub(s, #{from}, #{to});\n"
109
+ when Interscript::Node::Rule::Funcall
110
+ c += "s = Interscript.functions.#{r.name}(s, #{r.kwargs.to_json});\n"
111
+ when Interscript::Node::Rule::Run
112
+ if r.stage.map
113
+ doc = map.dep_aliases[r.stage.map].document
114
+ stage = doc.imported_stages[r.stage.name]
115
+ else
116
+ stage = map.imported_stages[r.stage.name]
117
+ end
118
+ c += "s = Interscript.transliterate(#{stage.doc_name.to_json}, s, #{stage.name.to_json});\n"
119
+ else
120
+ raise ArgumentError, "Can't compile unhandled #{r.class}"
121
+ end
122
+ c
123
+ end
124
+
125
+ def build_regexp(r, map=@map)
126
+ from = compile_item(r.from, map, :re)
127
+ before = compile_item(r.before, map, :re) if r.before
128
+ after = compile_item(r.after, map, :re) if r.after
129
+ not_before = compile_item(r.not_before, map, :re) if r.not_before
130
+ not_after = compile_item(r.not_after, map, :re) if r.not_after
131
+
132
+ re = ""
133
+ re += "(?<=#{before})" if before
134
+ re += "(?<!#{not_before})" if not_before
135
+ re += from
136
+ re += "(?!#{not_after})" if not_after
137
+ re += "(?=#{after})" if after
138
+
139
+ re
140
+ end
141
+
142
+ def compile_item i, doc=@map, target=nil
143
+ i = i.first_string if %i[str parstr].include? target
144
+ i = Interscript::Node::Item.try_convert(i)
145
+ if target == :parstr
146
+ parstr = true
147
+ target = :par
148
+ end
149
+
150
+ out = case i
151
+ when Interscript::Node::Item::Alias
152
+ astr = if i.map
153
+ d = doc.dep_aliases[i.map].document
154
+ a = d.imported_aliases[i.name]
155
+ raise ArgumentError, "Alias #{i.name} of #{i.stage.map} not found" unless a
156
+ "Interscript.get_alias_ALIASTYPE(#{a.doc_name.to_json}, #{a.name.to_json})"
157
+ elsif Interscript::Stdlib::ALIASES.include?(i.name)
158
+ if target != :re && Interscript::Stdlib.re_only_alias?(i.name)
159
+ raise ArgumentError, "Can't use #{i.name} in a #{target} context"
160
+ end
161
+ stdlib_alias = true
162
+ "Interscript.aliases.#{i.name}"
163
+ else
164
+ a = doc.imported_aliases[i.name]
165
+ raise ArgumentError, "Alias #{i.name} not found" unless a
166
+
167
+ "Interscript.get_alias_ALIASTYPE(#{a.doc_name.to_json}, #{a.name.to_json})"
168
+ end
169
+
170
+ if target == :str
171
+ astr = astr.sub("_ALIASTYPE(", "(")
172
+ elsif target == :re
173
+ astr = %{"+#{astr.sub("_ALIASTYPE(", "_re(")}+"}
174
+ elsif parstr && stdlib_alias
175
+ astr = Interscript::Stdlib::ALIASES[i.name]
176
+ elsif target == :par
177
+ # raise NotImplementedError, "Can't use aliases in parallel mode yet"
178
+ astr = Interscript::Stdlib::ALIASES[i.name]
179
+ end
180
+ when Interscript::Node::Item::String
181
+ if target == :str
182
+ # Replace $1 with \$1, this is weird, but it works!
183
+ i.data.gsub("$", "\\\\$").to_json
184
+ elsif target == :par
185
+ i.data
186
+ elsif target == :re
187
+ Regexp.escape(i.data)
188
+ end
189
+ when Interscript::Node::Item::Group
190
+ if target == :par
191
+ i.children.map do |j|
192
+ compile_item(j, doc, target)
193
+ end.reduce([""]) do |j,k|
194
+ Array(j).product(Array(k)).map(&:join)
195
+ end
196
+ elsif target == :str
197
+ i.children.map { |j| compile_item(j, doc, target) }.join("+")
198
+ elsif target == :re
199
+ i.children.map { |j| compile_item(j, doc, target) }.join
200
+ end
201
+ when Interscript::Node::Item::CaptureGroup
202
+ if target != :re
203
+ raise ArgumentError, "Can't use a CaptureGroup in a #{target} context"
204
+ end
205
+ "(" + compile_item(i.data, doc, target) + ")"
206
+ when Interscript::Node::Item::Maybe,
207
+ Interscript::Node::Item::MaybeSome,
208
+ Interscript::Node::Item::Some
209
+
210
+ resuffix = { Interscript::Node::Item::Maybe => "?" ,
211
+ Interscript::Node::Item::Some => "+" ,
212
+ Interscript::Node::Item::MaybeSome => "*" }[i.class]
213
+
214
+ if target == :par
215
+ raise ArgumentError, "Can't use a MaybeSome in a #{target} context"
216
+ end
217
+ if Interscript::Node::Item::String === i.data && i.data.data.length != 1
218
+ "(?:" + compile_item(i.data, doc, target) + ")" + resuffix
219
+ else
220
+ compile_item(i.data, doc, target) + resuffix
221
+ end
222
+ when Interscript::Node::Item::CaptureRef
223
+ if target == :par
224
+ raise ArgumentError, "Can't use CaptureRef in parallel mode"
225
+ elsif target == :re
226
+ "\\\\#{i.id}"
227
+ elsif target == :str
228
+ "\"$#{i.id}\""
229
+ end
230
+ when Interscript::Node::Item::Any
231
+ if target == :str
232
+ raise ArgumentError, "Can't use Any in a string context" # A linter could find this!
233
+ elsif target == :par
234
+ i.data.map(&:data)
235
+ elsif target == :re
236
+ case i.value
237
+ when Array
238
+ data = i.data.map { |j| compile_item(j, doc, target) }
239
+ "(?:"+data.join("|")+")"
240
+ when String
241
+ "[#{Regexp.escape(i.value)}]"
242
+ when Range
243
+ "[#{Regexp.escape(i.value.first)}-#{Regexp.escape(i.value.last)}]"
244
+ end
245
+ end
246
+ end
247
+ end
248
+
249
+ @maps_loaded = {}
250
+ @ctx = nil
251
+ class << self
252
+ attr_accessor :maps_loaded
253
+ attr_accessor :ctx
254
+ end
255
+
256
+ def load
257
+ if !self.class.maps_loaded[@map.name]
258
+ @map.dependencies.each do |dep|
259
+ dep = dep.full_name
260
+ if !self.class.maps_loaded[dep]
261
+ Interscript.load(dep, compiler: self.class).load
262
+ end
263
+ end
264
+
265
+ ctx = self.class.ctx
266
+ unless ctx
267
+ ctx = MiniRacer::Context.new
268
+ ctx.eval File.read(__dir__+"/../../../../js/test-compiler/xregexp.js")
269
+ # Compatibility with Safari: will come later
270
+ #ctx.eval File.read(__dir__+"/../../../js/xregexp-oniguruma.js")
271
+ ctx.eval File.read(__dir__+"/../../../../js/src/stdlib.js")
272
+ self.class.ctx = ctx
273
+ end
274
+ #puts @code
275
+ ctx.eval @code
276
+ self.class.maps_loaded[@map.name] = true
277
+ end
278
+ end
279
+
280
+ def call(str, stage=:main)
281
+ load
282
+ self.class.ctx.eval "Interscript.transliterate(#{@map.name.to_json}, #{str.to_json}, #{stage.to_json})"
283
+ end
284
+
285
+ def self.read_debug_data
286
+ self.ctx.eval "globalThis.map_debug || []"
287
+ end
288
+
289
+ def self.reset_debug_data
290
+ self.ctx.eval "globalThis.map_debug = []"
291
+ end
292
+ end