具有可变参数列表的函数的 Antlr4 语法
Antlr4 grammar for a function with variadic argument list
我正在尝试使用 antlr4 为我的 DSL 编写语法。本质上,我正在尝试创建一个 DSL 来描述树结构中的函数应用程序。
目前,我无法创建正确的语法(或正确使用 C# 中的访问者)来解析像
这样的表达式
#func1(jsonconfig)
#func1(jsonconfig, #func2(...))
#func1(#func2(...), #func3(...), ..., #func_n(...))
#func1(jsonconfig, #func2(...), #func3(...), ..., #func_n(...))
我的语法(为简洁起见删除了一些部分)
func
: FUNCTION_START IDENTIFIER LPAREN (config?) (argumentList?) RPAREN
;
argument
: func
;
argumentList
: (ARG_SEPARATOR argument)+
| ARG_SEPARATOR? argument
;
config
: json
;
然而,在尝试解析表达式时,我只得到第一个参数,而不是其余参数。
这是我的访客:
public class DslVisitor : JustDslBaseVisitor<Instruction>
{
public override Instruction VisitFunc(JustDslParser.FuncContext context)
{
var name = context.IDENTIFIER().GetText();
var conf = context.config()?.GetText();
var arguments = context.argumentList()?.argument() ?? Array.Empty<JustDslParser.ArgumentContext>();
var instruction = new Instruction
{
Name = name,
Config = conf == null ? null : JObject.Parse(conf),
Bindings = arguments.Select(x => x.Accept(this)).ToList()
};
return instruction;
}
public override Instruction VisitArgument(JustDslParser.ArgumentContext context)
{
return context.func().Accept(this);
}
}
我认为 antlr
定义中可能存在一些语法错误,因为它未能解析列表,但成功解析了单个项目。
过去我有一个稍微不同的语法,但它要求我总是传递一个不符合我需要的配置对象。
谢谢!
试试这样的:
func
: FUNCTION_START IDENTIFIER LPAREN functionArgs RPAREN
;
functionArgs :
: config (ARG_SEPERATOR argument)*
| argument (ARG_SEPERATOR argument)*
;
argument
: func
;
config
: json
;
或者(如果你真的想要 argumentList
节点)
func
: FUNCTION_START IDENTIFIER LPAREN functionArgs RPAREN
;
functionArgs :
: config (ARG_SEPERATOR argumentList)?
| argumentList
;
argumentList :
: argument (ARG_SEPERATOR argument)*
;
argument
: func
;
config
: json
;
您的代码有一些问题。
首先,您实际上并没有在解析后 your code 中测试解析结果。您应该添加一个 ErrorListener 并测试词法分析器 and/or 解析器是否确实发现了错误。您还可以使用它将输出分流到您喜欢的任何地方。
public class ErrorListener<S> : ConsoleErrorListener<S>
{
public bool had_error;
public override void SyntaxError(TextWriter output, IRecognizer recognizer, S offendingSymbol, int line,
int col, string msg, RecognitionException e)
{
had_error = true;
base.SyntaxError(output, recognizer, offendingSymbol, line, col, msg, e);
}
}
简单的创建一个监听器,调用AddErrorListener()
解析器,调用parse方法,然后测试had_error
监听器。请注意,您还应该向词法分析器添加一个侦听器。
接下来。需要大量编辑 this C# code 才能真正获得人们期望的输入。我删除了 C# 转义符并重新格式化以获取此输入:
#obj(
#property(
#unit(
{"value":"phoneNumbers"}
),
#agr_obj(
#valueof(
{"path":"$.phone_numbers"}
),
#current(
#valueof(
{"path":"$.type"}
) ),
#current(
#valueof(
{"path":"$.number"}
) ) ) ),
#property(
#unit(
{"value":"addrs"}
),
#agr_obj(
#valueof(
{"path":"$.addresses"}
),
#current(
#valueof(
{"path":"$.type"}
) ),
#current(
#obj(
#property(
#unit(
{"value":"city"}
),
#valueof(
{"path":"$.city"}
) ),
#property(
#unit(
{"value":"country"}
),
#valueof(
{"path":"$.country"}
) ),
#property(
#unit(
{"value":"street"}
),
#str_join(
{"separator":", "},
#valueof(
{"path":"$.street1"}
),
#valueof(
{"path":"$.street2"}
) ) ) ) ) ) ) )
第三。您不会使用在规则末尾具有 EOF 的条目规则来扩充语法。 EOF 增强规则强制解析器使用所有输入。在这里,我刚刚添加了“开始”的规则:
start : func EOF ;
您需要将入口点更改为 start()
而不是 func()
。
最后,您的语法无法识别后跟可选 func
参数的 json
参数。由于 func
的第一个参数可以是 json
或 json , func
或 func
,因此您需要为第一个参数设置一个例外。该语法修复了:
grammar JustDsl;
LPAREN: '(';
RPAREN: ')';
FUNCTION_START: '#';
ARG_SEPARATOR: ',';
IDENTIFIER
: [a-zA-Z] [a-zA-Z\-_] *
;
start : func EOF ;
func
: FUNCTION_START IDENTIFIER LPAREN argumentList? RPAREN
;
argument
: func
;
argumentList
: (config config_rest)?
| no_config_rest?
;
config_rest
: (ARG_SEPARATOR argument)*
;
no_config_rest
: argument (ARG_SEPARATOR argument)*
;
config
: json
;
json
: value
;
obj
: '{' pair (',' pair)* '}'
| '{' '}'
;
pair
: STRING ':' value
;
arr
: '[' value (',' value)* ']'
| '[' ']'
;
value
: STRING
| NUMBER
| obj
| arr
| 'true'
| 'false'
| 'null'
;
STRING
: '"' (ESC | SAFECODEPOINT)* '"'
;
fragment ESC
: '\' (["\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
fragment SAFECODEPOINT
: ~ ["\\u0000-\u001F]
;
NUMBER
: '-'? INT ('.' [0-9] +)? EXP?
;
fragment INT
: '0' | [1-9] [0-9]*
;
// no leading zeros
fragment EXP
: [Ee] [+\-]? INT
;
// \- since - means "range" inside [...]
WS
: [ \t\n\r] + -> skip
;
迈克走在正确的轨道上(但是 functionArgs
规则有错字)。但是没有输入,这个问题很难解决。
我正在尝试使用 antlr4 为我的 DSL 编写语法。本质上,我正在尝试创建一个 DSL 来描述树结构中的函数应用程序。
目前,我无法创建正确的语法(或正确使用 C# 中的访问者)来解析像
这样的表达式#func1(jsonconfig)
#func1(jsonconfig, #func2(...))
#func1(#func2(...), #func3(...), ..., #func_n(...))
#func1(jsonconfig, #func2(...), #func3(...), ..., #func_n(...))
我的语法(为简洁起见删除了一些部分)
func
: FUNCTION_START IDENTIFIER LPAREN (config?) (argumentList?) RPAREN
;
argument
: func
;
argumentList
: (ARG_SEPARATOR argument)+
| ARG_SEPARATOR? argument
;
config
: json
;
然而,在尝试解析表达式时,我只得到第一个参数,而不是其余参数。
这是我的访客:
public class DslVisitor : JustDslBaseVisitor<Instruction>
{
public override Instruction VisitFunc(JustDslParser.FuncContext context)
{
var name = context.IDENTIFIER().GetText();
var conf = context.config()?.GetText();
var arguments = context.argumentList()?.argument() ?? Array.Empty<JustDslParser.ArgumentContext>();
var instruction = new Instruction
{
Name = name,
Config = conf == null ? null : JObject.Parse(conf),
Bindings = arguments.Select(x => x.Accept(this)).ToList()
};
return instruction;
}
public override Instruction VisitArgument(JustDslParser.ArgumentContext context)
{
return context.func().Accept(this);
}
}
我认为 antlr
定义中可能存在一些语法错误,因为它未能解析列表,但成功解析了单个项目。
过去我有一个稍微不同的语法,但它要求我总是传递一个不符合我需要的配置对象。
谢谢!
试试这样的:
func
: FUNCTION_START IDENTIFIER LPAREN functionArgs RPAREN
;
functionArgs :
: config (ARG_SEPERATOR argument)*
| argument (ARG_SEPERATOR argument)*
;
argument
: func
;
config
: json
;
或者(如果你真的想要 argumentList
节点)
func
: FUNCTION_START IDENTIFIER LPAREN functionArgs RPAREN
;
functionArgs :
: config (ARG_SEPERATOR argumentList)?
| argumentList
;
argumentList :
: argument (ARG_SEPERATOR argument)*
;
argument
: func
;
config
: json
;
您的代码有一些问题。
首先,您实际上并没有在解析后 your code 中测试解析结果。您应该添加一个 ErrorListener 并测试词法分析器 and/or 解析器是否确实发现了错误。您还可以使用它将输出分流到您喜欢的任何地方。
public class ErrorListener<S> : ConsoleErrorListener<S>
{
public bool had_error;
public override void SyntaxError(TextWriter output, IRecognizer recognizer, S offendingSymbol, int line,
int col, string msg, RecognitionException e)
{
had_error = true;
base.SyntaxError(output, recognizer, offendingSymbol, line, col, msg, e);
}
}
简单的创建一个监听器,调用AddErrorListener()
解析器,调用parse方法,然后测试had_error
监听器。请注意,您还应该向词法分析器添加一个侦听器。
接下来。需要大量编辑 this C# code 才能真正获得人们期望的输入。我删除了 C# 转义符并重新格式化以获取此输入:
#obj(
#property(
#unit(
{"value":"phoneNumbers"}
),
#agr_obj(
#valueof(
{"path":"$.phone_numbers"}
),
#current(
#valueof(
{"path":"$.type"}
) ),
#current(
#valueof(
{"path":"$.number"}
) ) ) ),
#property(
#unit(
{"value":"addrs"}
),
#agr_obj(
#valueof(
{"path":"$.addresses"}
),
#current(
#valueof(
{"path":"$.type"}
) ),
#current(
#obj(
#property(
#unit(
{"value":"city"}
),
#valueof(
{"path":"$.city"}
) ),
#property(
#unit(
{"value":"country"}
),
#valueof(
{"path":"$.country"}
) ),
#property(
#unit(
{"value":"street"}
),
#str_join(
{"separator":", "},
#valueof(
{"path":"$.street1"}
),
#valueof(
{"path":"$.street2"}
) ) ) ) ) ) ) )
第三。您不会使用在规则末尾具有 EOF 的条目规则来扩充语法。 EOF 增强规则强制解析器使用所有输入。在这里,我刚刚添加了“开始”的规则:
start : func EOF ;
您需要将入口点更改为 start()
而不是 func()
。
最后,您的语法无法识别后跟可选 func
参数的 json
参数。由于 func
的第一个参数可以是 json
或 json , func
或 func
,因此您需要为第一个参数设置一个例外。该语法修复了:
grammar JustDsl;
LPAREN: '(';
RPAREN: ')';
FUNCTION_START: '#';
ARG_SEPARATOR: ',';
IDENTIFIER
: [a-zA-Z] [a-zA-Z\-_] *
;
start : func EOF ;
func
: FUNCTION_START IDENTIFIER LPAREN argumentList? RPAREN
;
argument
: func
;
argumentList
: (config config_rest)?
| no_config_rest?
;
config_rest
: (ARG_SEPARATOR argument)*
;
no_config_rest
: argument (ARG_SEPARATOR argument)*
;
config
: json
;
json
: value
;
obj
: '{' pair (',' pair)* '}'
| '{' '}'
;
pair
: STRING ':' value
;
arr
: '[' value (',' value)* ']'
| '[' ']'
;
value
: STRING
| NUMBER
| obj
| arr
| 'true'
| 'false'
| 'null'
;
STRING
: '"' (ESC | SAFECODEPOINT)* '"'
;
fragment ESC
: '\' (["\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
fragment SAFECODEPOINT
: ~ ["\\u0000-\u001F]
;
NUMBER
: '-'? INT ('.' [0-9] +)? EXP?
;
fragment INT
: '0' | [1-9] [0-9]*
;
// no leading zeros
fragment EXP
: [Ee] [+\-]? INT
;
// \- since - means "range" inside [...]
WS
: [ \t\n\r] + -> skip
;
迈克走在正确的轨道上(但是 functionArgs
规则有错字)。但是没有输入,这个问题很难解决。