parser: align function names with upstream Parse.zig
Rename assignOpTag to assignOpNode to match upstream. Extract inlined code into separate functions to match upstream's structure: expectTestDecl, expectIfStatement, expectParamDecl, parseSwitchProngList. Add parseSingleAssignExpr for upstream API surface alignment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
234
parser.c
234
parser.c
@@ -50,7 +50,7 @@ typedef struct {
|
|||||||
|
|
||||||
static AstNodeIndex addExtra(Parser*, const AstNodeIndex*, uint32_t);
|
static AstNodeIndex addExtra(Parser*, const AstNodeIndex*, uint32_t);
|
||||||
static AstNodeIndex addNode(AstNodeList*, AstNodeItem);
|
static AstNodeIndex addNode(AstNodeList*, AstNodeItem);
|
||||||
static AstNodeTag assignOpTag(TokenizerTag);
|
static AstNodeTag assignOpNode(TokenizerTag);
|
||||||
static AstTokenIndex assertToken(Parser*, TokenizerTag);
|
static AstTokenIndex assertToken(Parser*, TokenizerTag);
|
||||||
static void astNodeListEnsureCapacity(AstNodeList*, uint32_t);
|
static void astNodeListEnsureCapacity(AstNodeList*, uint32_t);
|
||||||
static void cleanupScratch(CleanupScratch*);
|
static void cleanupScratch(CleanupScratch*);
|
||||||
@@ -59,8 +59,11 @@ static AstTokenIndex eatToken(Parser*, TokenizerTag);
|
|||||||
static AstNodeIndex expectBlockExprStatement(Parser*);
|
static AstNodeIndex expectBlockExprStatement(Parser*);
|
||||||
static AstNodeIndex expectContainerField(Parser*);
|
static AstNodeIndex expectContainerField(Parser*);
|
||||||
static AstNodeIndex expectExpr(Parser*);
|
static AstNodeIndex expectExpr(Parser*);
|
||||||
|
static AstNodeIndex expectIfStatement(Parser*);
|
||||||
|
static AstNodeIndex expectParamDecl(Parser*);
|
||||||
static AstNodeIndex expectSemicolon(Parser*);
|
static AstNodeIndex expectSemicolon(Parser*);
|
||||||
static AstNodeIndex expectStatement(Parser*, bool);
|
static AstNodeIndex expectStatement(Parser*, bool);
|
||||||
|
static AstNodeIndex expectTestDecl(Parser*);
|
||||||
static AstTokenIndex expectToken(Parser*, TokenizerTag);
|
static AstTokenIndex expectToken(Parser*, TokenizerTag);
|
||||||
static AstNodeIndex expectTopLevelDecl(Parser*);
|
static AstNodeIndex expectTopLevelDecl(Parser*);
|
||||||
static AstNodeIndex expectVarDeclExprStatement(Parser*, AstTokenIndex);
|
static AstNodeIndex expectVarDeclExprStatement(Parser*, AstTokenIndex);
|
||||||
@@ -107,11 +110,13 @@ static AstNodeIndex parsePrimaryExpr(Parser*);
|
|||||||
static AstNodeIndex parsePrimaryTypeExpr(Parser*);
|
static AstNodeIndex parsePrimaryTypeExpr(Parser*);
|
||||||
static PtrModifiers parsePtrModifiers(Parser*);
|
static PtrModifiers parsePtrModifiers(Parser*);
|
||||||
static void parsePtrPayload(Parser*);
|
static void parsePtrPayload(Parser*);
|
||||||
|
static AstNodeIndex parseSingleAssignExpr(Parser*);
|
||||||
static AstNodeIndex parseSuffixExpr(Parser*);
|
static AstNodeIndex parseSuffixExpr(Parser*);
|
||||||
static AstNodeIndex parseSuffixOp(Parser*, AstNodeIndex);
|
static AstNodeIndex parseSuffixOp(Parser*, AstNodeIndex);
|
||||||
static AstNodeIndex parseSwitchExpr(Parser*);
|
static AstNodeIndex parseSwitchExpr(Parser*);
|
||||||
static AstNodeIndex parseSwitchItem(Parser*);
|
static AstNodeIndex parseSwitchItem(Parser*);
|
||||||
static AstNodeIndex parseSwitchProng(Parser*);
|
static AstNodeIndex parseSwitchProng(Parser*);
|
||||||
|
static AstSubRange parseSwitchProngList(Parser*);
|
||||||
static AstNodeIndex parseTypeExpr(Parser*);
|
static AstNodeIndex parseTypeExpr(Parser*);
|
||||||
static AstNodeIndex parseVarDeclProto(Parser*);
|
static AstNodeIndex parseVarDeclProto(Parser*);
|
||||||
static AstNodeIndex parseWhileContinueExpr(Parser*);
|
static AstNodeIndex parseWhileContinueExpr(Parser*);
|
||||||
@@ -1220,6 +1225,18 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
|
|||||||
return 0; // tcc
|
return 0; // tcc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AstNodeIndex expectParamDecl(Parser* p) {
|
||||||
|
eatDocComments(p);
|
||||||
|
eatToken(p, TOKEN_KEYWORD_COMPTIME);
|
||||||
|
eatToken(p, TOKEN_KEYWORD_NOALIAS);
|
||||||
|
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER
|
||||||
|
&& p->token_tags[p->tok_i + 1] == TOKEN_COLON)
|
||||||
|
p->tok_i += 2;
|
||||||
|
if (eatToken(p, TOKEN_KEYWORD_ANYTYPE) != null_token)
|
||||||
|
return 0;
|
||||||
|
return parseTypeExpr(p);
|
||||||
|
}
|
||||||
|
|
||||||
static SmallSpan parseParamDeclList(Parser* p) {
|
static SmallSpan parseParamDeclList(Parser* p) {
|
||||||
expectToken(p, TOKEN_L_PAREN);
|
expectToken(p, TOKEN_L_PAREN);
|
||||||
|
|
||||||
@@ -1235,18 +1252,7 @@ static SmallSpan parseParamDeclList(Parser* p) {
|
|||||||
if (varargs == 1)
|
if (varargs == 1)
|
||||||
varargs = 2;
|
varargs = 2;
|
||||||
|
|
||||||
eatDocComments(p);
|
if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) {
|
||||||
|
|
||||||
// Check for comptime or noalias
|
|
||||||
eatToken(p, TOKEN_KEYWORD_COMPTIME);
|
|
||||||
eatToken(p, TOKEN_KEYWORD_NOALIAS);
|
|
||||||
|
|
||||||
// Check for name: type or just type
|
|
||||||
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER
|
|
||||||
&& p->token_tags[p->tok_i + 1] == TOKEN_COLON) {
|
|
||||||
p->tok_i += 2; // consume name and colon
|
|
||||||
} else if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) {
|
|
||||||
// varargs (...)
|
|
||||||
p->tok_i++;
|
p->tok_i++;
|
||||||
if (varargs == 0)
|
if (varargs == 0)
|
||||||
varargs = 1;
|
varargs = 1;
|
||||||
@@ -1256,17 +1262,7 @@ static SmallSpan parseParamDeclList(Parser* p) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// anytype params are omitted from the AST
|
const AstNodeIndex type_expr = expectParamDecl(p);
|
||||||
if (eatToken(p, TOKEN_KEYWORD_ANYTYPE) != null_token) {
|
|
||||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA) {
|
|
||||||
p->tok_i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
expectToken(p, TOKEN_R_PAREN);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AstNodeIndex type_expr = parseTypeExpr(p);
|
|
||||||
if (type_expr != 0)
|
if (type_expr != 0)
|
||||||
SLICE_APPEND(AstNodeIndex, &p->scratch, type_expr);
|
SLICE_APPEND(AstNodeIndex, &p->scratch, type_expr);
|
||||||
|
|
||||||
@@ -2442,19 +2438,9 @@ static AstNodeIndex parseSwitchProng(Parser* p) {
|
|||||||
return case_node;
|
return case_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AstNodeIndex parseSwitchExpr(Parser* p) {
|
static AstSubRange parseSwitchProngList(Parser* p) {
|
||||||
const AstTokenIndex switch_token = eatToken(p, TOKEN_KEYWORD_SWITCH);
|
|
||||||
if (switch_token == null_token)
|
|
||||||
return null_node;
|
|
||||||
|
|
||||||
expectToken(p, TOKEN_L_PAREN);
|
|
||||||
const AstNodeIndex operand = expectExpr(p);
|
|
||||||
expectToken(p, TOKEN_R_PAREN);
|
|
||||||
expectToken(p, TOKEN_L_BRACE);
|
|
||||||
|
|
||||||
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
|
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
|
||||||
= initCleanupScratch(p);
|
= initCleanupScratch(p);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (eatToken(p, TOKEN_R_BRACE) != null_token)
|
if (eatToken(p, TOKEN_R_BRACE) != null_token)
|
||||||
break;
|
break;
|
||||||
@@ -2466,11 +2452,22 @@ static AstNodeIndex parseSwitchExpr(Parser* p) {
|
|||||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA)
|
if (p->token_tags[p->tok_i] == TOKEN_COMMA)
|
||||||
p->tok_i++;
|
p->tok_i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool comma = p->token_tags[p->tok_i - 2] == TOKEN_COMMA;
|
|
||||||
const uint32_t cases_len = p->scratch.len - scratch_top.old_len;
|
const uint32_t cases_len = p->scratch.len - scratch_top.old_len;
|
||||||
const AstSubRange span
|
return listToSpan(p, &p->scratch.arr[scratch_top.old_len], cases_len);
|
||||||
= listToSpan(p, &p->scratch.arr[scratch_top.old_len], cases_len);
|
}
|
||||||
|
|
||||||
|
static AstNodeIndex parseSwitchExpr(Parser* p) {
|
||||||
|
const AstTokenIndex switch_token = eatToken(p, TOKEN_KEYWORD_SWITCH);
|
||||||
|
if (switch_token == null_token)
|
||||||
|
return null_node;
|
||||||
|
|
||||||
|
expectToken(p, TOKEN_L_PAREN);
|
||||||
|
const AstNodeIndex operand = expectExpr(p);
|
||||||
|
expectToken(p, TOKEN_R_PAREN);
|
||||||
|
expectToken(p, TOKEN_L_BRACE);
|
||||||
|
|
||||||
|
const AstSubRange span = parseSwitchProngList(p);
|
||||||
|
const bool comma = p->token_tags[p->tok_i - 2] == TOKEN_COMMA;
|
||||||
return addNode(&p->nodes,
|
return addNode(&p->nodes,
|
||||||
(AstNodeItem) {
|
(AstNodeItem) {
|
||||||
.tag = comma ? AST_NODE_SWITCH_COMMA : AST_NODE_SWITCH,
|
.tag = comma ? AST_NODE_SWITCH_COMMA : AST_NODE_SWITCH,
|
||||||
@@ -2682,7 +2679,7 @@ static AstNodeIndex parsePrefixExpr(Parser* p) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static AstNodeTag assignOpTag(TokenizerTag tok) {
|
static AstNodeTag assignOpNode(TokenizerTag tok) {
|
||||||
switch (tok) {
|
switch (tok) {
|
||||||
case TOKEN_EQUAL:
|
case TOKEN_EQUAL:
|
||||||
return AST_NODE_ASSIGN;
|
return AST_NODE_ASSIGN;
|
||||||
@@ -2726,7 +2723,7 @@ static AstNodeTag assignOpTag(TokenizerTag tok) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AstNodeIndex finishAssignExpr(Parser* p, AstNodeIndex lhs) {
|
static AstNodeIndex finishAssignExpr(Parser* p, AstNodeIndex lhs) {
|
||||||
const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]);
|
const AstNodeTag assign_tag = assignOpNode(p->token_tags[p->tok_i]);
|
||||||
if (assign_tag == AST_NODE_ROOT)
|
if (assign_tag == AST_NODE_ROOT)
|
||||||
return lhs;
|
return lhs;
|
||||||
|
|
||||||
@@ -2747,6 +2744,23 @@ static AstNodeIndex parseAssignExpr(Parser* p) {
|
|||||||
return finishAssignExpr(p, expr);
|
return finishAssignExpr(p, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AstNodeIndex parseSingleAssignExpr(Parser* p) {
|
||||||
|
const AstNodeIndex expr = parseExpr(p);
|
||||||
|
if (expr == 0)
|
||||||
|
return null_node;
|
||||||
|
const AstNodeTag tag = assignOpNode(p->token_tags[p->tok_i]);
|
||||||
|
if (tag == AST_NODE_ROOT)
|
||||||
|
return expr;
|
||||||
|
const AstTokenIndex op_token = nextToken(p);
|
||||||
|
const AstNodeIndex rhs = expectExpr(p);
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = tag,
|
||||||
|
.main_token = op_token,
|
||||||
|
.data = { .lhs = expr, .rhs = rhs },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static AstNodeIndex parseBlockExpr(Parser* p) {
|
static AstNodeIndex parseBlockExpr(Parser* p) {
|
||||||
if (p->token_tags[p->tok_i] == TOKEN_L_BRACE)
|
if (p->token_tags[p->tok_i] == TOKEN_L_BRACE)
|
||||||
return parseBlock(p);
|
return parseBlock(p);
|
||||||
@@ -2869,6 +2883,54 @@ static AstNodeIndex expectVarDeclExprStatement(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AstNodeIndex expectIfStatement(Parser* p) {
|
||||||
|
const AstTokenIndex if_token = assertToken(p, TOKEN_KEYWORD_IF);
|
||||||
|
expectToken(p, TOKEN_L_PAREN);
|
||||||
|
const AstNodeIndex condition = expectExpr(p);
|
||||||
|
expectToken(p, TOKEN_R_PAREN);
|
||||||
|
parsePtrPayload(p);
|
||||||
|
bool else_required = false;
|
||||||
|
AstNodeIndex then_body;
|
||||||
|
const AstNodeIndex block2 = parseBlockExpr(p);
|
||||||
|
if (block2 != 0) {
|
||||||
|
then_body = block2;
|
||||||
|
} else {
|
||||||
|
then_body = parseAssignExpr(p);
|
||||||
|
if (then_body == 0)
|
||||||
|
fail(p, "expected block or assignment");
|
||||||
|
if (eatToken(p, TOKEN_SEMICOLON) != null_token)
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_IF_SIMPLE,
|
||||||
|
.main_token = if_token,
|
||||||
|
.data = { .lhs = condition, .rhs = then_body },
|
||||||
|
});
|
||||||
|
else_required = true;
|
||||||
|
}
|
||||||
|
if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) {
|
||||||
|
if (else_required)
|
||||||
|
fail(p, "expected_semi_or_else");
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_IF_SIMPLE,
|
||||||
|
.main_token = if_token,
|
||||||
|
.data = { .lhs = condition, .rhs = then_body },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
parsePayload(p);
|
||||||
|
const AstNodeIndex else_body = expectStatement(p, false);
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_IF,
|
||||||
|
.main_token = if_token,
|
||||||
|
.data = {
|
||||||
|
.lhs = condition,
|
||||||
|
.rhs = addExtra(p,
|
||||||
|
(AstNodeIndex[]) { then_body, else_body }, 2),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
|
static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
|
||||||
const AstTokenIndex comptime_token = eatToken(p, TOKEN_KEYWORD_COMPTIME);
|
const AstTokenIndex comptime_token = eatToken(p, TOKEN_KEYWORD_COMPTIME);
|
||||||
if (comptime_token != null_token) {
|
if (comptime_token != null_token) {
|
||||||
@@ -2950,55 +3012,8 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
|
|||||||
.rhs = 0,
|
.rhs = 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
case TOKEN_KEYWORD_IF: {
|
case TOKEN_KEYWORD_IF:
|
||||||
const AstTokenIndex if_token = nextToken(p);
|
return expectIfStatement(p);
|
||||||
expectToken(p, TOKEN_L_PAREN);
|
|
||||||
const AstNodeIndex condition = expectExpr(p);
|
|
||||||
expectToken(p, TOKEN_R_PAREN);
|
|
||||||
parsePtrPayload(p);
|
|
||||||
bool else_required = false;
|
|
||||||
AstNodeIndex then_body;
|
|
||||||
const AstNodeIndex block2 = parseBlockExpr(p);
|
|
||||||
if (block2 != 0) {
|
|
||||||
then_body = block2;
|
|
||||||
} else {
|
|
||||||
then_body = parseAssignExpr(p);
|
|
||||||
if (then_body == 0) {
|
|
||||||
fail(p, "expected block or assignment");
|
|
||||||
}
|
|
||||||
if (eatToken(p, TOKEN_SEMICOLON) != null_token)
|
|
||||||
return addNode(&p->nodes,
|
|
||||||
(AstNodeItem) {
|
|
||||||
.tag = AST_NODE_IF_SIMPLE,
|
|
||||||
.main_token = if_token,
|
|
||||||
.data = { .lhs = condition, .rhs = then_body },
|
|
||||||
});
|
|
||||||
else_required = true;
|
|
||||||
}
|
|
||||||
if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) {
|
|
||||||
if (else_required) {
|
|
||||||
fail(p, "expected_semi_or_else");
|
|
||||||
}
|
|
||||||
return addNode(&p->nodes,
|
|
||||||
(AstNodeItem) {
|
|
||||||
.tag = AST_NODE_IF_SIMPLE,
|
|
||||||
.main_token = if_token,
|
|
||||||
.data = { .lhs = condition, .rhs = then_body },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
parsePayload(p);
|
|
||||||
const AstNodeIndex else_body = expectStatement(p, false);
|
|
||||||
return addNode(&p->nodes,
|
|
||||||
(AstNodeItem) {
|
|
||||||
.tag = AST_NODE_IF,
|
|
||||||
.main_token = if_token,
|
|
||||||
.data = {
|
|
||||||
.lhs = condition,
|
|
||||||
.rhs = addExtra(p,
|
|
||||||
(AstNodeIndex[]) { then_body, else_body }, 2),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
case TOKEN_KEYWORD_ENUM:
|
case TOKEN_KEYWORD_ENUM:
|
||||||
case TOKEN_KEYWORD_STRUCT:
|
case TOKEN_KEYWORD_STRUCT:
|
||||||
case TOKEN_KEYWORD_UNION:;
|
case TOKEN_KEYWORD_UNION:;
|
||||||
@@ -3235,6 +3250,24 @@ static void findNextContainerMember(Parser* p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AstNodeIndex expectTestDecl(Parser* p) {
|
||||||
|
const AstTokenIndex test_token = assertToken(p, TOKEN_KEYWORD_TEST);
|
||||||
|
const AstTokenIndex test_name
|
||||||
|
= (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL
|
||||||
|
|| p->token_tags[p->tok_i] == TOKEN_IDENTIFIER)
|
||||||
|
? nextToken(p)
|
||||||
|
: null_token;
|
||||||
|
const AstNodeIndex body = parseBlock(p);
|
||||||
|
if (body == 0)
|
||||||
|
fail(p, "expected block after test");
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_TEST_DECL,
|
||||||
|
.main_token = test_token,
|
||||||
|
.data = { .lhs = test_name, .rhs = body },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static Members parseContainerMembers(Parser* p) {
|
static Members parseContainerMembers(Parser* p) {
|
||||||
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
|
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
|
||||||
= initCleanupScratch(p);
|
= initCleanupScratch(p);
|
||||||
@@ -3248,26 +3281,9 @@ static Members parseContainerMembers(Parser* p) {
|
|||||||
const AstTokenIndex doc_comment = eatDocComments(p);
|
const AstTokenIndex doc_comment = eatDocComments(p);
|
||||||
switch (p->token_tags[p->tok_i]) {
|
switch (p->token_tags[p->tok_i]) {
|
||||||
case TOKEN_KEYWORD_TEST: {
|
case TOKEN_KEYWORD_TEST: {
|
||||||
if (doc_comment != null_token) {
|
if (doc_comment != null_token)
|
||||||
fail(p, "test_doc_comment");
|
fail(p, "test_doc_comment");
|
||||||
}
|
const AstNodeIndex test_decl = expectTestDecl(p);
|
||||||
const AstTokenIndex test_token = nextToken(p);
|
|
||||||
// test name can be a string literal or identifier, or omitted
|
|
||||||
const AstTokenIndex test_name
|
|
||||||
= (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL
|
|
||||||
|| p->token_tags[p->tok_i] == TOKEN_IDENTIFIER)
|
|
||||||
? nextToken(p)
|
|
||||||
: null_token;
|
|
||||||
const AstNodeIndex body = parseBlock(p);
|
|
||||||
if (body == 0) {
|
|
||||||
fail(p, "expected block after test");
|
|
||||||
}
|
|
||||||
const AstNodeIndex test_decl = addNode(&p->nodes,
|
|
||||||
(AstNodeItem) {
|
|
||||||
.tag = AST_NODE_TEST_DECL,
|
|
||||||
.main_token = test_token,
|
|
||||||
.data = { .lhs = test_name, .rhs = body },
|
|
||||||
});
|
|
||||||
SLICE_APPEND(AstNodeIndex, &p->scratch, test_decl);
|
SLICE_APPEND(AstNodeIndex, &p->scratch, test_decl);
|
||||||
trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE;
|
trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE;
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user