如何为给定语言实现语义检查器

时间:2018-04-15 11:16:54

标签: parsing compiler-construction bison semantics lex

for(var x in z) {
 a = x + 1 + 2;
 foo();
 }
 function bar() {}
 for(t in []){
 function hello(a) {
 a = t + 'hello' + 'world';
 }
 bar();
 hello();
 }
 hello();
 function hello() {}
 15 * 30;
 1 + 2 + 3;
 a = 3 - 2 - 1;
 a + 10;

假设我们有一个语义规则,即在调用函数时,该函数应在当前作用域或父作用域之前声明。

所以自从foo();在上面的程序(第3行)中没有声明我们需要打印错误信息。

对于第13行,未声明hello(),因此我们需要打印错误消息。

我们有bison / flex实现,但缺少属性语法实现。

分析器:

%{
#include <stdio.h> 
void yyerror(const char * msg){
        printf("%s\n",msg);
}
%} 
%token tFOR tIN tSEMICOLON tLPAR tLBRKT tLT tSTAR tPLUS tPERCENT tINT tREAL tSTRING tWHILE tVAR tCOMMA tRPAR tRBRKT tRBRACE tGT tEQ tMINUS tNOT tIDENT tIF tCOLON tLBRACE tELSE tEQCHECK tFUNCTION
%left tMINUS tPLUS
%left tSTAR
%left tPERCENT
%left tLT tGT tEQCHECK
%left tNOT

%%
prog: statementList
;

statementList: statementList statement
             | statementList tSEMICOLON
             |
;

statement: assign
         | if
         | expr
         | while
         | for
         | functionCall
         | functionDeclaration
;

assign: tIDENT tEQ expr
      | tVAR tIDENT tEQ expr
;

if: ifPart elsePart
;

ifPart: tIF tLPAR expr tRPAR statementBlock
;

elsePart: tELSE statementBlock 
        | 
;

while: tWHILE tLPAR expr tRPAR statementBlock
;

for: tFOR tLPAR tIDENT tIN expr tRPAR statementBlock
   | tFOR tLPAR tVAR tIDENT tIN expr tRPAR statementBlock
;

functionDeclaration: tFUNCTION tIDENT tLPAR exprList tRPAR statementBlock
                   | tFUNCTION tIDENT tLPAR tRPAR statementBlock
;

statementBlock: tLBRACE statementList tRBRACE
;

functionCall: tIDENT tLPAR exprList tRPAR
            | tIDENT tLPAR tRPAR
;

expr: tIDENT
    | tREAL
    | tINT
    | tSTRING
    | tLBRKT tRBRKT
    | tLBRKT exprList tRBRKT
    | tLBRACE tRBRACE 
    | tLBRACE propertyList tRBRACE
    | tNOT expr
    | expr tPLUS expr
    | expr tMINUS expr
    | expr tSTAR expr
    | expr tEQCHECK expr
    | expr tLT expr
    | expr tGT expr
;

exprList: expr
        | exprList tCOMMA expr
;

propertyList: tIDENT tCOLON expr
            | propertyList tCOMMA tIDENT tCOLON expr
;

%%
int main() {
  return yyparse();
}

扫描仪:

%{
#include "parser.tab.h"
%}
%%
in      return tIN;
function      return tFUNCTION;
for      return tFOR;
while    return tWHILE;
var      return tVAR;
";"      return tSEMICOLON;
"("      return tLPAR;
")"      return tRPAR;
"["      return tLBRKT;
"]"      return tRBRKT;
"<"      return tLT;
">"      return tGT;
"*"      return tSTAR;
"+"      return tPLUS;
"%"      return tPERCENT;
","      return tCOMMA;
"{"      return tRBRACE;
"}"      return tLBRACE;
"=="      return tEQCHECK;
"="      return tEQ;
"-"      return tMINUS;
"!"      return tNOT;
"-"?[0-9]+  return tINT;
("-"[0-9]+"."[0-9]+)|([0-9]*"."[0-9]+)   return tREAL;
('[^']*')|(["][^"]*["]) return tSTRING;
[a-zA-Z_][a-zA-Z_0-9]*  return tIDENT;
[ \t\n] 
.      return yytext[0];
%%

1 个答案:

答案 0 :(得分:0)

以下语法tIDENTtFUNCTION是令牌。Foo()是令牌。 你必须保持

  

符号表

为了存储tIDENTtFUNCTION令牌。要检查之前是否已声明foo,您必须遍历符号表并查看它是否已经在里面。您必须这样做这也有变量。

例如:

assign: tFUNCTION   {   
                        SYMBOL *s= NULL
                        if((s=symlook_function($1,depth,yylineno,param_count,symbol_list)) == NULL ){
                            strcat(errstr, "Found a function which is not decalared");
                            yyerror(errstr);
                        }
                }

您必须实现自己的符号表及其功能,例如在C语言中添加和搜索。在symlook内搜索符号表,如果它没有找到令牌{{1 }} tFUNCTION inside会产生一条错误信息。你还必须在里面包含不同的语法。当一个函数或者veriable第一次($1)时,你可以在declaring找到它 检查这个C Yacc语法。这些是C语言的产生: https://www.lysator.liu.se/c/ANSI-C-grammar-y.html

看到这个:

an expression.

当制作为external_declaration : function_definition | declaration 时,令牌function_definition首次进入符号表内。如果您再次在另一个制作中找到foo,则必须搜索符号表,如果缺少则返回foo