[statement: All rights reserved, welcome to reprint, please do not use it for commercial purposes. Contact email: feixiaoxing @163.com]
Code that conforms to syntax does not necessarily conform to semantics. This sentence sounds awkward. Let's take an example. Suppose we define a variable int a;, At this time, we can't define an int a. This is an example of a variable.
We can also give an example of a function. Suppose a function has no return value, but a return true statement appears during implementation. Is this also unreasonable.
Because the syntax tree itself has been created, at this time, it is only necessary to advance layer by layer according to the structure of the syntax tree. The basic processing method is similar to the post order traversal of binary tree.
1. Entrance to semantic parsing
void CheckTranslationUnit(AstTranslationUnit transUnit)
2. Every syntax parsing place has a semantic parsing file
declchk.c
https://github.com/sheisc/ucc162.3/blob/master/ucc/ucl/declchk.c
exprchk.c
https://github.com/sheisc/ucc162.3/blob/master/ucc/ucl/exprchk.c
stmtchk.c
https://github.com/sheisc/ucc162.3/blob/master/ucc/ucl/stmtchk.c
3,symbol.c
A key file for semantic parsing
https://github.com/sheisc/ucc162.3/blob/master/ucc/ucl/symbol.c
Whether variables exist, functions exist, and types are correct are all related to symbol C closely related.
symbol. addTag and addFunction in C will appear in different semantic check files.
4. Without losing generality, let's analyze stmtchk c
4.1 entry function
static AstStatement CheckStatement(AstStatement stmt) { return (* StmtCheckers[stmt->kind - NK_ExpressionStatement])(stmt); }
4.2 check case statement
/** case-statement: case constant-expression : statement Constraints A case or default label shall appear only in a switch statement. Further constraints on such labels are discussed under the switch statement. Even the following code is legal: int a = 5; switch(a){ printf("123.\n"); ---------- these are dead-code. break; case 3: printf("3.\n"); printf("this printf is not part of case-statment from syntactic view.\n"); case 2: printf("2.\n"); } */ static AstStatement CheckCaseStatement(AstStatement stmt) { AstCaseStatement caseStmt = AsCase(stmt); AstSwitchStatement swtchStmt; // We have pushed the current switch statement in CheckSwitchStatement(...) swtchStmt = (AstSwitchStatement)TopStatement(CURRENTF->swtches); if (swtchStmt == NULL) { Error(&stmt->coord, "A case label shall appear in a switch statement."); return stmt; } caseStmt->expr = CheckConstantExpression(caseStmt->expr); if (caseStmt->expr == NULL) { Error(&stmt->coord, "The case value must be integer constant."); return stmt; } caseStmt->stmt = CheckStatement(caseStmt->stmt); caseStmt->expr = FoldCast(swtchStmt->expr->ty, caseStmt->expr); AddCase(swtchStmt, caseStmt); return stmt; }
The most common problems in case statement are that there is no switch statement outside, and that value is not an integer.
4.3 check for statement
/** iteration-statement: while ( expression ) statement do statement while ( expression ) ; */ static AstStatement CheckLoopStatement(AstStatement stmt) { AstLoopStatement loopStmt = AsLoop(stmt); PushStatement(CURRENTF->loops, stmt); PushStatement(CURRENTF->breakable, stmt); // Adjust expr's type to Pointer(...) when its type is FUNCTION/ARRAY loopStmt->expr = Adjust(CheckExpression(loopStmt->expr), 1); if (! IsScalarType(loopStmt->expr->ty)) { Error(&stmt->coord, "The expression in do or while statement shall be scalar type."); } loopStmt->stmt = CheckStatement(loopStmt->stmt); PopStatement(CURRENTF->loops); PopStatement(CURRENTF->breakable); return stmt; }
The main problem of circular statements is to judge whether expression is a scalar.
4.4 check break statement
// break; static AstStatement CheckBreakStatement(AstStatement stmt) { AstBreakStatement brkStmt = AsBreak(stmt); brkStmt->target = TopStatement(CURRENTF->breakable); if (brkStmt->target == NULL) { Error(&stmt->coord, "The break shall appear in a switch or loop"); } return stmt; }
The break statement is mainly used to judge whether it is in the loop.
4.5 check default statement
/** default-statement default : statement */ static AstStatement CheckDefaultStatement(AstStatement stmt) { AstDefaultStatement defStmt = AsDef(stmt); AstSwitchStatement swtchStmt; swtchStmt = (AstSwitchStatement)TopStatement(CURRENTF->swtches); if (swtchStmt == NULL) { Error(&stmt->coord, "A default label shall appear in a switch statement."); return stmt; } if (swtchStmt->defStmt != NULL) { Error(&stmt->coord, "There shall be only one default label in a switch statement."); return stmt; } defStmt->stmt = CheckStatement(defStmt->stmt); swtchStmt->defStmt = defStmt; return stmt; }
The default statement mainly has two problems: one is to judge whether it is not in the switch statement, and the other is to judge whether there is only one default statement.
4.6 summary
The part of statement semantic analysis is similar, mainly to list the possible problems. Generally speaking, if there is no problem, it will be considered that the statement is no problem, and the following binary translation can be carried out.