具有可变参数列表的函数的 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 的第一个参数可以是 jsonjson , funcfunc,因此您需要为第一个参数设置一个例外。该语法修复了:

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 规则有错字)。但是没有输入,这个问题很难解决。