自制编译器 青木峰郎 笔记 Ch4 基于JavaCC的扫描器的描述

4.1 javacc正则表达式

e.g:

  • 固定字符串:"int"
  • 连接:"ABC" "XYC"
  • 字符组: ["A", "B"]
  • 排除: ~["X", "Y"]
  • 任意字符: ~[]
  • ("o")+
  • ("o")?
  • ("o")*
  • ("o"){5}
  • ("o"){3,5}
  • "int"|"Int"

4.2 扫描没有结构的单词

// reserved words
// #@@range/lex_reswords{
TOKEN: {
      <VOID     : "void">
    | <CHAR     : "char">
    | <SHORT    : "short">
    | <INT      : "int">
    | <LONG     : "long">
    | <STRUCT   : "struct">
    | <UNION    : "union">
    | <ENUM     : "enum">
    | <STATIC   : "static">
    | <EXTERN   : "extern">
    | <CONST    : "const">
    | <SIGNED   : "signed">
    | <UNSIGNED : "unsigned">
    | <IF       : "if">
    | <ELSE     : "else">
    | <SWITCH   : "switch">
    | <CASE     : "case">
    | <DEFAULT_ : "default">
    | <WHILE    : "while">
    | <DO       : "do">
    | <FOR      : "for">
    | <RETURN   : "return">
    | <BREAK    : "break">
    | <CONTINUE : "continue">
    | <GOTO     : "goto">
    | <TYPEDEF  : "typedef">
    | <IMPORT   : "import">
    | <SIZEOF   : "sizeof">
}
// #@@}

// identifier
// #@@range/lex_ident{
TOKEN: {
    <IDENTIFIER: ["a"-"z", "A"-"Z", "_"] (["a"-"z", "A"-"Z", "_", "0"-"9"])*>
}
// #@@}

// integer literals
// #@@range/lex_integer{
TOKEN: {
    <INTEGER: ["1"-"9"] (["0"-"9"])* ("U")? ("L")?
            | "0" ["x", "X"] (["0"-"9", "a"-"f", "A"-"F"])+ ("U")? ("L")?
            | "0" (["0"-"7"])* ("U")? ("L")?
            >
}
// #@@}

注意javacc会尝试匹配所有的规则,然后选择匹配到的字符数目最多的,比如VoidFunction,与"Void"相比只匹配到4个,但是与Identifier的规则比能够匹配到12个,所以会被视为Identifier。
但是,如果字符数目相同,会优先选择最早定义的。因此,为了识别"void"这个单词,<VOID|"void">这个规则需要写在Identifier之前。

4.3 扫描不生成Token的单词

e.g:

// linear-white-spaces
// #@@range/lex_spaces{
SPECIAL_TOKEN: { <SPACES: ([" ", "	", "
", "
", "f"])+> }
// #@@}

SKIP:跳过,SPECIAL_TOKEN:跳过但保存跳过的token,可以通过下面的SpecialToken属性访问。

4.4 扫描具有结构的单词

javacc能够跳转状态或者从状态出发,注意没有特别指定的状态都是DEFAULT状态。

SKIP: {<"/*">: IN_BLOCK_COMMENT}
<IN_BLOCK_COMMENT> SKIP: {<~[]>}//这里因为是一个字符所以才敢写成如此,两个字符的话就会把*/忽略了
<IN_BLOCK_COMMENT> SKIP: {<"*/">: DEFAULT}

但是仅仅如此,无法在块注释没有关闭的情况下报错,也就是

int main(){
}/*

会被编译器认为是可以接受的。
所以必须使用MORE指令,说明仅仅匹配到该规则那么扫描不应该结束(遇到EOF)。

// block comment
// #@@range/lex_block_comment{
MORE: { <"/*"> : IN_BLOCK_COMMENT }
<IN_BLOCK_COMMENT> MORE: { <~[]> }
<IN_BLOCK_COMMENT> SPECIAL_TOKEN: { <BLOCK_COMMENT: "*/"> : DEFAULT }
// #@@}

扫描String Literal

// #@@range/lex_block_comment{
MORE: { <"/*"> : IN_BLOCK_COMMENT }
<IN_BLOCK_COMMENT> MORE: { <~[]> }
<IN_BLOCK_COMMENT> SPECIAL_TOKEN: { <BLOCK_COMMENT: "*/"> : DEFAULT }
// #@@}

原文地址:https://www.cnblogs.com/xuesu/p/14377498.html