我试图用LEX& amp;来实现时间解析器YACC。 我是这些工具和C编程的完全新手。
当输入其中一种格式时,程序必须打印一条消息(有效时间格式1:输入):下午4点,下午7点38分,23:42,3:16,凌晨3:16,否则会打印"无效字符" 消息。
lex档 time.l :
%{
#include <stdio.h>
#include "y.tab.h"
%}
%%
[0-9]+ {yylval=atoi(yytext); return digit;}
"am" { return am;}
"pm" { return pm;}
[ \t\n] ;
[:] { return colon;}
. { printf ("Invalid character\n");}
%%
yacc文件 time.y :
%{
void yyerror (char *s);
int yylex();
#include <stdio.h>
#include <string.h>
%}
%start time
%token digit
%token am
%token pm
%token colon
%%
time : hour ampm {printf ("Valid time format 1 : %s%s\n ", $1, $2);}
| hour colon minute {printf ("Valid time format 2 : %s:%s\n",$1, $3);}
| hour colon minute ampm {printf ("Valid time format 3 : %s:%s%s\n",$1, $3, $4); }
;
ampm : am {$$ = "am";}
| pm {$$ = "pm";}
;
hour : digit digit {$$ = $1 * 10 + $2;}
| digit { $$ = $1;}
;
minute : digit digit {$$ = $1 * 10 + $2;}
;
%%
int yywrap()
{
return 1;
}
int main (void) {
return yyparse();
}
void yyerror (char *s) {fprintf (stderr, "%s\n", s);}
使用此命令进行编译:
yacc -d time.y && lex time.l && cc lex.yy.c y.tab.c -o time
我收到了一些警告:
time.y:17:47: warning: format specifies type 'char *' but the argument has type
'YYSTYPE' (aka 'int') [-Wformat]
{printf ("Valid time format 1 : %s%s\n ", (yyvsp[(1) - (2)]), (yyvsp.
对于printf语句中的所有变量,都会显示此警告。
这些值都是char,因为即使时间字符串中的数字也使用atoi
函数进行转换。
使用有效输入执行程序会抛出此错误:
./time
1pm
[1] 2141 segmentation fault ./time
有人能帮助我吗? 提前谢谢。
答案 0 :(得分:1)
这(f)lex规则:
[0-9]+ {yylval=atoi(yytext); return digit;}
识别任何整数,而不仅仅是数字。 (它允许前导零,这可能适用于日期解析器。)它假定yylval
是int
,如果你不做某事来声明{{1}的类型就是这种情况。 1}}。
同时,这(f)lex规则:
yylval
识别令牌"am" { return am;}
,但未设置am
的值。
现在,在你的野牛文件中,你有:
yylval
由于hour : digit digit { $$ = $1 * 10 + $2; }
| digit { $$ = $1;}
;
实际上代表整个整数,因此digit
生成不正确。例如,它会识别输入digit digit
(因为您的flex文件忽略空格),但它会将其转换为值305(10 * 23 + 75)。这似乎不合适。同样,它假设语义值23 75
和$$
的类型是$1
,这是默认情况。
但是,制作:
int
要求结果语义值的类型为ampm : am {$$ = "am";}
| pm {$$ = "pm";}
;
(或甚至char *
)。由于您没有做任何声明语义值类型的操作,因此它们的类型为const char*
,并且赋值与C语句一样无效:
int
因此C编译器会发出错误消息。
此外,在您的制作中:
int ampm = "am";
您认为语义值time : hour ampm {printf ("Valid time format 1 : %s%s\n ", $1, $2);}
和$1
是字符串($2
)。 BUt值实际上是整数,因此char*
将执行一些未定义的事情,可能是灾难性的(在本例中为segfault)。 (由于C的性质,这不是编译时错误,但大多数C编译器会发出警告。显然,你的C编译器会这样做。)
如何解决这个问题取决于你对作业的解释。当它显示“打印消息(有效时间格式1:输入)”时,是否意味着应该打印文字输入字符串,还是可以打印字符串的解释?也就是说,给定实际输入
printf
您希望消息是
吗?8:23am
08:23am
或者是否适合规范化:
Valid time format 1: 8:23am
Valid time format 1: 08:23am
您应该(重新)阅读bison
manual on semantic types中的部分,然后决定您是希望类型为Valid time format 1: 8:23am
Valid time format 1: 8:23am
,int
还是两者的并集。< / p>
您需要考虑的其他一些事情:
您的flex文件识别任何整数,但小时和分钟都不能是任意整数。两者都限于两位数;通常,会议记录应始终为两位数(因此char*
不是一种写作方式9:3am
)。它们都具有有限的有效值范围;分钟必须介于9:03am
和00
之间,如果指定am或pm,则小时介于1到12之间,否则介于0和23之间。或者可能是24.(实际上,有很多不同的可能数小时的有效性约定;您可以选择灵活或严格。)
您的问题描述似乎不允许时间规范中的空格,但您的flex文件会忽略空格。这可能会导致您识别不正确的输入(同样取决于您希望的严格程度)。在这种情况下,请参阅有关输出的说明:输出中是否出现空白(假设可以接受)?
当您的flex文件看到无法识别的字符时会发出错误消息,但它不会停止。实际上,这意味着将从输入流中删除非法字符,以便输入如下:
59
将导致两条非法字符消息,后跟一条消息,指出输入是有效的1;:17rpm
。这不太可能是你想要的。
作为最后一点,我必须说,在我看来,理解C是使用flex和bison的绝对先决条件。试图同时教三个人都是教学上的嫌疑人。
答案 1 :(得分:0)
错误消息
time.y:17:47: warning: format specifies type 'char *' but the argument has type
'YYSTYPE' (aka 'int') [-Wformat]
代表该行
printf ("Valid time format 1 : %s%s\n ", $1, $2);
表示您指定了%s
(类型为char *
的C样式字符串)但实际上该参数的类型为YYSTYPE
(似乎是整数类型)
答案 2 :(得分:0)
正如@Elyasin所指出的那样,你给出的错误信息正在告诉你究竟是什么错误 - YYSTYPE默认为int但是你试图将它用作字符串(这个在每一行都会出现错误)。此外,你试图在某些地方使用它作为int,在其他地方使用字符串,这显然是不正确的。
你可以做的是创建一个字符串来保存输入并连接到它。您可以在初始yacc块中使用变量执行此操作,如下所示:
%{
void yyerror (char *s);
int yylex();
#include <stdio.h>
#include <string.h>
char time_str[15];
%}
time_str现在可用于整个解析器步骤,因此您可以复制到该步骤,然后在最后一步中,您可以打印出构建的字符串,例如
printf ("Valid time format 1 : %s", timestr);
答案 3 :(得分:0)
我已经解决了为am和pm值定义char数组的警告,并将YYSTYPE
变量视为int(如建议的那样)。
我还添加了空行的情况,每次输入后的逗号分隔,小时和分钟的验证,退出命令:
%{
void yyerror (char *s);
int yylex();
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char ampm_str[15] = "";
typedef int bool;
bool validFormat = 1;
%}
%start input
%token digit
%token am
%token pm
%token colon
%token sep
%token exit_command
%%
input : /* empty */
| input line
;
line : '\n'
| list '\n'
;
list : time
| time sep list
| exit_command {exit(EXIT_SUCCESS);}
;
time : hour ampm {if ($1 > 12 || $1 <= 0) {printf ("Hour out of range\n");validFormat = 0;} else if(validFormat) {printf("Valid time format %d%s\n", $1, ampm_str); } validFormat = 1;}
| hour colon minute {if ($1 > 24 || $1 <= 0) {printf ("Hour out of range\n");validFormat = 0;} else if(validFormat) {printf("Valid time format %d:%d\n", $1, $3); } validFormat = 1;}
| hour colon minute ampm {if ($1 > 12 || $1 <= 0) {printf ("Hour out of range\n");validFormat = 0;} else if(validFormat) {printf ("Valid time format %d:%d%s\n", $1, $3, ampm_str); } validFormat = 1;}
;
hour : two_digits { $$ = $1; }
| digit { $$ = $1; }
;
minute : two_digits { $$ = $1; if ($$ > 59) {printf ( "minute out of range\n");validFormat = 0;}}
| digit { $$ = $1; if ($$ > 59) {printf ( "minute out of range\n");validFormat = 0;}}
;
two_digits : digit digit {$$ = 0; $$ = $1 * 10 + $2; }
;
ampm : am {strcpy(ampm_str, "am");}
| pm {strcpy(ampm_str, "pm");}
;
%%
int yywrap()
{
return 1;
}
int main (void) {
printf ("Insert time, and press enter\n");
printf ("Type , after each time\n");
printf ("Valid formats : 2am, 12:00, 13:30pm\n");
printf ("exit to quit\n");
return yyparse();
}
void yyerror (char *s) {fprintf (stderr, "Invalid character: %s\n", s); validFormat = 0;}
答案 4 :(得分:0)
用于字节解析 在lex文件中 0x [0-9a-f] {8} {yylval.number = strtoll(yytext + 2,NULL,16);返回BYTE_4; } 在yacc文件中 您需要取消将此号码作为并集的一部分。