class Jekyll::Premonition::Attributes::Parser
Public: Premonition
block attributes parser.
Parses attributes found in block headers like this: > “info” [ foo = abcd, bar = “zot”, size = 3 ]
The parser itself utilizes the StringScanner in ruby. Each character will be parsed, validated and pushed to a stack (array) according to the rules inside the parser itself.
A stack object will be of a certain type:
0 : Outside an attributes block 1 : Inside an attributes block, but between keys or values 2 : Parsing an attribute key 3: Parsing an attribute value
Upon parser errors a pretty syntax error will be printed to stdout, showing where the error is.
Attributes
attributes[R]
Get parsed attributes as a Hash
Public Class Methods
new(str)
click to toggle source
Initialize a new Parser
AND start parsing
str - A string containing the attributes block to be parser.
# File lib/premonition/attributes/parser.rb, line 34 def initialize(str) @raw = str # Keeps th original string for later use @buffer = StringScanner.new(str) # Create StringScanner buffer @attributes = {} # Initialize the attributes hash @stack = [Stacker.new(0)] # Initialize the parser stack with initial "state" parse # Start parsing end
Private Instance Methods
append_char(char)
click to toggle source
# File lib/premonition/attributes/parser.rb, line 142 def append_char(char) @stack.last.append(char) end
block_mode?()
click to toggle source
# File lib/premonition/attributes/parser.rb, line 158 def block_mode? @stack.last.meta['mode'] == 'block' end
error(msg)
click to toggle source
# File lib/premonition/attributes/parser.rb, line 134 def error(msg) ParserError.new(msg, @raw, @buffer.pos) end
parse()
click to toggle source
# File lib/premonition/attributes/parser.rb, line 44 def parse raise error('No attributes block found in given string') unless @raw.match(/^.*\[.*\].*/) until @buffer.eos? char = @buffer.getch case @stack.last.type when 0 # Outside block mode push_stacker(1) if char == '[' when 1 # In between parse_in_between(char) when 2 # Key parse_key(char) when 3 # Value parse_value(char) end end end
parse_in_between(char)
click to toggle source
# File lib/premonition/attributes/parser.rb, line 62 def parse_in_between(char) return if char == ' ' # Ignoring all spaces before, between and after keys and values. if char == ']' pop_attribute_from_stack push_stacker(4) elsif char == ',' raise error("Attribute separator ',' not allowed here.") if @attributes.length.zero? push_stacker(2) elsif char.match(/^[a-zA-z0-9\-_]$/) push_stacker(2) append_char(char) else raise error("Illegal character '#{char}' outside key and value") end end
parse_key(char)
click to toggle source
# File lib/premonition/attributes/parser.rb, line 79 def parse_key(char) if char == '=' push_stacker(3) value_mode('plain') elsif char.match(/^[a-zA-z0-9\-_]$/) append_char(char) elsif char == ' ' m = @buffer.scan(/\s*\=\s*/) raise error('Space not allowed inside attribute key') if m.nil? push_stacker(3) value_mode('plain') else raise error("Illegal character '#{char}' for attribute key") end end
parse_value(char)
click to toggle source
# File lib/premonition/attributes/parser.rb, line 95 def parse_value(char) case char when ']' if plain_mode? pop_attribute_from_stack push_stacker(4) end when '"' pop_attribute_from_stack unless plain_mode? raise error('Illegal " found') unless @stack.last.value.nil? || @stack.last.value.empty? value_mode('block') when '\\' raise error('Backslash is not allowed in unquoted values') if plain_mode? raise error('Unsupported escaping of character inside string block') unless ['\\', '"'].include?(@buffer.peek(1)) append_char(@buffer.peek(1)) @buffer.pos += 1 when ',' if plain_mode? pop_attribute_from_stack push_stacker(1) else append_char(char) end when '=' raise error('Illegal use of equals character in unquoted value') if plain_mode? append_char(char) when ' ' if plain_mode? raise error('Illegal spacing in unquoted value') if @buffer.check(/\s*[,\]]/).nil? pop_attribute_from_stack push_stacker(1) else append_char(char) end else append_char(char) end end
plain_mode?()
click to toggle source
# File lib/premonition/attributes/parser.rb, line 154 def plain_mode? @stack.last.meta['mode'] == 'plain' end
pop_attribute_from_stack()
click to toggle source
# File lib/premonition/attributes/parser.rb, line 146 def pop_attribute_from_stack v = @stack.pop return if [0, 1, 2].include?(v.type) # Ignore these types from stack k = @stack.pop raise error("Expected key but got: #{k.value}, #{k.type}") unless k.type == 2 @attributes[k.value] = v.value end
push_stacker(type)
click to toggle source
# File lib/premonition/attributes/parser.rb, line 138 def push_stacker(type) @stack << Stacker.new(type) end
value_mode(str)
click to toggle source
# File lib/premonition/attributes/parser.rb, line 162 def value_mode(str) @stack.last.meta['mode'] = str end