我必须为词法分析器创建标识符和数字的转换图。
代码如下:
/* recursive factorial function */
int fact (int x )
{
if (x>1)
return x * fact (x-1);
else
return 1;
}
void main (void)
{
int x;
x = read();
if (x > 0) write (fact (x));
}
我对如何创建此图表感到有点迷茫。任何人都可以指出我正确的方向或包括可以帮助我完成这项任务的资源吗?
答案 0 :(得分:0)
词法分析器以null或初始状态开始。它击中了" i"。所以它知道它必须有关键字或标识符。它击中了' n'并且' t'并将它们添加到令牌。它击中了空间。所以它知道令牌的结尾,即" int",一个关键字。现在它击中了' f。同样的故事,但令牌是"事实",这不是关键字,所以它是一个标识符。现在'(' - 这是一个开括号括号。所以它继续。
当它出现' /'这可能是一个除法令牌或评论的开头,实际上它是评论的开头。所以它现在进入评论状态,直到达到* /。
除了那里有一些整数文字标记外,没有什么显着不同。为了方便你,没有任何字符串。 main是一个特殊情况,取决于词法分析器的编写方式,它可以被视为关键字或普通标识符。
答案 1 :(得分:0)
Malcolm McLean告诉你如何在实际代码中完成它,但我认为你需要一个有限状态机的理论方法。
首先进行库存检查:需要什么,我们有什么符号等等。示例代码中的EBNF:
space = ? US-ASCII character 32 ?;
zero = '0';
digit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
character = 'a' | 'A' | 'b' | 'B' ... 'z' | 'Z';
(* a single digit might be zero but a number must not start with a zero (no octals) *)
integer = (digit|zero) | ( digit,{(digit|zero)});
(* identifier must start with a character *)
identifier = character,{ (digit | character) };
(* the keywords from the example, feel free to add more *)
keywords = "if" | "else" | "return" | "int" | "void";
(* TODO: line-end, tabs, etc. *)
delimiter = space, {space};
braceleft = '{';
braceright = '}';
parenleft = '(';
parenright = ')';
equal = '=';
greater = '>';
smaller = '<';
minus = '-';
product = '*';
semicolon = ';'
end = ? byte denoting EOF (end of file) ?;
现在制作转换表。从状态START开始。 START只是一个开始状态,没什么特别的,没什么可做的,但我们需要从某个地方开始。所以从那里我们可以得到任何上述字符。实际上,在每个州之后总是如此,所以我们可以做C&amp; P;
START
zero -> ZERO
digit -> INTEGER
character -> IDENTIFIER
space -> START
braceleft -> BRACES
braceright -> BRACES
parenleft -> PARENTHESES
parenright -> PARENTHESES
equal -> COMPARING
greater -> COMPARING
smaller -> COMPARING
minus -> ARITHMETIC
product -> ARITHMETIC
semicolon -> START
end -> END
ZERO
zero -> ERROR (well...)
digit -> ERROR
character -> ERROR
space -> START
braceleft -> BRACES
braceright -> BRACES
parenleft -> PARENTHESES
parenright -> PARENTHESES
equal -> COMPARING
greater -> COMPARING
smaller -> COMPARING
minus -> ARITHMETIC
product -> ARITHMETIC
semicolon -> START
end -> END
INTEGER
zero -> INTEGER
digit -> INTEGER
character -> ERROR
space -> START
braceleft -> BRACES
braceright -> BRACES
parenleft -> PARENTHESES
parenright -> PARENTHESES
equal -> COMPARING
greater -> COMPARING
smaller -> COMPARING
minus -> ARITHMETIC
product -> ARITHMETIC
semicolon -> START
end -> END
状态IDENTIFIER
表示我们已经有了一个角色,所以
IDENTIFIER
zero -> IDENTIFIER
digit -> IDENTIFIER
character -> IDENTIFIER
space -> START
braceleft -> BRACES
braceright -> BRACES
parenleft -> PARENTHESES
parenright -> PARENTHESES
equal -> COMPARING
greater -> COMPARING
smaller -> COMPARING
minus -> ARITHMETIC
product -> ARITHMETIC
semicolon -> START
end -> END
除状态ERROR
ERROR
之后没有任何内容
ERROR -> ERROR
除状态END
ERROR
之后没有任何内容
END -> ERROR
ARITHMETIC
zero -> ZERO
digit -> INTEGER
character -> IDENTIFIER
space -> START
braceleft -> BRACES
braceright -> BRACES
parenleft -> PARENTHESES
parenright -> PARENTHESES
equal -> COMPARING
greater -> COMPARING
smaller -> COMPARING
minus -> ARITHMETIC
product -> ARITHMETIC
semicolon -> START
end -> END
将计数和余额检查留给解析器
BRACES -> START
PARENTHESES -> START
COMPARING
zero -> ZERO
digit -> INTEGER
character -> IDENTIFIER
space -> START
braceleft -> BRACES
braceright -> BRACES
parenleft -> PARENTHESES
parenright -> PARENTHESES
equal -> ERROR (only check for single characters here, no ">=" or similar)
greater -> ERROR
smaller -> ERROR
minus -> ERROR
product -> ERROR
semicolon -> ERROR
end -> ERROR
希望我没有实现任何严重错误,剩下的唯一问题是空格和关键字。 使用示例&#34; if&#34;:
首次出现角色
character -> KEYWORDS
KEYWORDS
'i' -> IF
'r' -> RETURN
...
any other character (exc. parens etc.) -> IDENTIFIER
IF
'f' -> IT_IS_IF
...
any other character (exc. parens etc.) -> IDENTIFIER
IT_IS_IF
'(' -> START
')' -> ERROR
'=' -> ERROR
...
digit or character -> IDENTIFIER
当然,您可以使用快捷方式进行操作,并将每个关键字设为单个符号,否则会非常繁琐。我猜是允许一点作弊?
再次出现在角色的第一次出现
character -> KEYWORDS
KEYWORDS
if_symbol -> IF
else_symbol -> ELSE
return_symbol -> RETURN
...
digit or character -> IDENTIFIER
IF
'(' -> PARENTHESES
')' -> ERROR
'=' -> ERROR
...
那么,可以你只是跳过所有的空格?像
这样的结构return x;
和
一样合法returnx;
因此,一旦你有一个完整的关键字,它后面跟一个空格(或一个分号或大括号或允许某个重新分隔的单词之后的任何符号)或后跟一个字符/数字,使其成为一个标识符,或者接着是不允许的事情。其余的可以,而且应该留给解析器。
或者你采取了第一个方法:一旦你有一个关键词,你就回去开始,所以returnx;
会被视为RETURN IDENTIFIER SEMICOLON
。但这会减少可能的标识符的数量,例如:ifitsone
将是IF ERROR
,这很可能会导致你的bug列表中有很多愤怒的条目。
通过上述所有信息,您可以构建表格。如果我们将行设置为状态,将列设置为符号
zero digit character space braceleft braceright parenleft ...
START ZERO INTEGER IDENTIFIER START BRACES BRACES PARENTHESES ...
ZERO ERROR ERROR ERROR START BRACES BRACES PARENTHESES ...
INTEGER INTEGER INTEGER ERROR START BRACES BRACES PARENTHESES ...
IDENTIFIER IDENTIFIER IDENTIFIER IDENTIFIER START BRACES BRACES PARENTHESES ...
...
注意:上述所有内容都非常简化,可能包含错误!但基本上它是如何运作的,它不是 复杂,它只是有一些你需要学习的花哨的名字。
刚刚看到Malcolm McLean的回答被认为是可以接受的,所以......