查看“Lexer-rules.md”的源代码
←
Lexer-rules.md
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{MARKDOWN}} # Lexer Rules 一个lexer grammar由lexer rules组成,可以选择分为多个modes。 Lexical modes允许我们将单个lexer grammar拆分为多个sublexers。 lexer只能返回与当前mode中的规则匹配的Token。 Lexer规则指定Token定义,并且或多或少遵循parser rules的语法,只是lexer rules不能有参数、返回值或局部变量。 Lexer规则名称必须以大写字母开头,这将它们与parser rule名称区分开来: ``` /** Optional document comment */ TokenName : alternative1 | ... | alternativeN ; ``` 您还可以定义一些特殊的规则,这些规则不是Token,而是有助于识别Token。 这些fragment rule不会产生parser可见的Token: ``` fragment HelperTokenRule : alternative1 | ... | alternativeN ; ``` 例如,`DIGIT` 是一个非常常见的片段规则: ``` INT : DIGIT+ ; // 引用DIGIT辅助规则 fragment DIGIT : [0-9] ; //本身不是Token ``` ## Lexical Modes Modes允许您按上下文对lexical rules进行分组,例如XML标记的内部和外部。 这就像有多个sublexers,每个上下文对应一个sublexers。 lexer只能返回通过在当前Mode中输入规则匹配的Tokens。 Lexers从所谓的默认Mode开始。 除非指定Mode命令,否则所有规则都将被视为处于默认Mode。 combined grammars中不允许使用Mode,只能在lexer grammars中使用。 (请参阅 grammar `XMLLexer` [Tokenizing XML](http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference)。) ``` rules in default mode ... mode MODE1; rules in MODE1 ... mode MODEN; rules in MODEN ... ``` ## Lexer Rule Elements Lexer rules允许两种parser rules无法使用的结构: .. range运算符和用方括号括起来的字符集表示法[characters]。 不要将字符集与parser rules的参数混淆。这里[characters] 仅表示lexer中的字符集。 以下是所有lexer rule元素的摘要: <table> <tr> <th>Syntax</th><th>Description</th> </tr> <tr> <td>T</td><td> 在当前输入位置匹配Token T。 Token总是以大写字母开头。</td> </tr> <tr> <td>’literal’</td><td> 匹配该字符或字符序列。例如,“while”或“=”。</td> </tr> <tr> <td>[char set]</td><td> <p>匹配字符集中指定的一个字符。 Interpret <tt>x-y</tt> as the set of characters between range <tt>x</tt> and <tt>y</tt>, inclusively. 以下转义字符被解释为单个特殊字符: <tt>\n</tt>, <tt>\r</tt>, <tt>\b</tt>, <tt>\t</tt>, <tt>\f</tt>, <tt>\uXXXX</tt>, and <tt>\u{XXXXXX}</tt>. To get <tt>]</tt> or <tt>\</tt> you must escape them with <tt>\</tt>. To get <tt>-</tt> you must escape it with <tt>\</tt> too, except for the case when <tt>-</tt> is the first or last character in the set.</p> <p>You can also include all characters matching Unicode properties (general category, boolean, or enumerated including scripts and blocks) with <tt>\p{PropertyName}</tt> or <tt>\p{EnumProperty=Value}</tt>. (You can invert the test with <tt>\P{PropertyName}</tt> or <tt>\P{EnumProperty=Value}</tt>).</p> <p>For a list of valid Unicode property names, see <a href="http://unicode.org/reports/tr44/#Properties">Unicode Standard Annex #44</a>. (ANTLR also supports <a href="http://unicode.org/reports/tr44/#General_Category_Values">short and long Unicode general category names and values</a> like <tt>\p{Lu}</tt>, <tt>\p{Z}</tt>, <tt>\p{Symbol}</tt>, <tt>\p{Blk=Latin_1_Sup}</tt>, and <tt>\p{Block=Latin_1_Supplement}</tt>.)</p> <p>As a shortcut for <tt>\p{Block=Latin_1_Supplement}</tt>, you can refer to blocks using <a href="http://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt">Unicode block names</a> prefixed with <tt>In</tt> and with spaces changed to <tt>_</tt>. For example: <tt>\p{InLatin_1_Supplement}</tt>, <tt>\p{InYijing_Hexagram_Symbols}</tt>, and <tt>\p{InAncient_Greek_Numbers}</tt>.</p> <p>A few extra properties are supported:</p> <ul> <li><tt>\p{Extended_Pictographic}</tt> (see <a href="http://unicode.org/reports/tr35/">UTS #35</a>)</li> <li><tt>\p{EmojiPresentation=EmojiDefault}</tt> (code points which have colorful emoji-style presentation by default but which can also be displayed text-style)</li> <li><tt>\p{EmojiPresentation=TextDefault}</tt> (code points which have black-and-white text-style presentation by default but which can also be displayed emoji-style)</li> <li><tt>\p{EmojiPresentation=Text}</tt> (code points which have only black-and-white text-style and lack a colorful emoji-style presentation)</li> </ul> <p>Property names are <b>case-insensitive</b>, and <tt>_</tt> and <tt>-</tt> are treated identically</p> <p>以下是几个例子:</p> <pre> WS : [ \n\u000D] -> skip ; // 与[\n\r]相同 UNICODE_WS : [\p{White_Space}] -> skip; // 匹配所有Unicode空格 ID : [a-zA-Z] [a-zA-Z0-9]* ; // 匹配常用identifier规范 UNICODE_ID : [\p{Alpha}\p{General_Category=Other_Letter}] [\p{Alnum}\p{General_Category=Other_Letter}]* ; //全Unicode字母ID匹配 EMOJI : [\u{1F4A9}\u{1F926}] ; // 注意Unicode码点> U FFFF DASHBRACK : [\-\]]+ ; // match - or ] one or more times DASH : [---] ; //匹配单个字符 - ,即介于 - 和 - 之间的“任意字符”(注意第一个和最后一个 - 不转义) </pre> </td> </tr> <tr> <td>’x’..’y’</td><td> 匹配范围x和y之间的任何单个字符(包括x和y)。例如,‘a’..‘z’。‘a’..‘z’等同于[a-z]。</td> </tr> <tr> <td>T</td><td> 调用lexer规则T; 通常允许递归,但不允许左递归。T可以是常规Token或fragment rule。 <pre> ID : LETTER (LETTER|'0'..'9')* ; fragment LETTER : [a-zA-Z\u0080-\u00FF_] ; </pre> </td> </tr> <tr> <td>.</td><td> 点是匹配任何单个字符的单字符通配符。例子: <pre> ESC : '\\' . ; // match any escaped \x character </pre> </td> </tr> <tr> <td>{«action»}</td><td> 从4.2开始,Lexer actions可以出现在任何地方,而不仅仅是出现在最外层alternative的末尾。 lexer根据规则中动作的位置在适当的输入位置执行动作。 要对具有多个alternatives的Rule执行单个操作,可以将alternatives括在括号中,然后将操作放在后面: <pre> END : ('endif'|'end') {System.out.println("found an end");} ; </pre> <p>该action符合目标语言的语法。 ANTLR将Action的内容逐字复制到生成的代码中; parser actions中没有像$x.y这样的表达式翻译。</p> <p> 仅执行最外层Token Rule内的Action。 换句话说,如果STRING调用ESC_CHAR并且ESC_CHAR具有一个Action,则当Lexer开始在STRING中匹配时,该操作不会执行。</p></td> </tr> <tr> <td>{«p»}?</td><td> 计算语义谓词«p»。 如果«p»在运行时的计算结果为false,则周围的规则将变为“不可见”(不可用)。 表达式 «p» 符合目标语言语法。 虽然语义谓词可以出现在lexer规则中的任何地方,但将它们放在规则末尾是最有效的。 有一点需要注意,语义谓词必须在lexer actions之前。 请参阅Lexer Rules中的Predicates。</td> </tr> <tr> <td>~x</td><td> 匹配不在x描述的集合中的任何单个字符。 Set x can be a single character literal, a range, or a subrule set like ~(’x’|’y’|’z’) or ~[xyz]. 以下是使用~的例子,匹配~[\r\n]*字符以外的任何字符的rule: <pre> COMMENT : '#' ~[\r\n]* '\r'? '\n' -> skip ; </pre> </td> </tr> </table> 就像parser rules一样,lexer rules允许括号和EBNF运算符中的subrules: `?`, `*`, `+`。 `COMMENT` rule说明了`*`和`?`运算符。 `+`的常见用法是`[0-9]+`来匹配整数。 Lexer subrules也可以在那些EBNF运算符上使用非贪婪`?`后缀。 ## Recursive Lexer Rules 与大多数词汇语法工具不同,ANTLR的lexer rules可以是递归的。 当您想要匹配嵌套的标记(如嵌套的动作块:`{...{...}...}` 时,这非常方便。 ``` lexer grammar Recur; ACTION : '{' ( ACTION | ~[{}] )* '}' ; WS : [ \r\t\n]+ -> skip ; ``` ## Redundant String Literals 请注意,不要在多个lexer rules的右侧指定相同的字符串文字。 这样的文本是不明确的,可以匹配多个token types。 ANTLR使此文字对parser不可用。 跨Mode的Rule也是如此。 例如,以下lexer grammar定义了两个具有相同字符序列的标记: ``` lexer grammar L; AND : '&' ; mode STR; MASK : '&' ; ``` parser grammar不能引用字面上的 ’&’,但是它可以引用Token的名称: ``` parser grammar P; options { tokenVocab=L; } a : '&' // results in a tool error: no such token AND // no problem MASK // no problem ; ``` 下面是一个构建和测试序列: ```bash $ antlr4 L.g4 # yields L.tokens file needed by tokenVocab option in P.g4 $ antlr4 P.g4 error(126): P.g4:3:4: cannot create implicit token for string literal '&' in non-combined grammar ``` ## Lexer Rule Actions ANTLR lexer在匹配lexical rule后创建Token对象。 每个Token请求都以 `Lexer.nextToken` 开始,一旦识别出Token,它就会调用 “emit”。 `emit`从lexer的当前状态收集信息以构建Token。 访问字段`_type`、`_text`、`_channel`、`_tokenStartCharIndex`、`_tokenStartLine`、和`_tokenStartCharPositionInLine`。 您可以使用各种setter方法 (例如 `setType`) 设置它们的状态。 例如,如果`enumIsKeyword`为false,则以下规则将`enum`转换为identifier。 ``` ENUM : 'enum' {if (!enumIsKeyword) setType(Identifier);} ; ``` ANTLR在lexer actions中不执行特殊的`$x`属性转换(与v3不同)。 lexical rule最多只能有一个动作,而不管该规则中有多少种alternatives。 ## Lexer Commands 为了避免将语法与特定目标语言绑定,ANTLR支持lexer命令。 与arbitrary embedded actions不同,这些命令遵循特定的语法,并且仅限于几个常见命令。 Lexer命令出现在lexer规则定义的最外层alternative的末尾。 与arbitrary actions一样,每个Token规则只能有一个。 lexer命令由`->`操作符和一个或多个命令名组成,这些命令名可以选择性地接受参数: ``` TokenName : «alternative» -> command-name TokenName : «alternative» -> command-name («identifier or integer») ``` alternative可以有多个命令用逗号分隔。 以下是有效的命令名: * skip * more * popMode * mode( x ) * pushMode( x ) * type( x ) * channel( x ) 有关用法,请参阅书中的源代码,示例如下所示: ### skip 'skip'命令告诉lexer程序获取另一个Token并丢弃当前文本。 ``` ID : [a-zA-Z]+ ; //匹配identifiers INT : [0-9]+ ; //匹配integers NEWLINE:'\r'? '\n' ; // 将换行符返回到parser (是结束语句信号) WS : [ \t]+ -> skip ; // 去掉空白 ``` ### mode(), pushMode(), popMode, and more MODE命令改变模式堆栈,从而改变lexer的模式。 'more' 命令迫使lexer获得另一个Token,但不会丢弃当前文本。 Token类型将是匹配的“final”规则的类型(即没有“more”或“skip”命令的类型)。 ``` // Default "mode": Everything OUTSIDE of a tag COMMENT : '<!--' .*? '-->' ; CDATA : '<![CDATA[' .*? ']]>' ; OPEN : '<' -> pushMode(INSIDE) ; ... XMLDeclOpen : '<?xml' S -> pushMode(INSIDE) ; SPECIAL_OPEN: '<?' Name -> more, pushMode(PROC_INSTR) ; // ----------------- Everything INSIDE of a tag --------------------- mode INSIDE; CLOSE : '>' -> popMode ; SPECIAL_CLOSE: '?>' -> popMode ; // close <?xml...?> SLASH_CLOSE : '/>' -> popMode ; ``` 另请查看: ``` lexer grammar Strings; LQUOTE : '"' -> more, mode(STR) ; WS : [ \r\t\n]+ -> skip ; mode STR; STRING : '"' -> mode(DEFAULT_MODE) ; // token we want parser to see TEXT : . -> more ; // collect more text for string ``` 弹出Mode堆栈的底层将导致异常。 使用`mode`切换模式会更改当前堆栈顶部。 多个`more`与只有一个`more`是一样的,位置并不重要。 ### type() ``` lexer grammar SetType; tokens { STRING } DOUBLE : '"' .*? '"' -> type(STRING) ; SINGLE : '\'' .*? '\'' -> type(STRING) ; WS : [ \r\t\n]+ -> skip ; ``` 对于多个 'type()'命令,只有最右边才有效果。 ### channel() ``` BLOCK_COMMENT : '/*' .*? '*/' -> channel(HIDDEN) ; LINE_COMMENT : '//' ~[\r\n]* -> channel(HIDDEN) ; ... // ---------- // Whitespace // // Characters and character constructs that are of no import // to the parser and are used to make the grammar easier to read // for humans. // WS : [ \t\r\n\f]+ -> channel(HIDDEN) ; ``` 从4.5开始,您还可以像enumerations一样,使用lexer rules之上的以下构造定义通道名称: ``` channels { WSCHANNEL, MYHIDDEN } ```
返回至“
Lexer-rules.md
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息