mirror of
https://github.com/Zandercraft/Aurora-lang.git
synced 2026-10-30 04:49:21 +00:00
Add variable support.
This commit is contained in:
@@ -6,8 +6,10 @@ This file sets out the various terms used to describe the
|
|||||||
`*`- zero or more of the thing before it
|
`*`- zero or more of the thing before it
|
||||||
|
|
||||||
## Main Vocabulary Table
|
## Main Vocabulary Table
|
||||||
|
*Organized by least priority to most priority.*
|
||||||
```
|
```
|
||||||
expr : term ((PLUS|MINUS) term)*
|
expr : KEYWORD:set IDENTIFIER EQ expr
|
||||||
|
: term ((PLUS|MINUS) term)*
|
||||||
|
|
||||||
term : factor ((MUL|DIV|POW) factor)*
|
term : factor ((MUL|DIV|POW) factor)*
|
||||||
|
|
||||||
@@ -16,6 +18,6 @@ factor : (PLUS|MINUS) factor
|
|||||||
|
|
||||||
power : atom (POW factor)*
|
power : atom (POW factor)*
|
||||||
|
|
||||||
atom : INT | FLOAT
|
atom : INT|FLOAT|IDENTIFIER
|
||||||
: LPAREN expr RPAREN
|
: LPAREN expr RPAREN
|
||||||
```
|
```
|
||||||
|
|||||||
190
aurora.py
190
aurora.py
@@ -11,12 +11,15 @@ Purpose: Main implementation of Aurora
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Imports
|
# Imports
|
||||||
|
import string
|
||||||
from internal.term_utils import point_at
|
from internal.term_utils import point_at
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# CONSTANTS
|
# CONSTANTS
|
||||||
####################
|
####################
|
||||||
DIGITS = "0123456789"
|
DIGITS = "0123456789"
|
||||||
|
LETTERS = string.ascii_letters
|
||||||
|
LETTERS_DIGITS = LETTERS + DIGITS
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
@@ -63,7 +66,7 @@ class RuntimeErr(Error):
|
|||||||
context = self.context
|
context = self.context
|
||||||
|
|
||||||
while context:
|
while context:
|
||||||
result = f' File {pos.fn} line {str(pos.ln + 1)}, in {context.display_name}\n' + result
|
result = f' File {pos.fn}, line {str(pos.ln + 1)}, in {context.display_name}\n' + result
|
||||||
pos = context.parent_entry_pos
|
pos = context.parent_entry_pos
|
||||||
context = context.parent
|
context = context.parent
|
||||||
|
|
||||||
@@ -102,15 +105,22 @@ class Position:
|
|||||||
# Token types
|
# Token types
|
||||||
TT_INT = "INT"
|
TT_INT = "INT"
|
||||||
TT_FLOAT = "FLOAT"
|
TT_FLOAT = "FLOAT"
|
||||||
|
TT_IDENTIFIER = "IDENTIFIER"
|
||||||
|
TT_KEYWORD = "KEYWORD"
|
||||||
TT_PLUS = "PLUS"
|
TT_PLUS = "PLUS"
|
||||||
TT_MINUS = "MINUS"
|
TT_MINUS = "MINUS"
|
||||||
TT_MUL = "MUL"
|
TT_MUL = "MUL"
|
||||||
TT_DIV = "DIV"
|
TT_DIV = "DIV"
|
||||||
TT_POW = "POW"
|
TT_POW = "POW"
|
||||||
|
TT_EQ = "EQ"
|
||||||
TT_LPAREN = "LPAREN"
|
TT_LPAREN = "LPAREN"
|
||||||
TT_RPAREN = "RPAREN"
|
TT_RPAREN = "RPAREN"
|
||||||
TT_EOF = "EOF"
|
TT_EOF = "EOF"
|
||||||
|
|
||||||
|
KEYWORDS = [
|
||||||
|
'set'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Token:
|
class Token:
|
||||||
def __init__(self, type_, value=None, pos_start=None, pos_end=None):
|
def __init__(self, type_, value=None, pos_start=None, pos_end=None):
|
||||||
@@ -123,7 +133,10 @@ class Token:
|
|||||||
self.pos_end.advance()
|
self.pos_end.advance()
|
||||||
|
|
||||||
if pos_end:
|
if pos_end:
|
||||||
self.pos_end = pos_end
|
self.pos_end = pos_end.copy()
|
||||||
|
|
||||||
|
def matches(self, type_, value):
|
||||||
|
return self.type == type_ and self.value == value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
@@ -156,6 +169,8 @@ class Lexer:
|
|||||||
self.advance()
|
self.advance()
|
||||||
elif self.current_char in DIGITS:
|
elif self.current_char in DIGITS:
|
||||||
tokens.append(self.make_number())
|
tokens.append(self.make_number())
|
||||||
|
elif self.current_char in LETTERS:
|
||||||
|
tokens.append(self.make_identifier())
|
||||||
elif self.current_char == '+':
|
elif self.current_char == '+':
|
||||||
tokens.append(Token(TT_PLUS, pos_start=self.pos))
|
tokens.append(Token(TT_PLUS, pos_start=self.pos))
|
||||||
self.advance()
|
self.advance()
|
||||||
@@ -171,6 +186,9 @@ class Lexer:
|
|||||||
elif self.current_char == '^':
|
elif self.current_char == '^':
|
||||||
tokens.append(Token(TT_POW, pos_start=self.pos))
|
tokens.append(Token(TT_POW, pos_start=self.pos))
|
||||||
self.advance()
|
self.advance()
|
||||||
|
elif self.current_char == '=':
|
||||||
|
tokens.append(Token(TT_EQ, pos_start=self.pos))
|
||||||
|
self.advance()
|
||||||
elif self.current_char == '(':
|
elif self.current_char == '(':
|
||||||
tokens.append(Token(TT_LPAREN, pos_start=self.pos))
|
tokens.append(Token(TT_LPAREN, pos_start=self.pos))
|
||||||
self.advance()
|
self.advance()
|
||||||
@@ -206,6 +224,17 @@ class Lexer:
|
|||||||
else:
|
else:
|
||||||
return Token(TT_FLOAT, float(num_str), pos_start, self.pos)
|
return Token(TT_FLOAT, float(num_str), pos_start, self.pos)
|
||||||
|
|
||||||
|
def make_identifier(self):
|
||||||
|
id_string = ''
|
||||||
|
pos_start = self.pos.copy()
|
||||||
|
|
||||||
|
while self.current_char is not None and self.current_char in (LETTERS_DIGITS + '_'):
|
||||||
|
id_string += self.current_char
|
||||||
|
self.advance()
|
||||||
|
|
||||||
|
token_type = TT_KEYWORD if id_string in KEYWORDS else TT_IDENTIFIER
|
||||||
|
return Token(token_type, id_string, pos_start, self.pos)
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# NODES
|
# NODES
|
||||||
@@ -220,6 +249,21 @@ class NumberNode:
|
|||||||
return f"{self.token}"
|
return f"{self.token}"
|
||||||
|
|
||||||
|
|
||||||
|
class VarAccessNode:
|
||||||
|
def __init__(self, var_name_token):
|
||||||
|
self.var_name_token = var_name_token
|
||||||
|
self.pos_start = self.var_name_token.pos_start
|
||||||
|
self.pos_end = self.var_name_token.pos_end
|
||||||
|
|
||||||
|
|
||||||
|
class VarAssignNode:
|
||||||
|
def __init__(self, var_name_token, value_node):
|
||||||
|
self.var_name_token = var_name_token
|
||||||
|
self.value_node = value_node
|
||||||
|
self.pos_start = self.var_name_token.pos_start
|
||||||
|
self.pos_end = self.var_name_token.pos_end
|
||||||
|
|
||||||
|
|
||||||
class BinaryOperationNode:
|
class BinaryOperationNode:
|
||||||
def __init__(self, left_node, operation, right_node):
|
def __init__(self, left_node, operation, right_node):
|
||||||
self.left_node = left_node
|
self.left_node = left_node
|
||||||
@@ -250,20 +294,24 @@ class ParsedResult:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.error = None
|
self.error = None
|
||||||
self.node = None
|
self.node = None
|
||||||
|
self.advance_count = 0
|
||||||
|
|
||||||
|
def register_advancement(self):
|
||||||
|
self.advance_count += 1
|
||||||
|
|
||||||
def register(self, result):
|
def register(self, result):
|
||||||
if isinstance(result, ParsedResult):
|
self.advance_count += result.advance_count
|
||||||
if result.error:
|
if result.error:
|
||||||
self.error = result.error
|
self.error = result.error
|
||||||
return result.node
|
return result.node
|
||||||
return result
|
|
||||||
|
|
||||||
def success(self, node):
|
def success(self, node):
|
||||||
self.node = node
|
self.node = node
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def failure(self, error):
|
def failure(self, error):
|
||||||
self.error = error
|
if not self.error or self.advance_count == 0:
|
||||||
|
self.error = error
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
@@ -320,15 +368,22 @@ class Parser:
|
|||||||
token = self.current_token
|
token = self.current_token
|
||||||
|
|
||||||
if token.type in (TT_INT, TT_FLOAT):
|
if token.type in (TT_INT, TT_FLOAT):
|
||||||
result.register(self.advance())
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
return result.success(NumberNode(token))
|
return result.success(NumberNode(token))
|
||||||
|
elif token.type == TT_IDENTIFIER:
|
||||||
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
|
return result.success(VarAccessNode(token))
|
||||||
elif token.type == TT_LPAREN:
|
elif token.type == TT_LPAREN:
|
||||||
result.register(self.advance())
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
expr = result.register(self.expr())
|
expr = result.register(self.expr())
|
||||||
if result.error:
|
if result.error:
|
||||||
return result
|
return result
|
||||||
if self.current_token.type == TT_RPAREN:
|
if self.current_token.type == TT_RPAREN:
|
||||||
result.register(self.advance())
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
return result.success(expr)
|
return result.success(expr)
|
||||||
else:
|
else:
|
||||||
return result.failure(InvalidSyntaxErr(
|
return result.failure(InvalidSyntaxErr(
|
||||||
@@ -339,18 +394,19 @@ class Parser:
|
|||||||
return result.failure(InvalidSyntaxErr(
|
return result.failure(InvalidSyntaxErr(
|
||||||
token.pos_start,
|
token.pos_start,
|
||||||
token.pos_end,
|
token.pos_end,
|
||||||
"Exprected int, float, '+', '-', or '('"
|
"Exprected int, float, identifier, '+', '-', or '('"
|
||||||
))
|
))
|
||||||
|
|
||||||
def power(self):
|
def power(self):
|
||||||
return self.binary_operation(self.atom, (TT_POW, ), self.factor)
|
return self.binary_operation(self.atom, (TT_POW,), self.factor)
|
||||||
|
|
||||||
def factor(self):
|
def factor(self):
|
||||||
result = ParsedResult()
|
result = ParsedResult()
|
||||||
token = self.current_token
|
token = self.current_token
|
||||||
|
|
||||||
if token.type in (TT_PLUS, TT_MINUS):
|
if token.type in (TT_PLUS, TT_MINUS):
|
||||||
result.register(self.advance())
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
factor = result.register(self.factor())
|
factor = result.register(self.factor())
|
||||||
if result.error:
|
if result.error:
|
||||||
return result
|
return result
|
||||||
@@ -362,7 +418,45 @@ class Parser:
|
|||||||
return self.binary_operation(self.factor, (TT_MUL, TT_DIV, TT_POW))
|
return self.binary_operation(self.factor, (TT_MUL, TT_DIV, TT_POW))
|
||||||
|
|
||||||
def expr(self):
|
def expr(self):
|
||||||
return self.binary_operation(self.term, (TT_PLUS, TT_MINUS))
|
result = ParsedResult()
|
||||||
|
|
||||||
|
if self.current_token.matches(TT_KEYWORD, 'set'):
|
||||||
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
|
|
||||||
|
if self.current_token.type != TT_IDENTIFIER:
|
||||||
|
return result.failure(InvalidSyntaxErr(
|
||||||
|
self.current_token.pos_start,
|
||||||
|
self.current_token.pos_end,
|
||||||
|
"Expected identifier"
|
||||||
|
))
|
||||||
|
|
||||||
|
var_name = self.current_token
|
||||||
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
|
|
||||||
|
if self.current_token.type != TT_EQ:
|
||||||
|
return result.failure(InvalidSyntaxErr(
|
||||||
|
self.current_token.pos_start,
|
||||||
|
self.current_token.pos_end,
|
||||||
|
"Expected '='"
|
||||||
|
))
|
||||||
|
|
||||||
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
|
expr = result.register(self.expr())
|
||||||
|
if result.error:
|
||||||
|
return result
|
||||||
|
return result.success(VarAssignNode(var_name, expr))
|
||||||
|
|
||||||
|
node = result.register(self.binary_operation(self.term, (TT_PLUS, TT_MINUS)))
|
||||||
|
if result.error:
|
||||||
|
return result.failure(InvalidSyntaxErr(
|
||||||
|
self.current_token.pos_start,
|
||||||
|
self.current_token.pos_end,
|
||||||
|
"Expected 'set', int, float, identifier, '+', '-' or '('"
|
||||||
|
))
|
||||||
|
return result.success(node)
|
||||||
|
|
||||||
def binary_operation(self, lfunc, operations, rfunc=None):
|
def binary_operation(self, lfunc, operations, rfunc=None):
|
||||||
if rfunc is None:
|
if rfunc is None:
|
||||||
@@ -374,7 +468,8 @@ class Parser:
|
|||||||
|
|
||||||
while self.current_token.type in operations:
|
while self.current_token.type in operations:
|
||||||
operation = self.current_token
|
operation = self.current_token
|
||||||
result.register(self.advance())
|
result.register_advancement()
|
||||||
|
self.advance()
|
||||||
right = result.register(rfunc())
|
right = result.register(rfunc())
|
||||||
if result.error:
|
if result.error:
|
||||||
return result
|
return result
|
||||||
@@ -429,6 +524,12 @@ class Number:
|
|||||||
if isinstance(other, Number):
|
if isinstance(other, Number):
|
||||||
return Number(self.value ** other.value).set_context(self.context), None
|
return Number(self.value ** other.value).set_context(self.context), None
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
copy = Number(self.value)
|
||||||
|
copy.set_pos(self.pos_start, self.pos_end)
|
||||||
|
copy.set_context(self.context)
|
||||||
|
return copy
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self.value)
|
return str(self.value)
|
||||||
|
|
||||||
@@ -441,6 +542,29 @@ class Context:
|
|||||||
self.display_name = display_name
|
self.display_name = display_name
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.parent_entry_pos = parent_entry_pos
|
self.parent_entry_pos = parent_entry_pos
|
||||||
|
self.symbol_table = None
|
||||||
|
|
||||||
|
|
||||||
|
####################
|
||||||
|
# SYMBOL TABLE
|
||||||
|
####################
|
||||||
|
class SymbolTable:
|
||||||
|
def __init__(self):
|
||||||
|
self.symbols = {}
|
||||||
|
self.parent = None # Used in functions
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
value = self.symbols.get(name, None)
|
||||||
|
|
||||||
|
if value is None and self.parent is not None:
|
||||||
|
return self.parent.get(name)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def set(self, name, value):
|
||||||
|
self.symbols[name] = value
|
||||||
|
|
||||||
|
def remove(self, name):
|
||||||
|
del self.symbols[name]
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
@@ -456,7 +580,34 @@ class Interpreter:
|
|||||||
raise Exception(f"No visit_{type(node).__name__} method defined")
|
raise Exception(f"No visit_{type(node).__name__} method defined")
|
||||||
|
|
||||||
def visit_NumberNode(self, node, context):
|
def visit_NumberNode(self, node, context):
|
||||||
return RuntimeResult().success(Number(node.token.value).set_context(context).set_pos(node.pos_start, node.pos_end))
|
return RuntimeResult().success(
|
||||||
|
Number(node.token.value).set_context(context).set_pos(node.pos_start, node.pos_end))
|
||||||
|
|
||||||
|
def visit_VarAccessNode(self, node, context):
|
||||||
|
result = RuntimeResult()
|
||||||
|
var_name = node.var_name_token.value
|
||||||
|
value = context.symbol_table.get(var_name)
|
||||||
|
|
||||||
|
if not value:
|
||||||
|
return result.failure(RuntimeErr(
|
||||||
|
node.pos_start,
|
||||||
|
node.pos_end,
|
||||||
|
f"'{var_name}' is not defined",
|
||||||
|
context
|
||||||
|
))
|
||||||
|
|
||||||
|
value = value.copy().set_pos(node.pos_start, node.pos_end)
|
||||||
|
return result.success(value)
|
||||||
|
|
||||||
|
def visit_VarAssignNode(self, node, context):
|
||||||
|
result = RuntimeResult()
|
||||||
|
var_name = node.var_name_token.value
|
||||||
|
value = result.register(self.visit(node.value_node, context))
|
||||||
|
if result.error:
|
||||||
|
return result
|
||||||
|
|
||||||
|
context.symbol_table.set(var_name, value)
|
||||||
|
return result.success(value)
|
||||||
|
|
||||||
def visit_BinaryOperationNode(self, node, context):
|
def visit_BinaryOperationNode(self, node, context):
|
||||||
rt_result = RuntimeResult()
|
rt_result = RuntimeResult()
|
||||||
@@ -499,6 +650,10 @@ class Interpreter:
|
|||||||
####################
|
####################
|
||||||
# RUNNER
|
# RUNNER
|
||||||
####################
|
####################
|
||||||
|
global_symbol_table = SymbolTable()
|
||||||
|
global_symbol_table.set("null", Number(0))
|
||||||
|
|
||||||
|
|
||||||
def evaluate(fn, text):
|
def evaluate(fn, text):
|
||||||
lexer = Lexer(fn, text)
|
lexer = Lexer(fn, text)
|
||||||
tokens, error = lexer.make_tokens()
|
tokens, error = lexer.make_tokens()
|
||||||
@@ -517,6 +672,7 @@ def evaluate(fn, text):
|
|||||||
# Run the program
|
# Run the program
|
||||||
interpreter = Interpreter()
|
interpreter = Interpreter()
|
||||||
context = Context("<program>")
|
context = Context("<program>")
|
||||||
|
context.symbol_table = global_symbol_table
|
||||||
result = interpreter.visit(ast.node, context)
|
result = interpreter.visit(ast.node, context)
|
||||||
|
|
||||||
return result.value, result.error
|
return result.value, result.error
|
||||||
|
|||||||
Reference in New Issue
Block a user