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 顺序回归通过。
540 lines
19 KiB
Plaintext
540 lines
19 KiB
Plaintext
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
|