wlang 0.8.5 → 0.9.1

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 (151) hide show
  1. data/CHANGELOG.rdoc +65 -0
  2. data/README.rdoc +85 -31
  3. data/bin/wlang +5 -0
  4. data/doc/specification/dialect.wtpl +14 -0
  5. data/doc/specification/dialects.wtpl +3 -0
  6. data/doc/specification/glossary.wtpl +4 -4
  7. data/doc/specification/rulesets.wtpl +9 -9
  8. data/doc/specification/specification.css +1 -0
  9. data/doc/specification/specification.html +68 -5
  10. data/doc/specification/specification.wtpl +12 -12
  11. data/doc/specification/specification.yml +9 -9
  12. data/doc/specification/symbols.wtpl +8 -8
  13. data/lib/wlang.rb +297 -75
  14. data/lib/wlang/dialect.rb +7 -3
  15. data/lib/wlang/dialects/coderay_dialect.rb +4 -4
  16. data/lib/wlang/dialects/plain_text_dialect.rb +13 -19
  17. data/lib/wlang/dialects/redcloth_dialect.rb +16 -0
  18. data/lib/wlang/dialects/ruby_dialect.rb +16 -2
  19. data/lib/wlang/dialects/standard_dialects.rb +20 -0
  20. data/lib/wlang/dialects/xhtml_dialect.rb +24 -1
  21. data/lib/wlang/encoder.rb +1 -1
  22. data/lib/wlang/encoder_set.rb +5 -0
  23. data/lib/wlang/errors.rb +70 -6
  24. data/lib/wlang/ext/hash_methodize.rb +13 -0
  25. data/lib/wlang/{ruby_extensions.rb → ext/string.rb} +9 -5
  26. data/lib/wlang/hash_scope.rb +89 -0
  27. data/lib/wlang/hosted_language.rb +146 -0
  28. data/lib/wlang/parser.rb +189 -126
  29. data/lib/wlang/parser_state.rb +94 -0
  30. data/lib/wlang/rule_set.rb +16 -3
  31. data/lib/wlang/rulesets/basic_ruleset.rb +14 -6
  32. data/lib/wlang/rulesets/buffering_ruleset.rb +20 -29
  33. data/lib/wlang/rulesets/context_ruleset.rb +16 -20
  34. data/lib/wlang/rulesets/imperative_ruleset.rb +4 -4
  35. data/lib/wlang/rulesets/ruleset_utils.rb +26 -5
  36. data/lib/wlang/template.rb +16 -34
  37. data/lib/wlang/wlang_command.rb +4 -7
  38. data/lib/wlang/wlang_command_options.rb +5 -0
  39. data/test/blackbox/basic/execution_1.exp +1 -0
  40. data/test/blackbox/basic/execution_1.tpl +1 -0
  41. data/test/blackbox/basic/execution_2.exp +1 -0
  42. data/test/blackbox/basic/execution_2.tpl +1 -0
  43. data/test/blackbox/basic/execution_3.exp +1 -0
  44. data/test/blackbox/basic/execution_3.tpl +1 -0
  45. data/test/blackbox/basic/execution_4.exp +1 -0
  46. data/test/blackbox/basic/execution_4.tpl +1 -0
  47. data/test/blackbox/basic/inclusion_1.exp +1 -0
  48. data/test/blackbox/basic/inclusion_1.tpl +1 -0
  49. data/test/blackbox/basic/inclusion_2.exp +1 -0
  50. data/test/blackbox/basic/inclusion_2.tpl +1 -0
  51. data/test/blackbox/basic/injection_1.exp +1 -0
  52. data/test/blackbox/basic/injection_1.tpl +1 -0
  53. data/test/blackbox/basic/injection_2.exp +1 -0
  54. data/test/blackbox/basic/injection_2.tpl +1 -0
  55. data/test/blackbox/basic/modulation_1.exp +1 -0
  56. data/test/blackbox/basic/modulation_1.tpl +1 -0
  57. data/test/blackbox/basic/modulation_2.exp +1 -0
  58. data/test/blackbox/basic/modulation_2.tpl +1 -0
  59. data/test/blackbox/basic/recursive_app_1.exp +1 -0
  60. data/test/blackbox/basic/recursive_app_1.tpl +1 -0
  61. data/test/blackbox/basic/recursive_app_2.exp +1 -0
  62. data/test/blackbox/basic/recursive_app_2.tpl +1 -0
  63. data/test/blackbox/buffering/data_1.rb +1 -0
  64. data/test/blackbox/buffering/data_assignment_1.exp +1 -0
  65. data/test/blackbox/buffering/data_assignment_1.tpl +1 -0
  66. data/test/blackbox/buffering/data_assignment_2.exp +1 -0
  67. data/test/blackbox/buffering/data_assignment_2.tpl +1 -0
  68. data/test/blackbox/buffering/data_assignment_3.exp +1 -0
  69. data/test/blackbox/buffering/data_assignment_3.tpl +1 -0
  70. data/test/blackbox/buffering/data_assignment_4.exp +1 -0
  71. data/test/blackbox/buffering/data_assignment_4.tpl +1 -0
  72. data/test/blackbox/buffering/input_1.exp +1 -0
  73. data/test/blackbox/buffering/input_1.tpl +1 -0
  74. data/test/blackbox/buffering/input_2.exp +1 -0
  75. data/test/blackbox/buffering/input_2.tpl +1 -0
  76. data/test/blackbox/buffering/input_3.exp +1 -0
  77. data/test/blackbox/buffering/input_3.tpl +1 -0
  78. data/test/blackbox/buffering/input_inclusion.exp +1 -0
  79. data/test/blackbox/buffering/input_inclusion.tpl +1 -0
  80. data/test/blackbox/buffering/input_inclusion_1.exp +0 -0
  81. data/test/blackbox/buffering/input_inclusion_1.tpl +1 -0
  82. data/test/blackbox/buffering/input_inclusion_2.exp +1 -0
  83. data/test/blackbox/buffering/input_inclusion_2.tpl +1 -0
  84. data/test/blackbox/buffering/input_inclusion_3.exp +1 -0
  85. data/test/blackbox/buffering/input_inclusion_3.tpl +1 -0
  86. data/test/blackbox/buffering/input_inclusion_4.exp +0 -0
  87. data/test/blackbox/buffering/input_inclusion_4.tpl +1 -0
  88. data/test/blackbox/buffering/input_inclusion_5.exp +1 -0
  89. data/test/blackbox/buffering/input_inclusion_5.tpl +1 -0
  90. data/test/blackbox/buffering/input_inclusion_6.exp +1 -0
  91. data/test/blackbox/buffering/input_inclusion_6.tpl +1 -0
  92. data/test/blackbox/buffering/input_inclusion_7.exp +0 -0
  93. data/test/blackbox/buffering/input_inclusion_7.tpl +1 -0
  94. data/test/blackbox/buffering/text_1.txt +1 -0
  95. data/test/blackbox/buffering/wlang.txt +1 -0
  96. data/test/blackbox/context/assignment_1.exp +1 -0
  97. data/test/blackbox/context/assignment_1.tpl +1 -0
  98. data/test/blackbox/context/assignment_2.exp +1 -0
  99. data/test/blackbox/context/assignment_2.tpl +1 -0
  100. data/test/blackbox/context/assignment_3.exp +2 -0
  101. data/test/blackbox/context/assignment_3.tpl +2 -0
  102. data/test/blackbox/context/assignment_4.exp +1 -0
  103. data/test/blackbox/context/assignment_4.tpl +1 -0
  104. data/test/blackbox/context/block_assignment_1.exp +1 -0
  105. data/test/blackbox/context/block_assignment_1.tpl +1 -0
  106. data/test/blackbox/context/block_assignment_2.exp +1 -0
  107. data/test/blackbox/context/block_assignment_2.tpl +1 -0
  108. data/test/blackbox/context/modulo_assignment_1.exp +1 -0
  109. data/test/blackbox/context/modulo_assignment_1.tpl +1 -0
  110. data/test/blackbox/context/modulo_assignment_2.exp +1 -0
  111. data/test/blackbox/context/modulo_assignment_2.tpl +1 -0
  112. data/test/blackbox/data_1.rb +1 -0
  113. data/test/blackbox/test_all.rb +59 -0
  114. data/test/spec/basic_object.spec +40 -0
  115. data/test/spec/global_extensions.rb +2 -0
  116. data/test/spec/hash_scope.spec +76 -0
  117. data/test/spec/redcloth_dialect.spec +24 -0
  118. data/test/spec/test_all.rb +8 -0
  119. data/test/spec/wlang.spec +53 -0
  120. data/test/spec/xhtml_dialect.spec +23 -0
  121. data/test/{test_all.rb → unit/test_all.rb} +1 -1
  122. data/test/{wlang → unit/wlang}/anagram_bugs_test.rb +2 -2
  123. data/test/{wlang → unit/wlang}/basic_ruleset_test.rb +1 -1
  124. data/test/{wlang → unit/wlang}/buffering_ruleset_test.rb +4 -4
  125. data/test/{wlang → unit/wlang}/buffering_template1.wtpl +0 -0
  126. data/test/{wlang → unit/wlang}/buffering_template2.wtpl +0 -0
  127. data/test/{wlang → unit/wlang}/buffering_template3.wtpl +0 -0
  128. data/test/unit/wlang/buffering_template4.wtpl +1 -0
  129. data/test/unit/wlang/buffering_template5.wtpl +1 -0
  130. data/test/{wlang → unit/wlang}/context_ruleset_test.rb +0 -0
  131. data/test/{wlang → unit/wlang}/data.rb +0 -0
  132. data/test/{wlang → unit/wlang}/encoder_set_test.rb +0 -0
  133. data/test/{wlang → unit/wlang}/imperative_ruleset_test.rb +0 -0
  134. data/test/{wlang → unit/wlang}/intelligent_buffer_test.rb +0 -0
  135. data/test/{wlang → unit/wlang}/othersymbols_test.rb +0 -0
  136. data/test/{wlang → unit/wlang}/parser_test.rb +10 -11
  137. data/test/{wlang → unit/wlang}/plain_text_dialect_test.rb +0 -0
  138. data/test/{wlang → unit/wlang}/ruby_dialect_test.rb +0 -0
  139. data/test/{wlang → unit/wlang}/ruby_expected.rb +0 -0
  140. data/test/{wlang → unit/wlang}/ruby_template.wrb +0 -0
  141. data/test/{wlang → unit/wlang}/ruleset_utils_test.rb +0 -0
  142. data/test/{wlang → unit/wlang}/specification_examples_test.rb +2 -2
  143. data/test/{wlang → unit/wlang}/test_utils.rb +1 -1
  144. data/test/{wlang → unit/wlang}/wlang_test.rb +0 -0
  145. metadata +135 -42
  146. data/lib/wlang/basic_object.rb +0 -19
  147. data/lib/wlang/parser_context.rb +0 -139
  148. data/test/sandbox.rb +0 -1
  149. data/test/wlang/buffering_template4.wtpl +0 -1
  150. data/test/wlang/buffering_template5.wtpl +0 -1
  151. data/test/wlang/parser_context_test.rb +0 -29
@@ -0,0 +1,94 @@
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
@@ -18,10 +18,22 @@ module WLang
18
18
  # == Detailed API
19
19
  class RuleSet
20
20
 
21
+ # Which modules are reused
22
+ attr_reader :reuse
23
+
21
24
  #
22
25
  # Creates an new dialect rule set.
23
26
  #
24
- def initialize() @rules, @pattern = {}, nil; end
27
+ def initialize()
28
+ @rules = {}
29
+ @reuse = []
30
+ @patterns = Hash.new{|h, k| h[k] = build_pattern(k)}
31
+ end
32
+
33
+ # Yields the block with name, rule pairs
34
+ def each
35
+ @rules.each_pair{|name,rule| yield(name, rule)}
36
+ end
25
37
 
26
38
  #
27
39
  # Adds a tag matching rule to this rule set. _tag_ must be a String with the
@@ -38,7 +50,7 @@ module WLang
38
50
  end
39
51
  raise(ArgumentError, "Rule expected") unless Rule===rule
40
52
  @rules[tag] = rule
41
- @pattern = nil
53
+ @patterns.clear
42
54
  end
43
55
 
44
56
  #
@@ -46,6 +58,7 @@ module WLang
46
58
  #
47
59
  def add_rules(mod, pairs=nil)
48
60
  raise(ArgumentError,"Module expected") unless Module===mod
61
+ reuse << mod
49
62
  pairs = mod::DEFAULT_RULESET if pairs.nil?
50
63
  pairs.each_pair do |symbol,method|
51
64
  meth = mod.method(method)
@@ -61,7 +74,7 @@ module WLang
61
74
  # not intended to be used by users themselve.
62
75
  #
63
76
  def pattern(block_symbols)
64
- build_pattern(block_symbols);
77
+ @patterns[block_symbols]
65
78
  end
66
79
 
67
80
  #
@@ -50,23 +50,31 @@ module WLang
50
50
  execution(parser, offset)
51
51
  end
52
52
 
53
- # Rule implementation of <tt>%!{wlang/ruby using ... with ...}</tt>
53
+ # Rule implementation of <tt>%!{wlang/ruby using ... with ...}{...}</tt>
54
54
  def self.recursive_application(parser, offset)
55
55
  dialect, reached = parser.parse(offset, "wlang/active-string")
56
56
  text, reached = parser.parse_block(reached)
57
57
 
58
58
  # decode expression
59
59
  decoded = U.expr(:qdialect,
60
+ ["share", :share, false],
60
61
  ["using", :expr, false],
61
62
  ["with", :with, false]).decode(dialect, parser)
62
63
  parser.syntax_error(offset) if decoded.nil?
63
64
 
64
65
  # build context
65
- context = U.context_from_using_and_with(decoded)
66
-
67
- # instantiate
68
- instantiated = WLang::instantiate(text, context, decoded[:qdialect])
69
- [instantiated, reached]
66
+ dialect2 = decoded[:qdialect]
67
+ shared = decoded[:share].nil? ? :root : decoded[:share]
68
+ context = U.context_from_using_and_with(decoded)
69
+
70
+ # TODO: refactor me!!
71
+ parser.branch(:template => WLang::template(text, dialect2),
72
+ :offset => 0,
73
+ :shared => shared,
74
+ :scope => context) {
75
+ instantiated, forget = parser.instantiate
76
+ [instantiated, reached]
77
+ }
70
78
  end
71
79
 
72
80
  end # module Basic
@@ -41,6 +41,7 @@ module WLang
41
41
  end
42
42
  ["", reached]
43
43
  else
44
+ text = parser.parse(offset, "wlang/dummy")[0]
44
45
  parser.error(offset, "unable to apply output rule >>{#{text}}, not a writable directory (#{file})")
45
46
  end
46
47
  end
@@ -59,12 +60,11 @@ module WLang
59
60
 
60
61
  # handle two different cases
61
62
  if parser.has_block?(reached)
62
- parser.context_push(decoded[:variable] => data)
63
- text, reached = parser.parse_block(reached)
64
- parser.context_pop
65
- [text, reached]
63
+ parser.branch_scope(decoded[:variable] => data) {
64
+ parser.parse_block(reached)
65
+ }
66
66
  else
67
- parser.context_define(decoded[:variable], data)
67
+ parser.scope_define(decoded[:variable], data)
68
68
  ["", reached]
69
69
  end
70
70
  else
@@ -77,38 +77,29 @@ module WLang
77
77
  def self.input_inclusion(parser, offset)
78
78
  uri, reached = parser.parse(offset, "wlang/uri")
79
79
 
80
- # decode expression
80
+ # decode the expression
81
81
  decoded = U.expr(:uri,
82
+ ["share", :share, false],
82
83
  ["using", :using, false],
83
84
  ["with", :with, false]).decode(uri, parser)
84
85
  parser.syntax_error(offset) if decoded.nil?
85
86
 
86
- # handle wit
87
- context = nil
88
- if decoded[:using]
89
- raise "<<+ does not support multiple with for now." if decoded[:using].size != 1
90
- context = decoded[:using][0]
91
- raise "Unexpected nil context when duplicated" if context.nil?
92
- else
93
- context = {}
94
- end
87
+ # Look for share and context
88
+ shared = decoded[:share].nil? ? :none : decoded[:share]
89
+ context = U.context_from_using_and_with(decoded, parser)
95
90
 
96
- # handle using now
97
- if decoded[:with]
98
- case context
99
- when WLang::Parser::Context::HashScope
100
- context = context.__branch(decoded[:with])
101
- when Hash
102
- context = context.merge(decoded[:with])
103
- else
104
- raise "Unexpected context #{context}"
105
- end
106
- end
91
+ # Resolve the file by delegation to the parser
92
+ file = parser.file_resolve(decoded[:uri])
107
93
 
108
- file = parser.template.file_resolve(decoded[:uri], false)
94
+ # Go for it
109
95
  if File.file?(file) and File.readable?(file)
110
- instantiated = WLang::file_instantiate(file, context)
111
- [instantiated, reached]
96
+ parser.branch(:template => parser.file_template(file),
97
+ :offset => 0,
98
+ :shared => shared,
99
+ :scope => context) {
100
+ instantiated, forget = parser.instantiate
101
+ [instantiated, reached]
102
+ }
112
103
  else
113
104
  text = parser.parse(offset, "wlang/dummy")[0]
114
105
  parser.error(offset, "unable to apply input-inclusion rule <<+{#{text}}, not a file or not readable (#{file})")
@@ -29,12 +29,11 @@ module WLang
29
29
 
30
30
  # handle two different cases
31
31
  if parser.has_block?(reached)
32
- parser.context_push(decoded[:variable] => value)
33
- text, reached = parser.parse_block(reached)
34
- parser.context_pop
35
- [text, reached]
32
+ parser.branch_scope(decoded[:variable] => value) {
33
+ parser.parse_block(reached)
34
+ }
36
35
  else
37
- parser.context_define(decoded[:variable], value)
36
+ parser.scope_define(decoded[:variable], value)
38
37
  ["", reached]
39
38
  end
40
39
  end
@@ -52,12 +51,11 @@ module WLang
52
51
 
53
52
  # handle two different cases
54
53
  if parser.has_block?(reached)
55
- parser.context_push(decoded[:variable] => value)
56
- text, reached = parser.parse_block(reached)
57
- parser.context_pop
58
- [text, reached]
54
+ parser.branch_scope(decoded[:variable] => value) {
55
+ parser.parse_block(reached)
56
+ }
59
57
  else
60
- parser.context_define(decoded[:variable], value)
58
+ parser.scope_define(decoded[:variable], value)
61
59
  ["", reached]
62
60
  end
63
61
  end
@@ -75,12 +73,11 @@ module WLang
75
73
 
76
74
  # handle two different cases
77
75
  if parser.has_block?(reached)
78
- parser.context_push(decoded[:variable] => value)
79
- text, reached = parser.parse_block(reached)
80
- parser.context_pop
81
- [text, reached]
76
+ parser.branch_scope(decoded[:variable] => value) {
77
+ parser.parse_block(reached)
78
+ }
82
79
  else
83
- parser.context_define(decoded[:variable], value)
80
+ parser.scope_define(decoded[:variable], value)
84
81
  ["", reached]
85
82
  end
86
83
  end
@@ -99,12 +96,11 @@ module WLang
99
96
 
100
97
  # handle two different cases
101
98
  if parser.has_block?(reached)
102
- parser.context_push(decoded[:variable] => value)
103
- text, reached = parser.parse_block(reached)
104
- parser.context_pop
105
- [text, reached]
99
+ parser.branch_scope(decoded[:variable] => value) {
100
+ parser.parse_block(reached)
101
+ }
106
102
  else
107
- parser.context_define(decoded[:variable], value)
103
+ parser.scope_define(decoded[:variable], value)
108
104
  ["", reached]
109
105
  end
110
106
  end
@@ -101,10 +101,10 @@ module WLang
101
101
  end
102
102
 
103
103
  # install arguments and parse block2, positioned at block3
104
- parser.context_push(merge_each_args(names, args))
105
- parsed, block3 = parser.parse_block(block2)
106
- parser.append_buffer(buffer, parsed, true)
107
- parser.context_pop
104
+ parser.branch_scope(merge_each_args(names, args)) {
105
+ parsed, block3 = parser.parse_block(block2)
106
+ parser.append_buffer(buffer, parsed, true)
107
+ }
108
108
  first = false
109
109
  end
110
110
 
@@ -35,6 +35,9 @@ module WLang
35
35
  # USING string for as expression
36
36
  USING = VAR + '(' + '\s*,\s*' + VAR + ')*'
37
37
 
38
+ # SHARE string for expression
39
+ SHARE = '(all|root|none)'
40
+
38
41
  # Basic blocks for building expressions
39
42
  BASIC_BLOCKS = {
40
43
  :dialect => {:str => DIALECT, :groups => 0, :decoder => nil},
@@ -46,7 +49,8 @@ module WLang
46
49
  :uri => {:str => URI, :groups => 0, :decoder => nil},
47
50
  :with => {:str => WITH, :groups => 6, :decoder => :decode_with},
48
51
  :multi_as => {:str => MULTI_AS, :groups => 1, :decoder => :decode_multi_as},
49
- :using => {:str => USING, :groups => 1, :decoder => :decode_using}
52
+ :using => {:str => USING, :groups => 1, :decoder => :decode_using},
53
+ :share => {:str => SHARE, :groups => 1, :decoder => :decode_share}
50
54
  }
51
55
 
52
56
  # Regular expressions of built expressions
@@ -134,11 +138,28 @@ module WLang
134
138
  expr.split(/\s*,\s*/).collect{|s| decode_expr(s, parser)}
135
139
  end
136
140
 
141
+ # Decodes a multi as expression
142
+ def self.decode_share(expr, parser)
143
+ expr.to_sym
144
+ end
145
+
146
+ # Converts something to a usable hash
147
+ def self.to_hash(hash)
148
+ case hash
149
+ when Hash
150
+ hash.dup
151
+ when NilClass
152
+ {}
153
+ when Array
154
+ hash.inject({}){|memo, n| memo.merge!(to_hash(n))}
155
+ else
156
+ raise ArgumentError, "Unable to convert #{hash} to an hash"
157
+ end
158
+ end
159
+
137
160
  # Builds a hash for 'using ... with ...' rules from a decoded expression
138
- def self.context_from_using_and_with(decoded)
139
- context = decoded[:using]
140
- context = context.dup unless context.nil?
141
- context = {} if context.nil?
161
+ def self.context_from_using_and_with(decoded, parser = nil)
162
+ context = to_hash(decoded[:using])
142
163
  context.merge!(decoded[:with]) unless decoded[:with].nil?
143
164
  context
144
165
  end
@@ -1,7 +1,9 @@
1
1
  module WLang
2
-
3
2
  #
4
- # Template in a given wlang dialect, providing a default context object.
3
+ # Template in a given wlang dialect and expecting :braces, :brackets or
4
+ # :parentheses as block delimiters. A template is an abstraction over a
5
+ # wlang source text. It also provides utilities to create friendly location
6
+ # messages for offsets in the source text.
5
7
  #
6
8
  class Template
7
9
 
@@ -13,28 +15,19 @@ module WLang
13
15
  # Template wlang dialect (wlang/...)
14
16
  attr_reader :dialect
15
17
 
16
- # Instantiation context
17
- attr_reader :context
18
-
19
18
  # Block symbols
20
19
  attr_reader :block_symbols
21
20
 
22
21
  # Attached file source
23
22
  attr_accessor :source_file
24
23
 
25
- #
26
24
  # Creates a template instance.
27
- #
28
- def initialize(source, dialect, context=nil, block_symbols = :braces)
25
+ def initialize(source, dialect, block_symbols = :braces)
26
+ dialect = WLang::dialect(dialect)
29
27
  raise(ArgumentError, "Source is mandatory") if source.nil?
30
- if String===dialect
31
- dname, dialect = dialect, WLang::dialect(dialect)
32
- raise(ArgumentError, "Unknown dialect #{dname}") if dialect.nil?
33
- end
34
- raise(ArgumentError, "Dialect is mandatory") unless WLang::Dialect===dialect
28
+ raise(ArgumentError, "Dialect instance expected for dialect, #{dialect} received") unless Dialect===dialect
35
29
  @source = source
36
30
  @dialect = dialect
37
- @context = WLang::Parser::Context.new(context)
38
31
  @block_symbols = block_symbols
39
32
  end
40
33
 
@@ -59,40 +52,29 @@ module WLang
59
52
  end
60
53
  end
61
54
 
62
- #
63
- # Instantiate the template, with an additional context and an output buffer.
64
- #
65
- def instantiate(buffer=nil, context=nil)
66
- unless context.nil?
67
- @context.push(context)
68
- end
69
- parser = WLang::Parser.instantiator(self, buffer)
70
- instantiated = parser.instantiate
71
- unless context.nil?
72
- @context.pop
73
- end
74
- instantiated[0]
55
+ # Instantiates the template, with optinal context and hosted language.
56
+ def instantiate(context = {}, hosted = ::WLang::HostedLanguage.new)
57
+ p = ::WLang::Parser.new(hosted, self, context)
58
+ p.instantiate[0]
75
59
  end
76
60
 
77
61
  # Returns a friendly position of an offset in the source text
78
62
  def where(offset)
79
63
  src = source_text
80
- "#{@source_file}:#{src.__wlang_line_of(offset)}:#{src.__wlang_column_of(offset)-1}"
64
+ source_file = self.source_file ? File.expand_path(self.source_file) : "no source file"
65
+ "#{source_file || 'inline template'}:#{src.__wlang_line_of(offset)}:#{src.__wlang_column_of(offset)-1}"
81
66
  end
82
67
 
83
68
  # Raises a WLang::Error for the given offset
84
69
  def error(offset, msg = "")
85
- src = source_text
86
- line, column = src.__wlang_line_of(offset), src.__wlang_column_of(offset)-1
87
- raise WLang::Error, "#{@source_file}:#{line}:#{column} #{msg}"
70
+ raise WLang::Error, "#{where(offset)} #{msg}"
88
71
  end
89
72
 
90
73
  # Raises a friendly ParseError, with positions and so on
91
74
  def parse_error(offset, msg = "")
92
75
  src = source_text
93
- line, column = src.__wlang_line_of(offset), src.__wlang_column_of(offset)-1
94
- ex = ParseError.new("#{@source_file}:#{line}:#{column} #{msg}")
95
- ex.line, ex.column = line, column
76
+ ex = ParseError.new("#{where(offset)} #{msg}")
77
+ ex.line, ex.column = src.__wlang_line_of(offset), src.__wlang_column_of(offset)-1
96
78
  raise ex
97
79
  end
98
80