在调用`implode`期间无法解释的IllegalArgument

问题描述 投票:1回答:1

我试图在Rascal中为cmake语言的一个子集制作一个DSL,但在调用implode时很难调试IllegalArgument异常。

CMake.rsc:

module CMake

import util::IDE;
import ParseTree;
import String;
import IO;
import Node;

import Implode;
import AST;
import Syntax;
import Parse;

private str STM_LANG = "CMakeLists";
private str STM_EXT = "cmake"; // change to txt

public void main() {
  registerLanguage(STM_LANG, STM_EXT, Tree(str src, loc location) {
     print("parse\n");
     return parse(src, location);
  });

    contribs = {
        builder(set[Message] (start[Build] pt) {
            ctl = implode(pt);
                out = (pt@\loc)[extension="txt"];
                class = split(".", out.file)[0];
                print("build\n");
//              writeFile(out, compile(class, ctl));
            return {};
        })
    };

    registerContributions(STM_LANG, contribs);    
}

Syntax.rsc:

module Syntax

extend lang::std::Layout;

start syntax Build = build: Section+ sections;

layout Layout = Comment*;
lexical Comment = @category="Comment" "#" ![\n]* [\n]; 

syntax Section = section: Target target Options+ options;
syntax Target = target: "set" "(" Id targetMacro Id targetName ")";
syntax Options = options: IdeFolder Dirs Sources+; 
syntax IdeFolder = ideFolder: "set" "(" "$" "{" Id targetMacro "}" "_IDE_FOLDER" "\"" Id ideFolderPath "\"" ")";
syntax Dirs = dirs: "set" "(" "$" "{" Id targetMacro "}" "_DIRS" DirsPath+ dirPaths ")";
syntax DirsPath = dirPaths: "$" "{" Id pathMacro "}" Id? path;
syntax Sources = sources: "set" "(" "$" "{" Id targetMacro "}" ("_SOURCES" | "_WINDOWS_SOURCES" ) Id+ sourceFiles ")";

lexical Id = ([a-zA-Z/.][a-zA-Z0-9_/.]* !>> [a-zA-Z0-9_/.]) \ Reserved ;
keyword Reserved = "set" | "$" | "{" | "}" | "_IDE_FOLDER" | "_DIRS" | "_SOURCES" | "_WINDOWS_SOURCES";

AST.rsc:

module AST

data Build = build(list[Section] sections);
data Section = section(Target target, list[IdeFolder] ideFolders);
data Target = target(str targetMacro, str targetName);
data Options = options(IdeFolder ideFolder); 
data IdeFolder = ideFolder(str targetMacro, str ideFolderPath);
data Dirs = dirs(str targetMacro, list[DirsPath] dirPaths);
data DirsPath = dirPaths(str pathMacro, str path);
data Sources = sources(str targetMacro, list[str] sourceFiles);

anno loc Build@location;
anno loc Section@location;
anno loc Options@location;
anno loc Target@location;
anno loc IdeFolder@location;
anno loc Dirs@location;
anno loc DirsPath@location;
anno loc Sources@location;

Parse.rsc:

module Parse

import Syntax;
import ParseTree;

public start[Build] parse(str src, loc origin) = parse(#start[Build], src, origin);
public start[Build] parse(loc origin) = parse(#start[Build], origin);

Implode.rsc:

module Implode

import Parse;
import AST;

import ParseTree;
import Node;

public Build implode(Tree pt) = implode(#Build, pt);
public Build load(loc l) = implode(#Build, parse(l));

CMakeLists.cmake:


# test

set(TARGET_NAME bb)

set(${TARGET_NAME}_IDE_FOLDER "test")

set(${TARGET_NAME}_DIRS
  ${BLA_PATH}/src
  ${BLA_GEN_PATH}
  ${BLA_GEN_PATH}/dir1/pub
  ${BLA_GEN_PATH}/dir2/pub
) 

set(${TARGET_NAME}_SOURCES
  ../inc/file3.h
  ../inc/file4.h
)

错误消息是:

|std:///ParseTree.rsc|(17152,5150,<507,0>,<652,60>): IllegalArgument(appl(prod(label("build",sort("Build")),[label("sections",\iter-seps(sort("Section"),[layouts("Standard")]))],{}),[appl(regular(\iter-seps(sort("Section"),[layouts("Standard")])),[appl(prod(label("section",sort("Section")),[label("target",sort("Target")),layouts("Standard"),label("options",\iter-seps(sort("Options"),[layouts("Standard")]))],{}),[appl(prod(label("target",sort("Target")),[lit("set"),layouts("Standard"),lit("("),layouts("Standard"),label("targetMacro",lex("Id")),layouts("Standard"),label("targetName",lex("Id")),layouts("Standard"),lit(")")],{}),[appl(prod(lit("set"),[\char-class([range(115,115)]),\char-class([range(101,101)]),\char-class([range(116,116)])],{}),[char(115),char(101),char(116)]),appl(prod(layouts("Standard"),[conditional(\iter-star(lex("WhitespaceOrComment")),{\not-follow(\char-class([range(9,13),range(32,32),range(133,133),range(160,160),range(5760,5760),range(6158,6158),range(8192,8202),range(8232,8233),range(8239,8239),range(8287,8287),range(12288,12288)])),\not-follow(lit("//"))})],{}),[appl(regular(\iter-star(lex("WhitespaceOrComment"))),[])[@loc=|project://Instrumentation/src/CMakeLists.cmake|(15,0,<4,3>,<4,3>)]])[@loc=|project://Instrumentation/src/CMakeLists.cmake|(15,0,<4,3>,<4,3>)],appl(prod(lit("("),[\char-class([range(40,40)])],{}),[char(40)]),appl(prod(layouts("Standard"),[conditional(\iter-star(lex("WhitespaceOrComment")),{\not-follow(\char-class([range(9,13),range(32,32),range(133,133),range(160,160),range(5760,5760),range(6158,6158),range(8192,8202),range(8232,8233),range(8239,8239),range(8287,8287),range(12288,12288)])),\not-follow(lit("//"))})],{}),[appl(regular(\iter-star(lex("WhitespaceOrComment"))),[])[@loc=|project://Instrumentation/src/CMakeLists.cmake|(16,0,<4,4>,<4,4>)]])[@loc=|project://Instrumentation/src/CMakeLists.cmake|(16,0,<4,4>,<4,4>)],appl(prod(lex("Id"),[conditional(seq([\char-class([range(46,47),range(65,90),range(97,122)]),conditional(\iter-star(\char-class([range(46,57),range(65,90),range(95,95),range(97,122)])),{\not-follow(\char-class([range(46,57),range(65,90),range(95,95),range(97,122)]))})]),{delete(keywords("Reserved"))})],{}),[appl(regular(seq([\char-class([range(46,47),range(65,90),range(97,122)]),conditional(\iter-star(\char-class([range(46,57),range(65,90),range(95,95),range(97,122)])),{\not-follow(\char-class([range(46,57),range(65,90),range(95,95),range(97,122)]))})])),[char(84),appl(regular(
        at *** somewhere ***(|std:///ParseTree.rsc|(17152,5150,<507,0>,<652,60>))
        at implode(|project://Instrumentation/src/Implode.rsc|(131,2,<9,48>,<9,50>))
        at Anonymous Function(|project://Instrumentation/src/CMake.rsc|(481,12,<25,9>,<25,21>))
        at $root$(|main://$root$|)
    ...truncated here...

dsl rascal
1个回答
0
投票

酷项目!

在@Matty将示例代码拆分为不同的模块后,我们消除了不同语法和数据类型之间混淆的可能性。

所以这是一个调试会话:

rascal>x = parse(#start[Build], |project://rascal-test-project/src/CMakeLists.cmake|);
start[Build]: (start[Build]) `# test

set(TARGET_NAME bb)

set(${TARGET_NAME}_IDE_FOLDER "test")

set(${TARGET_NAME}_DIRS
  ${BLA_PATH}/src
  ${BLA_GEN_PATH}
  ${BLA_GEN_PATH}/dir1/pub
  ${BLA_GEN_PATH}/dir2/pub
) 

set(${TARGET_NAME}_SOURCES
  ../inc/file3.h
  ../inc/file4.h
)

rascal>try { 
>>>>>>>  implode(#AST::Build, t); 
>>>>>>>} 
>>>>>>>catch IllegalArgument(x, m): 
>>>>>>>  println(m);
Cannot find a constructor for Build
ok

IllegalArgument异常既有参数也有原因。由于参数太长,它被终端截断,我们无法读取消息:-(消息是Build找不到与树匹配的抽象构造函数。

让我们看看抽象语法,看看我们是否缺少构造函数,或者它是否存在:

rascal>iprintln(#AST::Build)
type(
  adt(
    "Build",
    []),
  (
    adt(
      "IdeFolder",
      []): ..., // elided
    adt(
      "Target",
      []): ..., // elided
    adt(
      "Build",  // bingo!
      []):choice(
      adt(
        "Build",
        []),
      {cons(   // this is the constructor it should have found 
               // data Build = build(list[Section] sections);
          label(
            "build",
            adt(
              "Build",
              [])),
          [label(
              "sections",
              list(adt(
                  "Section",
                  [])))],
          [],
          {})}),
    })
  ))
ok

看来我们有一个构造函数可以使用。那么发生了什么?我介绍了Java中的implode代码,发现真正的原因被内部回溯部分中的一个错误隐藏了。现在已经修复了(请参阅更新站点上的下一个连续版本)。

当前更好的错误消息是:

rascal>try { implode(#AST::Build, x); } catch IllegalArgument (t, m) : println("<m> : <t>");
Cannot find a constructor for IdeFolder with name options and arity 3 for syntax type 'Options' : set(${TARGET_NAME}_IDE_FOLDER "test")

因此,这意味着内爆算法已经达到了一个名为options的语法规则,其类型为Options,包含3个子节点,同时期望在抽象数据类型中使用IdeFolder。

这意味着数据类型定义中缺少中间级别:

data Section = section(Target target, list[IdeFolder] ideFolders);

可以改为:

data Section = section(Target target, list[Options] ideFolders);

接下来尝试:

try { implode(#AST::Build, x); } catch IllegalArgument (t, m) : println("<m> : <t>");

Cannot find a constructor for Options with name options and arity 3 for syntax type 'Options' : set(${TARGET_NAME}_IDE_FOLDER "test")

现在名字对齐,但是arity没有。我们来解决这个问题:

syntax Options = options: IdeFolder Dirs Sources+;
data Options = options(IdeFolder ideFolder); // has to change to:
data Options = options(IdeFolder ideFolder, Dirs dirs, list[Sources] sources);

我们得到了下一个错误:

rascal>try { implode(#AST::Build, x); } catch IllegalArgument (t, m) : println("<m> : <t>");
Cannot find a constructor for Sources with name sources and arity 3 for syntax type 'Sources' : set(${TARGET_NAME}_SOURCES
  ../inc/file3.h
  ../inc/file4.h
)

你现在明白了这个想法:-)你在AST.rsc中还有更多要修复的东西。谢谢你提问。我们刚刚一起变得更好了。

© www.soinside.com 2019 - 2024. All rights reserved.