Class: WLang::Parser
- Inherits:
-
Object
- Object
- WLang::Parser
- Defined in:
- lib/wlang/parser.rb,
lib/wlang/parser_state.rb
Overview
Parser for wlang templates.
This class implements the parsing algorithm of wlang, recognizing special tags and replacing them using installed rules. Instanciating a template is done using instantiate. All other methods (parse, parse_block, has_block?) and the like are callbacks for rules and should not be used by users themselve.
Detailed API
Defined Under Namespace
Classes: State
Instance Method Summary (collapse)
-
- (Object) <<(str, block)
Pushes a given string on the output buffer.
-
- (Object) append_buffer(buffer, str, block)
Appends on a given buffer.
-
- (Object) block_missing_error(offset)
Raises a ParseError at a given offset for a missing block.
-
- (Object) branch(opts = {})
Branches the current parser.
-
- (Object) branch_scope(pairing = {}, which = :all)
Yields the block in a new scope branch, pushing pairing values on it.
-
- (Object) buffer
Returns the current buffer.
-
- (Object) encode(src, encoder, options = nil)
Encodes a given text using an encoder, that may be a qualified name or an Encoder instance.
-
- (Object) ensure_dialect(dialect)
Finds a real dialect instance from an argument (Dialect instance or qualified name).
-
- (Object) ensure_encoder(encoder)
Finds a real ecoder instance from an argument (Encoder instance or qualified or unqualified name).
-
- (Object) error(offset, message)
Raises an exception with a friendly message.
-
- (Object) evaluate(expression)
Evaluates a ruby expression on the current context.
-
- (Object) factor_buffer
Factors a specific buffer on the current dialect.
-
- (Object) file_resolve(uri)
Resolves an URI throught the current template.
-
- (Object) file_template(file, dialect = nil, block_symbols = :braces)
Factors a template instance for a given file.
-
- (Boolean) has_block?(offset)
Checks if a given offset is a starting block.
-
- (Parser) initialize(hosted, template, scope)
constructor
Initializes a parser instance.
-
- (Object) instantiate(apply_posttransform = true)
Parses the template's text and instantiate it.
-
- (Object) launch_rule(dialect, rule_symbol, rule, offset)
Checks the result of a given rule.
-
- (Object) offset=(offset)
Sets the current offset of the parser.
-
- (Object) parse(offset, req_dialect = nil, req_buffer = nil)
Launches a child parser for instantiation at a given offset in given dialect (same dialect than self if dialect is nil) and with an output buffer.
-
- (Object) parse_block(offset, dialect = nil, buffer = nil)
Parses a given block starting at a given offset, expressed in a given dialect and using an output buffer.
-
- (Object) scope_define(key, value)
Adds a key/value pair on the current scope.
-
- (Object) state
Returns the current parser state.
-
- (Object) syntax_error(offset, msg = nil)
Raises a ParseError at a given offset.
-
- (Object) template
Returns the current template.
-
- (Object) unexpected_eof(offset, expected)
Raises a ParseError at a given offset for a unexpected EOF specif.
Constructor Details
- (Parser) initialize(hosted, template, scope)
Initializes a parser instance.
19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/wlang/parser.rb', line 19 def initialize(hosted, template, scope) raise(ArgumentError, "Hosted language is mandatory (a ::WLang::HostedLanguage)") unless ::WLang::HostedLanguage===hosted raise(ArgumentError, "Template is mandatory (a ::WLang::Template)") unless ::WLang::Template===template raise(ArgumentError, "Scope is mandatory (a Hash)") unless ::Hash===scope @state = ::WLang::Parser::State.new(self).branch( :hosted => hosted, :template => template, :dialect => template.dialect, :offset => 0, :shared => :none, :scope => scope, :buffer => template.dialect.factor_buffer) end |
Instance Method Details
- (Object) <<(str, block)
Pushes a given string on the output buffer
250 251 252 |
# File 'lib/wlang/parser.rb', line 250 def <<(str, block) append_buffer(buffer, str, block) end |
- (Object) append_buffer(buffer, str, block)
Appends on a given buffer
241 242 243 244 245 246 247 |
# File 'lib/wlang/parser.rb', line 241 def append_buffer(buffer, str, block) if buffer.respond_to?(:wlang_append) buffer.wlang_append(str, block) else buffer << str end end |
- (Object) block_missing_error(offset)
Raises a ParseError at a given offset for a missing block
316 317 318 |
# File 'lib/wlang/parser.rb', line 316 def block_missing_error(offset) template.parse_error(offset, "parse error, block was expected") end |
- (Object) branch(opts = {})
Branches the current parser
60 61 62 63 64 65 66 |
# File 'lib/wlang/parser.rb', line 60 def branch(opts = {}) raise ArgumentError, "Parser branching requires a block" unless block_given? @state = @state.branch(opts) result = yield(@state) @state = @state.parent result end |
- (Object) branch_scope(pairing = {}, which = :all)
Yields the block in a new scope branch, pushing pairing values on it. Original scope is restored after that. Returns what the yielded block returned.
259 260 261 262 |
# File 'lib/wlang/parser.rb', line 259 def branch_scope(pairing = {}, which = :all) raise ArgumentError, "Parser.branch_scope expects a block" unless block_given? branch(:scope => pairing, :shared => which) { yield } end |
- (Object) buffer
Returns the current buffer
54 |
# File 'lib/wlang/parser.rb', line 54 def buffer() state.buffer; end |
- (Object) encode(src, encoder, options = nil)
Encodes a given text using an encoder, that may be a qualified name or an Encoder instance.
290 291 292 293 294 295 |
# File 'lib/wlang/parser.rb', line 290 def encode(src, encoder, =nil) = {} unless ['_encoder_'] = encoder ['_template_'] = template ensure_encoder(encoder).encode(src, ) end |
- (Object) ensure_dialect(dialect)
Finds a real dialect instance from an argument (Dialect instance or qualified name)
84 85 86 87 88 89 90 91 92 |
# File 'lib/wlang/parser.rb', line 84 def ensure_dialect(dialect) if String===dialect dname, dialect = dialect, WLang::dialect(dialect) raise(ParseError,"Unknown modulation dialect: #{dname}") if dialect.nil? elsif not(Dialect===dialect) raise(ParseError,"Unknown modulation dialect: #{dialect}") end dialect end |
- (Object) ensure_encoder(encoder)
Finds a real ecoder instance from an argument (Encoder instance or qualified or unqualified name)
96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/wlang/parser.rb', line 96 def ensure_encoder(encoder) if String===encoder if encoder.include?("/") ename, encoder = encoder, WLang::encoder(encoder) raise(ParseError,"Unknown encoder: #{ename}") if encoder.nil? else ename, encoder = encoder, self.dialect.find_encoder(encoder) raise(ParseError,"Unknown encoder: #{ename}") if encoder.nil? end elsif not(Encoder===encoder) raise(ParseError,"Unknown encoder: #{encoder}") end encoder end |
- (Object) error(offset, message)
Raises an exception with a friendly message
300 301 302 |
# File 'lib/wlang/parser.rb', line 300 def error(offset, ) template.error(offset, ) end |
- (Object) evaluate(expression)
Evaluates a ruby expression on the current context. See WLang::Parser::Context#evaluate.
275 276 277 |
# File 'lib/wlang/parser.rb', line 275 def evaluate(expression) hosted.evaluate(expression, state) end |
- (Object) factor_buffer
Factors a specific buffer on the current dialect
282 283 284 |
# File 'lib/wlang/parser.rb', line 282 def factor_buffer self.dialect.factor_buffer end |
- (Object) file_resolve(uri)
Resolves an URI throught the current template
71 72 73 |
# File 'lib/wlang/parser.rb', line 71 def file_resolve(uri) template.file_resolve(uri) end |
- (Object) file_template(file, dialect = nil, block_symbols = :braces)
Factors a template instance for a given file
78 79 80 |
# File 'lib/wlang/parser.rb', line 78 def file_template(file, dialect = nil, block_symbols = :braces) WLang::file_template(file, dialect, block_symbols) end |
- (Boolean) has_block?(offset)
Checks if a given offset is a starting block. For easy implementation of rules the check applied here is that text starting at offset in the template is precisely '}(the reason for that is that instantiate, parse, parse_block always stop parsing on a '')
222 223 224 |
# File 'lib/wlang/parser.rb', line 222 def has_block?(offset) self.source_text[offset,2] == template.block_endstart end |
- (Object) instantiate(apply_posttransform = true)
Parses the template's text and instantiate it. Dialect post_transformer is only applied of apply_posttransform is set to true.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/wlang/parser.rb', line 125 def instantiate(apply_posttransform = true) # Main variables put in local scope for efficiency: # - template: current parsed template # - source_text: current template's source text # - offset: matching current position # - pattern: current dialect's regexp pattern # - rules: handlers of '{' currently opened template = self.template symbols = self.template.block_symbols source_text = self.source_text dialect = self.dialect buffer = self.buffer pattern = dialect.pattern(template.block_symbols) rules = [] # we start matching everything in the ruleset while match_at=source_text.index(pattern, self.offset) match, match_length = $~[0], $~[0].length # puts pre_match (we can't use $~.pre_match !) self.<<(source_text[self.offset, match_at-self.offset], false) if match_at>0 if source_text[match_at,1]=='\\' # escaping sequence self.<<(match[1..-1], false) self.offset = match_at + match_length elsif match.length==1 # simple '{' or '}' here self.offset = match_at + match_length if match==Template::BLOCK_SYMBOLS[symbols][0] self.<<(match, false) # simple '{' are always pushed # we push '{' in rules to recognize it's associated '}' # that must be pushed on buffer also rules << match else # end of my job if I can't pop a previous rule break if rules.empty? # otherwise, push '}' only if associated to a simple '{' self.<<(match, false) unless Rule===rules.pop end elsif match[-1,1]==Template::BLOCK_SYMBOLS[symbols][0] # opening special tag # following line should never return nil as the matching pattern comes # from the ruleset itself! rule_symbol = match[0..-2] rule = dialect.ruleset[rule_symbol] rules << rule # Just added to get the last position in case of an error self.offset = match_at + match_length # lauch that rule, get it's replacement and my new offset replacement, self.offset = launch_rule(dialect, rule_symbol, rule, self.offset) # push replacement self.<<(replacement, true) unless replacement.empty? end end # while match_at=... # trailing data (end of template reached only if no match_at) unless match_at unexpected_eof(source_text.length, '}') unless rules.empty? self.<<(source_text[self.offset, 1+source_text.length-self.offset], false) self.offset = source_text.length end # Apply post-transformation only if required if apply_posttransform [dialect.apply_post_transform(buffer), self.offset-1] else [buffer, self.offset-1] end end |
- (Object) launch_rule(dialect, rule_symbol, rule, offset)
Checks the result of a given rule
114 115 116 117 118 119 |
# File 'lib/wlang/parser.rb', line 114 def launch_rule(dialect, rule_symbol, rule, offset) result = rule.start_tag(self, offset) raise WLang::Error, "Bad rule implementation #{dialect.qualified_name} #{rule_symbol}{}\n#{result.inspect}"\ unless result.size == 2 and String===result[0] and Integer===result[1] result end |
- (Object) offset=(offset)
Sets the current offset of the parser
51 |
# File 'lib/wlang/parser.rb', line 51 def offset=(offset) state.offset = offset; end |
- (Object) parse(offset, req_dialect = nil, req_buffer = nil)
Launches a child parser for instantiation at a given offset in given dialect (same dialect than self if dialect is nil) and with an output buffer.
206 207 208 209 210 211 212 213 214 |
# File 'lib/wlang/parser.rb', line 206 def parse(offset, req_dialect = nil, req_buffer = nil) dialect = ensure_dialect(req_dialect.nil? ? self.dialect : req_dialect) buffer = (req_buffer.nil? ? dialect.factor_buffer : req_buffer) branch(:offset => offset, :dialect => dialect, :buffer => buffer) do instantiate(!req_dialect.nil?) end end |
- (Object) parse_block(offset, dialect = nil, buffer = nil)
Parses a given block starting at a given offset, expressed in a given dialect and using an output buffer. This method raises a ParseError if there is no block at the offset. It implies that we are on a '}{', see has_block? for details. Rules may thus force mandatory block parsing without having to check anything. Optional blocks must be handled by rules themselve.
233 234 235 236 |
# File 'lib/wlang/parser.rb', line 233 def parse_block(offset, dialect=nil, buffer=nil) block_missing_error(offset+2) unless has_block?(offset) parse(offset+2, dialect, buffer) end |
- (Object) scope_define(key, value)
Adds a key/value pair on the current scope.
265 266 267 |
# File 'lib/wlang/parser.rb', line 265 def scope_define(key, value) state.scope[key] = value end |
- (Object) state
Returns the current parser state
36 |
# File 'lib/wlang/parser.rb', line 36 def state(); @state; end |
- (Object) syntax_error(offset, msg = nil)
Raises a ParseError at a given offset.
307 308 309 310 311 |
# File 'lib/wlang/parser.rb', line 307 def syntax_error(offset, msg=nil) text = self.parse(offset, "wlang/dummy", "") msg = msg.nil? ? '' : ": #{msg}" template.parse_error(offset, "parse error on '#{text}'#{msg}") end |
- (Object) template
Returns the current template
39 |
# File 'lib/wlang/parser.rb', line 39 def template() state.template; end |
- (Object) unexpected_eof(offset, expected)
Raises a ParseError at a given offset for a unexpected EOF specif. the expected character when EOF found
324 325 326 |
# File 'lib/wlang/parser.rb', line 324 def unexpected_eof(offset, expected) template.parse_error(offset, "#{expected} expected, EOF found") end |