class Mustermann::AST::Parser
Simple, StringScanner based parser. @!visibility private
Attributes
@!visibility private
@!visibility private
@!visibility private
Public Class Methods
Source
# File lib/mustermann/ast/parser.rb, line 49 def initialize(pattern: nil, **options) @pattern = pattern end
@!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 26 def self.on(*chars, &block) chars.each do |char| define_method("read %p" % char, &block) end end
Defines another grammar rule for first character.
@see Mustermann::Rails @see Mustermann::Sinatra
@see Mustermann::Template @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 16 def self.parse(string, **options) new(**options).parse(string) end
@param [String] string to be parsed @return [Mustermann::AST::Node] parse tree for string @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 36 def self.suffix(pattern = /./, after: :node, &block) @suffix ||= [] @suffix << [pattern, after, block] if block @suffix end
Defines another grammar rule for a suffix.
@see Mustermann::Sinatra
@!visibility private
Public Instance Methods
Source
# File lib/mustermann/ast/parser.rb, line 80 def default_node(char) char == ?/ ? node(:separator, char) : node(:char, char) end
Create a node for a character we don’t have an explicit rule for.
@param [String] char the character @return [Mustermann::AST::Node] the node @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 141 def expect(regexp, char: nil, **options) scan(regexp) || unexpected(char, **options) end
Asserts a regular expression matches what’s next on the buffer. Will return corresponding MatchData if regexp includes named captures.
@param [Regexp] regexp expected to match @return [String, MatchData] the match @raise [Mustermann::ParseError] if expectation wasn’t met @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 100 def min_size(start, stop, node) stop ||= start start ||= stop node.start = start unless node.start and node.start < start node.stop = stop unless node.stop and node.stop > stop node end
sets start on node to start if it’s not set to a lower value. sets stop on node to stop if it’s not set to a higher value. @return [Mustermann::AST::Node] the node passed as third argument @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 68 def node(type, *args, &block) type = Node[type] unless type.respond_to? :new start = pos node = block ? type.parse(*args, &block) : type.new(*args) min_size(start, pos, node) end
Source
# File lib/mustermann/ast/parser.rb, line 56 def parse(string) @string = string @buffer = ::StringScanner.new(string) node(:root, string) { read unless eos? } end
@param [String] string to be parsed @return [Mustermann::AST::Node] parse tree for string @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 87 def read start = pos char = getch method = "read %p" % char element= respond_to?(method) ? send(method, char) : default_node(char) min_size(start, pos, element) read_suffix(element) end
Reads the next element from the buffer. @return [Mustermann::AST::Node] next element @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 172 def read_args(key_separator, close, separator: ?,, symbol_keys: true, **options) list, map = [], {} while buffer.peek(1) != close scan(separator) entries = read_list(close, separator, separator: key_separator, **options) case entries.size when 1 then list += entries when 2 then map[symbol_keys ? entries.first.to_sym : entries.first] = entries.last else unexpected(key_separator) end buffer.pos -= 1 end expect(close) [list, map] end
Reads an argument string of the format arg1,args2,key:value
@!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 154 def read_brackets(open, close, char: nil, escape: ?\\, quote: false, **options) result = String.new escape = false if escape.nil? while (current = getch) case current when close then return result when open then result << open << read_brackets(open, close) << close when escape then result << escape << getch else result << current end end unexpected(char, **options) end
Allows to read a string inside brackets. It does not expect the string to start with an opening bracket.
@example
buffer.string = "fo<o>>ba<r>" read_brackets(?<, ?>) # => "fo<o>" buffer.rest # => "ba<r>"
@!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 210 def read_escaped(close, escape: ?\\, **options) result = String.new while current = getch case current when close then return result when escape then result << getch else result << current end end unexpected(current, **options) end
Read a string until a terminating character, ignoring escaped versions of said character.
@!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 191 def read_list(*close, separator: ?,, escape: ?\\, quotes: [?", ?'], ignore: " ", **options) result = [] while current = getch element = result.empty? ? result : result.last case current when *close then return result when ignore then nil # do nothing when separator then result << String.new when escape then element << getch when *quotes then element << read_escaped(current, escape: escape) else element << current end end unexpected(current, **options) end
Reads a separated list with the ability to quote, escape and add spaces.
@!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 112 def read_suffix(element) self.class.suffix.inject(element) do |ele, (regexp, after, callback)| next ele unless ele.is_a?(after) and payload = scan(regexp) content = instance_exec(payload, ele, &callback) min_size(element.start, pos, content) end end
Checks for a potential suffix on the buffer. @param [Mustermann::AST::Node] element node without suffix @return [Mustermann::AST::Node] node with suffix @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 128 def scan(regexp) regexp = Regexp.new(Regexp.escape(regexp)) unless regexp.is_a? Regexp string = buffer.scan(regexp) regexp.names.any? ? regexp.match(string) : string end
Wrapper around {StringScanner#scan} that turns strings into escaped regular expressions and returns a MatchData if the regexp has any named captures.
@param [Regexp, String] regexp @see StringScanner#scan @return [String, MatchData, nil] @!visibility private
Source
# File lib/mustermann/ast/parser.rb, line 228 def unexpected(char = nil, exception: ParseError) char ||= getch char = "space" if char == " " raise exception, "unexpected #{char || "end of string"} while parsing #{string.inspect}" end
Helper for raising an exception for an unexpected character. Will read character from buffer if buffer is passed in.
@param [String, nil] char the unexpected character @raise [Mustermann::ParseError, Exception] @!visibility private