wlang 0.8.4

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 (73) hide show
  1. data/LICENCE.rdoc +25 -0
  2. data/README.rdoc +111 -0
  3. data/bin/wlang +24 -0
  4. data/doc/specification/about.rdoc +61 -0
  5. data/doc/specification/dialects.wtpl +0 -0
  6. data/doc/specification/examples.rb +3 -0
  7. data/doc/specification/glossary.wtpl +14 -0
  8. data/doc/specification/hosting.rdoc +0 -0
  9. data/doc/specification/overview.rdoc +116 -0
  10. data/doc/specification/rulesets.wtpl +87 -0
  11. data/doc/specification/specification.css +52 -0
  12. data/doc/specification/specification.html +1361 -0
  13. data/doc/specification/specification.js +8 -0
  14. data/doc/specification/specification.wtpl +41 -0
  15. data/doc/specification/specification.yml +430 -0
  16. data/doc/specification/symbols.wtpl +16 -0
  17. data/lib/wlang.rb +186 -0
  18. data/lib/wlang/basic_object.rb +19 -0
  19. data/lib/wlang/dialect.rb +230 -0
  20. data/lib/wlang/dialect_dsl.rb +136 -0
  21. data/lib/wlang/dialect_loader.rb +69 -0
  22. data/lib/wlang/dialects/coderay_dialect.rb +35 -0
  23. data/lib/wlang/dialects/plain_text_dialect.rb +75 -0
  24. data/lib/wlang/dialects/rdoc_dialect.rb +33 -0
  25. data/lib/wlang/dialects/ruby_dialect.rb +35 -0
  26. data/lib/wlang/dialects/sql_dialect.rb +38 -0
  27. data/lib/wlang/dialects/standard_dialects.rb +113 -0
  28. data/lib/wlang/dialects/xhtml_dialect.rb +40 -0
  29. data/lib/wlang/encoder.rb +66 -0
  30. data/lib/wlang/encoder_set.rb +117 -0
  31. data/lib/wlang/errors.rb +37 -0
  32. data/lib/wlang/intelligent_buffer.rb +94 -0
  33. data/lib/wlang/parser.rb +251 -0
  34. data/lib/wlang/parser_context.rb +146 -0
  35. data/lib/wlang/ruby_extensions.rb +21 -0
  36. data/lib/wlang/rule.rb +66 -0
  37. data/lib/wlang/rule_set.rb +93 -0
  38. data/lib/wlang/rulesets/basic_ruleset.rb +75 -0
  39. data/lib/wlang/rulesets/buffering_ruleset.rb +103 -0
  40. data/lib/wlang/rulesets/context_ruleset.rb +115 -0
  41. data/lib/wlang/rulesets/encoding_ruleset.rb +73 -0
  42. data/lib/wlang/rulesets/imperative_ruleset.rb +132 -0
  43. data/lib/wlang/rulesets/ruleset_utils.rb +296 -0
  44. data/lib/wlang/template.rb +79 -0
  45. data/lib/wlang/wlang_command.rb +54 -0
  46. data/lib/wlang/wlang_command_options.rb +158 -0
  47. data/test/sandbox.rb +1 -0
  48. data/test/test_all.rb +8 -0
  49. data/test/wlang/anagram_bugs_test.rb +111 -0
  50. data/test/wlang/basic_ruleset_test.rb +52 -0
  51. data/test/wlang/buffering_ruleset_test.rb +102 -0
  52. data/test/wlang/buffering_template1.wtpl +1 -0
  53. data/test/wlang/buffering_template2.wtpl +1 -0
  54. data/test/wlang/buffering_template3.wtpl +1 -0
  55. data/test/wlang/buffering_template4.wtpl +1 -0
  56. data/test/wlang/buffering_template5.wtpl +1 -0
  57. data/test/wlang/context_ruleset_test.rb +32 -0
  58. data/test/wlang/data.rb +3 -0
  59. data/test/wlang/encoder_set_test.rb +42 -0
  60. data/test/wlang/imperative_ruleset_test.rb +107 -0
  61. data/test/wlang/intelligent_buffer_test.rb +194 -0
  62. data/test/wlang/othersymbols_test.rb +16 -0
  63. data/test/wlang/parser_context_test.rb +29 -0
  64. data/test/wlang/parser_test.rb +89 -0
  65. data/test/wlang/plain_text_dialect_test.rb +21 -0
  66. data/test/wlang/ruby_dialect_test.rb +100 -0
  67. data/test/wlang/ruby_expected.rb +3 -0
  68. data/test/wlang/ruby_template.wrb +3 -0
  69. data/test/wlang/ruleset_utils_test.rb +245 -0
  70. data/test/wlang/specification_examples_test.rb +52 -0
  71. data/test/wlang/test_utils.rb +25 -0
  72. data/test/wlang/wlang_test.rb +80 -0
  73. metadata +136 -0
@@ -0,0 +1,40 @@
1
+ require 'cgi'
2
+ module WLang
3
+ class EncoderSet
4
+
5
+ # Defines encoders of the whtml dialect
6
+ module XHtml
7
+
8
+ # Default encoders
9
+ DEFAULT_ENCODERS = {"main-encoding" => :entities_encoding,
10
+ "single-quoting" => :single_quoting,
11
+ "double-quoting" => :double_quoting,
12
+ "entities-encoding" => :entities_encoding}
13
+
14
+
15
+ # Single-quoting encoding
16
+ def self.single_quoting(src, options); src.gsub(/([^\\])'/,%q{\1\\\'}); end
17
+
18
+ # Double-quoting encoding
19
+ def self.double_quoting(src, options); src.gsub('"','\"'); end
20
+
21
+ # Entities-encoding
22
+ def self.entities_encoding(src, options);
23
+ CGI::escapeHTML(src)
24
+ end
25
+
26
+ end # module XHtml
27
+ end
28
+ class RuleSet
29
+
30
+ # Defines rulset of the wlang/xhtml dialect
31
+ module XHtml
32
+
33
+ # Default mapping between tag symbols and methods
34
+ DEFAULT_RULESET = {}
35
+
36
+ end # module XHtml
37
+
38
+ end
39
+ end
40
+
@@ -0,0 +1,66 @@
1
+ module WLang
2
+
3
+ #
4
+ # Encapsulates some encoding algorithm. This class is to EncoderSet what Rule
5
+ # is to RuleSet. Encoders are always installed on a EncoderSet (using EncoderSet#add_encoder),
6
+ # which is itself installed on a Dialect. Note that the method mentionned previously
7
+ # provides a DRY shortcut, allowing not using this class directly.
8
+ #
9
+ # Example:
10
+ # # Encoder subclassing can be avoided by providing a block to new
11
+ # # The following encoder job is to upcase the text:
12
+ # encoder = Encoder.new do |src,options|
13
+ # src.upcase
14
+ # end
15
+ #
16
+ # # It is even better to use the DSL
17
+ # WLang::dialect("mydialect") do
18
+ # # The following encoder job is to upcase the text and will be installed
19
+ # # under mydialect/myupcaser qualified name
20
+ # encoder("myupcaser") do |src,options|
21
+ # src.upcase
22
+ # end
23
+ # end
24
+ #
25
+ # Creating a a new encoder can be made in two ways: by subclassing this class and
26
+ # overriding the encoder method or by passing a block to new. In both cases,
27
+ # <b>encoders should always be stateless</b>, to allow reusable dialects that could
28
+ # even be used in a multi-threading environment.
29
+ #
30
+ # == Detailed API
31
+ class Encoder
32
+
33
+ #
34
+ # Creates a new encoder. If no block is given, the invocation of new MUST be made
35
+ # on a subclass overriding encoder. Otherwise, the block is considered as the
36
+ # effective stateless implementation of encoder and will be called with the
37
+ # same arguments.
38
+ #
39
+ def initialize(&block)
40
+ unless block.nil?
41
+ raise(ArgumentError, "Expected a rule block of arity 2")\
42
+ unless block.arity==2
43
+ end
44
+ @block = block
45
+ end
46
+
47
+ #
48
+ # Fired by the parser when a rule request encoding of some instantiated text.
49
+ # Typical example is the standard tag <tt>^{encoder/qualified/name}{...}</tt>
50
+ # (see WLang::RuleSet::Basic) which requires the second block instantiation to
51
+ # be encoded by the encoder whose qualified name is given by the first block.
52
+ # This method must simply return the encoded text.
53
+ #
54
+ # Arguments:
55
+ # - src: source text to encode.
56
+ # - options: encoding options through a Hash. Available options are documented
57
+ # by encoders themselve.
58
+ #
59
+ def encode(src, options)
60
+ raise(NotImplementedError) unless @block
61
+ @block.call(src, options)
62
+ end
63
+
64
+ end # class Encoder
65
+
66
+ end
@@ -0,0 +1,117 @@
1
+ require 'wlang/encoder'
2
+ module WLang
3
+
4
+ #
5
+ # This class allows grouping encoders together to build a given dialect.
6
+ # Encoders are always added with add_encoder, which also allows creating simple
7
+ # encoders on the fly (that is, without subclassing Encoder).
8
+ #
9
+ # Examples:
10
+ # # we will create a simple encoder set with two encoders, one for upcasing
11
+ # # the other for downcasing.
12
+ # encoders = EncoderSet.new
13
+ # encoder.add_encoder 'upcaser' do |src,options|
14
+ # src.upcase
15
+ # end
16
+ # encoder.add_encoder 'downcaser' do |src,options|
17
+ # src.downcase
18
+ # end
19
+ #
20
+ # == Detailed API
21
+ class EncoderSet
22
+
23
+ # Creates an empty encoder set.
24
+ def initialize
25
+ @encoders = {}
26
+ end
27
+
28
+ #
29
+ # Adds an encoder under a specific name. Supported signatures are as follows:
30
+ # - _name_ is a symbol: _encoder_ and _block_ are ignored and encoder is
31
+ # interpreted as being a method of the String class whose name is the symbol.
32
+ # - _name_ is a String and a block is provided: encoder is expected to be
33
+ # implemented by the block which takes |src,options| arguments.
34
+ # - _name_ is a String and _encoder_ is a Proc: same as if _encoder_ was a
35
+ # given block.
36
+ #
37
+ # Examples:
38
+ # encoders = EncoderSet.new
39
+ # # add an encoder by reusing String method
40
+ # encoders.add_encoder(:upcase)
41
+ #
42
+ # # add an encoder by providing a block
43
+ # encoders.add_encoder("ucase") do |src,options|
44
+ # src.upcase
45
+ # end
46
+ #
47
+ # # add an encoder by providing a Proc
48
+ # upcaser = Proc.new {|src,options| src.upcase}
49
+ # encoders.add_encoder("upcase", upcaser)
50
+ #
51
+ def add_encoder(name, encoder=nil, &block)
52
+ # handle String method through symbol
53
+ if Symbol===name
54
+ encoder, block = nil, Proc.new {|src,options| src.send(name)}
55
+ name = name.to_s
56
+ elsif Proc===encoder
57
+ encoder, block = nil, encoder
58
+ end
59
+
60
+ # check arguments
61
+ if encoder.nil?
62
+ raise(ArgumentError,"Block required") if block.nil?
63
+ encoder = Encoder.new(&block)
64
+ end
65
+ raise(ArgumentError, "Encoder expected") unless Encoder===encoder
66
+
67
+ # save encoder
68
+ @encoders[name] = encoder
69
+ end
70
+
71
+ #
72
+ # Adds reusable encoders defined in a Ruby module. _mod_ must be a Module instance.
73
+ # If _pairs_ is ommitted (nil), all encoders of the module are added, under their
74
+ # standard names (see DEFAULT_ENCODERS under WLang::EncoderSet::XHtml for example).
75
+ # Otherwise, _pairs_ is expected to be a Hash providing a mapping between encoder
76
+ # names and _mod_ methods (whose names are given by ruby symbols).
77
+ #
78
+ def add_encoders(mod, pairs=nil)
79
+ raise(ArgumentError,"Module expected",caller) unless Module===mod
80
+ pairs = mod::DEFAULT_ENCODERS if pairs.nil?
81
+ pairs.each_pair do |name,method|
82
+ meth = mod.method(method)
83
+ raise(ArgumentError,"No such method: #{method} in #{mod}") if meth.nil?
84
+ add_encoder(name, &meth.to_proc)
85
+ end
86
+ end
87
+
88
+ # Checks if an encoder is installed under _name.
89
+ def has_encoder?(name)
90
+ @encoders.has_key?(name)
91
+ end
92
+
93
+ # Returns an encoder by its name, nil if no such encoder.
94
+ def get_encoder(name)
95
+ @encoders[name]
96
+ end
97
+
98
+ #
99
+ # Shortcut for <tt>get_encoder(encoder).encode(source,options)</tt>
100
+ #
101
+ def encode(encoder, src, options=nil)
102
+ if String===encoder then
103
+ ename, encoder = encoder, get_encoder(encoder)
104
+ raise(ArgumentError,"No such encoder: #{ename}") if encoder.nil?
105
+ end
106
+ raise(ArgumentError,"Invalid encoder: #{encoder}") unless Encoder===encoder
107
+ encoder.encode(src, options)
108
+ end
109
+
110
+ # Returns a string with comma separated encoder names.
111
+ def to_s
112
+ @encoders.keys.join(", ")
113
+ end
114
+
115
+ end # class EncoderSet
116
+
117
+ end
@@ -0,0 +1,37 @@
1
+ module WLang
2
+
3
+ # Main error of all WLang errors.
4
+ class Error < StandardError; end
5
+
6
+ # Error raised by a WLang parser instanciation when an error occurs.
7
+ class ParseError < StandardError;
8
+
9
+ attr_reader :line, :column
10
+
11
+ # Creates an error with offset information
12
+ def initialize(message, offset=nil, template=nil)
13
+ @offset = offset
14
+ @line, @column = parse(template) if template
15
+ unless template and offset
16
+ super(message)
17
+ else
18
+ super("ParseError at #{@line}:#{@column} : #{message}")
19
+ end
20
+
21
+ end
22
+
23
+ # Reparses the template and finds line:column locations
24
+ def parse(template)
25
+ template = template[0,@offset]
26
+ if template =~ /\n/ then
27
+ lines = template[0,@offset].split(/\n/)
28
+ else
29
+ lines = [template]
30
+ end
31
+ line, column = lines.length, lines.last.length
32
+ return [line, column]
33
+ end
34
+
35
+ end # class ParseError
36
+
37
+ end # module WLang
@@ -0,0 +1,94 @@
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
@@ -0,0 +1,251 @@
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
+ #
9
+ # Parser for wlang templates.
10
+ #
11
+ # This class implements the parsing algorithm of wlang, recognizing special tags
12
+ # and replacing them using installed rules. Instanciating a template is done
13
+ # using instantiate. All other methods (parse, parse_block, has_block?) and the
14
+ # like are callbacks for rules and should not be used by users themselve.
15
+ #
16
+ # Obtaining a parser MUST be made through Parser.instantiator (new is private).
17
+ #
18
+ # == Detailed API
19
+ class Parser
20
+
21
+ # Factors a parser instance for a given template and an output buffer.
22
+ def self.instantiator(template, buffer=nil)
23
+ Parser.send(:new, nil, template, nil, 0, buffer)
24
+ end
25
+
26
+ # Current parsed template
27
+ attr_reader :template
28
+
29
+ # Current execution context
30
+ attr_reader :context
31
+
32
+ # Current buffer
33
+ attr_reader :buffer
34
+
35
+ #
36
+ # Initializes a parser instance. _parent_ is the Parser instance of the higher
37
+ # parsing stage. _template_ is the current instantiated template, _offset_ is
38
+ # where the parsing must start in the template and _buffer_ is the output buffer
39
+ # where the instantiation result must be pushed.
40
+ #
41
+ def initialize(parent, template, dialect, offset, buffer)
42
+ raise(ArgumentError, "Template is mandatory") unless WLang::Template===template
43
+ raise(ArgumentError, "Offset is mandatory") unless Integer===offset
44
+ dialect = template.dialect if dialect.nil?
45
+ buffer = dialect.factor_buffer if buffer.nil?
46
+ raise(ArgumentError, "Buffer is mandatory") unless buffer.respond_to?(:<<)
47
+ @parent = parent
48
+ @template = template
49
+ @context = template.context
50
+ @offset = offset
51
+ @dialect = dialect
52
+ @buffer = buffer
53
+ end
54
+
55
+ # Factors a specific buffer on the current dialect
56
+ def factor_buffer
57
+ @dialect.factor_buffer
58
+ end
59
+
60
+ # Appends on a given buffer
61
+ def append_buffer(buffer, str, block)
62
+ if buffer.respond_to?(:wlang_append)
63
+ buffer.wlang_append(str, block)
64
+ else
65
+ buffer << str
66
+ end
67
+ end
68
+
69
+ # Pushes a given string on the output buffer
70
+ def <<(str, block)
71
+ append_buffer(@buffer, str, block)
72
+ end
73
+
74
+ # Parses the text
75
+ def instantiate
76
+ # Main variables:
77
+ # - offset: matching current position
78
+ # - rules: handlers of '{' currently opened
79
+ offset, pattern, rules = @offset, @dialect.pattern(@template.block_symbols), []
80
+ @source_text = template.source_text
81
+
82
+ # we start matching everything in the ruleset
83
+ while match_at=@source_text.index(pattern,offset)
84
+ match, match_length = $~[0], $~[0].length
85
+
86
+ # puts pre_match (we can't use $~.pre_match !)
87
+ self.<<(@source_text[offset, match_at-offset], false) if match_at>0
88
+
89
+ if @source_text[match_at,1]=='\\' # escaping sequence
90
+ self.<<(match[1..-1], false)
91
+ offset = match_at + match_length
92
+
93
+ elsif match.length==1 # simple '{' or '}' here
94
+ offset = match_at + match_length
95
+ if match==Template::BLOCK_SYMBOLS[template.block_symbols][0]
96
+ self.<<(match, false) # simple '{' are always pushed
97
+ # we push '{' in rules to recognize it's associated '}'
98
+ # that must be pushed on buffer also
99
+ rules << match
100
+ else
101
+ # end of my job if I can't pop a previous rule
102
+ break if rules.empty?
103
+ # otherwise, push '}' only if associated to a simple '{'
104
+ self.<<(match, false) unless Rule===rules.pop
105
+ end
106
+
107
+ elsif match[-1,1]==Template::BLOCK_SYMBOLS[template.block_symbols][0] # opening special tag
108
+ # following line should never return nil as the matching pattern comes
109
+ # from the ruleset itself!
110
+ rule = @dialect.ruleset[match[0..-2]]
111
+ rules << rule
112
+
113
+ # lauch that rule, get it's replacement and my new offset
114
+ replacement, offset = rule.start_tag(self, match_at + match_length)
115
+ replacement = "" if replacement.nil?
116
+ raise "Bad implementation of rule #{match[0..-2]}" if offset.nil?
117
+
118
+ # push replacement
119
+ self.<<(replacement, true) unless replacement.empty?
120
+ end
121
+
122
+ end # while match_at=...
123
+
124
+ # trailing data (end of @template reached only if no match_at)
125
+ unless match_at
126
+ unexpected_eof(@source_text.length, '}') unless rules.empty?
127
+ self.<<(@source_text[offset, 1+@source_text.length-offset], false)
128
+ offset = @source_text.length
129
+ end
130
+ [@buffer, offset-1]
131
+ end
132
+
133
+ #
134
+ # Evaluates a ruby expression on the current context.
135
+ # See WLang::Parser::Context#evaluate.
136
+ #
137
+ def evaluate(expression)
138
+ @context.evaluate(expression)
139
+ end
140
+
141
+ #
142
+ # Launches a child parser for instantiation at a given _offset_ in given
143
+ # _dialect_ (same dialect than self if dialect is nil) and with an output
144
+ # _buffer_.
145
+ #
146
+ def parse(offset, dialect=nil, buffer=nil)
147
+ if dialect.nil?
148
+ dialect = @dialect
149
+ elsif String===dialect
150
+ dname, dialect = dialect, WLang::dialect(dialect)
151
+ raise(ParseError,"Unknown modulation dialect: #{dname}") if dialect.nil?
152
+ elsif not(Dialect===dialect)
153
+ raise(ParseError,"Unknown modulation dialect: #{dialect}")
154
+ end
155
+ Parser.send(:new, self, @template, dialect, offset, buffer).instantiate
156
+ end
157
+
158
+ #
159
+ # Checks if a given offset is a starting block. For easy implementation of rules
160
+ # the check applied here is that text starting at _offset_ in the template is precisely
161
+ # '}{' (the reason for that is that instantiate, parse, parse_block always stop
162
+ # parsing on a '}')
163
+ #
164
+ def has_block?(offset)
165
+ @source_text[offset,2]=='}{'
166
+ end
167
+
168
+ #
169
+ # Parses a given block starting at a given _offset_, expressed in a given
170
+ # _dialect_ and using an output _buffer_. This method raises a ParseError if
171
+ # there is no block at the offset. It implies that we are on a '}{', see
172
+ # has_block? for details. Rules may thus force mandatory block parsing without
173
+ # having to check anything. Optional blocks must be handled by rules themselve.
174
+ #
175
+ def parse_block(offset, dialect=nil, buffer=nil)
176
+ block_missing_error(offset+2) unless has_block?(offset)
177
+ parse(offset+2, dialect, buffer)
178
+ end
179
+
180
+ #
181
+ # Encodes a given text using an encoder, that may be a qualified name or an
182
+ # Encoder instance.
183
+ #
184
+ def encode(src, encoder, options=nil)
185
+ options = {} unless options
186
+ options['_encoder_'] = encoder
187
+ options['_template_'] = template
188
+ if String===encoder
189
+ if encoder.include?("/")
190
+ ename, encoder = encoder, WLang::encoder(encoder)
191
+ raise(ParseError,"Unknown encoder: #{ename}") if encoder.nil?
192
+ else
193
+ ename, encoder = encoder, @dialect.find_encoder(encoder)
194
+ raise(ParseError,"Unknown encoder: #{ename}") if encoder.nil?
195
+ end
196
+ elsif not(Encoder===encoder)
197
+ raise(ParseError,"Unknown encoder: #{encoder}")
198
+ end
199
+ encoder.encode(src, options)
200
+ end
201
+
202
+ #
203
+ # Raises a ParseError at a given offset.
204
+ #
205
+ def syntax_error(offset, msg=nil)
206
+ text = self.parse(offset, "wlang/dummy", "")
207
+ raise ParseError, "Parse error at #{offset} on '#{text}': #{msg}"
208
+ end
209
+
210
+ #
211
+ # Raises a ParseError at a given offset for a missing block
212
+ #
213
+ def block_missing_error(offset)
214
+ raise ParseError.new("Block expected", offset, @source_text)
215
+ end
216
+
217
+ #
218
+ # Raises a ParseError at a given offset for a unexpected EOF
219
+ # specif. the expected character when EOF found
220
+ #
221
+ def unexpected_eof(offset, expected)
222
+ raise ParseError.new("'#{expected}' expected, EOF found.", offset, @source_text)
223
+ end
224
+
225
+ #
226
+ # Puts a key/value pair in the current context. See Parser::Context::define
227
+ # for details.
228
+ #
229
+ def context_define(key, value)
230
+ @context.define(key,value)
231
+ end
232
+
233
+ #
234
+ # Pushes a new scope on the current context stack. See Parser::Context::push
235
+ # for details.
236
+ #
237
+ def context_push(context)
238
+ @context.push(context)
239
+ end
240
+
241
+ #
242
+ # Pops the top scope of the context stack. See Parser::Context::pop for details.
243
+ #
244
+ def context_pop
245
+ @context.pop
246
+ end
247
+
248
+ private_class_method :new
249
+ end # class Parser
250
+
251
+ end # module WLang