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
@@ -1,94 +0,0 @@
1
- module WLang
2
-
3
- # Provides an intelligent output buffer
4
- class IntelligentBuffer < String
5
-
6
- # Some string utilities
7
- module Methods
8
-
9
- # Aligns _str_ at left offset _n_
10
- # Credits: Treetop and Facets 2.0.2
11
- def tabto(str, n)
12
- if str =~ /^( *)\S/
13
- indent(str, n - $1.length)
14
- else
15
- str
16
- end
17
- end
18
-
19
- # Positive or negative indentation of _str_
20
- # Credits: Treetop and Facets 2.0.2
21
- def indent(str, n)
22
- if n >= 0
23
- str.gsub(/^/, ' ' * n)
24
- else
25
- str.gsub(/^ {0,#{-n}}/, "")
26
- end
27
- end
28
-
29
- # Checks if _str_ contains multiple lines
30
- def is_multiline?(str)
31
- str =~ /\n/ ? true : false
32
- end
33
-
34
- #
35
- # Strips a multiline block.
36
- #
37
- # Example:
38
- # ([\t ]*\n)?
39
- # ([\t ]\n)*
40
- # [\t ]*some text here\n
41
- # [\t ]* indented also\n?
42
- # [\t ]*
43
- #
44
- # becomes:
45
- # some text here\n
46
- # indented also\n
47
- #
48
- def strip_block(str)
49
- match = str.match(/\A[\t ]*\n?/)
50
- str = match.post_match if match
51
- match = str.match(/\n[\t ]*\Z/)
52
- str = (match.pre_match << "\n") if match
53
- str
54
- end
55
-
56
- # Returns column number of a specific offset
57
- # Credits: Treetop and Facets 2.0.2
58
- def column_of(str, index)
59
- return 1 if index == 0
60
- newline_index = str.rindex("\n", index - 1)
61
- if newline_index
62
- index - newline_index
63
- else
64
- index + 1
65
- end
66
- end
67
-
68
- # Returns the column of the last character
69
- def last_column(str)
70
- column_of(str, str.length)
71
- end
72
-
73
- # Pushes a string, aligning it first
74
- def <<(str, block=false)
75
- if block and is_multiline?(str) and stripped = strip_block(str)
76
- str = tabto(stripped, last_column(self)-1)
77
- str = str.match(/\A[\t ]*/).post_match
78
- end
79
- super(str)
80
- end
81
-
82
- # WLang explicit appending
83
- def wlang_append(str, block)
84
- self.<<(str, block)
85
- end
86
-
87
- end
88
-
89
- # Include utilities
90
- include Methods
91
-
92
- end
93
-
94
- end
@@ -1,332 +0,0 @@
1
- require 'stringio'
2
- require 'wlang/rule'
3
- require 'wlang/rule_set'
4
- require 'wlang/errors'
5
- require 'wlang/template'
6
- module WLang
7
- #
8
- # Parser for wlang templates.
9
- #
10
- # This class implements the parsing algorithm of wlang, recognizing special tags
11
- # and replacing them using installed rules. Instanciating a template is done
12
- # using instantiate. All other methods (parse, parse_block, has_block?) and the
13
- # like are callbacks for rules and should not be used by users themselve.
14
- #
15
- # == Detailed API
16
- class Parser
17
-
18
- # Initializes a parser instance.
19
- def initialize(hosted, template, scope)
20
- raise(ArgumentError, "Hosted language is mandatory (a ::WLang::HostedLanguage)") unless ::WLang::HostedLanguage===hosted
21
- raise(ArgumentError, "Template is mandatory (a ::WLang::Template)") unless ::WLang::Template===template
22
- raise(ArgumentError, "Scope is mandatory (a Hash)") unless ::Hash===scope
23
- @state = ::WLang::Parser::State.new(self).branch(
24
- :hosted => hosted,
25
- :template => template,
26
- :dialect => template.dialect,
27
- :offset => 0,
28
- :shared => :none,
29
- :scope => scope,
30
- :buffer => template.dialect.factor_buffer)
31
- end
32
-
33
- ###################################################################### Facade on the parser state
34
-
35
- # Returns the current parser state
36
- def state(); @state; end
37
-
38
- # Returns the current template
39
- def template() state.template; end
40
-
41
- # Returns the current buffer
42
- def dialect() state.dialect; end
43
-
44
- # Returns the current template's source text
45
- def source_text() state.template.source_text; end
46
-
47
- # Returns the current offset
48
- def offset() state.offset; end
49
-
50
- # Sets the current offset of the parser
51
- def offset=(offset) state.offset = offset; end
52
-
53
- # Returns the current buffer
54
- def buffer() state.buffer; end
55
-
56
- # Returns the current hosted language
57
- def hosted() state.hosted; end
58
-
59
- # Branches the current parser
60
- def branch(opts = {})
61
- raise ArgumentError, "Parser branching requires a block" unless block_given?
62
- @state = @state.branch(opts)
63
- result = yield(@state)
64
- @state = @state.parent
65
- result
66
- end
67
-
68
- ###################################################################### Facade on the file system
69
-
70
- # Resolves an URI throught the current template
71
- def file_resolve(uri)
72
- template.file_resolve(uri)
73
- end
74
-
75
- ###################################################################### Facade on wlang itself
76
-
77
- # Factors a template instance for a given file
78
- def file_template(file, dialect = nil, block_symbols = :braces)
79
- WLang::file_template(file, dialect, block_symbols)
80
- end
81
-
82
- # Finds a real dialect instance from an argument (Dialect instance or
83
- # qualified name)
84
- def ensure_dialect(dialect)
85
- if String===dialect
86
- dname, dialect = dialect, WLang::dialect(dialect)
87
- raise(ParseError,"Unknown modulation dialect: #{dname}") if dialect.nil?
88
- elsif not(Dialect===dialect)
89
- raise(ParseError,"Unknown modulation dialect: #{dialect}")
90
- end
91
- dialect
92
- end
93
-
94
- # Finds a real ecoder instance from an argument (Encoder instance or
95
- # qualified or unqualified name)
96
- def ensure_encoder(encoder)
97
- if String===encoder
98
- if encoder.include?("/")
99
- ename, encoder = encoder, WLang::encoder(encoder)
100
- raise(ParseError,"Unknown encoder: #{ename}") if encoder.nil?
101
- else
102
- ename, encoder = encoder, self.dialect.find_encoder(encoder)
103
- raise(ParseError,"Unknown encoder: #{ename}") if encoder.nil?
104
- end
105
- elsif not(Encoder===encoder)
106
- raise(ParseError,"Unknown encoder: #{encoder}")
107
- end
108
- encoder
109
- end
110
-
111
- ###################################################################### Main parser methods
112
-
113
- # Checks the result of a given rule
114
- def launch_rule(dialect, rule_symbol, rule, offset)
115
- result = rule.start_tag(self, offset)
116
- raise WLang::Error, "Bad rule implementation #{dialect.qualified_name} #{rule_symbol}{}\n#{result.inspect}"\
117
- unless result.size == 2 and String===result[0] and Integer===result[1]
118
- result
119
- end
120
-
121
- #
122
- # Parses the template's text and instantiate it. Dialect post_transformer is
123
- # only applied of _apply_posttransform_ is set to true.
124
- #
125
- def instantiate(apply_posttransform = true)
126
- # Main variables put in local scope for efficiency:
127
- # - template: current parsed template
128
- # - source_text: current template's source text
129
- # - offset: matching current position
130
- # - pattern: current dialect's regexp pattern
131
- # - rules: handlers of '{' currently opened
132
- template = self.template
133
- symbols = self.template.block_symbols
134
- source_text = self.source_text
135
- dialect = self.dialect
136
- buffer = self.buffer
137
- pattern = dialect.pattern(template.block_symbols)
138
- rules = []
139
-
140
- # we start matching everything in the ruleset
141
- while match_at=source_text.index(pattern, self.offset)
142
- match, match_length = $~[0], $~[0].length
143
-
144
- # puts pre_match (we can't use $~.pre_match !)
145
- self.<<(source_text[self.offset, match_at-self.offset], false) if match_at>0
146
-
147
- if source_text[match_at,1]=='\\' # escaping sequence
148
- self.<<(match[1..-1], false)
149
- self.offset = match_at + match_length
150
-
151
- elsif match.length==1 # simple '{' or '}' here
152
- self.offset = match_at + match_length
153
- if match==Template::BLOCK_SYMBOLS[symbols][0]
154
- self.<<(match, false) # simple '{' are always pushed
155
- # we push '{' in rules to recognize it's associated '}'
156
- # that must be pushed on buffer also
157
- rules << match
158
- else
159
- # end of my job if I can't pop a previous rule
160
- break if rules.empty?
161
- # otherwise, push '}' only if associated to a simple '{'
162
- self.<<(match, false) unless Rule===rules.pop
163
- end
164
-
165
- elsif match[-1,1]==Template::BLOCK_SYMBOLS[symbols][0] # opening special tag
166
- # following line should never return nil as the matching pattern comes
167
- # from the ruleset itself!
168
- rule_symbol = match[0..-2]
169
- rule = dialect.ruleset[rule_symbol]
170
- rules << rule
171
-
172
- # Just added to get the last position in case of an error
173
- self.offset = match_at + match_length
174
-
175
- # lauch that rule, get it's replacement and my new offset
176
- replacement, self.offset = launch_rule(dialect, rule_symbol, rule, self.offset)
177
-
178
- # push replacement
179
- self.<<(replacement, true) unless replacement.empty?
180
- end
181
-
182
- end # while match_at=...
183
-
184
- # trailing data (end of template reached only if no match_at)
185
- unless match_at
186
- unexpected_eof(source_text.length, '}') unless rules.empty?
187
- self.<<(source_text[self.offset, 1+source_text.length-self.offset], false)
188
- self.offset = source_text.length
189
- end
190
-
191
- # Apply post-transformation only if required
192
- if apply_posttransform
193
- [dialect.apply_post_transform(buffer), self.offset-1]
194
- else
195
- [buffer, self.offset-1]
196
- end
197
- end
198
-
199
- ###################################################################### Callbacks for rule sets
200
-
201
- #
202
- # Launches a child parser for instantiation at a given _offset_ in given
203
- # _dialect_ (same dialect than self if dialect is nil) and with an output
204
- # _buffer_.
205
- #
206
- def parse(offset, req_dialect = nil, req_buffer = nil)
207
- dialect = ensure_dialect(req_dialect.nil? ? self.dialect : req_dialect)
208
- buffer = (req_buffer.nil? ? dialect.factor_buffer : req_buffer)
209
- branch(:offset => offset,
210
- :dialect => dialect,
211
- :buffer => buffer) do
212
- instantiate(!req_dialect.nil?)
213
- end
214
- end
215
-
216
- #
217
- # Checks if a given offset is a starting block. For easy implementation of rules
218
- # the check applied here is that text starting at _offset_ in the template is precisely
219
- # '}{' (the reason for that is that instantiate, parse, parse_block always stop
220
- # parsing on a '}')
221
- #
222
- def has_block?(offset)
223
- self.source_text[offset,2] == template.block_endstart
224
- end
225
-
226
- #
227
- # Parses a given block starting at a given _offset_, expressed in a given
228
- # _dialect_ and using an output _buffer_. This method raises a ParseError if
229
- # there is no block at the offset. It implies that we are on a '}{', see
230
- # has_block? for details. Rules may thus force mandatory block parsing without
231
- # having to check anything. Optional blocks must be handled by rules themselve.
232
- #
233
- def parse_block(offset, dialect=nil, buffer=nil)
234
- block_missing_error(offset+2) unless has_block?(offset)
235
- parse(offset+2, dialect, buffer)
236
- end
237
-
238
- ###################################################################### Facade on the buffer
239
-
240
- # Appends on a given buffer
241
- def append_buffer(buffer, str, block)
242
- if buffer.respond_to?(:wlang_append)
243
- buffer.wlang_append(str, block)
244
- else
245
- buffer << str
246
- end
247
- end
248
-
249
- # Pushes a given string on the output buffer
250
- def <<(str, block)
251
- append_buffer(buffer, str, block)
252
- end
253
-
254
- ###################################################################### Facade on the scope
255
-
256
- # Yields the block in a new scope branch, pushing pairing values on it.
257
- # Original scope is restored after that. Returns what the yielded block
258
- # returned.
259
- def branch_scope(pairing = {}, which = :all)
260
- raise ArgumentError, "Parser.branch_scope expects a block" unless block_given?
261
- branch(:scope => pairing, :shared => which) { yield }
262
- end
263
-
264
- # Adds a key/value pair on the current scope.
265
- def scope_define(key, value)
266
- state.scope[key] = value
267
- end
268
-
269
- ###################################################################### Facade on the hosted language
270
-
271
- #
272
- # Evaluates a ruby expression on the current context.
273
- # See WLang::Parser::Context#evaluate.
274
- #
275
- def evaluate(expression)
276
- hosted.evaluate(expression, state)
277
- end
278
-
279
- ###################################################################### Facade on the dialect
280
-
281
- # Factors a specific buffer on the current dialect
282
- def factor_buffer
283
- self.dialect.factor_buffer
284
- end
285
-
286
- #
287
- # Encodes a given text using an encoder, that may be a qualified name or an
288
- # Encoder instance.
289
- #
290
- def encode(src, encoder, options=nil)
291
- options = {} unless options
292
- options['_encoder_'] = encoder
293
- options['_template_'] = template
294
- ensure_encoder(encoder).encode(src, options)
295
- end
296
-
297
- ###################################################################### About errors
298
-
299
- # Raises an exception with a friendly message
300
- def error(offset, message)
301
- template.error(offset, message)
302
- end
303
-
304
- #
305
- # Raises a ParseError at a given offset.
306
- #
307
- def syntax_error(offset, msg=nil)
308
- text = self.parse(offset, "wlang/dummy", "")
309
- msg = msg.nil? ? '' : ": #{msg}"
310
- template.parse_error(offset, "parse error on '#{text}'#{msg}")
311
- end
312
-
313
- #
314
- # Raises a ParseError at a given offset for a missing block
315
- #
316
- def block_missing_error(offset)
317
- template.parse_error(offset, "parse error, block was expected")
318
- end
319
-
320
- #
321
- # Raises a ParseError at a given offset for a unexpected EOF
322
- # specif. the expected character when EOF found
323
- #
324
- def unexpected_eof(offset, expected)
325
- template.parse_error(offset, "#{expected} expected, EOF found")
326
- end
327
-
328
- # Protected methods are...
329
- protected :hosted, :offset, :source_text, :dialect
330
-
331
- end # class Parser
332
- end # module WLang
@@ -1,94 +0,0 @@
1
- module WLang
2
- class Parser
3
- #
4
- # Encapsulates all state information of a WLang parser
5
- #
6
- class State
7
-
8
- # The attached parser
9
- attr_accessor :parser
10
-
11
- # The parent state
12
- attr_accessor :parent
13
-
14
- # The current hosted language
15
- attr_accessor :hosted
16
-
17
- # The current template
18
- attr_accessor :template
19
-
20
- # The current dialect
21
- attr_accessor :dialect
22
-
23
- # The current offset in template's source code
24
- attr_accessor :offset
25
-
26
- # The current scope
27
- attr_accessor :scope
28
-
29
- # The current output buffer
30
- attr_accessor :buffer
31
-
32
- # Creates a new state instance for a given parser and optional
33
- # parent.
34
- def initialize(parser, parent = nil)
35
- @parser, @parent = parser, parent
36
- end
37
-
38
- # Checks internals
39
- def check
40
- raise "WLang::Parser::State fatal: invalid parser #{parser}" unless ::WLang::Parser===parser
41
- raise "WLang::Parser::State fatal: invalid hosted #{hosted}" unless ::WLang::HostedLanguage===hosted
42
- raise "WLang::Parser::State fatal: missing template #{template}" unless ::WLang::Template===template
43
- raise "WLang::Parser::State fatal: missing dialect #{dialect}" unless ::WLang::Dialect===dialect
44
- raise "WLang::Parser::State fatal: missing offset #{offset}" unless Integer===offset
45
- raise "WLang::Parser::State fatal: missing scope #{scope}" unless ::WLang::HashScope===scope
46
- raise "WLang::Parser::State fatal: missing buffer #{buffer}" unless buffer.respond_to?(:<<)
47
- self
48
- end
49
-
50
- #
51
- # Branches this state.
52
- #
53
- # Branching allows creating a child parser state of this one. Options are:
54
- # - :hosted => a new hosted language
55
- # - :template => a new template
56
- # - :dialect => a new dialect
57
- # - :offset => a new offset in the template
58
- # - :shared => :all, :root or :none (which scoping should be shared)
59
- # - :scope => a Hash of new pairing to push on the new scope
60
- # - :buffer => a new output buffer to use
61
- #
62
- def branch(opts = {})
63
- child = State.new(parser, self)
64
- child.hosted = opts[:hosted] || hosted
65
- child.template = opts[:template] || template
66
- child.dialect = opts[:dialect] || child.template.dialect
67
- child.offset = opts[:offset] || offset
68
- child.buffer = opts[:buffer] || child.dialect.factor_buffer
69
- child.scope = case opts[:shared]
70
- when :all, NilClass
71
- scope.branch(opts[:scope])
72
- when :root
73
- scope.root.branch(opts[:scope])
74
- when :none
75
- ::WLang::HashScope.new(opts[:scope])
76
- else
77
- raise ArgumentError, "Invalid ParserState.branch option :shared #{opts[:shared]}"
78
- end
79
- child.check
80
- end
81
-
82
- # Returns a friendly location for this parser state
83
- def where
84
- template ? template.where(offset) : "Source template tree"
85
- end
86
-
87
- # Returns a friendly wlang backtrace as an array
88
- def backtrace
89
- parent ? parent.backtrace.unshift(where) : [where]
90
- end
91
-
92
- end # class State
93
- end # class Parser
94
- end # module WLang