rpn-python/lexer.py

129 lines
3.0 KiB
Python

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2019 pavle <pavle.portic@tilda.center>
#
# Distributed under terms of the BSD-3-Clause license.
import math
import operator
from constants import (
NUMBER, EOF,
PLUS, MINUS, MUL, DIV, IDIV, MOD, POW, SQRT,
HEX, DEC, BIN,
PI,
SWAP, POP,
)
class Token():
def __init__(self, type, value):
self.type = type
self.value = value
def __repr__(self):
return "<{} {}>".format(self.type, self.value)
class Lexer():
def __init__(self, text):
self.text = text
self.pos = 0
self.current_char = self.text[self.pos]
def error(self, value=None):
if value is None:
raise Exception(f'Unexpected character {self.current_char}')
else:
raise Exception(f'Unexpected token {value}')
def advance(self):
self.pos += 1
if self.pos > len(self.text) - 1:
self.current_char = None
else:
self.current_char = self.text[self.pos]
def number(self):
number = ''
while self.current_char is not None and (self.current_char.isdigit() or self.current_char == '.'):
number += self.current_char
self.advance()
return float(number)
def skip_whitespace(self):
while(self.current_char is not None and self.current_char.isspace()):
self.advance()
def get_next_token(self):
while self.current_char is not None:
if self.current_char.isspace():
self.skip_whitespace()
continue
elif self.current_char.isdigit():
return Token(NUMBER, self.number())
elif self.current_char == '+':
self.advance()
return Token(PLUS, operator.add)
elif self.current_char == '-':
self.advance()
if self.current_char is not None and self.current_char.isdigit():
return Token(NUMBER, -self.number())
return Token(MINUS, operator.sub)
elif self.current_char == '*':
self.advance()
return Token(MUL, operator.mul)
elif self.current_char == '/':
self.advance()
if self.current_char == '/':
self.advance()
return Token(IDIV, operator.floordiv)
return Token(DIV, operator.truediv)
elif self.current_char == '%':
self.advance()
return Token(MOD, operator.mod)
elif self.current_char == '^':
self.advance()
return Token(POW, operator.pow)
elif self.current_char and self.current_char.isalpha():
text = ''
while self.current_char and (self.current_char.isalpha() or self.current_char.isdigit() or self.current_char == '_'):
text += self.current_char
self.advance()
check = text.lower()
if check.lower() == HEX.lower():
return Token(HEX, hex)
elif check.lower() == DEC.lower():
return Token(DEC, float)
elif check.lower() == BIN.lower():
return Token(BIN, bin)
elif check.lower() == SQRT.lower():
return Token(SQRT, math.sqrt)
elif check.lower() == PI.lower():
return Token(NUMBER, math.pi)
elif check.lower() == SWAP.lower():
return Token(SWAP, text)
elif check.lower() == POP.lower():
return Token(POP, text)
else:
self.error(text)
self.error()
return Token(EOF, None)