Files
Viper_VRTE/ast/tokens.vp
T
TermiNexus 0055ab7ea7 refactor: rawptr[T] -> T* 与 t.CConst 前缀迁移(R14/R16)
R14:rawptr[X] -> X*(41 文件 / 2037 行改动),X 含模块限定名与嵌套下标(rawptr[GSList[NameEntry]] -> GSList[NameEntry]*)。保留 6 处 rawptr[t.CPtr](规范例外:存纯裸指针);# 注释与字符串里的 rawptr[...] 属元语法描述,未动。 R16(保守子集):t.CConst | str/bytes -> const str/bytes,t.CConst | char | t.CPtr -> const str;typedef 右值位置豁免(const str 在该处无法注册,与 R1 同源)。 验证:TPC 自举三重重编 SUCCESS;TransPyV.exe 全量 77/77 顺序回归通过。
2026-10-05 17:38:21 +08:00

540 lines
19 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import memhub
import string
import stdio
import stdlib
def _ast_pool_fatal(what: str, need: sizet):
"""Unified fail-fast when a pool alloc fails (returns NULL).
Reports the real cause (pool exhausted) and terminates instead of leaving NULL str_val / dummy tokens.
"""
stdio.printf("[AST] %s 分配失败:内存池已耗尽(alloc 返回 NULL,need=%zu 字节)"
"——上游有块被写穿/丢失,见 [MEM] 报告\n", what, need)
stdio.fflush(None)
stdlib.exit(1)
# ============================================================
# Token type enumeration (refer to CPython Lib/token.py)
# Organized as a type-safe enum using t.CEnum; old names XXX retained as compatible aliases
# ============================================================
enum TokenType:
EndMarker
NewLine
Indent
Dedent
Name
Number
String
FStringStart
FStringMiddle
FStringEnd
Op
Nl
Comment
Encoding
# ============================================================
# Keyword enumeration (refer to CPython Lib/keyword.py)
# Organized as a type-safe enum using t.CEnum; old names KW_XXX are kept as compatible aliases
# NotKeyword(0) represents a non-keyword (sentinel to avoid conflict between False_=0 and kw_subtype=0)
# ============================================================
enum Keyword:
NotKeyword
False_
None_
True_
And
As
Assert
Async
Await
Break
Class
Continue
Def
Del
Elif
Else
Except
Finally
For
From
Global
If
Import
In
Is
Lambda
Nonlocal
Not
Or
Pass
Raise
Return
Try
While
With
Yield
Match
Case
Type
# def alias keywords (fn/fun/func/function, identical semantics to def)
Fn
Fun
Func
Function
# extern function declaration keyword (FFI, equivalent to t.State; soft test in _is_funcdef_kw)
Extern
# export function declaration keyword (equivalent to t.CExport; soft test in _is_funcdef_kw)
Export
# define macro-function keyword (same semantics as def, return annotation gets t.CDefine;
# soft test in _is_funcdef_kw)
Define
# enum definition keyword (sugar: `enum X:` == `class X(t.CEnum):`, see _parse_enumdef).
# Appended at the enum tail so existing kw_subtype values stay unchanged.
Enum
# typedef alias keyword (sugar: `typedef T1 = T2` == `T1: t.CTypedef = T2`, see _parse_typedef).
# Appended at the enum tail: existing kw_subtype values unchanged.
Typedef
# const/volatile descriptor keywords (sugar: `a: const int` == `a: t.CConst | int`, _parse_type_ann),
# appended at the enum tail. Soft keywords: only in annotation position; `t.attr.const()` unaffected.
Const
Volatile
# virtual/novirtual modifier keywords (sugar for @t.CVTable / @t.NoVTable, _ParseVirtualModified),
# appended at the enum tail. Soft keywords: only at statement start before class/def; identifiers unaffected.
Virtual
Novirtual
# union definition keyword (sugar: `union X:` == `class X(t.CUnion):`, see _parse_uniondef),
# appended at the enum tail; `union` is never used as an identifier in the repo.
Union
# renum variant-type keyword (sugar: `renum X:` == `class X(t.REnum):`, see _parse_renumdef),
# appended at the enum tail; `renum` is never used as an identifier in the repo.
Renum
# struct data-type keyword (sugar: `struct X:` == `class X(t.CStruct):` + @t.NoVTable, _parse_structdef),
# appended at the enum tail. Data-only: no virtual/novirtual, no def in body; never an identifier.
Struct
# switch statement keyword (`switch <expr>:` + `case`/`default`, see _parse_switch), appended
# at the enum tail. C switch semantics: no fall-through unless `fallthrough` is written.
Switch
# fallthrough statement keyword (legal only inside a switch case body; error inside match),
# appended at the enum tail.
FallThrough
# ============================================================
# Operator/Separator Enumeration
# Organized as a type-safe enum using t.CEnum; old names OP_XXX retained as compatible aliases
# Stored in the token.op_subtype field
# ============================================================
enum TokOp:
LPar # (
RPar # )
Lsqb # [
Rsqb # ]
LBrace # {
RBrace # }
Comma # ,
Colon # :
Dot # .
Semi # ;
At # @
Equal # =
RArrow # ->
PlusEq # +=
MinusEq # -=
StarEq # *=
SlashEq # /=
DSlashEq # //=
PercentEq # %=
AtEq # @=
AmpEq # &=
VBarEq # |=
CaretEq # ^=
GtGtEq # >>=
LtLtEq # <<=
StarEqEq # **=
DSlash # //
StarStar # **
LtLt # <<
GtGt # >>
LessEq # <=
GreaterEq # >=
EqEq # ==
ExclaimEq # !=
Less # <
Greater # >
Plus # +
Minus # -
Star # *
Slash # /
Percent # %
Amp # &
VBar # |
Caret # ^
Tilde # ~
ColonEq # :=
Ellipsis # ...
Bang # !(Non-standard, used for parsing tolerance)
# ============================================================
# Token stucture
# ============================================================
class Token:
type: int # Token type (TokenType.EndMarker / TokenType.NewLine / TokenType.Name / ...)
op_subtype: int # Operator subtype (OP_*). Only valid when type == TokenType.Op
kw_subtype: int # Keyword subtype (KW_*). Only valid when type == TokenType.Name and it is a keyword
str_val: str # Token text (NUL‑terminated, allocated from pool)
int_val: int64 # Integer value (used by TokenType.Number)
float_val: double # Floating‑point value (used by TokenType.Number)
is_float: int # 1 = floating‑point number, 0 = integer
is_complex: int # 1 = complex number, 0 = real number
lineno: int # Starting line number
col_offset: int # Starting column offset
end_lineno: int # Ending line number
end_col_offset: int # Ending column offset
next: Token* # Next token in linked list
def __new__(self, pool: memhub.MemManager*):
"""Allocate zero‑initialized Token struct memory from mpool"""
ptr: Token* = pool.alloc(Token.__sizeof__())
if ptr == None:
_ast_pool_fatal("Token", sizet(Token.__sizeof__()))
string.memset(ptr, 0, Token.__sizeof__())
return ptr
# ============================================================
# Keyword lookup table
# Since TransPyC has no dict literals, implemented with linked lists
# ============================================================
class KwEntry:
name: str
kw_id: int
next: KwEntry*
def __new__(self, pool: memhub.MemManager*):
ptr: KwEntry* = pool.alloc(KwEntry.__sizeof__())
if ptr == None:
_ast_pool_fatal("KwEntry", sizet(KwEntry.__sizeof__()))
string.memset(ptr, 0, KwEntry.__sizeof__())
return ptr
# Global keyword linked list head (lazy initialization)
_kw_head: KwEntry* = None
_kw_pool: memhub.MemManager* = None
# Keyword hash index (open addressing, linear probing): slot u64, 0 = empty else KwEntry address;
# expected O(1) lookups, falling back to the list only when not built (alloc failure). Miss returns 0.
_kw_index: sizet* = None
# Index slot count (power of two > 2*46, load < 0.5 so probing always terminates)
define _KW_IDX_CAP = 128
# FNV-1a 64-bit constants (same as hashtable.vp)
_KW_FNV_OFFSET: uint64 = 0xcbf29ce484222325
_KW_FNV_PRIME: uint64 = 0x100000001b3
def _kw_hash_str(key: str) -> uint64:
"""FNV-1a 64-bit hash of a keyword name (same key -> same value; used only for slot location)"""
h: uint64 = _KW_FNV_OFFSET
k: str = key
while k[0] != 0:
h = h ^ uint64(k[0])
h = h * _KW_FNV_PRIME
k += 1
return uint64(h)
def _kw_index_put(entry: KwEntry*):
"""Write a keyword entry into the hash index (silently skipped if not built); a duplicate key keeps
the first registrant, and collisions are separated by strcmp (hash alone could return a wrong kw_id)."""
if _kw_index == None: return
h: uint64 = _kw_hash_str(entry.name)
mask: sizet = sizet(_KW_IDX_CAP - 1)
idx: sizet = sizet(h) & mask
for i in range(_KW_IDX_CAP):
slot: sizet* = (sizet*)(t.CPtr(uint64(_kw_index) + uint64(idx * 8)))
cur_s: sizet = slot[0]
if cur_s == 0:
slot[0] = sizet(entry)
return
cur: KwEntry* = (KwEntry*)(t.CPtr(uint64(cur_s)))
if string.strcmp(cur.name, entry.name) == 0:
return
idx = (idx + 1) & mask
def _kw_intern(pool: memhub.MemManager*, name: str, kw_id: int):
"""Register keyword name in the linked list"""
global _kw_head
entry: KwEntry* = KwEntry(pool)
if entry == None: return
slen: sizet = string.strlen(name)
buf: str = pool.alloc(slen + 1)
if buf == None: return
string.memcpy(buf, name, slen + 1)
entry.name = buf
entry.kw_id = kw_id
entry.next = _kw_head
_kw_head = entry
# Also write to the hash index (the linked list above is still the single source of truth)
_kw_index_put(entry)
def _kw_lookup(name: str) -> int:
"""Look up keyword name, return the kw_id (0 if not a keyword).
Fast path: hash index (expected O(1), no allocation); falls back to the linked-list scan if not built.
"""
if name == None:
# Defensive: a NULL str_val would crash _kw_hash_str and mask the real cause (pool exhausted).
stdio.printf("[AST] _kw_lookup 收到 NULL 名称:token.str_val 未分配成功(内存池耗尽 / 未 set)\n")
stdio.fflush(None)
stdlib.exit(1)
if _kw_index != None:
h: uint64 = _kw_hash_str(name)
mask: sizet = sizet(_KW_IDX_CAP - 1)
idx: sizet = sizet(h) & mask
for i in range(_KW_IDX_CAP):
slot: sizet* = (sizet*)(t.CPtr(uint64(_kw_index) + uint64(idx * 8)))
cur_s: sizet = slot[0]
if cur_s == 0:
return 0
cur: KwEntry* = (KwEntry*)(t.CPtr(uint64(cur_s)))
if string.strcmp(cur.name, name) == 0:
return cur.kw_id
idx = (idx + 1) & mask
return 0
cur2: KwEntry* = _kw_head
while cur2 != None:
if string.strcmp(cur2.name, name) == 0:
return cur2.kw_id
cur2 = cur2.next
return 0
def _init_keywords(pool: memhub.MemManager*):
"""Initialize keyword linked list (only once)"""
global _kw_head, _kw_pool, _kw_index
if _kw_head != None: return
_kw_pool = pool
# Allocate the index slot array once; on failure _kw_index stays None -> lookups fall back to the list
idx_buf: sizet* = pool.alloc(_KW_IDX_CAP * 8)
if idx_buf != None:
string.memset(idx_buf, 0, _KW_IDX_CAP * 8)
_kw_index = idx_buf
_kw_intern(pool, "False", Keyword.False_)
_kw_intern(pool, "None", Keyword.None_)
_kw_intern(pool, "True", Keyword.True_)
_kw_intern(pool, "and", Keyword.And)
_kw_intern(pool, "as", Keyword.As)
_kw_intern(pool, "assert", Keyword.Assert)
_kw_intern(pool, "async", Keyword.Async)
_kw_intern(pool, "await", Keyword.Await)
_kw_intern(pool, "break", Keyword.Break)
_kw_intern(pool, "class", Keyword.Class)
_kw_intern(pool, "continue", Keyword.Continue)
_kw_intern(pool, "def", Keyword.Def)
_kw_intern(pool, "del", Keyword.Del)
_kw_intern(pool, "elif", Keyword.Elif)
_kw_intern(pool, "else", Keyword.Else)
_kw_intern(pool, "except", Keyword.Except)
_kw_intern(pool, "finally", Keyword.Finally)
_kw_intern(pool, "for", Keyword.For)
_kw_intern(pool, "from", Keyword.From)
_kw_intern(pool, "global", Keyword.Global)
_kw_intern(pool, "if", Keyword.If)
_kw_intern(pool, "import", Keyword.Import)
_kw_intern(pool, "in", Keyword.In)
_kw_intern(pool, "is", Keyword.Is)
_kw_intern(pool, "lambda", Keyword.Lambda)
_kw_intern(pool, "nonlocal", Keyword.Nonlocal)
_kw_intern(pool, "not", Keyword.Not)
_kw_intern(pool, "or", Keyword.Or)
_kw_intern(pool, "pass", Keyword.Pass)
_kw_intern(pool, "raise", Keyword.Raise)
_kw_intern(pool, "return", Keyword.Return)
_kw_intern(pool, "try", Keyword.Try)
_kw_intern(pool, "while", Keyword.While)
_kw_intern(pool, "with", Keyword.With)
_kw_intern(pool, "yield", Keyword.Yield)
_kw_intern(pool, "match", Keyword.Match)
_kw_intern(pool, "case", Keyword.Case)
_kw_intern(pool, "type", Keyword.Type)
# def alias keywords (fn/fun/func/function, identical semantics to def)
_kw_intern(pool, "fn", Keyword.Fn)
_kw_intern(pool, "fun", Keyword.Fun)
_kw_intern(pool, "func", Keyword.Func)
_kw_intern(pool, "function", Keyword.Function)
_kw_intern(pool, "extern", Keyword.Extern)
_kw_intern(pool, "export", Keyword.Export)
_kw_intern(pool, "define", Keyword.Define)
_kw_intern(pool, "enum", Keyword.Enum)
_kw_intern(pool, "typedef", Keyword.Typedef)
# Type-descriptor soft keywords (annotation position only, see _parse_type_ann)
_kw_intern(pool, "const", Keyword.Const)
_kw_intern(pool, "volatile", Keyword.Volatile)
# virtual/novirtual modifier soft keywords (statement start before class/def only,
# see _ParseVirtualModified)
_kw_intern(pool, "virtual", Keyword.Virtual)
_kw_intern(pool, "novirtual", Keyword.Novirtual)
_kw_intern(pool, "union", Keyword.Union)
# renum variant-type keyword (sugar, same as enum/union; see _parse_renumdef)
_kw_intern(pool, "renum", Keyword.Renum)
# struct data-type keyword (sugar, same as enum/union/renum; see _parse_structdef:
# base t.CStruct + pseudo decorator @t.NoVTable, no virtual/novirtual and no def)
_kw_intern(pool, "struct", Keyword.Struct)
# switch / fallthrough keywords (see _parse_switch and _parse_small_stmt)
_kw_intern(pool, "switch", Keyword.Switch)
_kw_intern(pool, "fallthrough", Keyword.FallThrough)
# ============================================================
# Operator lookup table
# Since TransPyC doesn't have dict, use an array with linear search
# Operators are sorted by descending length for greedy matching
# ============================================================
class OpEntry:
name: str # Operator text (e.g. "==", "+=")
op_id: int
length: int
next: OpEntry*
def __new__(self, pool: memhub.MemManager*):
ptr: OpEntry* = pool.alloc(OpEntry.__sizeof__())
if ptr: string.memset(ptr, 0, OpEntry.__sizeof__())
return ptr
_op_head: OpEntry* = None
def _op_intern(pool: memhub.MemManager*, name: str, op_id: int):
global _op_head
slen: sizet = string.strlen(name)
entry: OpEntry* = OpEntry(pool)
if entry == None: return
buf: str = pool.alloc(slen + 1)
if buf == None: return
string.memcpy(buf, name, slen + 1)
entry.name = buf
entry.op_id = op_id
entry.length = int(slen)
entry.next = _op_head
_op_head = entry
def _init_operators(pool: memhub.MemManager*):
"""Initialize the operator table (register in descending length order for easy greedy matching)"""
global _op_head
if _op_head != None: return
# 3-character operators
_op_intern(pool, "**=", TokOp.StarEqEq)
_op_intern(pool, "//=", TokOp.DSlashEq)
_op_intern(pool, ">>=", TokOp.GtGtEq)
_op_intern(pool, "<<=", TokOp.LtLtEq)
_op_intern(pool, "...", TokOp.Ellipsis)
# 2-character operators
_op_intern(pool, "->", TokOp.RArrow)
_op_intern(pool, "+=", TokOp.PlusEq)
_op_intern(pool, "-=", TokOp.MinusEq)
_op_intern(pool, "*=", TokOp.StarEq)
_op_intern(pool, "/=", TokOp.SlashEq)
_op_intern(pool, "%=", TokOp.PercentEq)
_op_intern(pool, "@=", TokOp.AtEq)
_op_intern(pool, "&=", TokOp.AmpEq)
_op_intern(pool, "|=", TokOp.VBarEq)
_op_intern(pool, "^=", TokOp.CaretEq)
_op_intern(pool, "//", TokOp.DSlash)
_op_intern(pool, "**", TokOp.StarStar)
_op_intern(pool, "<<", TokOp.LtLt)
_op_intern(pool, ">>", TokOp.GtGt)
_op_intern(pool, "<=", TokOp.LessEq)
_op_intern(pool, ">=", TokOp.GreaterEq)
_op_intern(pool, "==", TokOp.EqEq)
_op_intern(pool, "!=", TokOp.ExclaimEq)
_op_intern(pool, ":=", TokOp.ColonEq)
# 1-character operators
_op_intern(pool, "(", TokOp.LPar)
_op_intern(pool, ")", TokOp.RPar)
_op_intern(pool, "[", TokOp.Lsqb)
_op_intern(pool, "]", TokOp.Rsqb)
_op_intern(pool, "{", TokOp.LBrace)
_op_intern(pool, "}", TokOp.RBrace)
_op_intern(pool, ",", TokOp.Comma)
_op_intern(pool, ":", TokOp.Colon)
_op_intern(pool, ".", TokOp.Dot)
_op_intern(pool, ";", TokOp.Semi)
_op_intern(pool, "@", TokOp.At)
_op_intern(pool, "=", TokOp.Equal)
_op_intern(pool, "<", TokOp.Less)
_op_intern(pool, ">", TokOp.Greater)
_op_intern(pool, "+", TokOp.Plus)
_op_intern(pool, "-", TokOp.Minus)
_op_intern(pool, "*", TokOp.Star)
_op_intern(pool, "/", TokOp.Slash)
_op_intern(pool, "%", TokOp.Percent)
_op_intern(pool, "&", TokOp.Amp)
_op_intern(pool, "|", TokOp.VBar)
_op_intern(pool, "^", TokOp.Caret)
_op_intern(pool, "~", TokOp.Tilde)
def _init_tables(pool: memhub.MemManager*):
"""Initialize all lookup tables (idempotent)"""
_init_keywords(pool)
_init_operators(pool)
# ============================================================
# Token creation helper functions
# ============================================================
def new_token(pool: memhub.MemManager*, ttype: int, lineno: int,
col_offset: int) -> Token*:
"""Create a new empty Token and fill in fields later"""
tok: Token* = Token(pool)
if tok == None: return None
tok.type = ttype
tok.lineno = lineno
tok.col_offset = col_offset
tok.end_lineno = lineno
tok.end_col_offset = col_offset
return tok
def token_set_str(pool: memhub.MemManager*, tok: Token*,
src: str, start: sizet, length: sizet):
"""Copy text from src[start:start + length] to token.str_val
Note: When length == 0, still allocate 1 byte for NUL, so that an empty string is distinguished from a NULL pointer.
"""
if tok == None: return
buf: str = pool.alloc(length + 1)
if buf == None:
_ast_pool_fatal("token.str_val", length + 1)
for i in range(length):
buf[i] = src[start + i]
buf[length] = '\0'
tok.str_val = buf
def token_set_str_literal(pool: memhub.MemManager*, tok: Token*,
text: str):
"""Copy a C string to token.str_val"""
if tok == None or text == None: return
slen: sizet = string.strlen(text)
buf: str = pool.alloc(slen + 1)
if buf == None:
_ast_pool_fatal("token.str_val(字面量)", slen + 1)
string.memcpy(buf, text, slen + 1)
tok.str_val = buf