refactor(tpc): 删除类型联合语法并将修饰迁移为 AST 字段
类型注解不再接受任何 T1 | T2;const/volatile/static/export/extern/inline/define/typedef/__attribute__/BigEndian/LittleEndian/Bit 改为注解节点字段 Markers/Attributes/ByteOrder/BitWidth;指针与引用改为 CPtr[T]/CRef[T]/CTempRef[T]/CArrayPtr[T]/CAutoPtr[T];Python 与 vast_c 两套解析器、类型解析与声明端同步;C 扩展早于 vast_c.c 时自动回退纯 Python 解析器。
This commit is contained in:
1 parent
3269775945
commit
660a15bef8
19 files changed
+1312
-1576
No files matched your search
+322
-83
@@ -2557,27 +2557,33 @@ static PyObject *ParseArith(Parser *P) {
|
||||
|
||||
/* ============ C 风格指针后缀(* / **,对齐 vast ExprParser) ============ */
|
||||
|
||||
/* 构造一层指针包装 base | t.CPtr(对齐 Python _MakePtrType)。 */
|
||||
static PyObject *MakePtrType(Parser *P, PyObject *Base, Token *Tok) {
|
||||
PyObject *CPtr = MakeTAttrNode(P, "CPtr");
|
||||
if (CPtr == NULL) {
|
||||
/* 构造类型包装 Subscript(Name(WrapperName), Inner):CPtr[T] / CRef[T] / CTempRef[T]。 */
|
||||
static PyObject *MakeTypeWrapperSubscript(Parser *P, const char *WrapperName,
|
||||
PyObject *Inner, Token *Tok) {
|
||||
PyObject *Wrapper = MakeTAttrNode(P, WrapperName);
|
||||
if (Wrapper == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *Op = MakeSingleton(P, "BitOr");
|
||||
if (Op == NULL) {
|
||||
Py_DECREF(CPtr);
|
||||
PyObject *Load = MakeCtx(P, "Load");
|
||||
if (Load == NULL) {
|
||||
Py_DECREF(Wrapper);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *Argv[3] = { Base, Op, CPtr };
|
||||
PyObject *Node = NewNode(P, "BinOp", Argv, 3);
|
||||
Py_DECREF(Op);
|
||||
Py_DECREF(CPtr);
|
||||
PyObject *Argv[3] = { Wrapper, Inner, Load };
|
||||
PyObject *Node = NewNode(P, "Subscript", Argv, 3);
|
||||
Py_DECREF(Wrapper);
|
||||
Py_DECREF(Load);
|
||||
if (Node != NULL) {
|
||||
FixLoc(P, Node, Tok);
|
||||
}
|
||||
return Node;
|
||||
}
|
||||
|
||||
/* 构造一层指针包装 CPtr[T](对齐 Python _MakePtrType)。 */
|
||||
static PyObject *MakePtrType(Parser *P, PyObject *Base, Token *Tok) {
|
||||
return MakeTypeWrapperSubscript(P, "CPtr", Base, Tok);
|
||||
}
|
||||
|
||||
/* 判定当前位置的 '*'/'**' 串是否为 C 风格指针后缀(作用于"类型原子"节点)。
|
||||
*
|
||||
* 判据(两条同时成立,对齐 Python ExprParser._AtPtrSuffix):
|
||||
@@ -2712,13 +2718,295 @@ static PyObject *ConsumePtrSuffixCall(Parser *P, PyObject *Base, Token *Start) {
|
||||
return Call;
|
||||
}
|
||||
|
||||
/* ============ 类型注解修饰前缀(const/volatile/... / __attribute__ / 字节序) ============ */
|
||||
|
||||
static int IsSpaceByte(char C) {
|
||||
return C == ' ' || C == '\t' || C == '\r' || C == '\n';
|
||||
}
|
||||
|
||||
/* 追加一个字符串到惰性创建的 Python list;失败返回 0。 */
|
||||
static int AppendStringItem(PyObject **List, const char *Text, Py_ssize_t Len) {
|
||||
if (*List == NULL) {
|
||||
*List = NewList();
|
||||
if (*List == NULL) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
PyObject *Item = PyUnicode_FromStringAndSize(Text, Len);
|
||||
if (Item == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int Rc = PyList_Append(*List, Item);
|
||||
Py_DECREF(Item);
|
||||
return Rc == 0;
|
||||
}
|
||||
|
||||
/* 追加单个 __attribute__ 属性(去掉首尾空白)。 */
|
||||
static int AppendAttributeSlice(PyObject **List, const char *Text, Py_ssize_t Len) {
|
||||
while (Len > 0 && IsSpaceByte(*Text)) {
|
||||
Text++;
|
||||
Len--;
|
||||
}
|
||||
while (Len > 0 && IsSpaceByte(Text[Len - 1])) {
|
||||
Len--;
|
||||
}
|
||||
if (Len == 0) {
|
||||
return 1;
|
||||
}
|
||||
return AppendStringItem(List, Text, Len);
|
||||
}
|
||||
|
||||
/* 从 `__attribute__((...))` 源码片段提取属性列表,按顶层逗号拆分后写入 List。
|
||||
* 例:__attribute__((section(".x"), aligned(16))) -> ["section(\".x\")", "aligned(16)"] */
|
||||
static int AppendAttributesFromText(PyObject **List, const char *Text, Py_ssize_t Len) {
|
||||
Py_ssize_t I = 0;
|
||||
while (I < Len && Text[I] != '(') {
|
||||
I++;
|
||||
}
|
||||
if (I >= Len) {
|
||||
return 1;
|
||||
}
|
||||
I++; /* 跳过 __attribute__ 后的 '(' */
|
||||
Py_ssize_t J = Len;
|
||||
while (J > I && Text[J - 1] != ')') {
|
||||
J--;
|
||||
}
|
||||
if (J <= I) {
|
||||
return 1;
|
||||
}
|
||||
J--; /* 去掉与之配对的 ')' */
|
||||
while (I < J && IsSpaceByte(Text[I])) {
|
||||
I++;
|
||||
}
|
||||
while (J > I && IsSpaceByte(Text[J - 1])) {
|
||||
J--;
|
||||
}
|
||||
/* GNU 双括号形式 __attribute__((...)):剥掉内层一对括号 */
|
||||
if (J - I >= 2 && Text[I] == '(' && Text[J - 1] == ')') {
|
||||
I++;
|
||||
J--;
|
||||
}
|
||||
Py_ssize_t Start = I;
|
||||
int Depth = 0;
|
||||
char Quote = 0;
|
||||
for (Py_ssize_t K = I; K < J; K++) {
|
||||
char C = Text[K];
|
||||
if (Quote != 0) {
|
||||
if (C == '\\' && K + 1 < J) {
|
||||
K++;
|
||||
} else if (C == Quote) {
|
||||
Quote = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (C == '"' || C == '\'') {
|
||||
Quote = C;
|
||||
} else if (C == '(' || C == '[' || C == '{') {
|
||||
Depth++;
|
||||
} else if (C == ')' || C == ']' || C == '}') {
|
||||
if (Depth > 0) {
|
||||
Depth--;
|
||||
}
|
||||
} else if (C == ',' && Depth == 0) {
|
||||
if (!AppendAttributeSlice(List, Text + Start, K - Start)) {
|
||||
return 0;
|
||||
}
|
||||
Start = K + 1;
|
||||
}
|
||||
}
|
||||
return AppendAttributeSlice(List, Text + Start, J - Start);
|
||||
}
|
||||
|
||||
/* 下一 token 能否作为类型起点(前缀修饰后必须仍跟着实际类型)。 */
|
||||
static int TokStartsType(Parser *P, int Off) {
|
||||
Token *T = PeekTok(P, Off);
|
||||
if (T->Kind == TK_NAME) {
|
||||
return 1;
|
||||
}
|
||||
return T->Kind == TK_OP
|
||||
&& (TokIsOp(T, "&") || TokIsOp(T, "[") || TokIsOp(T, "("));
|
||||
}
|
||||
|
||||
/* 消费注解前缀:const/volatile/static/export/extern/inline/state/define/typedef、
|
||||
* __attribute__((...))、BigEndian/bigend、LittleEndian/littlend。
|
||||
* 裸标记名(后面没有类型)不消费,按普通类型名解析。 */
|
||||
static int ParseTypePrefixes(Parser *P, PyObject **Markers, PyObject **Attributes,
|
||||
const char **ByteOrder) {
|
||||
for (;;) {
|
||||
Token *T = PeekTok(P, 0);
|
||||
if (T->Kind != TK_NAME || !TokStartsType(P, 1)) {
|
||||
break;
|
||||
}
|
||||
if (TokIsName(T, "__attribute__")) {
|
||||
if (!TokIsOp(PeekTok(P, 1), "(")) {
|
||||
break;
|
||||
}
|
||||
Token *AttrTok = NextTok(P);
|
||||
const char *End = NULL;
|
||||
int Depth = 0;
|
||||
for (;;) {
|
||||
Token *U = PeekTok(P, 0);
|
||||
if (U->Kind == TK_ENDMARKER) {
|
||||
SyntaxErrorAt(P, U, "`__attribute__(...)` 括号未闭合");
|
||||
return 0;
|
||||
}
|
||||
NextTok(P);
|
||||
if (TokIsOp(U, "(")) {
|
||||
Depth++;
|
||||
} else if (TokIsOp(U, ")")) {
|
||||
Depth--;
|
||||
if (Depth == 0) {
|
||||
End = U->End;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!AppendAttributesFromText(Attributes, AttrTok->Start, End - AttrTok->Start)) {
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
static const char *MarkerNames[] = {
|
||||
"const", "volatile", "static", "export", "extern",
|
||||
"inline", "state", "define", "typedef"
|
||||
};
|
||||
const char *Marker = NULL;
|
||||
for (size_t I = 0; I < sizeof(MarkerNames) / sizeof(MarkerNames[0]); I++) {
|
||||
if (TokIsName(T, MarkerNames[I])) {
|
||||
Marker = MarkerNames[I];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Marker != NULL) {
|
||||
if (!AppendStringItem(Markers, Marker, (Py_ssize_t)strlen(Marker))) {
|
||||
return 0;
|
||||
}
|
||||
NextTok(P);
|
||||
continue;
|
||||
}
|
||||
if (TokIsName(T, "BigEndian") || TokIsName(T, "bigend")) {
|
||||
*ByteOrder = "big";
|
||||
NextTok(P);
|
||||
continue;
|
||||
}
|
||||
if (TokIsName(T, "LittleEndian") || TokIsName(T, "littlend")) {
|
||||
*ByteOrder = "little";
|
||||
NextTok(P);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 名称或属性名是否等于 Name(Name.id / Attribute.attr)。 */
|
||||
static int IsNameOrAttrNamed(PyObject *Node, const char *Name) {
|
||||
const char *Field = NodeIs(Node, "Name") ? "id"
|
||||
: (NodeIs(Node, "Attribute") ? "attr" : NULL);
|
||||
if (Field == NULL) {
|
||||
return 0;
|
||||
}
|
||||
PyObject *V = PyObject_GetAttrString(Node, Field);
|
||||
if (V == NULL) {
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
int Hit = PyUnicode_Check(V) && PyUnicode_CompareWithASCIIString(V, Name) == 0;
|
||||
Py_DECREF(V);
|
||||
return Hit;
|
||||
}
|
||||
|
||||
/* Constant 节点的整数值;非整数返回 0。 */
|
||||
static int ConstantIntValue(PyObject *Node, long *Out) {
|
||||
if (Node == NULL || !NodeIs(Node, "Constant")) {
|
||||
return 0;
|
||||
}
|
||||
PyObject *V = PyObject_GetAttrString(Node, "value");
|
||||
if (V == NULL) {
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
int Ok = 0;
|
||||
if (PyLong_Check(V)) {
|
||||
long L = PyLong_AsLong(V);
|
||||
if (L == -1 && PyErr_Occurred()) {
|
||||
PyErr_Clear();
|
||||
} else {
|
||||
*Out = L;
|
||||
Ok = 1;
|
||||
}
|
||||
}
|
||||
Py_DECREF(V);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
/* Bit(N) / Bit[N] 注解:设 BitWidth(无法求值宽度时记 1)。 */
|
||||
static void ApplyBitWidthAttr(PyObject *Node) {
|
||||
PyObject *Value = NULL;
|
||||
PyObject *WidthNode = NULL;
|
||||
if (NodeIs(Node, "Call")) {
|
||||
Value = PyObject_GetAttrString(Node, "func");
|
||||
if (Value != NULL && IsNameOrAttrNamed(Value, "Bit")) {
|
||||
PyObject *Args = PyObject_GetAttrString(Node, "args");
|
||||
if (Args != NULL && PyList_Check(Args) && PyList_GET_SIZE(Args) > 0) {
|
||||
WidthNode = PyList_GET_ITEM(Args, 0);
|
||||
Py_INCREF(WidthNode);
|
||||
}
|
||||
Py_XDECREF(Args);
|
||||
}
|
||||
} else if (NodeIs(Node, "Subscript")) {
|
||||
Value = PyObject_GetAttrString(Node, "value");
|
||||
if (Value != NULL && IsNameOrAttrNamed(Value, "Bit")) {
|
||||
WidthNode = PyObject_GetAttrString(Node, "slice");
|
||||
}
|
||||
}
|
||||
if (WidthNode != NULL) {
|
||||
long Width = 0;
|
||||
SetAttrLong(Node, "BitWidth", ConstantIntValue(WidthNode, &Width) ? Width : 1);
|
||||
Py_DECREF(WidthNode);
|
||||
}
|
||||
Py_XDECREF(Value);
|
||||
}
|
||||
|
||||
/* 把前缀修饰写入注解节点的 Markers / Attributes / ByteOrder。 */
|
||||
static void ApplyTypeModifiers(PyObject *Node, PyObject *Markers, PyObject *Attributes,
|
||||
const char *ByteOrder) {
|
||||
if (Markers != NULL && PyList_GET_SIZE(Markers) > 0) {
|
||||
if (PyObject_SetAttrString(Node, "Markers", Markers) < 0) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
if (Attributes != NULL && PyList_GET_SIZE(Attributes) > 0) {
|
||||
if (PyObject_SetAttrString(Node, "Attributes", Attributes) < 0) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
if (ByteOrder != NULL) {
|
||||
PyObject *Order = PyUnicode_FromString(ByteOrder);
|
||||
if (Order != NULL) {
|
||||
if (PyObject_SetAttrString(Node, "ByteOrder", Order) < 0) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
Py_DECREF(Order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 类型注解入口:置注解模式标志(对齐 Python ExprParser._ParseTypeAnnotation)。
|
||||
*
|
||||
* 值引用参数注解(仅形参位有效,对齐纯 Python 端):
|
||||
* 前导 '&' + 可选名 'temp',合成联合形态 base | t.CRef(ref)/ base | t.CTempRef(temp)。
|
||||
* 前导 '&' + 可选名 'temp',构造 Subscript(Name('CRef'), T) / Subscript(Name('CTempRef'), T)。
|
||||
* CRef/CTempRef 非真实类,仅作注解 AST 内的名字标记(语义侧按名字识别)。
|
||||
* '&'/'&temp' 之后缺类型(行尾 / ')' / ',' / ':' / '=' / ']' / '}' / ';')时响亮失败。 */
|
||||
static PyObject *ParseTypeAnnotation(Parser *P) {
|
||||
PyObject *Markers = NULL;
|
||||
PyObject *Attributes = NULL;
|
||||
const char *ByteOrder = NULL;
|
||||
if (!ParseTypePrefixes(P, &Markers, &Attributes, &ByteOrder)) {
|
||||
Py_XDECREF(Markers);
|
||||
Py_XDECREF(Attributes);
|
||||
return NULL;
|
||||
}
|
||||
int RefTemp = 0; /* 0=非值引用 1=ref 2=temp */
|
||||
Token *RefTok = PeekTok(P, 0);
|
||||
if (AtOp(P, "&")) {
|
||||
@@ -2737,6 +3025,8 @@ static PyObject *ParseTypeAnnotation(Parser *P) {
|
||||
|| TokIsOp(End, ";")) {
|
||||
SyntaxErrorAt(P, End,
|
||||
"值引用参数注解缺少类型:`&` 后必须紧跟类型 T(ref:`&T`;temp:`&temp T`)");
|
||||
Py_XDECREF(Markers);
|
||||
Py_XDECREF(Attributes);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -2745,31 +3035,26 @@ static PyObject *ParseTypeAnnotation(Parser *P) {
|
||||
PyObject *Node = ParseExpr(P);
|
||||
P->InTypeAnnotation = Saved;
|
||||
if (Node == NULL) {
|
||||
Py_XDECREF(Markers);
|
||||
Py_XDECREF(Attributes);
|
||||
return NULL;
|
||||
}
|
||||
if (RefTemp == 0) {
|
||||
return Node;
|
||||
}
|
||||
PyObject *Marker = MakeTAttrNode(P, RefTemp == 2 ? "CTempRef" : "CRef");
|
||||
if (Marker == NULL) {
|
||||
if (RefTemp != 0) {
|
||||
PyObject *Wrap = MakeTypeWrapperSubscript(
|
||||
P, RefTemp == 2 ? "CTempRef" : "CRef", Node, RefTok);
|
||||
Py_DECREF(Node);
|
||||
return NULL;
|
||||
if (Wrap == NULL) {
|
||||
Py_XDECREF(Markers);
|
||||
Py_XDECREF(Attributes);
|
||||
return NULL;
|
||||
}
|
||||
Node = Wrap;
|
||||
}
|
||||
PyObject *Op = MakeSingleton(P, "BitOr");
|
||||
if (Op == NULL) {
|
||||
Py_DECREF(Node);
|
||||
Py_DECREF(Marker);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *Argv[3] = { Node, Op, Marker };
|
||||
PyObject *Wrap = NewNode(P, "BinOp", Argv, 3);
|
||||
Py_DECREF(Op);
|
||||
Py_DECREF(Marker);
|
||||
Py_DECREF(Node);
|
||||
if (Wrap != NULL) {
|
||||
FixLoc(P, Wrap, RefTok);
|
||||
}
|
||||
return Wrap;
|
||||
ApplyTypeModifiers(Node, Markers, Attributes, ByteOrder);
|
||||
ApplyBitWidthAttr(Node);
|
||||
Py_XDECREF(Markers);
|
||||
Py_XDECREF(Attributes);
|
||||
return Node;
|
||||
}
|
||||
|
||||
/* term:factor (*|/|//|%|@ factor)* */
|
||||
@@ -5489,7 +5774,7 @@ static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators)
|
||||
if (Start->Kind == TK_NAME && TokIsName(Start, "async")) {
|
||||
Start = NextTok(P); /* def */
|
||||
}
|
||||
/* define 宏函数关键字(语法与 def 一致,返回注解自动追加 t.CDefine) */
|
||||
/* define 宏函数关键字(语法与 def 一致,产出 FunctionDef.Define=True) */
|
||||
int IsDefine = (Start->Kind == TK_NAME && TokIsName(Start, "define"));
|
||||
Token *NameTok = ExpectName(P, NULL);
|
||||
if (NameTok == NULL) {
|
||||
@@ -5566,31 +5851,8 @@ static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators)
|
||||
Py_DECREF(Argv[3]);
|
||||
}
|
||||
if (Node != NULL && IsDefine) {
|
||||
/* 标记 define 宏函数(Unparser 据此还原语法) */
|
||||
/* 标记 define 宏函数(Unparser 据此还原语法);返回注解保持原样 */
|
||||
PyObject_SetAttrString(Node, "Define", Py_True);
|
||||
/* 返回注解改写:<原类型> | t.CDefine / 单独 t.CDefine(宏函数标记,
|
||||
NewNode 后 Returns 引用仍有效,此处仅替换 Node.returns) */
|
||||
PyObject *TAttr = MakeTAttrNode(P, "CDefine");
|
||||
if (TAttr != NULL) {
|
||||
if (Returns != NULL) {
|
||||
PyObject *Op = MakeSingleton(P, "BitOr");
|
||||
if (Op != NULL) {
|
||||
PyObject *ArgvB[3];
|
||||
ArgvB[0] = Returns;
|
||||
ArgvB[1] = Op;
|
||||
ArgvB[2] = TAttr;
|
||||
PyObject *Bin = NewNode(P, "BinOp", ArgvB, 3);
|
||||
if (Bin != NULL) {
|
||||
PyObject_SetAttrString(Node, "returns", Bin);
|
||||
Py_DECREF(Bin);
|
||||
}
|
||||
Py_DECREF(Op);
|
||||
}
|
||||
} else {
|
||||
PyObject_SetAttrString(Node, "returns", TAttr);
|
||||
}
|
||||
Py_DECREF(TAttr);
|
||||
}
|
||||
}
|
||||
Py_XDECREF(Returns);
|
||||
Py_DECREF(TypeParams);
|
||||
@@ -5619,8 +5881,7 @@ static PyObject *MakeTAttrNode(Parser *P, const char *AttrName) {
|
||||
/* extern/export 函数声明(FFI/裸名导出,对齐 vast Parser._ParseExternDef/_ParseExportDef)。
|
||||
* 允许语法(4 种,配不配 def 都一样,无 `:` 时 body 为空):
|
||||
* extern [def] A(x: int) -> int [': pass']
|
||||
* 产出标准 FunctionDef:返回注解改写为 <原类型> | t.State(extern)/ <原类型> | t.CExport(export),
|
||||
* 并设 Extern=True / Export=True 供 Unparser 还原。 */
|
||||
* 产出标准 FunctionDef:返回注解保持原样,设 Extern=True / Export=True 供 Unparser 还原。 */
|
||||
static PyObject *ParseExternDef(Parser *P, int IsExport, PyObject *Decorators) {
|
||||
Token *Start = NextTok(P); /* 'extern' / 'export' */
|
||||
if (AtName(P, "def")) {
|
||||
@@ -5710,30 +5971,8 @@ static PyObject *ParseExternDef(Parser *P, int IsExport, PyObject *Decorators) {
|
||||
Py_XDECREF(Returns);
|
||||
return NULL;
|
||||
}
|
||||
/* 标记 extern/export(Unparser 据此还原语法) */
|
||||
/* 标记 extern/export(Unparser 据此还原语法);返回注解保持原样 */
|
||||
PyObject_SetAttrString(Node, IsExport ? "Export" : "Extern", Py_True);
|
||||
/* 返回注解改写:<原类型> | t.State / <原类型> | t.CExport(NewNode 后 Returns 引用仍有效) */
|
||||
PyObject *TAttr = MakeTAttrNode(P, IsExport ? "CExport" : "State");
|
||||
if (TAttr != NULL) {
|
||||
if (Returns != NULL) {
|
||||
PyObject *Op = MakeSingleton(P, "BitOr");
|
||||
if (Op != NULL) {
|
||||
PyObject *ArgvB[3];
|
||||
ArgvB[0] = Returns;
|
||||
ArgvB[1] = Op;
|
||||
ArgvB[2] = TAttr;
|
||||
PyObject *Bin = NewNode(P, "BinOp", ArgvB, 3);
|
||||
if (Bin != NULL) {
|
||||
PyObject_SetAttrString(Node, "returns", Bin);
|
||||
Py_DECREF(Bin);
|
||||
}
|
||||
Py_DECREF(Op);
|
||||
}
|
||||
} else {
|
||||
PyObject_SetAttrString(Node, "returns", TAttr);
|
||||
}
|
||||
Py_DECREF(TAttr);
|
||||
}
|
||||
Py_XDECREF(Returns);
|
||||
FixLoc(P, Node, Start);
|
||||
return Node;
|
||||
|
||||
Reference in new issue
Block a user