wlang 0.10.2 → 2.0.0.beta

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 (269) hide show
  1. data/CHANGELOG.md +3 -121
  2. data/Gemfile +23 -1
  3. data/Gemfile.lock +32 -28
  4. data/LICENCE.md +18 -21
  5. data/Manifest.txt +4 -5
  6. data/README.md +100 -174
  7. data/Rakefile +1 -13
  8. data/bin/wlang +3 -29
  9. data/lib/wlang.rb +21 -394
  10. data/lib/wlang/command.rb +94 -0
  11. data/lib/wlang/compiler.rb +78 -0
  12. data/lib/wlang/compiler/autospacing.rb +60 -0
  13. data/lib/wlang/compiler/dialect_enforcer.rb +91 -0
  14. data/lib/wlang/compiler/filter.rb +32 -0
  15. data/lib/wlang/compiler/grammar.citrus +67 -0
  16. data/lib/wlang/compiler/parser.rb +26 -0
  17. data/lib/wlang/compiler/proc_call_removal.rb +15 -0
  18. data/lib/wlang/compiler/static_merger.rb +28 -0
  19. data/lib/wlang/compiler/strconcat_flattener.rb +25 -0
  20. data/lib/wlang/compiler/to_ruby_abstraction.rb +22 -0
  21. data/lib/wlang/compiler/to_ruby_code.rb +55 -0
  22. data/lib/wlang/dialect.rb +40 -237
  23. data/lib/wlang/dialect/dispatching.rb +51 -0
  24. data/lib/wlang/dialect/evaluation.rb +30 -0
  25. data/lib/wlang/dialect/tags.rb +50 -0
  26. data/lib/wlang/dummy.rb +32 -0
  27. data/lib/wlang/html.rb +106 -0
  28. data/lib/wlang/loader.rb +6 -0
  29. data/lib/wlang/mustang.rb +90 -0
  30. data/lib/wlang/scope.rb +57 -0
  31. data/lib/wlang/scope/binding_scope.rb +18 -0
  32. data/lib/wlang/scope/object_scope.rb +25 -0
  33. data/lib/wlang/scope/proxy_scope.rb +18 -0
  34. data/lib/wlang/scope/root_scope.rb +24 -0
  35. data/lib/wlang/template.rb +16 -86
  36. data/lib/wlang/version.rb +9 -8
  37. data/spec/fixtures/dialect/foobar.rb +31 -0
  38. data/spec/fixtures/dialect/upcasing.rb +13 -0
  39. data/spec/fixtures/templates/hello.tpl +1 -0
  40. data/spec/integration/examples/1-basics.txt +65 -0
  41. data/spec/integration/examples/2-imperative.txt +51 -0
  42. data/spec/integration/examples/3-partials.txt +76 -0
  43. data/spec/integration/examples/4-recursion.txt +16 -0
  44. data/spec/integration/html/test_ampersand.rb +15 -0
  45. data/spec/integration/html/test_bang.rb +38 -0
  46. data/spec/integration/html/test_caret.rb +33 -0
  47. data/spec/integration/html/test_dollar.rb +16 -0
  48. data/spec/integration/html/test_greater.rb +23 -0
  49. data/spec/integration/html/test_modulo.rb +16 -0
  50. data/spec/integration/html/test_plus.rb +48 -0
  51. data/spec/integration/html/test_question.rb +33 -0
  52. data/spec/integration/html/test_sharp.rb +21 -0
  53. data/spec/integration/html/test_slash.rb +16 -0
  54. data/spec/integration/html/test_star.rb +37 -0
  55. data/spec/integration/test_dummy.rb +51 -0
  56. data/spec/integration/test_examples.rb +29 -0
  57. data/spec/integration/test_mustang.rb +120 -0
  58. data/spec/integration/test_readme.rb +56 -0
  59. data/spec/integration/test_upcasing.rb +22 -0
  60. data/spec/spec_helper.rb +62 -1
  61. data/spec/test_wlang.rb +101 -0
  62. data/spec/unit/compiler/autospacing/test_right_strip.rb +30 -0
  63. data/spec/unit/compiler/autospacing/test_unindent.rb +30 -0
  64. data/spec/unit/compiler/test_dialect_enforcer.rb +168 -0
  65. data/spec/unit/compiler/test_grammar.rb +207 -0
  66. data/spec/unit/compiler/test_parser.rb +69 -0
  67. data/spec/unit/compiler/test_proc_call_removal.rb +24 -0
  68. data/spec/unit/compiler/test_static_merger.rb +29 -0
  69. data/spec/unit/compiler/test_strconcat_flattener.rb +30 -0
  70. data/spec/unit/compiler/test_to_ruby_abstraction.rb +59 -0
  71. data/spec/unit/compiler/test_to_ruby_code.rb +24 -0
  72. data/spec/unit/dialect/test_compile.rb +52 -0
  73. data/spec/unit/dialect/test_dispatching.rb +19 -0
  74. data/spec/unit/dialect/test_evaluate.rb +41 -0
  75. data/spec/unit/dialect/test_render.rb +33 -0
  76. data/spec/unit/dialect/test_tags.rb +32 -0
  77. data/spec/unit/dialect/test_with_scope.rb +18 -0
  78. data/spec/unit/scope/test_binding_scope.rb +27 -0
  79. data/spec/unit/scope/test_coerce.rb +22 -0
  80. data/spec/unit/scope/test_object_scope.rb +38 -0
  81. data/spec/unit/scope/test_proxy_scope.rb +22 -0
  82. data/spec/unit/scope/test_root_scope.rb +22 -0
  83. data/spec/unit/test_assumptions.rb +29 -0
  84. data/spec/unit/test_scope.rb +57 -0
  85. data/tasks/debug_mail.rake +42 -45
  86. data/tasks/gem.rake +22 -17
  87. data/tasks/spec_test.rake +9 -17
  88. data/tasks/unit_test.rake +11 -12
  89. data/tasks/yard.rake +13 -13
  90. data/wlang.gemspec +36 -32
  91. data/wlang.noespec +27 -35
  92. metadata +268 -451
  93. data/doc/specification/about.rdoc +0 -61
  94. data/doc/specification/analytics.wtpl +0 -13
  95. data/doc/specification/dialect.wtpl +0 -14
  96. data/doc/specification/dialects.wtpl +0 -3
  97. data/doc/specification/examples.rb +0 -3
  98. data/doc/specification/glossary.wtpl +0 -14
  99. data/doc/specification/hosting.rdoc +0 -0
  100. data/doc/specification/overview.rdoc +0 -116
  101. data/doc/specification/rulesets.wtpl +0 -87
  102. data/doc/specification/specification.css +0 -53
  103. data/doc/specification/specification.html +0 -1690
  104. data/doc/specification/specification.js +0 -8
  105. data/doc/specification/specification.wtpl +0 -42
  106. data/doc/specification/specification.yml +0 -432
  107. data/doc/specification/symbols.wtpl +0 -16
  108. data/lib/wlang/dialect_dsl.rb +0 -141
  109. data/lib/wlang/dialect_loader.rb +0 -74
  110. data/lib/wlang/dialects/bluecloth_dialect.rb +0 -16
  111. data/lib/wlang/dialects/coderay_dialect.rb +0 -45
  112. data/lib/wlang/dialects/hosted_dialect.rb +0 -50
  113. data/lib/wlang/dialects/plain_text_dialect.rb +0 -69
  114. data/lib/wlang/dialects/rdoc_dialect.rb +0 -33
  115. data/lib/wlang/dialects/redcloth_dialect.rb +0 -16
  116. data/lib/wlang/dialects/ruby_dialect.rb +0 -118
  117. data/lib/wlang/dialects/sql_dialect.rb +0 -38
  118. data/lib/wlang/dialects/standard_dialects.rb +0 -181
  119. data/lib/wlang/dialects/xhtml_dialect.rb +0 -63
  120. data/lib/wlang/dialects/yaml_dialect.rb +0 -30
  121. data/lib/wlang/encoder.rb +0 -62
  122. data/lib/wlang/encoder_set.rb +0 -122
  123. data/lib/wlang/errors.rb +0 -80
  124. data/lib/wlang/ext/hash_methodize.rb +0 -13
  125. data/lib/wlang/ext/string.rb +0 -44
  126. data/lib/wlang/hash_scope.rb +0 -89
  127. data/lib/wlang/hosted_language.rb +0 -146
  128. data/lib/wlang/intelligent_buffer.rb +0 -94
  129. data/lib/wlang/parser.rb +0 -332
  130. data/lib/wlang/parser_state.rb +0 -94
  131. data/lib/wlang/rule.rb +0 -66
  132. data/lib/wlang/rule_set.rb +0 -106
  133. data/lib/wlang/rulesets/basic_ruleset.rb +0 -83
  134. data/lib/wlang/rulesets/buffering_ruleset.rb +0 -115
  135. data/lib/wlang/rulesets/context_ruleset.rb +0 -111
  136. data/lib/wlang/rulesets/encoding_ruleset.rb +0 -73
  137. data/lib/wlang/rulesets/imperative_ruleset.rb +0 -132
  138. data/lib/wlang/rulesets/ruleset_utils.rb +0 -317
  139. data/lib/wlang/wlang_command.rb +0 -51
  140. data/lib/wlang/wlang_command_options.rb +0 -163
  141. data/spec/basic_object.spec +0 -40
  142. data/spec/coderay_dialect.spec +0 -8
  143. data/spec/dialect/apply_post_transform.spec +0 -16
  144. data/spec/global_extensions.rb +0 -2
  145. data/spec/hash_scope.spec +0 -76
  146. data/spec/redcloth_dialect.spec +0 -24
  147. data/spec/test_all.rb +0 -8
  148. data/spec/wlang.spec +0 -53
  149. data/spec/wlang_spec.rb +0 -8
  150. data/spec/xhtml_dialect.spec +0 -22
  151. data/tasks/genspec.rake +0 -5
  152. data/test/blackbox/basic/execution_1.exp +0 -1
  153. data/test/blackbox/basic/execution_1.tpl +0 -1
  154. data/test/blackbox/basic/execution_2.exp +0 -1
  155. data/test/blackbox/basic/execution_2.tpl +0 -1
  156. data/test/blackbox/basic/execution_3.exp +0 -1
  157. data/test/blackbox/basic/execution_3.tpl +0 -1
  158. data/test/blackbox/basic/execution_4.exp +0 -1
  159. data/test/blackbox/basic/execution_4.tpl +0 -1
  160. data/test/blackbox/basic/inclusion_1.exp +0 -1
  161. data/test/blackbox/basic/inclusion_1.tpl +0 -1
  162. data/test/blackbox/basic/inclusion_2.exp +0 -1
  163. data/test/blackbox/basic/inclusion_2.tpl +0 -1
  164. data/test/blackbox/basic/injection_1.exp +0 -1
  165. data/test/blackbox/basic/injection_1.tpl +0 -1
  166. data/test/blackbox/basic/injection_2.exp +0 -1
  167. data/test/blackbox/basic/injection_2.tpl +0 -1
  168. data/test/blackbox/basic/modulation_1.exp +0 -1
  169. data/test/blackbox/basic/modulation_1.tpl +0 -1
  170. data/test/blackbox/basic/modulation_2.exp +0 -1
  171. data/test/blackbox/basic/modulation_2.tpl +0 -1
  172. data/test/blackbox/basic/recursive_app_1.exp +0 -1
  173. data/test/blackbox/basic/recursive_app_1.tpl +0 -1
  174. data/test/blackbox/basic/recursive_app_2.exp +0 -1
  175. data/test/blackbox/basic/recursive_app_2.tpl +0 -1
  176. data/test/blackbox/buffering/data_1.rb +0 -1
  177. data/test/blackbox/buffering/data_assignment_1.exp +0 -1
  178. data/test/blackbox/buffering/data_assignment_1.tpl +0 -1
  179. data/test/blackbox/buffering/data_assignment_2.exp +0 -1
  180. data/test/blackbox/buffering/data_assignment_2.tpl +0 -1
  181. data/test/blackbox/buffering/data_assignment_3.exp +0 -1
  182. data/test/blackbox/buffering/data_assignment_3.tpl +0 -1
  183. data/test/blackbox/buffering/data_assignment_4.exp +0 -1
  184. data/test/blackbox/buffering/data_assignment_4.tpl +0 -1
  185. data/test/blackbox/buffering/input_1.exp +0 -1
  186. data/test/blackbox/buffering/input_1.tpl +0 -1
  187. data/test/blackbox/buffering/input_2.exp +0 -1
  188. data/test/blackbox/buffering/input_2.tpl +0 -1
  189. data/test/blackbox/buffering/input_3.exp +0 -1
  190. data/test/blackbox/buffering/input_3.tpl +0 -1
  191. data/test/blackbox/buffering/input_inclusion.exp +0 -1
  192. data/test/blackbox/buffering/input_inclusion.tpl +0 -1
  193. data/test/blackbox/buffering/input_inclusion_1.exp +0 -0
  194. data/test/blackbox/buffering/input_inclusion_1.tpl +0 -1
  195. data/test/blackbox/buffering/input_inclusion_2.exp +0 -1
  196. data/test/blackbox/buffering/input_inclusion_2.tpl +0 -1
  197. data/test/blackbox/buffering/input_inclusion_3.exp +0 -1
  198. data/test/blackbox/buffering/input_inclusion_3.tpl +0 -1
  199. data/test/blackbox/buffering/input_inclusion_4.exp +0 -0
  200. data/test/blackbox/buffering/input_inclusion_4.tpl +0 -1
  201. data/test/blackbox/buffering/input_inclusion_5.exp +0 -1
  202. data/test/blackbox/buffering/input_inclusion_5.tpl +0 -1
  203. data/test/blackbox/buffering/input_inclusion_6.exp +0 -1
  204. data/test/blackbox/buffering/input_inclusion_6.tpl +0 -1
  205. data/test/blackbox/buffering/input_inclusion_7.exp +0 -0
  206. data/test/blackbox/buffering/input_inclusion_7.tpl +0 -1
  207. data/test/blackbox/buffering/text_1.txt +0 -1
  208. data/test/blackbox/buffering/wlang.txt +0 -1
  209. data/test/blackbox/context/assignment_1.exp +0 -1
  210. data/test/blackbox/context/assignment_1.tpl +0 -1
  211. data/test/blackbox/context/assignment_2.exp +0 -1
  212. data/test/blackbox/context/assignment_2.tpl +0 -1
  213. data/test/blackbox/context/assignment_3.exp +0 -2
  214. data/test/blackbox/context/assignment_3.tpl +0 -2
  215. data/test/blackbox/context/assignment_4.exp +0 -1
  216. data/test/blackbox/context/assignment_4.tpl +0 -1
  217. data/test/blackbox/context/block_assignment_1.exp +0 -1
  218. data/test/blackbox/context/block_assignment_1.tpl +0 -1
  219. data/test/blackbox/context/block_assignment_2.exp +0 -1
  220. data/test/blackbox/context/block_assignment_2.tpl +0 -1
  221. data/test/blackbox/context/modulo_assignment_1.exp +0 -1
  222. data/test/blackbox/context/modulo_assignment_1.tpl +0 -1
  223. data/test/blackbox/context/modulo_assignment_2.exp +0 -1
  224. data/test/blackbox/context/modulo_assignment_2.tpl +0 -1
  225. data/test/blackbox/data_1.rb +0 -1
  226. data/test/blackbox/postblock/hello.exp +0 -1
  227. data/test/blackbox/postblock/hello.pre +0 -1
  228. data/test/blackbox/postblock/hello.tpl +0 -1
  229. data/test/blackbox/postblock/hello_input_inclusion.exp +0 -1
  230. data/test/blackbox/postblock/hello_input_inclusion.tpl +0 -1
  231. data/test/blackbox/postblock/hello_to_authors.exp +0 -1
  232. data/test/blackbox/postblock/hello_to_authors.tpl +0 -1
  233. data/test/blackbox/poststring/hello.exp +0 -1
  234. data/test/blackbox/poststring/hello.tpl +0 -1
  235. data/test/blackbox/test_all.rb +0 -70
  236. data/test/standard_dialects/ruby/data.rb +0 -7
  237. data/test/standard_dialects/ruby/inclusion.exp +0 -6
  238. data/test/standard_dialects/ruby/inclusion.tpl +0 -6
  239. data/test/standard_dialects/test_all.rb +0 -29
  240. data/test/standard_dialects/yaml/assumptions_test.rb +0 -13
  241. data/test/standard_dialects/yaml/data.rb +0 -3
  242. data/test/standard_dialects/yaml/inclusion_1.exp +0 -7
  243. data/test/standard_dialects/yaml/inclusion_1.tpl +0 -2
  244. data/test/standard_dialects/yaml/inclusion_2.exp +0 -5
  245. data/test/standard_dialects/yaml/inclusion_2.tpl +0 -3
  246. data/test/unit/test_all.rb +0 -9
  247. data/test/unit/wlang/anagram_bugs_test.rb +0 -111
  248. data/test/unit/wlang/basic_ruleset_test.rb +0 -52
  249. data/test/unit/wlang/buffering_ruleset_test.rb +0 -102
  250. data/test/unit/wlang/buffering_template1.wtpl +0 -1
  251. data/test/unit/wlang/buffering_template2.wtpl +0 -1
  252. data/test/unit/wlang/buffering_template3.wtpl +0 -1
  253. data/test/unit/wlang/buffering_template4.wtpl +0 -1
  254. data/test/unit/wlang/buffering_template5.wtpl +0 -1
  255. data/test/unit/wlang/context_ruleset_test.rb +0 -32
  256. data/test/unit/wlang/data.rb +0 -3
  257. data/test/unit/wlang/encoder_set_test.rb +0 -42
  258. data/test/unit/wlang/imperative_ruleset_test.rb +0 -107
  259. data/test/unit/wlang/intelligent_buffer_test.rb +0 -194
  260. data/test/unit/wlang/othersymbols_test.rb +0 -16
  261. data/test/unit/wlang/parser_test.rb +0 -88
  262. data/test/unit/wlang/plain_text_dialect_test.rb +0 -21
  263. data/test/unit/wlang/ruby_dialect_test.rb +0 -100
  264. data/test/unit/wlang/ruby_expected.rb +0 -3
  265. data/test/unit/wlang/ruby_template.wrb +0 -3
  266. data/test/unit/wlang/ruleset_utils_test.rb +0 -245
  267. data/test/unit/wlang/specification_examples_test.rb +0 -54
  268. data/test/unit/wlang/test_utils.rb +0 -25
  269. data/test/unit/wlang/wlang_test.rb +0 -80
data/Rakefile CHANGED
@@ -1,15 +1,3 @@
1
- begin
2
- gem "bundler", "~> 1.0"
3
- require "bundler/setup"
4
- rescue LoadError => ex
5
- puts ex.message
6
- abort "Bundler failed to load, (did you run 'gem install bundler' ?)"
7
- end
8
-
9
- # Dynamically load the gem spec
10
- $gemspec_file = File.expand_path('../wlang.gemspec', __FILE__)
11
- $gemspec = Kernel.eval(File.read($gemspec_file))
12
-
13
1
  # We run tests by default
14
2
  task :default => :test
15
3
 
@@ -19,5 +7,5 @@ task :default => :test
19
7
  # See .rake files there for complete documentation.
20
8
  #
21
9
  Dir["tasks/*.rake"].each do |taskfile|
22
- instance_eval File.read(taskfile), taskfile
10
+ load taskfile
23
11
  end
data/bin/wlang CHANGED
@@ -1,30 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- #
3
- # WLang: Code generation/Templating engine tool
4
- # (see lib/wlang/wlang.rb for more information)
5
- #
6
- # Copyright (c) 2009 University of Louvain, Bernard & Louis Lambeau
7
- # Released under a MIT or Ruby licence
8
- #
9
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
10
- require 'wlang'
11
- require 'wlang/wlang_command'
12
-
13
- begin
14
- r = WLang::WLangCommand.new
15
- r.run ARGV
16
- rescue ::WLang::Error => e
17
- $stderr.puts e.message
18
- $stderr.puts e.wlang_backtrace.join("\n\t")
19
- $stderr.puts e.backtrace.join("\n\t")
20
- rescue Interrupt => e
21
- $stderr.puts
22
- $stderr.puts "Interrupted"
23
- raise e
24
- rescue OptionParser::ParseError => e
25
- $stderr.puts e.message
26
- raise e
27
- rescue => e
28
- $stderr.puts e.message
29
- raise e
30
- end
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'wlang/command'
4
+ WLang::Command.run(ARGV)
@@ -1,407 +1,34 @@
1
- require 'wlang/loader'
2
- require 'wlang/version'
3
- require 'wlang/ext/string'
4
- require 'stringio'
5
- require 'wlang/rule'
6
- require 'wlang/rule_set'
7
- require 'wlang/encoder_set'
8
- require 'wlang/dialect'
9
- require 'wlang/dialect_dsl'
10
- require 'wlang/dialect_loader'
11
- require 'wlang/hosted_language'
12
- require 'wlang/hash_scope'
13
- require 'wlang/parser'
14
- require 'wlang/parser_state'
15
- require 'wlang/intelligent_buffer'
16
-
1
+ require "wlang/version"
2
+ require "wlang/loader"
17
3
  #
18
- # Main module of the _wlang_ code generator/template engine, providing a facade
19
- # on _wlang_ tools. See also the Roadmap section of {README}[link://files/README.html]
20
- # to enter the library.
4
+ # WLang is a powerful code generation and templating engine
21
5
  #
22
6
  module WLang
23
-
24
- ######################################################################## About files and extensions
25
-
26
- # Regular expression for file extensions
27
- FILE_EXTENSION_REGEXP = /^\.[a-zA-Z0-9]+$/
28
-
29
- # Checks that _ext_ is a valid file extension or raises an ArgumentError
30
- def self.check_file_extension(ext)
31
- raise ArgumentError, "Invalid file extension #{ext} (/^\.[a-zA-Z-0-9]+$/ expected)", caller\
32
- unless FILE_EXTENSION_REGEXP =~ ext
33
- end
34
-
35
- # Raises an ArgumentError unless file is a real readable file
36
- def self.check_readable_file(file)
37
- raise ArgumentError, "File #{file} is not readable or not a file"\
38
- unless File.exists?(file) and File.file?(file) and File.readable?(file)
39
- end
40
-
41
- ######################################################################## About dialects
42
-
43
- # Reusable string for building dialect name based regexps
44
- DIALECT_NAME_REGEXP_STR = "[-a-z]+"
45
-
46
- # Regular expression for dialect names.
47
- DIALECT_NAME_REGEXP = /^([-a-z]+)*$/
48
-
49
- # Reusable string for building dialect name based regexps
50
- QUALIFIED_DIALECT_NAME_REGEXP_STR = "[-a-z]+([\/][-a-z]+)*"
51
-
52
- # Regular expression for dialect qualified names. Dialect qualified names are
53
- # '/' seperated names, where a name is [-a-z]+.
54
- #
55
- # Examples: wlang/xhtml/uri, wlang/plain-text, ...
56
- QUALIFIED_DIALECT_NAME_REGEXP = /^[-a-z]+([\/][-a-z]+)*$/
57
-
58
- # Checks that _name_ is a valid qualified dialect name or raises an ArgumentError
59
- def self.check_qualified_dialect_name(name)
60
- raise ArgumentError, "Invalid dialect qualified name '#{name}' (/^[-a-z]+([\/][-a-z]+)*$/ expected)", caller\
61
- unless QUALIFIED_DIALECT_NAME_REGEXP =~ name
62
- end
63
7
 
64
- #
65
- # Provides installed {file extension => dialect} mappings. File extensions
66
- # (keys) contain the first dot (like .wtpl, .whtml, ...). Dialects (values) are
67
- # qualified names, not Dialect instances.
68
- #
69
- FILE_EXTENSIONS = {}
8
+ # These are allows block symbols
9
+ SYMBOLS = "!^%\"$&'*+?@~#,-./:;=<>|_".chars.to_a
70
10
 
71
- #
72
- # Main anonymous dialect. All installed dialects are children of this one,
73
- # which is anonymous because it does not appear in qualified names.
74
- #
75
- @dialect = Dialect.new("", nil)
11
+ # Template braces
12
+ BRACES = ['{', '}']
76
13
 
77
- # Returns the root of the dialect tree
78
- def self.dialect_tree
79
- @dialect
80
- end
81
-
82
- #
83
- # Maps a file extension to a dialect qualified name.
14
+ # Defines an anonymous dialect on the fly.
84
15
  #
85
16
  # Example:
86
17
  #
87
- # # We create an 'example' dialect
88
- # WLang::dialect('example') do
89
- # # see WLang::dialect about creating a dialect
18
+ # d = WLang::dialect do
19
+ # tag('$') do |buf,fn| buf << evaluate(fn) end
20
+ # ...
90
21
  # end
22
+ # d.render("Hello ${who}!", :who => "world")
23
+ # # => "Hello world!"
91
24
  #
92
- # # We map .wex file extensions to our new dialect
93
- # WLang::file_extension_map('.wex', 'example')
94
- #
95
- # This method raises an ArgumentError if the extension or dialect qualified
96
- # name is not valid.
97
- #
98
- def self.file_extension_map(extension, dialect_qname)
99
- check_file_extension(extension)
100
- check_qualified_dialect_name(dialect_qname)
101
- WLang::FILE_EXTENSIONS[extension] = dialect_qname
25
+ def dialect(superdialect = WLang::Dialect, &defn)
26
+ Class.new(superdialect, &defn)
102
27
  end
28
+ module_function :dialect
103
29
 
104
- #
105
- # Infers a dialect from a file extension. Returns nil if no dialect is currently
106
- # mapped to the given extension (see file_extension_map)
107
- #
108
- # This method never raises errors.
109
- #
110
- def self.infer_dialect(uri)
111
- WLang::FILE_EXTENSIONS[File.extname(uri)]
112
- end
113
-
114
- #
115
- # Ensures, installs or query a dialect.
116
- #
117
- # <b>When name is a Dialect</b>, returns it immediately. This helper is provided
118
- # for methods that accept both qualified dialect name and dialect instance
119
- # arguments. Calling <code>WLang::dialect(arg)</code> ensures that the result will
120
- # be a Dialect instance in all cases (if the arg is valid).
121
- #
122
- # Example:
123
- #
124
- # # This methods does something with a wlang dialect. _dialect_ argument may
125
- # # be a Dialect instance or a qualified dialect name.
126
- # def my_method(dialect = 'wlang/active-string')
127
- # # ensures the Dialect instance or raises an ArgumentError if the dialect
128
- # # qualified name is invalid (returns nil otherwise !)
129
- # dialect = WLang::dialect(dialect)
130
- # end
131
- #
132
- # <b>When called with a block</b>, this method installs a _wlang_ dialect under
133
- # _name_ (which cannot be qualified). Extensions can be provided to let _wlang_
134
- # automatically recognize files that are expressed in this dialect. The block
135
- # is interpreted as code in the dialect DSL (domain specific language, see
136
- # WLang::Dialect::DSL). Returns nil in this case.
137
- #
138
- # Example:
139
- #
140
- # # New dialect with 'my_dialect' qualified name and automatically installed
141
- # # to recognize '.wmyd' file extensions
142
- # WLang::dialect("my_dialect", '.wmyd') do
143
- # # see WLang::Dialect::DSL for this part of the code
144
- # end
145
- #
146
- # <b>When called without a block</b> this method returns a Dialect instance
147
- # installed under name (which can be a qualified name). Extensions are ignored
148
- # in this case. Returns nil if not found, a Dialect instance otherwise.
149
- #
150
- # Example:
151
- #
152
- # # Lookup for the 'wlang/xhtml' dialect
153
- # wxhtml = WLang::dialect('wlang/xhtml')
154
- #
155
- # This method raises an ArgumentError if
156
- # * _name_ is not a valid dialect qualified name
157
- # * any of the file extension in _extensions_ is invalid
158
- #
159
- def self.dialect(name, *extensions, &block)
160
- # first case, already a dialect
161
- return name if Dialect===name
162
-
163
- # other cases, argument validations
164
- check_qualified_dialect_name(name)
165
- extensions.each {|ext| check_file_extension(ext)}
166
-
167
- if block_given?
168
- # first case, dialect installation
169
- raise "Unsupported qualified names in dialect installation"\
170
- unless name.index('/').nil?
171
- Dialect::DSL.new(@dialect).dialect(name, *extensions, &block)
172
- else
173
- # second case, dialect lookup
174
- @dialect.dialect(name)
175
- end
176
- end
177
-
178
- ######################################################################## About encoders
179
-
180
- # Reusable string for building encoder name based regexps
181
- ENCODER_NAME_REGEXP_STR = "[-a-z]+"
182
-
183
- # Regular expression for encoder names.
184
- ENCODER_NAME_REGEXP = /^([-a-z]+)*$/
185
-
186
- # Reusable string for building qualified encoder name based regexps
187
- QUALIFIED_ENCODER_NAME_REGEXP_STR = "[-a-z]+([\/][-a-z]+)*"
188
-
189
- # Regular expression for encoder qualified names. Encoder qualified names are
190
- # '/' seperated names, where a name is [-a-z]+.
191
- #
192
- # Examples: xhtml/entities-encoding, sql/single-quoting, ...
193
- QUALIFIED_ENCODER_NAME_REGEXP = /^([-a-z]+)([\/][-a-z]+)*$/
194
-
195
- # Checks that _name_ is a valid qualified encoder name or raises an ArgumentError
196
- def self.check_qualified_encoder_name(name)
197
- raise ArgumentError, "Invalid encoder qualified name #{name} (/^[-a-z]+([\/][-a-z]+)*$/ expected)", caller\
198
- unless QUALIFIED_ENCODER_NAME_REGEXP =~ name
199
- end
200
-
201
- #
202
- # Returns an encoder installed under a qualified name. Returns nil if not
203
- # found. If name is already an Encoder instance, returns it immediately.
204
- #
205
- # Example:
206
- #
207
- # encoder = WLang::encoder('xhtml/entities-encoding')
208
- # encoder.encode('something that needs html entities escaping')
209
- #
210
- # This method raises an ArgumentError if _name_ is not a valid encoder qualified
211
- # name.
212
- #
213
- def self.encoder(name)
214
- check_qualified_encoder_name(name)
215
- @dialect.encoder(name)
216
- end
217
-
218
- #
219
- # Shortcut for
220
- #
221
- # WLang::encoder(encoder_qname).encode(source, options)
222
- #
223
- # This method raises an ArgumentError
224
- # * if _source_ is not a String
225
- # * if the encoder qualified name is invalid
226
- #
227
- # It raises a WLang::Error if the encoder cannot be found
228
- #
229
- def self.encode(source, encoder_qname, options = {})
230
- raise ArgumentError, "String expected for source" unless String===source
231
- check_qualified_encoder_name(encoder_qname)
232
- encoder = WLang::encoder(encoder_qname)
233
- raise WLang::Error, "Unable to find encoder #{encoder_qname}" if encoder.nil?
234
- encoder.encode(source, options)
235
- end
236
-
237
- ######################################################################## About data loading
238
-
239
- #
240
- # Provides installed {file extension => data loader} mapping. File extensions
241
- # (keys) contain the first dot (like .wtpl, .whtml, ...). Data loades are
242
- # Proc instances that take a single |uri| argument.
243
- #
244
- DATA_EXTENSIONS = {}
245
-
246
- #
247
- # Adds a data loader for file extensions. A data loader is a block of arity 1,
248
- # taking a file as parameter and returning data decoded from the file.
249
- #
250
- # Example:
251
- #
252
- # # We have some MyXMLDataLoader class that is able to create a ruby object
253
- # # from things expressed .xml files
254
- # WLang::data_loader('.xml') {|file|
255
- # MyXMLDataLaoder.parse_file(file)
256
- # }
257
- #
258
- # # Later in a template (see the buffering ruleset that gives you <<={...})
259
- # <<={resources.xml as resources}
260
- # <html>
261
- # *{resources as r}{
262
- # ...
263
- # }
264
- # </html>
265
- #
266
- # This method raises an ArgumentError if
267
- # * no block is given or if the block is not of arity 1
268
- # * any of the file extensions in _exts_ is invalid
269
- #
270
- def self.data_loader(*exts, &block)
271
- raise(ArgumentError, "WLang::data_loader expects a block") unless block_given?
272
- raise(ArgumentError, "WLang::data_loader expects a block of arity 1") unless block.arity==1
273
- exts.each {|ext| check_file_extension(ext) }
274
- exts.each {|ext| DATA_EXTENSIONS[ext] = block}
275
- end
276
-
277
- #
278
- # Loads data from a given URI. If _extension_ is omitted, tries to infer it
279
- # from the uri, otherwise use it directly. Returns loaded data.
280
- #
281
- # This method raises a WLang::Error if no data loader is installed for the found
282
- # extension. It raises an ArgumentError if the file extension is invalid.
283
- #
284
- def self.load_data(uri, extension=nil)
285
- check_file_extension(extension = extension.nil? ? File.extname(uri) : extension)
286
- loader = DATA_EXTENSIONS[extension]
287
- raise ::WLang::Error, "No data loader for #{extension}" if loader.nil?
288
- loader.call(uri)
289
- end
290
-
291
- ######################################################################## About templates and instantiations
292
-
293
- #
294
- # Factors a template instance for a given string source, dialect (default to
295
- # 'wlang/active-string') and block symbols (default to :braces)
296
- #
297
- # Example:
298
- #
299
- # # The template source code must be interpreted as wlang/xhtml
300
- # template = WLang::template('<p>Hello ${who}!</p>', 'wlang/xhtml')
301
- # str = template.instantiate(:hello => 'world')
302
- #
303
- # # We may also use other block symbols...
304
- # template = WLang::template('<p>Hello $(who)!</p>', 'wlang/xhtml', :parentheses)
305
- # str = template.instantiate(:hello => 'world')
306
- #
307
- # This method raises an ArgumentError if
308
- # * _source_ is not a String
309
- # * _dialect_ is not a valid dialect qualified name or Dialect instance
310
- # * _block_symbols_ is not in [:braces, :brackets, :parentheses]
311
- #
312
- def self.template(source, dialect = 'wlang/active-string', block_symbols = :braces)
313
- raise ArgumentError, "String expected for source" unless String===source
314
- raise ArgumentError, "Invalid symbols for block #{block_symbols}"\
315
- unless ::WLang::Template::BLOCK_SYMBOLS.keys.include?(block_symbols)
316
- template = Template.new(source, WLang::dialect(dialect), block_symbols)
317
- end
318
-
319
- #
320
- # Factors a template instance for a given file, optional dialect (if nil is
321
- # passed, the dialect is infered from the extension) and block symbols
322
- # (default to :braces)
323
- #
324
- # Example:
325
- #
326
- # # the file index.wtpl is a wlang source code in 'wlang/xhtml' dialect
327
- # # (automatically infered from file extension)
328
- # template = WLang::template('index.wtpl')
329
- # puts template.instantiate(:who => 'world') # puts 'Hello world!'
330
- #
331
- # This method raises an ArgumentError
332
- # * if _file_ does not exists, is not a file or is not readable
333
- # * if _dialect_ is not a valid qualified dialect name, Dialect instance, or nil
334
- # * _block_symbols_ is not in [:braces, :brackets, :parentheses]
335
- #
336
- # It raises a WLang::Error
337
- # * if no dialect can be infered from the file extension (if _dialect_ was nil)
338
- #
339
- def self.file_template(file, dialect = nil, block_symbols = :braces)
340
- check_readable_file(file)
341
-
342
- # Check the dialect
343
- dialect = self.infer_dialect(file) if dialect.nil?
344
- raise WLang::Error, "No known dialect for file extension '#{File.extname(file)}'\n"\
345
- "Known extensions are: " << WLang::FILE_EXTENSIONS.keys.join(", ") if dialect.nil?
346
-
347
- # Build the template now
348
- template = template(File.read(file), dialect, block_symbols)
349
- template.source_file = file
350
- template
351
- end
352
-
353
- #
354
- # Instantiates a template written in some _wlang_ dialect, using a given _context_
355
- # (providing instantiation data). Returns instantiatiation as a String. If you want
356
- # to instantiate the template in a specific buffer (a file or console for example),
357
- # use Template. _template_ is expected to be a String and _context_ a Hash. To
358
- # know available dialects, see WLang::StandardDialects. <em>block_symbols</em>
359
- # can be <tt>:braces</tt> ('{' and '}' pairs), <tt>:brackets</tt> ('[' and ']'
360
- # pairs) or <tt>:parentheses</tt> ('(' and ')' pairs).
361
- #
362
- # Examples:
363
- # WLang.instantiate "Hello ${who} !", {"who" => "Mr. Jones"}
364
- # WLang.instantiate "SELECT * FROM people WHERE name='{name}'", {"who" => "Mr. O'Neil"}, "wlang/sql"
365
- # WLang.instantiate "Hello $(who) !", {"who" => "Mr. Jones"}, "wlang/active-string", :parentheses
366
- #
367
- # This method raises an ArgumentError if
368
- # * _source_ is not a String
369
- # * _context_ is not nil or a Hash
370
- # * _dialect_ is not a valid dialect qualified name or Dialect instance
371
- # * _block_symbols_ is not in [:braces, :brackets, :parentheses]
372
- #
373
- # It raises a WLang::Error
374
- # * something goes wrong during instantiation (see WLang::Error and subclasses)
375
- #
376
- def self.instantiate(source, context = {}, dialect="wlang/active-string", block_symbols = :braces)
377
- raise ArgumentError, "Hash expected for context argument" unless (context.nil? or Hash===context)
378
- template(source, dialect, block_symbols).instantiate(context || {}).to_s
379
- end
380
-
381
- #
382
- # Instantiates a file written in some _wlang_ dialect, using a given _context_
383
- # (providing instantiation data). If _dialect_ is nil, tries to infer it from the file
384
- # extension; otherwise _dialect_ is expected to be a qualified dialect name or a Dialect
385
- # instance. See instantiate about <tt>block_symbols</tt>.
386
- #
387
- # Examples:
388
- # Wlang.file_instantiate "template.wtpl", {"who" => "Mr. Jones"}
389
- # Wlang.file_instantiate "template.xxx", {"who" => "Mr. Jones"}, "wlang/xhtml"
390
- #
391
- # This method raises an ArgumentError if
392
- # * _file_ is not a readable file
393
- # * _context_ is not nil or a Hash
394
- # * _dialect_ is not a valid dialect qualified name, Dialect instance or nil
395
- # * _block_symbols_ is not in [:braces, :brackets, :parentheses]
396
- #
397
- # It raises a WLang::Error
398
- # * if no dialect can be infered from the file extension (if _dialect_ was nil)
399
- # * something goes wrong during instantiation (see WLang::Error and subclasses)
400
- #
401
- def self.file_instantiate(file, context = nil, dialect = nil, block_symbols = :braces)
402
- raise ArgumentError, "Hash expected for context argument" unless (context.nil? or Hash===context)
403
- file_template(file, dialect, block_symbols).instantiate(context || {}).to_s
404
- end
405
-
406
- end
407
- require 'wlang/dialects/standard_dialects'
30
+ end # module WLang
31
+ require 'wlang/compiler'
32
+ require 'wlang/template'
33
+ require 'wlang/dialect'
34
+ require 'wlang/scope'