diff --git a/_withcontent.vp b/_withcontent.vp index 9187494..7151026 100644 --- a/_withcontent.vp +++ b/_withcontent.vp @@ -22,8 +22,7 @@ class _ProviderStack: _tls_index: ulong = 0 -@t.TLS -def _init_tls(): +tls def _init_tls(): """Initialize TLS index (executes once; subsequent calls are skipped).""" global _tls_index # Double guard: VPC now implements @t.TLS (a per-function one-shot guard setting __tls_init_done), but this re-check is kept for compatibility with older VPC.exe builds. diff --git a/ast/parser.vp b/ast/parser.vp index 8a2bc2b..9b286fa 100644 --- a/ast/parser.vp +++ b/ast/parser.vp @@ -688,6 +688,66 @@ def _ParseInlineModified(ps: Parser*, parent: AST*): parent.append(stmt) +def _IsTlsModifier(ps: Parser*) -> int: + """Whether the current token is a `tls` modifier before a function definition, 1 = yes / 0 = no. + + Soft keyword: matches only at statement start with a following def (or the fn/fun/func/function + alias + '(' / '['), so `tls: local = local()` / `tls.set(...)` / `tls.get()` stay plain identifiers. + """ + if _cur_type(ps) != TokenType.Name: + return 0 + if _cur_kw(ps) != Keyword.Tls: + return 0 + nxt: Token* = ps.cur.next + if nxt is None or nxt.type != TokenType.Name: + return 0 + if nxt.kw_subtype == Keyword.Def: + return 1 + nxt2: Token* = nxt.next + if nxt.kw_subtype == Keyword.Fn or nxt.kw_subtype == Keyword.Fun \ + or nxt.kw_subtype == Keyword.Func or nxt.kw_subtype == Keyword.Function: + if nxt2 is not None and nxt2.type == TokenType.Op \ + and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb): + return 1 + return 0 + + +def _MakeTlsMarker(ps: Parser*) -> AST*: + """Build the marker node Attribute(Name('t'), 'TLS') from the current tls token. + + Field-identical to the hand-written @t.TLS decorator, so FuncGenDef / ClassGenBase's existing + _has_decorator("TLS") one-shot-init path is reused without any new test. + """ + marker: AST* = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), + "TLS", ASTCtx.Load) + _set_pos(marker, _cur_lineno(ps), _cur_col(ps), 0, 0) + return marker + + +def _ParseTlsModified(ps: Parser*, parent: AST*): + """`tls def f(...)`: consume tls, synthesize @t.TLS and hand off to _parse_funcdef. + + Same pending_decorators channel as _ParseVirtualModified / _ParseInlineModified, so + `@deco` + `tls def` merge and position does not matter. + """ + marker: AST* = _MakeTlsMarker(ps) + if marker is None: + return + _advance(ps) # consume the tls modifier + merged: list[AST*]* = list[AST*](ps.pool, 8) + if merged is None: + return + if ps.pending_decorators is not None: + dn: sizet = len(ps.pending_decorators) + for di in range(int(dn)): + merged.append(ps.pending_decorators.get(di)) + merged.append(marker) + ps.pending_decorators = merged + stmt: AST* = _parse_funcdef(ps, 0) + if stmt is not None: + parent.append(stmt) + + def _IsStaticModifier(ps: Parser*) -> int: """Whether the current token is a `static` modifier (soft keyword), 1 = yes / 0 = no. @@ -2868,6 +2928,11 @@ def _parse_statement_into(ps: Parser*, parent: AST*): if _IsInlineModifier(ps) != 0: _ParseInlineModified(ps, parent) return + # tls modifier (soft keyword): statement start + tls + def; synthesizes @t.TLS and reuses the + # existing one-shot-init codegen path (FuncGenDef / ClassGenBase _has_decorator("TLS")). + if _IsTlsModifier(ps) != 0: + _ParseTlsModified(ps, parent) + return # static modifier (soft keyword): statement start + static + (def | `Name :`). The function branch # synthesizes @t.CStatic (returns); the variable branch flags pending_static for _parse_type_ann. if _IsStaticModifier(ps) != 0: diff --git a/ast/tokens.vp b/ast/tokens.vp index e9f5ad2..6180043 100644 --- a/ast/tokens.vp +++ b/ast/tokens.vp @@ -134,6 +134,10 @@ enum Keyword: # `t.attr.bigend()` and identifiers named bigend/littlend are unaffected. BigEnd LittleEnd + # tls modifier keyword (sugar for @t.TLS, see _ParseTlsModified), appended at the enum tail so + # existing kw_subtype values stay unchanged. Soft keyword: only at statement start before a + # function definition; identifiers named tls (e.g. a `vthreading.local` variable) are unaffected. + Tls # ============================================================ @@ -401,6 +405,8 @@ def _init_keywords(pool: memhub.MemManager*): # see _ParseVirtualModified) _kw_intern(pool, "virtual", Keyword.Virtual) _kw_intern(pool, "novirtual", Keyword.Novirtual) + # tls modifier soft keyword (statement start before def only, see _ParseTlsModified) + _kw_intern(pool, "tls", Keyword.Tls) _kw_intern(pool, "union", Keyword.Union) # renum variant-type keyword (sugar, same as enum/union; see _parse_renumdef) _kw_intern(pool, "renum", Keyword.Renum)