Antlr4如何构建允许关键字作为标识符的语法
Antlr4 how to build a grammar allowed keywords as identifier
这是一个演示代码
label:
var id
let id = 10
goto label
如果允许关键字作为标识符将是
let:
var var
let var = 10
goto let
这是完全合法的代码。但是在antlr中似乎很难做到这一点。
AFAIK,如果 antlr 匹配令牌 let,将永远不会回退到 id 令牌。所以对于antlr它会看到
LET_TOKEN :
VAR_TOKEN <missing ID_TOKEN>VAR_TOKEN
LET_TOKEN <missing ID_TOKEN>VAR_TOKEN = 10
尽管 antlr 允许 predicate,但我必须控制所有令牌匹配和问题。语法变成这样
grammar Demo;
options {
language = Go;
}
@parser::members{
var _need = map[string]bool{}
func skip(name string,v bool){
_need[name] = !v
fmt.Println("SKIP",name,v)
}
func need(name string)bool{
fmt.Println("NEED",name,_need[name])
return _need[name]
}
}
proj@init{skip("inst",false)}: (line? NL)* EOF;
line
: VAR ID
| LET ID EQ? Integer
;
NL: '\n';
VAR: {need("inst")}? 'var' {skip("inst",true)};
LET: {need("inst")}? 'let' {skip("inst",true)};
EQ: '=';
ID: ([a-zA-Z] [a-zA-Z0-9]*);
Integer: [0-9]+;
WS: [ \t] -> skip;
看起来很糟糕。
但这在 peg 中很容易,请在 pegjs
中测试
Expression = (Line? _ '\n')* ;
Line
= 'var' _ ID
/ 'let' _ ID _ "=" _ Integer
Integer "integer"
= [0-9]+ { return parseInt(text(), 10); }
ID = [a-zA-Z] [a-zA-Z0-9]*
_ "whitespace"
= [ \t]*
我的问题是如何在 antlr4.6 中处理这些语法,我对 antlr4.6 go 目标感到非常兴奋,但似乎我为我的语法选择了错误的工具?
最简单的方法是为标识符定义解析器规则:
id: ID | VAR | LET;
VAR: 'var';
LET: 'let';
ID: [a-zA-Z] [a-zA-Z0-9]*;
然后在您的解析器规则中使用 id
而不是 ID
。
另一种方法是使用 ID
作为标识符 和 关键字,并使用谓词来消除歧义。但它的可读性较差,所以我会改用第一种方式。
这是一个演示代码
label:
var id
let id = 10
goto label
如果允许关键字作为标识符将是
let:
var var
let var = 10
goto let
这是完全合法的代码。但是在antlr中似乎很难做到这一点。
AFAIK,如果 antlr 匹配令牌 let,将永远不会回退到 id 令牌。所以对于antlr它会看到
LET_TOKEN :
VAR_TOKEN <missing ID_TOKEN>VAR_TOKEN
LET_TOKEN <missing ID_TOKEN>VAR_TOKEN = 10
尽管 antlr 允许 predicate,但我必须控制所有令牌匹配和问题。语法变成这样
grammar Demo;
options {
language = Go;
}
@parser::members{
var _need = map[string]bool{}
func skip(name string,v bool){
_need[name] = !v
fmt.Println("SKIP",name,v)
}
func need(name string)bool{
fmt.Println("NEED",name,_need[name])
return _need[name]
}
}
proj@init{skip("inst",false)}: (line? NL)* EOF;
line
: VAR ID
| LET ID EQ? Integer
;
NL: '\n';
VAR: {need("inst")}? 'var' {skip("inst",true)};
LET: {need("inst")}? 'let' {skip("inst",true)};
EQ: '=';
ID: ([a-zA-Z] [a-zA-Z0-9]*);
Integer: [0-9]+;
WS: [ \t] -> skip;
看起来很糟糕。
但这在 peg 中很容易,请在 pegjs
中测试Expression = (Line? _ '\n')* ;
Line
= 'var' _ ID
/ 'let' _ ID _ "=" _ Integer
Integer "integer"
= [0-9]+ { return parseInt(text(), 10); }
ID = [a-zA-Z] [a-zA-Z0-9]*
_ "whitespace"
= [ \t]*
我的问题是如何在 antlr4.6 中处理这些语法,我对 antlr4.6 go 目标感到非常兴奋,但似乎我为我的语法选择了错误的工具?
最简单的方法是为标识符定义解析器规则:
id: ID | VAR | LET;
VAR: 'var';
LET: 'let';
ID: [a-zA-Z] [a-zA-Z0-9]*;
然后在您的解析器规则中使用 id
而不是 ID
。
另一种方法是使用 ID
作为标识符 和 关键字,并使用谓词来消除歧义。但它的可读性较差,所以我会改用第一种方式。