Source code for sirepo.template.line_parser

# -*- coding: utf-8 -*-
"""Simple line parser.

Parses a line of input one character at a time.

:copyright: Copyright (c) 2016 RadiaSoft LLC.  All Rights Reserved.
:license: http://www.apache.org/licenses/LICENSE-2.0.html
"""
from __future__ import absolute_import, division, print_function
import re

[docs]class LineParser(object): """Parses a line of input one character at a time. Call set_line() prior to other metho calls. """ def __init__(self, next_id=0): self._id = next_id self.index = 0 self.line = '' self.line_number = 0
[docs] def assert_char(self, char): """Raises an Error unless the current character matches""" if self.next_char() != char: self.raise_error('expected {}'.format(char)) self.ignore_whitespace()
[docs] def assert_end_of_line(self, comment_char='!'): """Raises an error unless the remaining line is empty""" self.ignore_whitespace() if self.has_char() and self.peek_char() != comment_char: self.raise_error('left-over input')
[docs] def has_char(self): return self.index < len(self.line)
[docs] def ignore_whitespace(self): """Skip whitespace""" while self.has_char() and re.search(r'\s', self.peek_char()): self.next_char()
[docs] def increment_line_number(self): self.line_number += 1
[docs] def get_index(self): return self.index
[docs] def next_char(self): """Advance to the next character. Returns None if end of line""" if self.has_char(): current = self.line[self.index] self.index += 1 return current return None
[docs] def next_id(self): self._id += 1 return self._id
[docs] def parse_quoted_value(self): self.assert_char('"') value = self.read_until('"') if self.line[self.index - 1] == '\\': value += '\"' + self.parse_quoted_value() else: self.assert_char('"') return value
[docs] def parse_value(self, end_regex=r'[\s,=\!)*]'): """Parses a value, possibly quoted.""" if self.peek_char() == '"': return self.parse_quoted_value() return self.read_until(end_regex)
[docs] def peek_char(self): if self.has_char(): return self.line[self.index] return None
[docs] def raise_error(self, message): raise IOError('line {}, {}: {}'.format( self.line_number, message, self.line[self.index:]))
[docs] def read_until(self, regex): """Reads until the end-of-line or the character regex is matched""" value = '' while self.has_char() and not re.search(regex, self.peek_char()): value += self.next_char() self.ignore_whitespace() return value
[docs] def reset_index(self, index): """Reset the character index""" self.index = index
[docs] def set_line(self, line): """Prepares a line for parsing.""" self.line = line self.index = 0