Ctags: [systemverilog]解析器将typedef误认为模块的端口

创建于 2020-02-05  ·  24评论  ·  资料来源: universal-ctags/ctags

解析器的名称: verilog.c

您用来运行ctags的命令行:

$ ctags --options=NONE foo.sv

输入文件内容:
foo.sv

typedef bit[31:0] int32_t;
module mod(
  input bit clk,
  input int32_t a
);
endmodule

您不满意的标签输出:

clk     foo.sv  /^  input bit clk,$/;"  p       module:mod
int32_t foo.sv  /^  input int32_t a$/;" p       module:mod
int32_t foo.sv  /^typedef bit[31:0] int32_t;$/;"        T
mod     foo.sv  /^module mod($/;"       m
mod.clk foo.sv  /^  input bit clk,$/;"  p       module:mod
mod.int32_t     foo.sv  /^  input int32_t a$/;" p       module:mod

您期望的标签输出:

clk     foo.sv  /^  input bit clk,$/;"  p       module:mod
a       foo.sv  /^  input int32_t a$/;" p       module:mod
int32_t foo.sv  /^typedef bit[31:0] int32_t;$/;"        T
mod     foo.sv  /^module mod($/;"       m
mod.clk foo.sv  /^  input bit clk,$/;"  p       module:mod
mod.a   foo.sv  /^  input int32_t a$/;" p       module:mod

ctags的版本:

$ ctags --version
Universal Ctags 0.0.0(81fa5b1a), Copyright (C) 2015 Universal Ctags Team                    
  Universal Ctags is derived from Exuberant Ctags.                                            
  Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert                                 
    Compiled: Feb  4 2020, 14:36:50                                                           
    URL: https://ctags.io/                                                                    
    Optional compiled features: +wildcards, +regex, +iconv, +option-directory, +xpath, +json, 
  +interactive, +sandbox, +yaml, +packcc
Parser buenhancement

所有24条评论

感谢您的举报。
您可以尝试#2492吗?

非常感谢您的回复!

2492修复了我的原始文件。 修复绝对可以改善事情。

但是,如果执行此操作,它仍然会失败: ctags --options=NONE a.sv b.sv

哪里:
SV

`include "b.sv"

module mod(
  input bit clk,
  input int32_t a
);
endmodule

病毒

typedef bit[31:0] int32_t;

当前,ctags无法跟踪交叉输入文件的依赖性,例如a.sv和b.sv。
我正在尝试添加处理交叉输入文件依赖项的功能:
https://github.com/universal-ctags/ctags/issues/2428

但是,将需要一年多的时间来实施它。
因此,我将首先合并#2492。

@masatake懂了!

我认为有一种方法可以解决此问题,而无需交叉输入文件依赖项。
相当简单:输入/输出/输入端口的名称始终是逗号或最后一个括号之前的最后一个单词。

module mod(
  input dont_need_to_know_what_this_is port_name[64]
);

您认为这是否可以实现?

谢谢,
安托万

显然可以实现。
问题是谁实现了该算法。

systemverilog解析器的原始维护者不想采用这种算法。

https://github.com/universal-ctags/ctags/issues/1488
https://github.com/universal-ctags/ctags/issues/80
https://github.com/universal-ctags/ctags/pull/1495

我正在寻找实现该算法的新维护人员。
如果找不到这样的人,我们必须等到我完成“多遍历/多文件”基础结构。
这将需要更长的时间。

因此,像往常一样,我必须说“请求很好”。

顺便说一句,您能帮我写一个提交日志来修复SystemVerilog解析器吗?
您非常了解SystemVerilog。 所以我想看https://github.com/universal-ctags/ctags/issues/2489

@masatake ,明白了。
我已经看过代码并尝试在打开此票证之前对其进行修复。
C代码在我的TBH中有些超出。
将会再次拍摄而没有任何结果的保证:笑脸:

我只是在2489年回答了您的问题。
如果您有关于SV的更多问题,请随时询问我,我很乐意为您提供帮助:wink:

谢谢,
安托万

@antoinemadec ,谢谢。

更改verilog.c中的逻辑的困难之一是解析器(systemverilog和verilog)解析器使用“ kind”(verilogKind)作为令牌类型,例如:


typedef struct sTokenInfo {
    verilogKind         kind;

kind是在输出中分配给标签条目的东西。
另一方面,“令牌”是解析器私有的东西。

参见python.c。 它为令牌定义类型,例如:

typedef enum eTokenType {
    /* 0..255 are the byte's value */
    TOKEN_EOF = 256,
    TOKEN_UNDEFINED,
    TOKEN_INDENT,
    TOKEN_KEYWORD,
    TOKEN_OPERATOR,
    TOKEN_IDENTIFIER,
    TOKEN_STRING,
    TOKEN_WHITESPACE,
} tokenType;

typedef struct {
    int             type;

为令牌定义类型为解析器代码带来了极大的灵活性。
如果找不到轻松的方法来实现所需的功能,则可能会提示“为令牌定义类型并且仅将这些类型用于发出标签条目”。

感谢您尝试改进解析器。
随时问我这个问题。 我不知道SystemVerilog。 但是,我了解ctags,尤其是语言的共同部分。

我不擅长英语。 因此表达我的“感谢”的方式非常有限。
当我收到您的拉票请求时,会保留“非常感谢”,“非常感谢”后缀形式的“谢谢”。

祝好运。

ps#2489,谢谢。

我正在考虑组织“语言顾问”。
以后遇到有关SystemVerilog的问题时,可以允许我与您联系吗?
我想看#2494。

@vhda ,如果可以的话,您可以看看这个并给我们评论。
我也希望您看到#2494。
顺便说一句,最后我实现了一个用于对输入文件进行多遍解析的基础结构。
我正在对多个输入文件进行多次分析。

Verilog的问题是您可以同时拥有

input name,

input type name,

也就是说,当您解析type之后的单词时,您不知道它是类型还是名称。
但是应该可以存储每个字符串,并且仅在找到,;时确定令牌名称。 实际上,我隐约记得当时在解决其他问题时做了这样的事情。

让我们执行以下操作:让我继续进行中的工作,这里我要扩展对结构的支持。 这应该可以帮助我记住所有代码的编码方式并刷新我的C,此后一直没有使用过。 然后,我将基于master分支更改此基础,以了解更改的内容。 最后,我将研究这个问题。

干杯,
维特尔

远非如此! 最好我们甚至可以谈论一下软木塞,看看我是否最终理解了。

有关软木塞的信息,请参见http://docs.ctags.io/en/latest/internal.html#output -tag-stream。
并且随时问我有关ctags本身的问题。

SystemVerilog解析器和Ada解析器在设计级别上都有相同的限制。
他们将其种类索引重新用于其令牌类型。
它们必须分开。 种类索引用于标签发射。 令牌类型用于令牌化。
他们是不同的。 这种分离为我们带来了极大的灵活性,可以改进和更改解析器
不会将解析器的内部更改暴露给用户的视线。

diff --git a/parsers/verilog.c b/parsers/verilog.c
index 737fc815..a3f4ce6e 100644
--- a/parsers/verilog.c
+++ b/parsers/verilog.c
@@ -607,8 +607,8 @@ static void dropEndContext (tokenInfo *const token)
    vStringDelete(endTokenName);
 }

-
-static void createTag (tokenInfo *const token)
+static int createTag (tokenInfo *const token);
+static int createTagFull (tokenInfo *const token, int *corkIndexFQ)
 {
    tagEntryInfo tag;
    verilogKind kind;
@@ -627,7 +627,7 @@ static void createTag (tokenInfo *const token)
    if (vStringLength (token->name) == 0 || ! kindEnabled (kind))
    {
        verbose ("Unexpected empty token or kind disabled\n");
-       return;
+       return CORK_NIL;
    }

    /* Create tag */
@@ -668,7 +668,10 @@ static void createTag (tokenInfo *const token)
        tag.name = vStringValue (scopedName);

        markTagExtraBit (&tag, XTAG_QUALIFIED_TAGS);
-       makeTagEntry (&tag);
+       if (corkIndexFQ)
+           *corkIndexFQ = makeTagEntry (&tag);
+       else
+           makeTagEntry (&tag);

        vStringDelete (scopedName);
    }
@@ -705,6 +708,12 @@ static void createTag (tokenInfo *const token)

    /* Clear no longer required inheritance information */
    vStringClear (token->inheritance);
+   return corkIndex;
+}
+
+static int createTag (tokenInfo *const token)
+{
+   return createTagFull (token, NULL);
 }

 static bool findBlockName (tokenInfo *const token)
@@ -1162,7 +1171,7 @@ static void tagNameList (tokenInfo* token, int c)
 {
    verilogKind localKind;
    bool repeat;
-
+   int cork = CORK_NIL, fqcork = CORK_NIL;
    /* Many keywords can have bit width.
    *   reg [3:0] net_name;
    *   inout [(`DBUSWIDTH-1):0] databus;
@@ -1206,7 +1215,7 @@ static void tagNameList (tokenInfo* token, int c)
            else
            /* Create tag in case name is not a known kind ... */
            {
-               createTag (token);
+               cork = createTagFull (token, &fqcork);
            }
        }
        else
@@ -1237,9 +1246,22 @@ static void tagNameList (tokenInfo* token, int c)
        }
        if (c == ',')
        {
+           cork = CORK_NIL;
+           fqcork = CORK_NIL;
            c = skipWhite (vGetc ());
            repeat = true;
        }
+       else if (isIdentifierCharacter(c))
+       {
+           tagEntryInfo *e;
+           e = getEntryInCorkQueue (cork);
+           if (e)
+               e->placeholder = 1;
+           e = getEntryInCorkQueue (fqcork);
+           if (e)
+               e->placeholder = 1;
+           repeat = true;
+       }
    } while (repeat);
    vUngetc (c);
 }
[jet@living]~/var/ctags% cat /tmp/foo.sv 
cat /tmp/foo.sv 
pack.sv:
package PACK;
    localparam N = 100;
    typedef logic [N-1:0] PORT_TYPE_T;
endpackage // PACK

port.sv:
import PACK::*;

module port (
    input  PORT_TYPE_T  port,
    input  PORT_TYPE_T  port2);
endmodule // port
[jet@living]~/var/ctags% ./ctags  --sort=no -o - /tmp/foo.sv
./ctags  --sort=no -o - /tmp/foo.sv
PACK    /tmp/foo.sv /^package PACK;$/;" K
N   /tmp/foo.sv /^    localparam N = 100;$/;"   c   package:PACK
PORT_TYPE_T /tmp/foo.sv /^    typedef logic [N-1:0] PORT_TYPE_T;$/;"    T   package:PACK
port    /tmp/foo.sv /^module port ($/;" m
port    /tmp/foo.sv /^    input  PORT_TYPE_T  port,$/;" p   module:port
port2   /tmp/foo.sv /^    input  PORT_TYPE_T  port2);$/;"   p   module:port
[jet@living]~/var/ctags% cat /tmp/bar.sv 
cat /tmp/bar.sv 
`include "b.sv"

module mod(
  input bit clk,
  input int32_t a
);
endmodule // mod
[jet@living]~/var/ctags% ./ctags  --sort=no -o - /tmp/bar.sv
./ctags  --sort=no -o - /tmp/bar.sv
mod /tmp/bar.sv /^module mod($/;"   m
clk /tmp/bar.sv /^  input bit clk,$/;"  p   module:mod
a   /tmp/bar.sv /^  input int32_t a$/;" p   module:mod

input t a,

算法:

  1. 像以前一样为t创建标签。
  2. 如果找到一个可以作为标识符一部分的字符,则取消标记t的发射。
  3. 为标识符a创建标签,转到2。

步骤2.基于@vhda编写的内容。

我猜这不好用,因为createTag()是递归调用的。 因此取消部分起作用。

你好
有人在解决这个问题吗? 如果没有,我想尝试实现@antoinemadec的算法(https://github.com/universal-ctags/ctags/issues/2413#issuecomment-609486797)。


SystemVerilog BNF非常复杂,因此我对其进行了压缩。
https://docs.google.com/document/d/1Sb0-iO9HofB7BR52XUarhGYPph6mFMx5oIFcU8eeFj0/edit?usp=sharing

这是我的基本想法。

do {
  skip light-gray tokens
  If the first token is identifier:
     the identifier is a type_identifier or an identifier with implicit_data_type (https://github.com/universal-ctags/ctags/issues/2413#issuecomment-609580989)
  else
     the token is a keyword: (already implemented)
}

@masatake san,

https://github.com/universal-ctags/ctags/issues/2413#issuecomment -609580989

更改verilog.c中的逻辑的困难之一是解析器(systemverilog和verilog)解析器使用“ kind”(verilogKind)作为令牌类型,例如:
...
kind是在输出中分配给标签条目的东西。
另一方面,“令牌”是解析器私有的东西。

通过阅读以下内容,我认为verilog.c的当前实现是简单明了的;

  • enum verilogKindKeywordTable[]适用于各种代币
  • VerilogKinds[]SystemVerilogKinds[]用于标识符令牌(在输出中分配给标签条目)

欢迎任何意见或反馈。 谢谢。

@hirooih ,我希望您了解典型的良好解析器实现。

通常,在ctags中,“种类”用于标记,而不用于标记。
用户可以使用“ ctags --list-kinds-full =”了解解析器中可用的种类。”。

解析器中定义的“种类”是API的一部分。 我们应该尽可能保持它们的兼容性。
我们可以通过多种方式实现一种语言的解析器。 但是,实现中的更改不应该可见
作为“种类”定义的变化。

在编写面向令牌的解析器时,您可能希望为令牌分配类型,例如NUMBER,SYMBOL,SEPARATOR,STRING-LITERAL...。您可以根据需要定义它们。 它们应在解析器内部使用。 如果更改类型定义和/或分配,则不应更改解析器的“种类”定义。 令牌的类型和标签的种类是不同的。 用户不应该知道令牌的类型。 实际上,我们不提供--list-token-types选项。

像Python解析器这样的优秀解析器使用令牌类型和令牌关键字。

typedef enum eTokenType {
    /* 0..255 are the byte's value */
    TOKEN_EOF = 256,
    TOKEN_UNDEFINED,
    TOKEN_INDENT,
    TOKEN_KEYWORD,
    TOKEN_OPERATOR,
    TOKEN_IDENTIFIER,
    TOKEN_STRING,
    TOKEN_ARROW,                /* -> */
    TOKEN_WHITESPACE,
} tokenType;

如果令牌具有TOKEN_KEYWORD类型,则它可以具有一个称为关键字的子类型:

enum {
    KEYWORD_as,
    KEYWORD_async,
    KEYWORD_cdef,
    KEYWORD_class,
    KEYWORD_cpdef,
    KEYWORD_def,
    KEYWORD_extern,
    KEYWORD_from,
    KEYWORD_import,
    KEYWORD_inline,
    KEYWORD_lambda,
    KEYWORD_pass,
    KEYWORD_return,
};

这些常量在解析器内部使用。
python的种类分别定义:

typedef enum {
    K_CLASS,
    K_FUNCTION,
    K_METHOD,
    K_VARIABLE,
    K_NAMESPACE,
    K_MODULE,
    K_UNKNOWN,
    K_PARAMETER,
    K_LOCAL_VARIABLE,
    COUNT_KIND
} pythonKind;

SystemVerilog是混合使用关键字和种类的解析器之一。
我认为这种混合使改进解析器更加困难(不破坏API)。

我写了很多。 但是,您可以根据需要解决该问题。

我想知道的是“种类”是解析器API的一部分。 我们应该尝试保持兼容性。
SystemVerilog解析器具有足够的测试用例。 他们将检测到意外引入的不兼容性。


枚举verilogKind和KeywordTable []用于各种令牌

我认为这不好。 使用kind,这是令牌中用户可见的东西,用于键入。

VerilogKinds []和SystemVerilogKinds []用于识别符令牌(在输出中分配给标签条目)

您的代码读取是正确的。 但是,对令牌使用kind不是一个好主意。 种类用于标签。
标签和令牌是不同的东西。 标记表示读取输入源文件的结果。
标签代表ctags的输出。

顺便说一句,如果您需要解释registerEntry()和foreachEntriesInScope()来解决此问题,请告诉我。

@masatake san

我相信我理解你的所作所为。 我在#2576,#2618和此问题中阅读了您的类似说明。
我刚刚写过,我们可以将“ kind”用作一般的英语单词。

我还认为enum verilogKind有点棘手。 它桥接KeywordTable[]VerilogKinds[]/SystemVerilogKinds[] 。 如果verilogKind大于或等于0,则可以将其用作VerilogKinds[]/SystemVerilogKinds[]的索引。

API( initTagEntrymakeTagEntry )仅引用VerilogKinds[]/SystemVerilogKinds[]enum verilogKind (> = 0)。 verilog.c不会破坏API。

顺便说一句,如果您需要解释registerEntry()和foreachEntriesInScope()来解决此问题,请告诉我。

谢谢。 我认为我不必更改此部分,但是如果需要,我会问您的帮助。


在开始工作之前,我想建议按如下所示更改Units/parser-verilog.r/*/args.ctags以输出所有受支持的信息。 我们可以检测到输出的任何变化而不会破坏兼容性。 --sort=no有助于调试。

--extras=+q
--fields=+i
--kinds-systemverilog=+Q
--sort=no

(这是cat */args.ctags | sort | uniqUnits/parser-verilog.r/ 。)

当然,我也会更新*/expected.tags

我可以那样做吗?

--sort = no是可以接受的。 但是,我认为统一args.ctags不是一个好主意。
每种测试都是针对自己的目的而开发的。 并且每个args.ctags都是为此目的而设计的。
统一args.ctags隐藏了测试用例的初衷。

您是唯一一个改善地球上SystemVerilog解析器的人。 因此,如果您确实需要统一的args.ctags,则可以这样做。

我认为统一args.ctags不是一个好主意。

我试着发现你是对的。 他们只添加了噪音。
我只添加了“ --sort = no”。
我已发送拉取请求#2650


--extras = + q

    parser-verilog.r/verilog-sf_bug73_3
    parser-verilog.r/systemverilog-assignment
    parser-verilog.r/systemverilog-2d-array
    parser-verilog.r/systemverilog-github2635
    parser-verilog.r/verilog-sf_bug108_2
    parser-verilog.r/verilog-memleak

仅发出无用的消息。

--fields = + i

没变化

--kinds-systemverilog = + Q

没变化

@antoinemadec
我已在#2653上解决了此问题。 请尝试一下。

让我结束这个问题。

此页面是否有帮助?
0 / 5 - 0 等级