wlang 0.10.2 → 2.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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