mylanguage

 avatar
unknown
python
9 months ago
5.8 kB
13
Indexable
import re

# Token types
NUMBER    = 'NUMBER'
PLUS      = 'PLUS'
MINUS     = 'MINUS'
MUL       = 'MUL'
DIV       = 'DIV'
ASSIGN    = 'ASSIGN'
ID        = 'ID'
PRINT     = 'PRINT'
LPAREN    = 'LPAREN'
RPAREN    = 'RPAREN'
EOF       = 'EOF'  # End of File / End of Input

# A simple lexer/tokenizer
class Lexer:
    def __init__(self, text):
        self.text = text
        self.pos = 0
        self.current_char = self.text[self.pos]

    def advance(self):
        """Advance the position and update the current_char."""
        self.pos += 1
        if self.pos > len(self.text) - 1:
            self.current_char = None  # Indicates end of input
        else:
            self.current_char = self.text[self.pos]

    def skip_whitespace(self):
        while self.current_char is not None and self.current_char.isspace():
            self.advance()

    def number(self):
        """Return a multi-digit number."""
        result = ''
        while self.current_char is not None and self.current_char.isdigit():
            result += self.current_char
            self.advance()
        return int(result)

    def get_next_token(self):
        """Tokenizes the input one token at a time."""
        while self.current_char is not None:
            if self.current_char.isspace():
                self.skip_whitespace()
                continue

            if self.current_char.isdigit():
                return (NUMBER, self.number())

            if self.current_char.isalpha():
                return (ID, self.identifier())

            if self.current_char == '=':
                self.advance()
                return (ASSIGN, '=')

            if self.current_char == '+':
                self.advance()
                return (PLUS, '+')

            if self.current_char == '-':
                self.advance()
                return (MINUS, '-')

            if self.current_char == '*':
                self.advance()
                return (MUL, '*')

            if self.current_char == '/':
                self.advance()
                return (DIV, '/')

            if self.current_char == '(':
                self.advance()
                return (LPAREN, '(')

            if self.current_char == ')':
                self.advance()
                return (RPAREN, ')')

            raise Exception(f"Invalid character: {self.current_char}")

        return (EOF, None)

    def identifier(self):
        """Return an identifier or a reserved keyword like 'print'."""
        result = ''
        while self.current_char is not None and self.current_char.isalnum():
            result += self.current_char
            self.advance()

        if result == 'print':
            return PRINT
        return result


# Parser that converts tokens into statements
class Parser:
    def __init__(self, lexer):
        self.lexer = lexer
        self.current_token = self.lexer.get_next_token()

    def eat(self, token_type):
        if self.current_token[0] == token_type:
            self.current_token = self.lexer.get_next_token()
        else:
            raise Exception(f"Invalid token {self.current_token}, expected {token_type}")

    def factor(self):
        """Handles numbers and parentheses"""
        token = self.current_token
        if token[0] == NUMBER:
            self.eat(NUMBER)
            return token[1]
        elif token[0] == LPAREN:
            self.eat(LPAREN)
            result = self.expr()
            self.eat(RPAREN)
            return result
        else:
            raise Exception("Factor: Invalid Syntax")

    def term(self):
        """Handles *, /"""
        result = self.factor()
        while self.current_token[0] in (MUL, DIV):
            token = self.current_token
            if token[0] == MUL:
                self.eat(MUL)
                result *= self.factor()
            elif token[0] == DIV:
                self.eat(DIV)
                result /= self.factor()
        return result

    def expr(self):
        """Handles +, -"""
        result = self.term()
        while self.current_token[0] in (PLUS, MINUS):
            token = self.current_token
            if token[0] == PLUS:
                self.eat(PLUS)
                result += self.term()
            elif token[0] == MINUS:
                self.eat(MINUS)
                result -= self.term()
        return result

    def assignment(self):
        """Handles variable assignments and print"""
        if self.current_token[0] == ID:
            var_name = self.current_token[1]
            self.eat(ID)
            self.eat(ASSIGN)
            value = self.expr()
            return ('assign', var_name, value)
        elif self.current_token[0] == PRINT:
            self.eat(PRINT)
            var_name = self.current_token[1]
            return ('print', var_name)
        else:
            raise Exception("Invalid Syntax")


# Interpreter to evaluate the parsed expressions
class Interpreter:
    def __init__(self, parser):
        self.parser = parser
        self.variables = {}

    def eval(self):
        node = self.parser.assignment()
        if node[0] == 'assign':
            self.variables[node[1]] = node[2]
        elif node[0] == 'print':
            var_name = node[1]
            if var_name in self.variables:
                print(self.variables[var_name])
            else:
                raise Exception(f"Variable '{var_name}' is not defined")

# Main program
def main():
    while True:
        try:
            text = input('basic_lang> ')
            if text.strip() == 'exit':
                break

            lexer = Lexer(text)
            parser = Parser(lexer)
            interpreter = Interpreter(parser)
            interpreter.eval()
        except Exception as e:
            print(e)

if __name__ == '__main__':
    main()
Editor is loading...
Leave a Comment