необъяснимый IllegalArgument во время вызова «взрыва» - PullRequest
1 голос
/ 17 апреля 2019

Я попытался создать DSL в Rascal для подмножества языка cmake, но во время вызова 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...

1 Ответ

0 голосов
/ 17 апреля 2019

Классный проект!

После того, как @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

Кажется, у нас есть конструктор для работы. Так что происходит? Я прошел через кодimplode в Java, чтобы выяснить, что настоящая причина была скрыта из-за ошибки в части отслеживания возврата. Это теперь исправлено (см. Следующий непрерывный выпуск на сайте обновлений).

Текущая, лучше, ошибкасообщение:

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 в абстрактных данных.type.

Это означает, что в определениях типов данных отсутствует промежуточный уровень:

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")

Теперь имена совпадают, а арность - нет.Давайте исправим это:

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.Спасибо, что задали вопрос.Мы только что взорвались вместе немного лучше.

...