239 lines
7.8 KiB
Plaintext
239 lines
7.8 KiB
Plaintext
from stdint import *
|
|
from stdio import printf
|
|
import memhub
|
|
import stdlib
|
|
import sys
|
|
import string
|
|
import ast
|
|
import win32.win32console
|
|
|
|
|
|
# MBuddy arena size: 1MB
|
|
define ARENA_SIZE = 1048576
|
|
# mpool memory size: 512KB
|
|
define POOL_SIZE = 524288
|
|
# dump buffer size: 256KB
|
|
define DUMP_SIZE = 262144
|
|
|
|
|
|
def classify_list(lst: list[ast.AST*]*, depth: int):
|
|
"""Recursively walk a list[AST | t.CPtr] container, classify each element."""
|
|
if lst is None:
|
|
return
|
|
n: sizet = lst.__len__()
|
|
for i in range(n):
|
|
classify_print(lst.get(i), depth + 1)
|
|
|
|
|
|
def classify_print(node: ast.AST*, depth: int):
|
|
"""Recursively walk the parse tree and print each node's category at runtime
|
|
via VPC isinstance vtable subclass-set comparison (Stmt vs ExprNode vs other).
|
|
"""
|
|
if node is None:
|
|
return
|
|
for sp in range(depth):
|
|
printf(" ")
|
|
if isinstance(node, ast.Stmt) != 0:
|
|
printf("STMT %s\n", node.type_name())
|
|
elif isinstance(node, ast.ExprNode) != 0:
|
|
printf("EXPR %s\n", node.type_name())
|
|
else:
|
|
printf("OTHER %s\n", node.type_name())
|
|
# Walk statement expression fields by kind (bodies live in children); cast to
|
|
# the concrete class first, since ast.AST base-class field access fast-fails.
|
|
k: int = node.kind()
|
|
if k == ast.ASTKind.Assign:
|
|
asg: ast.Assign* = (ast.Assign*)(node)
|
|
classify_print(asg.value, depth + 1)
|
|
elif k == ast.ASTKind.If:
|
|
ifn: ast.If* = (ast.If*)(node)
|
|
classify_print(ifn.test, depth + 1)
|
|
classify_list(ifn.orelse, depth + 1)
|
|
elif k == ast.ASTKind.While:
|
|
whl: ast.While* = (ast.While*)(node)
|
|
classify_print(whl.test, depth + 1)
|
|
classify_list(whl.orelse, depth + 1)
|
|
elif k == ast.ASTKind.For:
|
|
for_nd: ast.For* = (ast.For*)(node)
|
|
classify_print(for_nd.iter, depth + 1)
|
|
classify_print(for_nd.target, depth + 1)
|
|
classify_list(for_nd.orelse, depth + 1)
|
|
elif k == ast.ASTKind.Return:
|
|
ret_nd: ast.Return* = (ast.Return*)(node)
|
|
classify_print(ret_nd.value, depth + 1)
|
|
elif k == ast.ASTKind.Expr:
|
|
expr_nd: ast.Expr* = (ast.Expr*)(node)
|
|
classify_print(expr_nd.value, depth + 1)
|
|
elif k == ast.ASTKind.BinOp:
|
|
bin_nd: ast.BinOp* = (ast.BinOp*)(node)
|
|
classify_print(bin_nd.left, depth + 1)
|
|
classify_print(bin_nd.right, depth + 1)
|
|
elif k == ast.ASTKind.UnaryOp:
|
|
una_nd: ast.UnaryOp* = (ast.UnaryOp*)(node)
|
|
classify_print(una_nd.operand, depth + 1)
|
|
elif k == ast.ASTKind.Compare:
|
|
cmp_nd: ast.Compare* = (ast.Compare*)(node)
|
|
classify_print(cmp_nd.left, depth + 1)
|
|
classify_list(cmp_nd.comparators, depth + 1)
|
|
elif k == ast.ASTKind.BoolOp:
|
|
bool_nd: ast.BoolOp* = (ast.BoolOp*)(node)
|
|
classify_list(bool_nd.values, depth + 1)
|
|
elif k == ast.ASTKind.Call:
|
|
call_nd: ast.Call* = (ast.Call*)(node)
|
|
classify_print(call_nd.func, depth + 1)
|
|
classify_list(call_nd.args, depth + 1)
|
|
else:
|
|
pass
|
|
# children (statement bodies)
|
|
if node.children is not None:
|
|
n: sizet = node.children.__len__()
|
|
for i in range(n):
|
|
classify_print(node.children.get(i), depth + 1)
|
|
|
|
|
|
export def main() -> int:
|
|
win32.win32console.SetConsoleCP(65001)
|
|
win32.win32console.SetConsoleOutputCP(65001)
|
|
|
|
printf("AST Comprehensive Test Start\n")
|
|
|
|
# Initialize the global MBuddy allocator
|
|
arena: bytes = stdlib.malloc(ARENA_SIZE)
|
|
if arena == None:
|
|
printf("FAIL: arena alloc failed\n")
|
|
return 1
|
|
mbuddy_obj: memhub.MemBuddy* = memhub.MemBuddy(arena, ARENA_SIZE)
|
|
if mbuddy_obj == None:
|
|
printf("FAIL: mbuddy init failed\n")
|
|
return 1
|
|
|
|
sys._mbuddy = mbuddy_obj
|
|
ast._mbuddy = mbuddy_obj
|
|
|
|
# Create an mpool for the ast parser
|
|
pool_mem: bytes = stdlib.malloc(POOL_SIZE)
|
|
if pool_mem == None:
|
|
printf("FAIL: malloc for pool failed\n")
|
|
return 1
|
|
pool: memhub.MemPool* = memhub.MemPool(pool_mem, POOL_SIZE)
|
|
if pool == None:
|
|
printf("FAIL: memhub.MemPool init failed\n")
|
|
return 1
|
|
|
|
# dump buffer (separately allocated)
|
|
dump_mem: bytes = stdlib.malloc(DUMP_SIZE)
|
|
if dump_mem == None:
|
|
printf("FAIL: malloc for dump failed\n")
|
|
return 1
|
|
dump_buf: char* = char(dump_mem, t.CPtr)
|
|
|
|
# Comprehensive test source covering main CPython syntax
|
|
src: str = """x = 1
|
|
y = 2
|
|
z = x + y
|
|
a = x - y
|
|
b = x * y
|
|
c = x / y
|
|
d = x == y
|
|
e = x < y
|
|
f = x > y
|
|
g = x and y
|
|
h = x or y
|
|
i = not x
|
|
def foo(a, b):
|
|
return a + b
|
|
result = foo(x, y)
|
|
if x > 0:
|
|
print(x)
|
|
else:
|
|
print(y)
|
|
for i in range(10):
|
|
print(i)
|
|
while x > 0:
|
|
x = x - 1
|
|
class Point:
|
|
pass
|
|
"""
|
|
|
|
printf("Parsing comprehensive source...\n")
|
|
|
|
# Parse
|
|
ast._init_tables(pool)
|
|
lx_dbg: t.CPtr = ast.new_lexer(pool)
|
|
if lx_dbg == None:
|
|
printf("FAIL: new_lexer returned None\n")
|
|
stdlib.free(pool_mem)
|
|
stdlib.free(dump_mem)
|
|
return 1
|
|
ast._lexer_init(lx_dbg, src, pool)
|
|
tokens_dbg: t.CPtr = ast.tokenize(lx_dbg)
|
|
if tokens_dbg == None:
|
|
printf("FAIL: tokenize returned None\n")
|
|
stdlib.free(pool_mem)
|
|
stdlib.free(dump_mem)
|
|
return 1
|
|
tree: ast.AST* = ast.parse_tokens(pool, tokens_dbg)
|
|
if tree == None:
|
|
printf("FAIL: parse returned None\n")
|
|
stdlib.free(pool_mem)
|
|
stdlib.free(dump_mem)
|
|
return 1
|
|
|
|
printf("PASS: parse succeeded\n")
|
|
|
|
# dump AST
|
|
printf("Calling ast.dump...\n")
|
|
ast.dump(tree, dump_buf, DUMP_SIZE)
|
|
printf("ast.dump returned, now printing buffer...\n")
|
|
printf("=== AST DUMP ===\n%s\n=== END ===\n", dump_buf)
|
|
|
|
# ===== Stmt/Expr runtime distinction test (isinstance vtable compare) =====
|
|
printf("=== STMT/EXPR RUNTIME DISTINGUISH TEST ===\n")
|
|
classify_print(tree, 0)
|
|
|
|
# Assert 1: Module is neither Stmt nor ExprNode (top-level container)
|
|
if isinstance(tree, ast.Stmt) != 0 or isinstance(tree, ast.ExprNode) != 0:
|
|
printf("FAIL: Module misclassified as Stmt/ExprNode\n")
|
|
else:
|
|
printf("PASS: Module is neither Stmt nor ExprNode\n")
|
|
|
|
# Assert 2: every top-level body statement is a Stmt
|
|
top_ok: int = 1
|
|
top_n: sizet = tree.children.__len__()
|
|
for i2 in range(top_n):
|
|
top_node: ast.AST* = tree.children.get(i2)
|
|
if isinstance(top_node, ast.Stmt) == 0:
|
|
top_ok = 0
|
|
printf("FAIL: top-level node not Stmt: %s\n", top_node.type_name())
|
|
if top_ok != 0:
|
|
printf("PASS: all %lld top-level nodes are Stmt\n", top_n)
|
|
|
|
# Assert 3: Assign is Stmt, not ExprNode
|
|
first_assign: ast.Assign* = (ast.Assign*)(tree.children.get(0))
|
|
if isinstance(first_assign, ast.Stmt) != 0 and isinstance(first_assign, ast.ExprNode) == 0:
|
|
printf("PASS: Assign is Stmt, not ExprNode\n")
|
|
else:
|
|
printf("FAIL: Assign classification wrong\n")
|
|
|
|
# Assert 4: Assign.value (an expression) is ExprNode, not Stmt
|
|
if isinstance(first_assign.value, ast.ExprNode) != 0 \
|
|
and isinstance(first_assign.value, ast.Stmt) == 0:
|
|
printf("PASS: Assign.value is ExprNode (%s)\n", first_assign.value.type_name())
|
|
else:
|
|
printf("FAIL: Assign.value not ExprNode\n")
|
|
|
|
# Assert 5: expression statement Expr (e.g. print(x)) is a Stmt
|
|
if top_n >= 15:
|
|
if_node: ast.If* = (ast.If*)(tree.children.get(14))
|
|
if if_node.children is not None and if_node.children.__len__() > 0:
|
|
expr_stmt: ast.Expr* = (ast.Expr*)(if_node.children.get(0))
|
|
if isinstance(expr_stmt, ast.Stmt) != 0 and isinstance(expr_stmt, ast.ExprNode) == 0:
|
|
printf("PASS: Expr(stmt) is Stmt, not ExprNode (%s)\n", expr_stmt.type_name())
|
|
else:
|
|
printf("FAIL: Expr(stmt) classification wrong\n")
|
|
printf("=== END RUNTIME TEST ===\n")
|
|
|
|
stdlib.free(pool_mem)
|
|
stdlib.free(dump_mem)
|
|
return 0
|