本文先描述了LEXYACC的书写方法。然后利用LEXYACC编写了一个简单正则语言的引擎(暂时不支持闭包与或运算),生成的中间语言为C语言

正则引擎应直接生成NFA或DFA模拟器的输入文件,但在SDT的设计上就走的是增加,修改状态节点。而本文则采用类似语言编译器,翻译生产对应C语言的语句。

熟悉LEX和YACC工具

lex工具简介

lex格式

[第一部分:定义段]
%%
[第二部分:词法规则段]
%%
[第三部分:辅助函数段]

第一部分

Part1.1

  • 第一部分以符号%{和%}包裹,里面为以C语法写的一些定义和声明
  • 例如,文件包含,宏定义,常数定义,全局变量及外部变量定义,函数声明等。
  • 这一部分被Lex翻译器处理后会全部拷贝到文件lex.yy.c中。
  • 注意,特殊括号%{和%}都必须顶着行首写。
%{
#include <ctype.h>
char tokenString[MAXTOKENLEN+1];
%}

Part1.2

  • 第二部分是一组正规定义和状态定义。
  • 正规定义是为了简化后面的词法规则而给部分正规式定义了名字。
  • 每条正规定义也都要顶着行首写。
digit       [0-9]
number {digit}+
letter [a-zA-Z]
identifier {letter}+
newline \n
whitespace [ \t]+
  • 状态定义也叫环境定义,它定义了匹配正规式时所处的状态的名字。
  • 状态定义以%s开始,后跟所定义的状态的名字,注意%s也要顶行首写。
  • 例如下面一行就定义了一个名为COMMENT的状态和一个名为BAD的状态,状态名之间用空白分隔:
%s COMMENT BAD

第二部分

  • 词法规则段列出的是词法分析器需要匹配的正规式,以及匹配该正规式后需要进行的相关动作。
"if"            {return IF;}
"then" {return THEN;}
"else" {return ELSE;}
  • 每行都是一条规则,该规则的前一部分是正规式,需要顶行首写。
  • 后一部分是匹配该正规式后需要进行的动作,这个动作是用C语法来写的,被包裹在{}之内,被Lex翻译器翻译后会被直接拷贝进lex.yy.c。
  • 正规式和语义动作之间要有空白隔开。其中用{}扩住的正规式表示正规定义的名字。
  • 也可以若干个正规式匹配同一条语义动作,此时正规式之间要用 | 分隔。

第三部分

  • 辅助函数段用C语言语法来写,辅助函数一般是在词法规则段中用到的函数。这一部分一般会被直接拷贝到lex.yy.c中。

LEX中的变量

  • yyin和yyout:这是Lex中本身已定义的输入和输出文件指针。这两个变量指明了lex生成的词法分析器从哪里获得输入和输出到哪里。默认:键盘输入,屏幕输出。
  • yytext和yyleng:这也是lex中已定义的变量,直接用就可以了。
  • yytext:指向当前识别的词法单元(词文)的指针
  • yyleng:当前词法单元的长度。
  • ECHO:Lex中预定义的宏,可以出现在动作中,相当于fprintf(yyout, “%s”,yytext),即输出当前匹配的词法单元。
  • yylex():词法分析器驱动程序,用Lex翻译器生成的lex.yy.c内必然含有这个函数。
  • yywrap():词法分析器遇到文件结尾时会调用yywrap()来决定下一步怎么做:
  • 若yywrap()返回0,则继续扫描
  • 若返回1,则返回报告文件结尾的0标记。
  • 由于词法分析器总会调用yywrap,因此辅助函数中最好提供yywrap,如果不提供,则在用C编译器编译lex.yy.c时,需要链接相应的库,库中会给出标准的yywrap函数(标准函数返回1)

一个简易的词法分析器

保留字有

int float main return while do if else for

符号有

< <= = <> > >= + - * / ( ) { } ;

双引号内容当作MSG整体。

书写lex分析程序如下:

%{
#include <stdio.h>
#include <stdlib.h>
#define LT 1
#define LE 2
#define GT 3
#define GE 4
#define EQ 5
#define NE 6 #define WHILE 18
#define DO 19
#define ID 20
#define NUMBER 21
#define RELOP 22
#define NEWLINE 23
#define IF 24
#define ELSE 25
#define FOR 26 #define PLUS 27
#define MINUS 28
#define TIMES 29
#define OVER 30
#define LPAREN 31
#define RPAREN 32
#define SEMI 33
#define OP 34 #define LBRACE 35
#define RBRACE 36 #define INT 37
#define MAIN 38
#define RETURN 39
#define FLOAT 40
#define MSG 41 #define ERRORCHAR 42 int yylval;
int installID ();
int installNum ();
%} delim [ \t \n]
ws {delim}+
letter [A-Za-z_]
digit [0-9]
id {letter}({letter}|{digit})*
number {digit}+(\.{digit}+)?(E[+-]?{digit}+)? %% {ws} {;}
int {return (INT);}
float {return (FLOAT);}
main {return (MAIN);}
return {return (RETURN);}
while {return (WHILE);}
do {return (DO);}
if {return (IF);}
else {return (ELSE);}
for {return (FOR);}
{id} {yylval = installID (); return (ID);}
{number} {yylval = installNum (); return (NUMBER);}
"<" {yylval = LT; return (RELOP);}
"<=" {yylval = LE; return (RELOP);}
"=" {yylval = EQ; return (RELOP);}
"<>" {yylval = NE; return (RELOP);}
">" {yylval = GT; return (RELOP);}
">=" {yylval = GE; return (RELOP);}
"+" {yylval = PLUS; return (OP);}
"-" {yylval = MINUS; return (OP);}
"*" {yylval = TIMES; return (OP);}
"/" {yylval = OVER; return (OP);}
"(" {return LPAREN;}
")" {return RPAREN;}
";" {return SEMI;}
"{" {return LBRACE;}
"}" {return RBRACE;}
"\"" {
char c;
int i = 0;
do{
yytext[i++]=c;
c = input();
} while (c != '\"');
yytext[i]='\0';
return MSG;
}
. {yylval = ERRORCHAR; return ERRORCHAR;} %%
int installID () {
return ID;
} int installNum () {
return NUMBER;
} int yywrap (){
return 1;
} void writeout(int c){
switch(c){
case ERRORCHAR: fprintf(yyout, "(ERRORCHAR, \"%s\") ", yytext);break;
case RELOP: fprintf(yyout, "(RELOP, \"%s\") ", yytext);break;
case WHILE: fprintf(yyout, "(WHILE, \"%s\") ", yytext);break;
case DO: fprintf(yyout, "(DO, \"%s\") ", yytext);break;
case NUMBER: fprintf(yyout, "(NUM, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yytext);break;
case NEWLINE: fprintf(yyout, "\n");break;
case IF: fprintf(yyout, "(IF, \"%s\") ", yytext);break;
case ELSE: fprintf(yyout, "(ELSE, \"%s\") ", yytext);break;
case FOR: fprintf(yyout, "(FOR, \"%s\") ", yytext);break;
case PLUS:fprintf(yyout, "(PLUS, \"%s\") ", yytext);break;
case MINUS:fprintf(yyout, "(MINUS, \"%s\") ", yytext);break;
case OVER:fprintf(yyout, "(OVER, \"%s\") ", yytext);break;
case LPAREN:fprintf(yyout, "(LPAREN, \"%s\") ", yytext);break;
case RPAREN:fprintf(yyout, "(RPAREN, \"%s\") ", yytext);break;
case SEMI:fprintf(yyout, "(SEMI, \"%s\") ", yytext);break;
case LBRACE:fprintf(yyout, "(LBRACE, \"%s\") ", yytext);break;
case RBRACE:fprintf(yyout, "(RBRACE, \"%s\") ", yytext);break;
case INT:fprintf(yyout, "(INT, \"%s\") ", yytext);break;
case MAIN:fprintf(yyout, "(MAIN, \"%s\") ", yytext);break;
case RETURN:fprintf(yyout, "(RETURN, \"%s\") ", yytext);break;
case FLOAT:fprintf(yyout, "(FLOAT, \"%s\") ", yytext);break;
case MSG:fprintf(yyout, "(MSG, \"%s\") ", yytext);break;
case OP:{
switch(yylval){
case PLUS: fprintf(yyout, "(PLUS, \"%s\") ", yytext);break;
case MINUS: fprintf(yyout, "(MINUS, \"%s\") ", yytext);break;
case TIMES: fprintf(yyout, "(TIMES, \"%s\") ", yytext);break;
case OVER: fprintf(yyout, "(OVER, \"%s\") ", yytext);break;
}
};break;
default:fprintf(yyout, "(我不知道发生了什么,但是你大概是写错了. \"%s\") ", yytext);break;
}
return;
} int main (int argc, char* argv[]){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
if (argc>=3){
yyout=fopen(argv[2], "w");
}
}
else{
printf("Usage:./scanner [source file] [dst file]\n");
exit(0);
}
while ((c = yylex())!=0){
writeout(c);
writeout(NEWLINE);
}
if(argc>=2){
fclose(yyin);
if (argc>=3) fclose(yyout);
}
return 0;
}

学习YACC工具

Yacc 代表 Yet Another Compiler Compiler。 Yacc 的 GNU 版叫做 Bison。

  • 它是一种工具,将任何一种编程语言的所有语法翻译成针对此种语言的 Yacc 语法解析器。
  • 它用巴科斯范式(BNF, Backus Naur Form)来书写。
  • 按照惯例,Yacc 文件有 .y 后缀。

引用网上的一个比喻

为了更加说清这一概念,让我们以英语为例。 这一套标记可能是:名词, 动词, 形容词等等。为了使用这些标记造一个语法正确的句子,你的结构必须符合一定的规则。 一个简单的句子可能是名词+动词或者名词+动词+名词。(如 I care. See spot run.)

创建有四个步骤

  • 通过在语法文件上运行 Yacc 生成一个解析器。
  • 说明语法:
    • 编写一个 .y 的语法文件(同时说明 C 在这里要进行的动作)。
    • 编写一个词法分析器来处理输入并将标记传递给解析器。 这可以使用 Lex 来完成。
    • 编写一个函数,通过调用 yyparse() 来开始解析。
    • 编写错误处理例程(如 yyerror())
  • 编译 Yacc 生成的代码以及其他相关的源文件。
  • 将目标文件链接到适当的可执行解析器库。

YACC 语法规则

Yacc 语法规则具有以下一般格式:

result: components { /* action to be taken in C */ };

在这个例子中,result 是规则描述的非终端符号。Components 是根据规则放在一起的不同的终端和非终端符号。 如果匹配特定序列的话 Components 后面可以跟随要执行的动作。

param : NAME EQ NAME { printf("\tName:%s\tValue(name):%s\n", $1,$3);}
| NAME EQ VALUE{ printf("\tName:%s\tValue(value):%s\n",$1,$3);}
;

如果上例中序列 NAME EQ NAME 被匹配,将执行相应的 { } 括号中的动作。 这里另一个有用的就是 $1$3 的使用, 它们引用了标记 NAME 和 NAME(或者第二行的 VALUE)的值。

lexer 通过 Yacc 的变量 yylval 返回这些值。标记 NAME 的 Lex 代码是这样的:

char [A-Za-z]
name {char}+
%%
{name} { yylval = strdup(yytext);
return NAME; }

文件解析例子的规则段是这样的:

文件解析的语法
file : record file
| record
;
record: NAME EQ AGE { printf("%s is now %s years old!!!", $1, $3);};
%%

一般来说,Yacc 最好提供 yyerror(char msg) 函数的代码。 当解析器遇到错误时调用 yyerror(char msg)。错误消息作为参数来传递。 一个简单的 yyerror( char* ) 可能是这样的:

int yyerror(char* msg){
printf("Error: %s encountered at line number:%d\n", msg, yylineno);
}

用LEX和YACC工具分析正则表达式

正则语言的词法定义

  • 全集为ANCII码33-126的所有字符和空

  • 元符号有

    d_quotation   "
    backslash /
    l_parentheses (
    r_parentheses )
    l_square_brack [
    r_square_brack ]
    l_curly_braces {
    r_curly_braces }
    star *
    plus +
    horizontal_line -
    dot .
    question_mark ?
    vertical_line |
    caret ^
    equal =
    empty ε
    blank [\t\r\n]+

写出其词法分析器为

%{
#include <stdio.h>
#include <string.h>
#define LETTER 1
#define D_QUOTATION 2
#define BACKSLASH 3
#define L_PARENTHESES 4
#define R_PARENTHESES 5
#define L_SQUARE_BRACK 6
#define R_SQUARE_BRACK 7
#define L_CURLY_BRACES 8
#define R_CURLY_BRACES 9
#define STAR 10
#define PLUS 11
#define HORIZONTAL_LINE 12
#define DOT 13
#define QUESTION_MARK 14
#define VERTICAL_LINE 15
#define CARET 16
#define EQUAL 17
#define EMPTY 18
#define BLANK 19
%} allset [!-~]
blank [\t\r\n]+ %%
"\"" {return (D_QUOTATION);}
"\\" {return (BACKSLASH);}
"(" {return (L_PARENTHESES);}
")" {return (R_PARENTHESES);}
"[" {return (L_SQUARE_BRACK);}
"]" {return (R_SQUARE_BRACK);}
"{" {return (L_CURLY_BRACES);}
"}" {return (R_CURLY_BRACES);}
"*" {return (STAR);}
"+" {return (PLUS);}
"-" {return (HORIZONTAL_LINE);}
"." {return (DOT);}
"?" {return (QUESTION_MARK);}
"|" {return (VERTICAL_LINE);}
"^" {return (CARET);}
"=" {return (EQUAL);}
"ε" {return (EMPTY);}
{blank} {return (BLANK);}
{allset} {return (LETTER);} %% int yywrap(){
return 1;
} int main (int argc, char* argv[]){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
}
else{
printf("Usage:./scanner [source file] [dst file]\n");
exit(0);
}
while ((c = yylex())!=0){
printf("%d\n",c);
}
fclose(yyin);
return 0;
}

简单测试RE的词法分析器

正则表达式的语法规则

可以用字母开头的字母数字串来作为标识符命名正则表达式。在使用名字的时候要加花括号,如

binary = (0|1)+
real = {integer}.{nature}

正则表达式的上下文无关文法为

# List表示多个正则表达式的列表
List → List Named_RE | Named_RE
# Name_RE表示命名或未命名的正则表达式
Named_RE → ID = RE | RE
# RE 表示正则表达式
RE → RE “|” No_Select | No_Select
# No_Select表示不包含选择运算的正则表达式
No_Select → No_Select No_Join | No_Join
# No_Join表示不包含选择或者连接运算的正则表达式
No_Join → Base | Base * | Base + | Base ?
# Base表示最基本的正则表达式
Base → ( RE ) | LETTER | [No_Select] | "No_select " | . | { ID }
# ID表示标识符
ID → ID <letter> | ID <number> | <letter>

根据这些规则写的CFG:

%token D_QUOTATION BACKSLASH L_PARENTHESES R_PARENTHESES L_SQUARE_BRACK
R_SQUARE_BRACK L_CURLY_BRACES R_CURLY_BRACES STAR PLUS HORIZONTAL_LINE
DOT QUESTION_MARK VERTICAL_LINE CARET EQUAL EMPTY RE_LETTER LETTER
NUMBER BLANK %%
List : Named_RE {printf("Finish one line\n\n\n");} List
| Named_RE {printf("Finish prog! End!\n\n");}
;
Named_RE : ID EQUAL {printf("Find an ID\n\n");} RE BLANK
| RE {printf("Find an RE\n");} BLANK
;
RE : RE VERTICAL_LINE {printf("Finish VERTICAL_LINE\n");} No_Select
| { printf("%d No_select start\n",debug_i); debug_i++;}
No_Select
{ printf("Finish %d No_Select\n",debug_i);debug_i--;}
;
No_Select : No_Join {printf("Find next join\n");} No_Join
| No_Join {printf("End of No_Select\n");}
;
No_Join : Base
| Base STAR
| Base PLUS
| Base QUESTION_MARK
;
Base : L_PARENTHESES RE R_PARENTHESES
| L_SQUARE_BRACK RE R_SQUARE_BRACK
| D_QUOTATION No_Select D_QUOTATION
| L_CURLY_BRACES ID {printf("Confirm an ID\n");} R_CURLY_BRACES
| DOT
| LETTER
| NUMBER
| RE_LETTER
;
ID : ID LETTER
| ID NUMBER
| LETTER
;
%%

尝试出现很多conflict ,利用Yacc -v生成查错log

Yacc工具利用的是LALR文法,生成的查错报告也是将整个LALR打印在文件中。

文件的前一部分为归约规则,如下:

0 $accept: List $end

    1 @1: /* empty */

    2 List: Named_RE @1 List
3 | Named_RE
...

中间部分为每个状态的意义与GOTOACTION

state 20

   13 No_Select: No_Join . @6 No_Join
14 | No_Join . D_QUOTATION reduce using rule 12 (@6)
D_QUOTATION [reduce using rule 14 (No_Select)]
R_PARENTHESES reduce using rule 14 (No_Select)
R_SQUARE_BRACK reduce using rule 14 (No_Select)
VERTICAL_LINE reduce using rule 14 (No_Select)
BLANK reduce using rule 14 (No_Select)
$default reduce using rule 12 (@6) @6 go to state 32

可以看到,在状态20中,遇到D_QUOTATION有冲突。

为了解决这个问题,这里简单处理,舍弃其中的一条规则。我的语言只ID = RE的表达形式

但是对于引号的判断还是有问题,修改引号部分如下:

Base        : L_PARENTHESES RE R_PARENTHESES
| L_SQUARE_BRACK SQUARE_PART R_SQUARE_BRACK
| D_QUOTATION QUOTA_PART D_QUOTATION
| L_CURLY_BRACES ID R_CURLY_BRACES
| DOT
| LETTER
| NUMBER
| RE_LETTER
;
QUOTA_PART : QUOTA_PART LETTER
| LETTER
;

解决YACC冲突有以下方法:

  • 仔细选择书写的先后顺序,YACC默认移入/归约冲突时移入,归约/归约时选第一条。

    一般我们的语言可以选择最长匹配。

  • 如同上面引号,引入新的非终结符来解决冲突。

最终的RE.y文件中的CFG

%token D_QUOTATION BACKSLASH L_PARENTHESES R_PARENTHESES L_SQUARE_BRACK
R_SQUARE_BRACK L_CURLY_BRACES R_CURLY_BRACES STAR PLUS HORIZONTAL_LINE
DOT QUESTION_MARK VERTICAL_LINE CARET EQUAL EMPTY RE_LETTER LETTER
NUMBER BLANK %%
List : Named_RE {printf("======Finish one line======\n\n\n");} List
| Named_RE {printf("======Finish prog! End!======\n");}
;
Named_RE : ID EQUAL {printf("==Find an ID==\n\n");} RE {printf("==Find a RE==\n\n");} BLANK
;
RE : RE VERTICAL_LINE {printf("Finish VERTICAL_LINE\n");} No_Select
| { printf("%d No_select start\n",debug_i); debug_i++;}
No_Select
{ printf("Finish %d No_Select\n",debug_i); debug_i--;}
;
No_Select : No_Select {printf("Find next join\n");} No_Join
| No_Join {printf("End of No_Select\n");}
;
No_Join : Base
| Base STAR
| Base PLUS
| Base QUESTION_MARK
;
Base : L_PARENTHESES RE R_PARENTHESES
| L_SQUARE_BRACK SQUARE_PART R_SQUARE_BRACK
| D_QUOTATION QUOTA_PART {printf("QUOTATION END\n");}
| L_CURLY_BRACES ID {printf("Confirm an ID\n\n");} R_CURLY_BRACES
| DOT
| LETTER
| NUMBER
| RE_LETTER
;
QUOTA_PART : LETTER QUOTA_PART
| LETTER D_QUOTATION
;
SQUARE_PART : LETTER HORIZONTAL_LINE LETTER
| NUMBER HORIZONTAL_LINE NUMBER
; ID : ID LETTER
| ID NUMBER
| LETTER
;
%%

输入测试文件

letter = [A-Z]
number = [0-9]
id = ({letter}_)+({number}|{letter})*

测试结果

TOKEN:LETTER
TOKEN:LETTER
TOKEN:LETTER
TOKEN:LETTER
TOKEN:LETTER
TOKEN:LETTER
TOKEN:EQUAL
==Find an ID== 0 No_select start
TOKEN:L_SQUARE_BRACK
TOKEN:LETTER
TOKEN:HORIZONTAL_LINE
TOKEN:LETTER
TOKEN:R_SQUARE_BRACK
TOKEN:BLANK
End of No_Select
Finish 1 No_Select
==Find a RE== TOKEN:LETTER
======Finish one line====== ...# 后面略去
======Finish prog! End!======

生成中间语言(C语言)

考虑直接使用控制流的执行难度,这里用了正则语言的一个子集,去除了所有NFA的不确定的东西。

由于YACC中生成的头文件中默认为自己定义的YYSTYPE,这里需要在Makefile里面修改该关键字

Makefile如下:

CC = gcc
test:parse example
./parse example
gcc out.c -o test
parse:lex.yy.c y.tab.c
gcc lex.yy.c y.tab.c -o parse
y.tab.c:RE.y
yacc -d RE.y
cp y.tab.h{,.tmp}
sed 's/typedef int YYSTYPE/typedef char* YYSTYPE/' y.tab.h.tmp > y.tab.h
rm y.tab.h.tmp
lex.yy.c:RE.l
flex RE.l
clean:
-rm out.c
-rm test
-rm parse
-rm lex.yy.c
-rm y.tab.c

Re.y中的语法定义部分如下:

%%

List        : Named_RE  List
| Named_RE
;
Named_RE : ID { printf("== Find an ID ==> %s ==\n\n",$1); gen_id($1); }
EQUAL RE {printf("== Find a RE== \n\n"); gen_id_trail();} BLANK
;
RE : { printf("%d No_select start\n",debug_i); debug_i++;}
No_Select
{ printf("Finish %d No_Select\n",debug_i); debug_i--;}
;
No_Select : No_Select {printf("Find next join\n");} No_Join {base_i++;}
| No_Join {base_i++;printf("End of No_Select\n");}
;
No_Join : Base
;
Base : L_PARENTHESES RE R_PARENTHESES
| L_SQUARE_BRACK SQUARE_PART R_SQUARE_BRACK
| D_QUOTATION QUOTA_PART {printf("= QUOTATION %s =\n",$2);gen_quote($2);} D_QUOTATION
| L_CURLY_BRACES ID {printf("= Call an ID %s =\n",$2);gen_call($2);} R_CURLY_BRACES
| DOT {gen_dot();}
| LETTER {gen_next($1);}
| NUMBER {gen_next($1);}
| RE_LETTER {gen_next($1);}
;
QUOTA_PART : QUOTA_PART LETTER {strcat($1,$2);}
| LETTER
| QUOTA_PART NUMBER {strcat($1,$2);}
| NUMBER
;
SQUARE_PART : LETTER HORIZONTAL_LINE LETTER {gen_square($1,$3);}
| NUMBER HORIZONTAL_LINE NUMBER {gen_square($1,$3);}
;
ID : ID LETTER {strcat($1,$2);}
| ID NUMBER {strcat($1,$2);}
| LETTER
;
%% int main(int argc, char* argv[]){
if(argc == 2){
yyin = fopen(argv[1],"r");
if(yyin == NULL){
printf("Cannot open file!\n");
exit(0);
}
outfile = fopen("out.c","w");
gen_head();
yyparse();
gen_main();
fclose(outfile);
}
else{
printf("Usage:./test [filename]\n");
}
return 0;
} int yyerror(char *msg){
printf("Error encountered: %s \n", msg);
return 0;
}

其中用于生成C语言中间代码的功能函数如下:

void gen_main(){
fprintf(outfile,"int main(int argc,char* argv[]){\n");
fprintf(outfile,"\tcur_pos = 0;\n");
fprintf(outfile,"\tif(argc == 2){\n");
fprintf(outfile,"\t\tstrcpy(in_str,argv[1]);\n");
fprintf(outfile,"\t\ttarget();\n");
fprintf(outfile,"\t\tprintf(\"ACCEPT\\n\");\n");
fprintf(outfile,"\t}\n");
fprintf(outfile,"\treturn 0;\n");
fprintf(outfile,"}\n");
}

由于大部分类似,这里就不贴过多代码了。

测试文件:

letter = [A-Z]
num = [0-9]
target = "test"{letter} {num}.

生成C语言代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char in_str[1000];
int cur_pos;
void letter(){
if(in_str[cur_pos]>=65&&in_str[cur_pos]<=90){
cur_pos++;
}
else{
printf("ERROR DOT\n");exit(0);
}
}
void num(){
if(in_str[cur_pos]>=48&&in_str[cur_pos]<=57){
cur_pos++;
}
else{
printf("ERROR DOT\n");exit(0);
}
}
void target(){
if(in_str[cur_pos]=='t'){
cur_pos++;
}
else{
printf("ERROR t\n");exit(0);
}
if(in_str[cur_pos]=='e'){
cur_pos++;
}
else{
printf("ERROR e\n");exit(0);
}
if(in_str[cur_pos]=='s'){
cur_pos++;
}
else{
printf("ERROR s\n");exit(0);
}
if(in_str[cur_pos]=='t'){
cur_pos++;
}
else{
printf("ERROR t\n");exit(0);
}
letter();
num();
if(in_str[cur_pos]>=33&&in_str[cur_pos]<=126){
cur_pos++;
}
else{
printf("ERROR DOT\n");exit(0);
}
}
int main(int argc,char* argv[]){
cur_pos = 0;
if(argc == 2){
strcpy(in_str,argv[1]);
target();
printf("ACCEPT\n");
}
return 0;
}

测试:

./test testA9E
ACCEPT

最新文章

  1. 源码编译安装screen
  2. APACHE重写去除入口文件index.php
  3. ☀【JS】Code
  4. python统计英文首字母出现的次数
  5. CSS---网络编程
  6. MySQLdb安装和使用2
  7. 运用JavaScript构建你的第一个Metro式应用程序(on Windows 8)(一)
  8. leetcode先刷_Climbing Stairs
  9. hdu_5762_Teacher Bo(鸽笼原理)
  10. Clustering text documents using k-means
  11. 2018-2019-2 20175306实验二面向对象程序设计《Java开发环境的熟悉》实验报告
  12. 微信公众号开发JS-SDK(1.2)
  13. (1.13)mysql优化数据库对象
  14. LAMP架构介绍MySQL、MariaDB介绍 MySQL安装
  15. 反射导出 Excel
  16. sql server数据行号
  17. Swift - 获取状态栏一些信息
  18. Contest 6
  19. [javascript]jquery选择器笔记
  20. C#图解教程读书笔记(第7章 类和继承)

热门文章

  1. PDF修改器
  2. SMBLoris windows拒绝服务漏洞
  3. 4.写一个控制台应用程序,接收一个长度大于3的字符串,完成下列功能: 1)输出字符串的长度。 2)输出字符串中第一个出现字母a的位置。 3)在字符串的第3个字符后面插入子串“hello”,输出新字符串。 4)将字符串“hello”替换为“me”,输出新字符串。 5)以字符“m”为分隔符,将字符串分离,并输出分离后的字符串。 */
  4. vue指令v-for示例解析
  5. NYOJ 252 01串(斐波那契数列变形)
  6. 初探IAT
  7. ArrayList 和Vector ,HashTable和HashMap异同
  8. iOS 横竖屏切换解决方案
  9. bootstrap&amp;NPM淘宝代理镜像
  10. 前端到后台ThinkPHP开发整站(6)