view arith/parse.py @ 0:e85652916197 draft

Initial commit
author Lewin Bormann <lbo@spheniscida.de>
date Fri, 17 May 2019 22:28:13 +0200
parents
children
line wrap: on
line source

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun May 12 19:23:08 2019

@author: lbo
"""

"""
Parse arithmetic terms.

Terms are structured like this:

    Sum (consists of) products (consist of) factors
"""

from tree import Value, Symbol, Product, Term, Power

class ParseState:
    """Encapsulates state as the parser goes through input."""

    _input = ''
    _index = 0

    def __init__(self, s):
        self._input = s

    def repr(self):
        return 'ParseState({} < {} > {}'.format(
                self._input[0:self._index], self._input[self._index], self._input[self._index:])

    def next(self):
        current = self.peek()
        self._index += 1
        while not self.finished() and self.peek().isspace():
            self._index += 1
        return current

    def peek(self):
        return self._input[self._index]

    def index(self):
        return self._index

    def reset(self, ix):
        self._index = ix

    def __iter__(self):
        return self

    def __next__(self):
        return self.next()

    def finished(self):
        return self._index == len(self._input)

def parse(s):
    st = ParseState(s)
    return parse_term(st)

def parse_term(st):
    left = parse_product(st)
    result = left
    if st.finished():
        return result
    op = st.peek()
    while op in [Term.PLUS, Term.MINUS]:
        st.next()
        right = parse_product(st)
        result = Term(result, right)
        result.typ = op
        if st.finished():
            break
        op = st.peek()
    return result

def parse_product(st):
    left = parse_power(st)
    result = left
    if st.finished():
        return result
    op = st.peek()
    while not st.finished() and op in [Product.PROD, Product.QUOT]:
        st.next()
        right = parse_product(st)
        result = Product(result, right)
        result.typ = op
        if st.finished():
            break
        op = st.peek()
    return result

def parse_power(st):
    left = parse_atom(st)
    result = left
    if st.finished():
        return result
    next = st.peek()
    if next == '^':
        st.next()
        right = parse_atom(st)
        result = Power(left, right)
    return result


def parse_atom(st):
    next = st.next()
    if next == '(':
        term = parse_term(st)
        st.next() # Consume closing paren
        return term

    full = next

    def is_number(s):
        return all([a.isnumeric() or a == '.' for a in s])

    while not st.finished() and (st.peek().isalpha() or is_number(st.peek())):
        full += st.next()

    if is_number(full):
        return Value(float(full))
    if full.isalnum():
        return Symbol(full)