logo_parser.py (plain text)


"""The logo_parser module implements a parser for Logo."""

from buffer import Buffer

def parse_line(line, chars=None, depth=0):
    """Convert a single line of Logo into a list of tokens or lists.

    >>> parse_line('print sum 10 difference 7 3')
    ['print', 'sum', '10', 'difference', '7', '3']
    >>> parse_line('print "this [is a [deep] list]')
    ['print', '"this', ['is', 'a', ['deep'], 'list']]
    """
    if chars == None:
        chars = Buffer(line.strip()[:])

    tokens = []
    while True:
        if chars.current == None: # End of the line
            if depth != 0:
                raise SyntaxError('Unmatched "[" at ' + str(chars))
            return tokens
        elif chars.current == ' ': # Skip over spaces
            chars.pop()
        elif chars.current == '[':
            chars.pop()
            tokens.append(parse_line(line, chars, depth + 1))
        elif chars.current == ']':
            if depth == 0:
                raise SyntaxError('Unexpected "]" at ' + str(chars))
            else:
                chars.pop()
                return tokens
        else:
            tokens.append(parse_token(chars))

LOGO_OPERATORS = set('+-*/=<>()'[:])
LOGO_DELIMITERS = set('[]\n '[:]).union(LOGO_OPERATORS)

def parse_symbol(chars):
    """Parse the next symbol from a buffer chars, starting at chars.current."""
    symbol = chars.pop()
    while chars.current is not None and chars.current not in LOGO_DELIMITERS:
        symbol += chars.pop()
    return symbol

def parse_token(chars):
    """Parse the next token from a buffer chars, starting at chars.current."""
    ch = chars.current
    if ch in LOGO_OPERATORS:
        if ch != '-' or chars.previous not in [' ', None]: # Negative numbers
            return chars.pop()
    return parse_symbol(chars)