summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew <saintruler@gmail.com>2021-07-12 16:57:10 +0400
committerAndrew <saintruler@gmail.com>2021-07-12 16:57:10 +0400
commit95ab5fec4a930114fabeef77f7a458c8dd163142 (patch)
treeccec54fdcbb15276689249d70381fe87fda53f97
parent49df60f32aca6428706bc895b7e48ab2d68444b5 (diff)
Added ability to call functions as a statement.
-rw-r--r--consts.py2
-rw-r--r--main.py14
-rw-r--r--parser.py100
3 files changed, 100 insertions, 16 deletions
diff --git a/consts.py b/consts.py
index 0a595c2..c28b33b 100644
--- a/consts.py
+++ b/consts.py
@@ -13,7 +13,7 @@ class TokenType:
class NodeType:
(
- NUMBER, OPERATOR, SYMBOL, FUNCALL, UNARY, ASSIGNMENT, *_
+ NUMBER, OPERATOR, SYMBOL, FUNCALL, ARGUMENTS, UNARY, ASSIGNMENT, *_
) = range(100)
diff --git a/main.py b/main.py
index 0ba9d47..e21d0c8 100644
--- a/main.py
+++ b/main.py
@@ -31,7 +31,8 @@ def factorial(n):
runtime_functions = {
- "factorial": factorial
+ "factorial": factorial,
+ "print": print,
}
# END OF RUNTIME
@@ -45,8 +46,8 @@ def _eval(node):
return runtime.get(node.value)
elif node.type == NodeType.FUNCALL:
- fun_name = node.value[0]
- args = list(map(_eval, node.value[1]))
+ fun_name = node.left.value
+ args = list(map(_eval, node.right.value))
return runtime_functions.get(fun_name)(*args)
elif node.type == NodeType.OPERATOR:
@@ -64,9 +65,10 @@ def main(*argv):
data = argv[0]
else:
# data = "2 + 3 * 4"
- data = "let a = 2 + 3; let e = 4;"
+ # data = "let a = 2 + 3; let e = 4;"
# data = "(3 + 3 * 2) * -4"
# data = "(2 + factorial(4, )) * 9"
+ data = "print(2);"
tokens = tokenize(data)
# print(tokens)
@@ -74,8 +76,8 @@ def main(*argv):
for statement in statements:
_eval(statement)
# node, _ = parse_expr(tokens, 0)
- # print(expr_eval(node))
- print(1)
+ # print(_eval(node))
+ pass
if __name__ == "__main__":
diff --git a/parser.py b/parser.py
index 19fa952..fa32b97 100644
--- a/parser.py
+++ b/parser.py
@@ -7,7 +7,7 @@ class ParserError(Exception):
class Node:
- def __init__(self, node_type, value=None, subtype=None):
+ def __init__(self, node_type, *, value=None, subtype=None):
self.type = node_type
self.value = value
self.subtype = subtype
@@ -50,7 +50,7 @@ def parse_parenthesis(tokens, start):
if depth == 0 and token.type == TokenType.RIGHT_PARENTHESIS:
break
end += 1
- node = parse_expr(tokens[start + 1: end])
+ node, _ = parse_expr(tokens[start + 1: end], 0)
return node, end
@@ -78,7 +78,11 @@ def parse_args(tokens, start):
if len(arg) != 0:
args.append(arg)
- return list(map(parse_expr, args))
+ tmp = []
+ for arg in args:
+ node, _ = parse_expr(arg, 0)
+ tmp.append(node)
+ return tmp
def parse_expr(tokens, start):
@@ -91,7 +95,8 @@ def parse_expr(tokens, start):
if state == State.OPERATOR and token.type == TokenType.LEFT_PARENTHESIS:
args = parse_args(tokens, i)
current_node.type = NodeType.FUNCALL
- current_node.value = (current_node.value, args)
+ current_node.left = Node(NodeType.SYMBOL, value=current_node.value)
+ current_node.right = Node(NodeType.ARGUMENTS, value=args)
elif token.type in [TokenType.LEFT_PARENTHESIS, TokenType.NUMBER, TokenType.SYMBOL] or token.subtype == TokenType.UNARY:
@@ -146,7 +151,7 @@ def parse_expr(tokens, start):
raise ValueError("Not a valid expression")
state = State.NAME
- elif token.type == TokenType.SEMICOLON:
+ elif token.type in [TokenType.SEMICOLON, TokenType.COMMA]:
break
i += 1
@@ -205,18 +210,95 @@ def parse_let_statement(tokens: list[Token], start: int):
return root_node, i
+def parse_function_call(tokens: list[Token], start: int):
+ class FuncStates:
+ (WAIT_SYMBOL, WAIT_LEFT_PAR, PARSING_EXPR, WAIT_SEMICOLON, PARSE_END, *_) = range(100)
+
+ root_node = Node(NodeType.FUNCALL)
+ state = FuncStates.WAIT_SYMBOL
+ i = start
+ args = []
+ arg = []
+
+ while i < len(tokens):
+ token = tokens[i]
+ if state == FuncStates.WAIT_SYMBOL:
+ if token.type == TokenType.SYMBOL:
+ root_node.left = Node(NodeType.SYMBOL, value=token.value)
+ state = FuncStates.WAIT_LEFT_PAR
+ else:
+ raise ParserError(f"Waited for symbol, got {token}")
+
+ elif state == FuncStates.WAIT_LEFT_PAR:
+ if token.type == TokenType.LEFT_PARENTHESIS:
+ state = FuncStates.PARSING_EXPR
+ else:
+ raise ParserError(f"Waited for left parenthesis, got {token}")
+
+ elif state == FuncStates.PARSING_EXPR:
+ if token.type == TokenType.COMMA:
+ node, _ = parse_expr(arg, 0)
+ args.append(node)
+ arg.clear()
+ elif token.type == TokenType.RIGHT_PARENTHESIS:
+ node, _ = parse_expr(arg, 0)
+ args.append(node)
+ root_node.right = Node(NodeType.ARGUMENTS, value=args)
+ state = FuncStates.WAIT_SEMICOLON
+ else:
+ arg.append(token)
+
+ elif state == FuncStates.WAIT_SEMICOLON:
+ if token.type == TokenType.SEMICOLON:
+ state = FuncStates.PARSE_END
+ break
+ else:
+ raise ParserError(f"Waited for semicolon, got {token}")
+
+ i += 1
+
+ if state != FuncStates.PARSE_END:
+ raise ParserError("Statement ended unexpectedly")
+
+ return root_node, i
+
+
def parse(tokens: list[Token]):
+ class ParseState:
+ (ANY, GOT_SYMBOL, *_) = range(100)
+
_statements = [
[Keyword.LET, TokenType.SYMBOL, TokenType.EQUALS, "EXPR", TokenType.SEMICOLON],
- [Keyword.FN, TokenType.SYMBOL, TokenType.LEFT_PARENTHESIS, "ARGS", TokenType.RIGHT_PARENTHESIS, TokenType.LEFT_BRACE, ["STATEMENTS"], TokenType.RIGHT_BRACE],
+ [TokenType.SYMBOL, TokenType.EQUALS, "EXPR", TokenType.SEMICOLON],
+ [TokenType.SYMBOL, TokenType.LEFT_PARENTHESIS, "ARGS", TokenType.RIGHT_PARENTHESIS, TokenType.SEMICOLON],
+ [Keyword.FN, TokenType.SYMBOL, TokenType.LEFT_PARENTHESIS, "ARGS", TokenType.RIGHT_PARENTHESIS, TokenType.LEFT_BRACE, ["STATEMENT"], TokenType.RIGHT_BRACE],
]
+ state = ParseState.ANY
+
statements = []
+ symbol = None
i = 0
while i < len(tokens):
- if tokens[i].type == TokenType.KEYWORD and tokens[i].subtype == Keyword.LET:
- node, i = parse_let_statement(tokens, i)
- statements.append(node)
+ token = tokens[i]
+
+ if state == ParseState.ANY:
+ if token.type == TokenType.KEYWORD:
+ if token.subtype == Keyword.LET:
+ node, i = parse_let_statement(tokens, i)
+ statements.append(node)
+ elif token.type == TokenType.SYMBOL:
+ state = ParseState.GOT_SYMBOL
+ continue
+
+ elif state == ParseState.GOT_SYMBOL:
+ if token.type == TokenType.SYMBOL:
+ node, i = parse_function_call(tokens, i)
+ statements.append(node)
+ state = ParseState.ANY
+ else:
+ raise ParserError("oops...")
+
i += 1
return statements