129 lines
3.0 KiB
Python
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)
|
|
|