wlang 0.8.5 → 0.9.1

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