class Ovec::Parser
TODO: verbatimy; TeX prikazy obsahujici kusy k ovlnkovani
Constants
- COMMAND_REGEX
A command: (letters) or (a single nonletter)
- COMMENT_REGEX
- NONTEXT_CONTENT_COMMANDS
TODO: seznam prikazu, ve kterych se nevlnkuje
- NORMAL_REGEX
- TEXT_COMMANDS
- VERBATIM_COMMANDS
Public Class Methods
delimiter_right_side(left)
click to toggle source
# File lib/ovec/parser.rb, line 47 def self.delimiter_right_side(left) { '{' => '}', '[' => ']', '(' => ')', '<' => '>' }[left] || left end
new(debug: false)
click to toggle source
# File lib/ovec/parser.rb, line 43 def initialize(debug: false) @debug = debug end
Public Instance Methods
eat_node(code, index)
click to toggle source
# File lib/ovec/parser.rb, line 53 def eat_node(code, index) to_eat = code[index...code.length] if to_eat.empty? raise ParseError, "Parsing an empty string" end # Cut off comments match = COMMENT_REGEX.match(to_eat) unless match.nil? match = match[0] node = CommentNode.new(match) debug "Eaten #{match.length} chars of comments." return node, match.length end # Cut off normal text match = NORMAL_REGEX.match(to_eat) unless match.nil? match = match[0] node = TextNode.new(match) debug "Eaten #{match.length} chars of normal text." return node, match.length end # Parse a command. If the command looks like a text-command, try to eat everything between { and }. match = COMMAND_REGEX.match(to_eat) unless match.nil? command = match[1] match_len = match[0].length debug "Eating command #{command}." if TEXT_COMMANDS.include?(command) || NONTEXT_CONTENT_COMMANDS.include?(command) if to_eat.length > match_len && to_eat[match_len] == '{' debug "Looks like a text command, trying to parse recursively." begin other_side = find_other_side(to_eat, match_len, '{', '}') content = parse(code[index + match_len + 1...index + other_side]) # TODO: rename textcommands to something better: command with params? node = TextCommandsNode.new(command, content, text: TEXT_COMMANDS.include?(command)) debug "Eaten #{other_side + 1} chars of text command." return node, (other_side + 1) rescue ParseError debug "Parse error encountered. Giving up on this node." end end end if VERBATIM_COMMANDS.include?(command) if to_eat.length <= match_len raise ParseError, "Verbatim at EOF with no delimiter." end left_delimiter = to_eat[match_len] right_delimiter = self.class.delimiter_right_side(left_delimiter) # TODO: nesting in verbatims ??? other_side = find_other_side(to_eat, match_len, left_delimiter, right_delimiter) content = to_eat[match_len + 1...other_side] node = VerbatimNode.new(command, left_delimiter, content) debug "Eaten #{other_side + 1} chars of verbatim." return node, (other_side + 1) end node = CommandsNode.new(command) debug "Eaten #{match_len} chars of commands." return node, match_len end if to_eat.length > 1 && to_eat[0..1] == "$$" # Lookaround assertion to handle escaped dollars in math. ending = to_eat[2...to_eat.length].index(/(?<!\\)\$\$/) if ending.nil? raise ParseError, "Unterminated $$ starting at #{index}" end node = MathNode.new(MathNode::DISPLAY, to_eat[2...ending+2]) eaten = ending + 4 debug "Eaten #{eaten} chars of inline math." return node, eaten end if to_eat[0] == '$' # Lookaround assertion to handle escaped dollars in math. ending = to_eat[1...to_eat.length].index(/(?<!\\)\$/) if ending.nil? raise ParseError, "Unterminated $ starting at #{index}" end node = MathNode.new(MathNode::INLINE, to_eat[1...ending+1]) eaten = ending + 2 debug "Eaten #{eaten} chars of inline math." return node, eaten end raise ParseError, "Don't know how to parse '#{to_eat}'." end
parse(code)
click to toggle source
# File lib/ovec/parser.rb, line 161 def parse(code) result = CombinedNode.new index = 0 while index < code.length node, shift = eat_node(code, index) index += shift result << node end return result end
Private Instance Methods
debug(*args)
click to toggle source
# File lib/ovec/parser.rb, line 21 def debug(*args) if @debug $stderr.puts(*args) end end
find_other_side(string, start, left = '{', right = '}')
click to toggle source
# File lib/ovec/parser.rb, line 27 def find_other_side(string, start, left = '{', right = '}') level = 0 for i in start...string.length if string[i] == left && (right != left || i == start) level += 1 elsif string[i] == right level -= 1 end return i if level == 0 end raise ParseError, "No matching #{right} found for #{left} in #{string} from index #{start}." end