vhdl-ls / rust_hdl Goto Github PK
View Code? Open in Web Editor NEWLicense: Other
License: Other
The next step is to work on visibility rules for use clauses and nested scopes.
Keywords: potentially visible, directly visible, implicit, explicit, declarative region, immediate scope.
I have identified the following rules.
status: Done
constant ident : natural := 0;
use work.pkg.ident;
-- local ident trumps use
constant ident2 : natural := ident;
status: Pending analysis of formal part of association
use work.pkg;
type rec_t is record
const : natural;
end record;
constant pkg : rec_t := (const => 0);
-- constant pkg : natural := 0; Error if pkg was not record in selected name below
-- Uses record
constant const3 : natural := pkg.const;
status: Pending resolving overloaded names by signature
package pkg is
procedure foo;
end package;
package body pkg is
procedure foo is
begin
report "pkg foo";
end procedure;
end package body;
entity ent is
end entity;
architecture a of ent is
procedure foo is
begin
report "arch foo";
end procedure;
begin
main : process
use work.pkg.foo;
begin
foo; -- arch foo
end process;
end architecture;
status: Done
use work.pkg1.ident;
use work.pkg2.ident;
-- Error requires disambiguation event if pkg1.ident and pkg2.ident are different types
-- Also true if .all was used
constant ident2 : natural := ident;
-- The following would work without ambiguity though
use work.pkg1.ident;
constant ident2 : natural := ident;
use work.pkg2.ident;
status: Pending resolving overloaded names by signature
package pkg is
function fun return natural;
function same return natural;
end package;
package pkg2 is
function fun return boolean;
function same return natural
end package;
use work.pkg.all;
use work.pkg2.all;
-- Works
constant ident : natural := fun;
-- Requires disambiguation
constant ident2 : natural := same;
status: Done
package pkg is
function fun return natural;
function same return natural;
end package;
package pkg2 is
constant fun : boolean := false;
constant same : natural := 0;
end package;
use work.pkg.all;
use work.pkg2.all;
-- Requires disambiguation
constant ident : natural := fun;
-- Requires disambiguation
constant ident2 : natural := same;
status: Done
package pkg is
type enum_t is (v1, v2);
end package;
use work.pkg.enum_t;
constant ident: enum_t := v1;
status: Done
package pkg is
type enum_t is (v1, v2);
end package;
package pkg is
type enum2_t is (v1, v2);
end package;
use work.pkg.enum_t;
use work.pkg2.enum2_t;
-- works because of oveloading
constant ident: enum_t := v1;
status: Done
package pkg is
type enum_t is (v1, v2);
end package;
package pkg is
constant v1 := 0;
end package;
use work.pkg.enum_t;
use work.pkg2.v1;
-- Ambiguous
constant ident : enum_t := v1;
status: Done
package gpkg is
generic (constant init : natural);
constant const : natural := init;
end package;
package pkg is
use work.gpkg;
package ipkg is new gpkg generic map (init => 1);
end package;
package pkg2 is
use work.pkg.ipkg.all;
constant ident : natural := const;
end package;
status: Done
library lib;
package pkg is
type rec_t is record
pkg : natural;
end record;
constant work : rec_t := (pkg => 0);
constant foo : natural := work.pkg;
constant lib : rec_t := (pkg => 0);
constant foo : natural := lib.pkg;
-- Should not work
use lib.pkg.all;
end package;
status: Requires type inferences to make names unambigous. Currently no error
package pkg is
type enum_t is (v1, v2);
type enum2_t is (v2, v1);
end package;
use work.pkg.enum_t;
-- Ambiguous if not commented out
-- use work.pkg.enum2_t;
package pkg2 is
-- A use of an enum type should not bring make all v1 names visible only the one from the specific typ.
constant const : string := v1'instance_name;
end package;
status: Done
package pkg is
constant const : natural := 0;
end package;
entity tb_ent is
end entity;
architecture a of tb_ent is
constant const : natural := 1;
begin
main : process
use work.pkg.const;
begin
assert const = 1;
end process;
end architecture;
status: Done
package pkg is
constant const : natural := 0;
end package;
package pkg2 is
constant const : natural := 2;
end package;
entity tb_ent is
end entity;
architecture a of tb_ent is
use work.pkg.const;
begin
main : process
use work.pkg2.const;
begin
report integer'image(const); -- Should be an error as const is ambiguous
end process;
end architecture;
status: Pending
package pkg is
function MINIMUM (L, R: REAL) return REAL;
end package;
package body pkg is
function MINIMUM (L, R: REAL) return REAL is
begin
return L + R;
end;
end package body;
entity ent is
end entity;
use std.standard.MINIMUM;
use work.pkg.MINIMUM;
architecture a of ent is
begin
main : process
begin
report real'image(MINIMUM(1.0, 2.0)); -- Not ambiguous, prints 3.0
end process;
end architecture;
@kraigher would you be so kind to explain, what was the intention of analysis
directory and how it differs from syntax
directory? What should be added/looked for in any of them?
I'd like to use this program to export the LSIF data to a file so that it can be consumed by GitLab for its "Code Intelligence" feature. This enables hover features, etc. while browsing the code in the web IDE.
Is generating this LSIF dump currently possible? The statement in the README, "...has no intended usability at this point", makes me think it may not be possible, but I was hoping you could tell me clearly whether it is or not. If not, is that an intended feature in the future? Thanks!
p.s. If this is something that would be a "good first issue", I'd be willing to look into it. I'm interested in working in Rust and have read the documentation for the language, but haven't had an opportunity to work on a real project in that language. It would probably be several months before I have the free time to work on this though.
Block comments (/* */
) are not supported.
See IEEE Std 1076-2008 15.4 (p. 234):
A comment is either a single-line comment or a delimited comment. A single-line comment starts with two adjacent hyphens and extends up to the end of the line. A delimited comment starts with a solidus (slash) character immediately followed by an asterisk character and extends up to the first subsequent occurrence of an asterisk character immediately followed by a solidus character.
Create NeoVim integration of VHDL language server and publish instructions on how to use it.
I cannot build project on my machine:
[slawek@slawek-desktop rust_hdl]$ uname -a
Linux 4.18.19-100.fc27.x86_64 #1 SMP Wed Nov 14 22:04:34 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
[slawek@slawek-desktop rust_hdl]$ rustc -V
rustc 1.30.1
Output:
[slawek@slawek-desktop rust_hdl]$ cargo run --bin vhdl_parser --verbose
Fresh unicode-width v0.1.5
Fresh ansi_term v0.11.0
Fresh bitflags v1.0.4
Fresh vec_map v0.8.1
Fresh strsim v0.7.0
Fresh fnv v1.0.6
Fresh textwrap v0.10.0
Fresh pad v0.1.5
Fresh libc v0.2.45
Fresh serde v1.0.82
Fresh atty v0.2.11
Fresh toml v0.4.10
Fresh clap v2.32.0
Compiling vhdl_parser v0.4.0 (/home/slawek/git/github/rust_hdl/vhdl_parser)
Running `rustc --edition=2018 --crate-name vhdl_parser vhdl_parser/src/lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=3b2de8b47672ecc5 -C extra-filename=-3b2de8b47672ecc5 --out-dir /home/slawek/git/github/rust_hdl/target/debug/deps -C incremental=/home/slawek/git/github/rust_hdl/target/debug/incremental -L dependency=/home/slawek/git/github/rust_hdl/target/debug/deps --extern clap=/home/slawek/git/github/rust_hdl/target/debug/deps/libclap-663bd2b0902d3fff.rlib --extern fnv=/home/slawek/git/github/rust_hdl/target/debug/deps/libfnv-f76d38542e21cab9.rlib --extern pad=/home/slawek/git/github/rust_hdl/target/debug/deps/libpad-1b67d0c55374ab28.rlib --extern toml=/home/slawek/git/github/rust_hdl/target/debug/deps/libtoml-6542184d7b6585e4.rlib`
error: Edition 2018 is unstable and only available for nightly builds of rustc.
error: Could not compile `vhdl_parser`.
Caused by:
process didn't exit successfully: `rustc --edition=2018 --crate-name vhdl_parser vhdl_parser/src/lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=3b2de8b47672ecc5 -C extra-filename=-3b2de8b47672ecc5 --out-dir /home/slawek/git/github/rust_hdl/target/debug/deps -C incremental=/home/slawek/git/github/rust_hdl/target/debug/incremental -L dependency=/home/slawek/git/github/rust_hdl/target/debug/deps --extern clap=/home/slawek/git/github/rust_hdl/target/debug/deps/libclap-663bd2b0902d3fff.rlib --extern fnv=/home/slawek/git/github/rust_hdl/target/debug/deps/libfnv-f76d38542e21cab9.rlib --extern pad=/home/slawek/git/github/rust_hdl/target/debug/deps/libpad-1b67d0c55374ab28.rlib --extern toml=/home/slawek/git/github/rust_hdl/target/debug/deps/libtoml-6542184d7b6585e4.rlib` (exit code: 1)
According to kbknapp/cargo-outdated#141 it is somehow related to edition switch.
In order to add e.g. VITAL packages to the IEEE library, the entire ieee library must be redefined as any vhdl_ls.toml defining an ieee library with the VITAL files will override the built-in definition. Same problem occurs for the Synopsys libraries,
vhdl_ls
in order to better support legacy designs. The license states that they can be freely distributed.There would still be some corner cases with e.g. Mentor's packages but those could be solved by re-defining ieee.
Similar to #31 label
, subtype
, literal
, units
,group
,file
,property
and sequence
is also not supported for user-defined attributes.
See IEEE Std 1076-2008 7.2 (p. 95):
attribute_specification ::=
attribute attribute_designator of entity_specification is expression ;
entity_specification ::=
entity_name_list : entity_class
entity_class ::=
entity
| architecture
| configuration
| procedure
| function
| package
| type
| subtype
| constant
| signal
| variable
| component
| label
| literal
| units
| group
| file
| property
| sequence
If adding a vhdl_ls.toml
without all standard libraries used, the workspace can get disgustingly cluttered (i.e. 15k problems reported in VSC). I don't have all the libraries for some things as I jump around a lot and would rather not point to them in multiple workspaces. Can a check for standard libraries be added along with a flag to possibly ignore these? The part I care about is the wiring between design files and syntax, not that I don't have my environment configured to point to all standard libraries and vendor libraries.
EDIT: Ignore this Issue. This issue belongs in the vscode client repo. sry
"pure function ..." and "impure function ..." don't seem to Syntax highlight properly.
The Problem seems to be that the match for "function" only takes whitespace before "function".
Removing "^\s*" in the file vhdl.tmLanguage fixes the Problem for me.
<key>function_prototype_pattern</key>
<dict>
<key>patterns</key>
<array>
<dict>
<key>begin</key>
<string>(?x)
# From the beginning of the line
^\s* <-- remove this line
# The word function $1
((?i:function))\s+
...
<key>function_definition_pattern</key>
<dict>
<key>patterns</key>
<array>
<dict>
<key>begin</key>
<string>(?x)
# From the beginning of the line
^\s* <-- remove this line
# The word function $1
((?i:function))\s+
...
When running projects with pre-compiled/vendor libraries or mixed language projects, not all files will be in vhdl libraries added to the vhdl_ls.toml file.
It would be very useful to be able to add primary units with "missing declarations" and unknown libraries, to an ignore list;
for unknown libraries (lib_x) it would be an option to add them to the vhdl_ls.toml file without any files, in which case all:
"use lib_x.pkg" statements would be skipped by the semantics checker
Tests for both language server and parser fail on Windows. I don't think any of them look serious, it's mostly just directory separator conventions.
rust_hdl>cargo test --package vhdl_ls
Finished dev [unoptimized + debuginfo] target(s) in 1.68s
Running target\debug\deps\vhdl_ls-b008e55f06f9c466.exe
running 7 tests
test vhdl_server::tests::initialize ... ok
test vhdl_server::tests::utf8_to_latin1_conversion_gives_error_diagnostic ... ok
test vhdl_server::tests::did_open_with_diagnostics_and_change_without ... ok
test vhdl_server::tests::did_open_no_diagnostics ... ok
test vhdl_server::tests::initialize_with_config_missing_files ... FAILED
test vhdl_server::tests::initialize_with_bad_config ... ok
test vhdl_server::tests::initialize_with_config ... FAILED
failures:
---- vhdl_server::tests::initialize_with_config_missing_files stdout ----
thread 'vhdl_server::tests::initialize_with_config_missing_files' panicked at 'Object({"message": String("Loaded workspace root configuration file: C:\\Users\\mark_\\AppData\\Local\\Temp\\.tmpDOjfq4\\vhdl_ls.toml"), "type": Number(4)}) does not contain string "Loaded workspace root configuration file: C:\\Users\\mark_\\AppData\\Local\\Temp\\.tmpDOjfq4\\vhdl_ls.toml"', vhdl_ls\src\rpc_channel.rs:158:25
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
---- vhdl_server::tests::initialize_with_config stdout ----
thread 'vhdl_server::tests::initialize_with_config' panicked at 'Object({"message": String("Loaded workspace root configuration file: C:\\Users\\mark_\\AppData\\Local\\Temp\\.tmpa9KZcR\\vhdl_ls.toml"), "type": Number(4)}) does not contain string "Loaded workspace root configuration file: C:\\Users\\mark_\\AppData\\Local\\Temp\\.tmpa9KZcR\\vhdl_ls.toml"', vhdl_ls\src\rpc_channel.rs:158:25
failures:
vhdl_server::tests::initialize_with_config
vhdl_server::tests::initialize_with_config_missing_files
test result: FAILED. 5 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
rust_hdl>cargo test --package vhdl_parser
Finished dev [unoptimized + debuginfo] target(s) in 0.10s
Running target\debug\deps\vhdl_parser-2639f44ec7d3e3aa.exe
running 525 tests
ommited test list
failures:
---- config::tests::test_warning_on_emtpy_glob_pattern stdout ----
thread 'config::tests::test_warning_on_emtpy_glob_pattern' panicked at 'assertion failed: `(left == right)`
Diff < left / right > :
[
Message {
message_type: Warning,
< message: "Pattern \'parent_folder\\missing*.vhd\' did not match any file",
> message: "Pattern \'parent_folder/missing*.vhd\' did not match any file",
},
]
', vhdl_parser\src\config.rs:420:9
---- config::tests::test_append_config stdout ----
thread 'config::tests::test_append_config' panicked at 'assertion failed: `(left == right)`
Diff < left / right > :
Config {
libraries: {
"lib1": LibraryConfig {
name: "lib1",
files: [
< "parent_folder0\\pkg1.vhd",
> "parent_folder0/pkg1.vhd",
],
},
"lib3": LibraryConfig {
name: "lib3",
files: [
< "parent_folder1\\pkg3.vhd",
> "parent_folder1/pkg3.vhd",
],
},
"lib2": LibraryConfig {
name: "lib2",
files: [
< "parent_folder0\\pkg2.vhd",
< "parent_folder1\\ent.vhd",
> "parent_folder0/pkg2.vhd",
> "parent_folder1/ent.vhd",
],
},
},
}
', vhdl_parser\src\config.rs:323:9
---- config::tests::test_warning_on_missing_file stdout ----
thread 'config::tests::test_warning_on_missing_file' panicked at 'assertion failed: `(left == right)`
Diff < left / right > :
[
Message {
message_type: Warning,
< message: "File parent_folder\\missing.vhd does not exist",
> message: "File parent_folder/missing.vhd does not exist",
},
]
', vhdl_parser\src\config.rs:344:9
---- config::tests::config_from_str stdout ----
thread 'config::tests::config_from_str' panicked at 'assertion failed: `(left == right)`
Diff < left / right > :
[
"C:\\Users\\mark_\\AppData\\Local\\Temp\\.tmphdYH7e\\pkg2.vhd",
> "\\\\?\\C:\\Users\\mark_\\AppData\\Local\\Temp\\.tmpfc9mko\\absolute.vhd",
]
', vhdl_parser\src\config.rs:266:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
failures:
config::tests::config_from_str
config::tests::test_append_config
config::tests::test_warning_on_emtpy_glob_pattern
config::tests::test_warning_on_missing_file
test result: FAILED. 521 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out
When using the case syntax, a nice feature would be to automatically insert all case names. To accomplish this, the parser would need to see that the case variable as shown below is an enumerate, and if so, it would automatically insert all options:
type fsm_t is (IDLE, STEP_1, DONE);
signal state : fsm_t := IDLE;
...
process(all)
begin
case state is -- after user presses tab when entering case select variable, it will auto populate options like below only if parser recognizes signals type as an enumerate
when IDLE =>
when STEP_1 =>
when DONE =>
when others => null; -- could have option for the user to set state to first option in enumerate for the others case
end case;
end process;
I've been trying rust_hdl with my projects which are mostly targeting Xilinx FPGAs with the Vivado tools. It's looking very, very useful (thanks for the hard work!), but I've got a bit of a problem with the parsing.
The tool throws a lot of errors with the message
Expected 'entity', 'architecture', 'configuration', 'package', 'signal', 'variable', 'procedure' or 'function'
wherever user-defined attributes are used. Typically there are only a few of these in my own VHDL files, but when I include the Xilinx component libraries I have thousands of errors.
The problem also prevents other errors being thrown (related to #25 I guess). In the sample below there is an extra comma on the instantiation which is not flagged.
Here's a sample which shows the problem. It happens whether or not there's a vhdl_ls.toml
included.
sample.vhd
:
entity sample1 is
port (
CLK : in std_logic;
nCLK : out std_logic
);
end entity;
architecture a of sample1 is
component sample2 is
port (
CLK : in std_logic;
nCLK : out std_logic
);
end component sample2;
attribute BOX_TYPE : string;
attribute BOX_TYPE of sample2 : component is "user_black_box";
begin
Inst_sample2 : sample2
port map (
CLK <= CLK,
nCLK <= nCLK,
);
end a;
entity sample2 is
port (
CLK : in std_logic;
nCLK : out std_logic
);
end entity;
architecture a of sample2 is
signal s_nclk : std_logic;
begin
s_nclk <= not CLK;
nCLK <= s_nclk;
end a;
I would like to add functionality for generating .dot files with syntax tree graphs. It requires the root of the syntax tree for particular file.
Now, correct my if I am wrong anywhere.
It is impossible right now, because design_unit
is a root for single syntax tree, not design_file
. However, it looks like it is possible to get design units from particular file. First I need to get DesignRoot
from Project
. Then single Library
from DesignRoot
. Next, from units_by_source
hash map I can get UnitId
for units coming from the file I am interested in. From UnitId
I can fetch UnitKey
. Then I have to come back to the Library
and from units
I can fetch LockedUnit
. LockedUnit
contains unit
, which contains AnyDesignUnit
, which is the root of syntax tree for particular design unit.
I have mixed attribute names with type names, but I hope you understand.
If you had any implementation guidelines I'd really appreciate.
There is a need for a configuration file format to declare which files belong to which libraries.
For the language server the configuration file will have to be placed in the workspace by the user.
I think TOML will be a suitable format for the configuration file format using this rust library https://docs.rs/toml/0.4.8/toml/.
In the absense of a configuration file the user would be warned and semantic analysis would be turned off giving the user just syntax errors.
The command line parser demonstrator could also read the configuration file. Is it still usable for the parser demonstrator to be able to read files as a list in argv without library information to just perform syntax checking? Maybe the parser-only without semantic analysis should be a separate binary if we need it. You have started to use the parser binary @mbrobbel, what do you think?
The data structure for the library would have to support clearing all design units from a certain file and updating them again to work with the language server when a file is edited and design units are removed or added.
In summary this still needs to be done;
vhdl_parser
as packagedv.*.*.*
tagrelease_notes/v*.*.*.md
fileI use Sublime as editor and would like to try this out. Does anyone know if you can/should add sublime package to the main repo, or a separate one?
GHDL has a linter plugin implemented as a separate repository.
While it works fine on most of the definitions, running "Goto definition" on function or procedure calls seems not to be implemented yet.
(Tested on VS Code running current master build 189c98a)
When a postponed process is used, vhdl_ls doesn't like it when the end tag is end postponed process rather than just end process or end. It marks the end postponed as an error and says it was expecting "process".
I'm not sure what the standard says, but questasim, synopsys vcs, and aldec riviera all accept the end postponed process syntax.
proc_label: postponed process
variable whatever: integer;
begin
whatever := 0;
end postponed process;
^^^^^^^^^
"expected process"
There is uncertainty in the standard regarding externally visible generics:
Se GHDL issue:
ghdl/ghdl#440
package gpkg is
generic (const : natural);
end package;
package ipkg is new work.gpkg generic map (const => 1);
entity ent is
end entity
architecture a of ent is
begin
main : process
-- Case 1
assert work.ipkg.const = 1; -- Should this result in a 'no declaration of const' error?
-- case 2
assert << constant @work.ipkg.const : natural>> = 1; -- Should this be visible?
end process;
end architecture;
Create an Atom integration of VHDL Language Server and publish instructions.
Conversion from slice of bytes to utf-8 in latin_1.rs is done with from_utf8_unchecked
.
From documentation:
Converts a slice of bytes to a string slice without checking that the string contains valid UTF-8.
I think it has been chosen to achieve better performance, however in case of invalid UTF-8, the later execution may result with undefined behavior.
Tool such as document generators and code formatters also need to care about comments.
To achieve this the parser shall also extract comments into the AST.
There are two types of comments leading and trailing.
An element may have several leading comments and at most one trailing comment.
-- A leading comment associated with foo
-- Another leading comment associated with foo
signal foo : natural; -- A trailing comment associated with foo
-- A leading comment associated with bar
signal bar : natural;
There are plenty of opportunities for placing comments in unnatural places:
if foo
-- Unnatural comment but legal
then
end
-- Unnatural comment but legal
if
-- Unnatural comment but legal
;
I think the acceptable solution for such comments are:
Not acceptable solutions would be:
There are several possible solutions:
It would be possible to add a token kind for comments, leading and trailing comments must be distinguishable. These token need to be handled differently than other tokens otherwise the cost will huge as all parsing functions must be able to handle comments everywhere. For example if a parsing function today expects a SemiColon
it must be able to ignored a comment. Thus the TokenStream
must have dedicated methods to optionally expect a leading or a trailing comment.
A better approach might be to add a method to the TokenStream
to extract the leading and trailing comments for the last token. The TokenStream
could keep track of comments which had not been extracted and thus were ignored. For each of these ignored comments a warning could be emitted after parsing is completed. Thus support for comments can be added incrementally and comments not handled will be explicitly flagged as ignored.
A variant of 2. is to attach the comments as metadata to the Token
structs. This is very clean as the comments are directly associated with the token. There would still need to be a mechanism to detect comments which where not handled. I think this can be handled by storing a reference in the Token
struct to an external storage where the comment remains if it was not extracted. Thus ignored comments will remain in the external structure which can be traversed after parsing to print warnings for ignored comments.
I tried to set up vhdl_ls
for my VHDL project, for which I use ghdl
for simulation. To fully profit of vhdl_ls
, I also created a vhdl_ls.toml
file referencing my project sources as well as the sources of the used packages from libraries std
and ieee
. I found the sources of the standard packages in my GHDL installation below $PREFIX/lib/ghdl/src/
. I only missed the source of std.standard
. Using the standard.vhd
file from your example_project
, I was able to parse my project sources and use vhdl_ls
in my Emacs. However, your standard.vhd
is for VHDL'08 and my project is still restricted to VHDL'93. So, I looked wether ghdl
could provide the source of std.standard
(cf. to ghdl issue #802). @tgingold pointed me to use ghdl --disp-standard --std=93
to generate the VHDL source of std.standard
.
Unfortunately, vhdl_parser
and vhdl_ls
fail to parse the generated VHDL source. vhdl_parser
generates parsing errors on at least the definition of type UNIVERSAL_INTEGER
(Integer too large for 64-bits signed), real
(invalid float literal due to use of +
in exponent), and time
(same as for UNIVERSAL_INTEGER
). It would be great if rust_hdl
's VHDL parser could be enhanced to parse the std.standard
package VHDL source generated by ghdl --disp-standard
for all supported language standards by ghdl
(VHDL'87, VHDL'93, VHDL'08).
To reproduce the issue, build ghdl
and rust_hdl
from their respective master branches. Then, generate the std.standard
package VHDL source and try to parse it using vhdl_parser
of rust_hdl
:
$ ghdl --disp-standard --std=87 > standard.v87
$ ghdl --disp-standard --std=87 > standard.v93
$ ghdl --disp-standard --std=87 > standard.v08
$ vhdl_parser standard.v87
$ vhdl_parser standard.v93
$ vhdl_parser standard.v08
For example, this is the output for standard.v93
:
$ vhdl_parser standard.v93
Error when parsing standard.v93
error: Integer too large for 64-bits signed
--> standard.v93:103
|
101 | -- function "<=" (<anonymous>: severity_level;
102 | -- <anonymous>: severity_level) return boolean;
103 --> type UNIVERSAL_INTEGER is range -9223372036854775808 to 9223372036854775807;
| ~~~~~~~~~~~~~~~~~~~
104 | -- function "=" (<anonymous>: <UNIVERSAL_INTEGER>;
105 | -- <anonymous>: <UNIVERSAL_INTEGER>) return boolean;
error: Expected 'library', 'use', 'context', 'entity', 'architecture', 'configuration' or 'package'
--> standard.v93:103
|
101 | -- function "<=" (<anonymous>: severity_level;
102 | -- <anonymous>: severity_level) return boolean;
103 --> type UNIVERSAL_INTEGER is range -9223372036854775808 to 9223372036854775807;
| ~~
104 | -- function "=" (<anonymous>: <UNIVERSAL_INTEGER>;
105 | -- <anonymous>: <UNIVERSAL_INTEGER>) return boolean;
Summary:
Found no warnings
BAD: Found errors in 1 files
v0.16.0 crates.io release action failed due to API key revocation.
error: api errors (status 401 Unauthorized): The given API token does not match the format used by crates.io. Tokens generated before 2020-07-14 were generated with an insecure random number generator, and have been revoked. You can generate a new token at https://crates.io/me. For more information please see https://blog.rust-lang.org/2020/07/14/crates-io-security-advisory.html. We apologize for any inconvenience.
In an effort to make myself familiar with the code base, I've started adding documentation to types, traits, and the occasional method. While I'm at it, I also fixed some typos I noticed. Example.
@kraigher Are you interested in these kinds of changes? If not, that's not a problem at all, I'll simply keep them in my local repo. If yes, I'd gather some more before I create a pull request.
You also cautioned me against documenting the analysis
module because it will change frequently. Would it be okay if I add brief, high-level comments there anyway? Like I said, I'm doing this mostly for myself to aid understanding, and I wouldn't mind at all if you delete them later in case they become out of date.
A declarative part contains declarations. For the purpose of recovery we divide declarations into two categories.
Simply scanning for a synchronization token is enough.
constant bad :
variable good : natural;
constant bad :
begin
constant bad :
end
A compound declaration may contain nested declarations and thus recovery cannot simply search for a synchronization token unless we risk synchronizing with a nested declaration.
-- parent region
procedure proc is
procedure nested is
begin
if -- Syntax error
end;
-- We cannot synchronize with this if the above syntax error
-- is propagated all the way back to the parent region
variable good : natural;
begin
end;
A goal of the analyser is to automatically compute the analysis order of design units such that the user does not have to care about it.
Automatically computing the analysis order requires traversing the entire AST and finding all use clauses, packages instances as well as instantiations. Circular dependencies might occur which should trigger an error.
use lib.unit;
Simple case, introduces depenency on lib.unit
use lib.all;
Trickier case especially if lib
is work
. All names within lib
cannot be considered a dependency automatically since it might create circular dependencies especially for work
but also with co-dependent libraries.
We will have to check which names are actually used and only then create the dependency.
use lib.all; -- contains pkg
use pkg.foo; -- Only here we create the depenency on lib.pkg
This also unfortunately interacts with normal declarative part analysis, for example.
use lib.all; -- contains pkg
package pkg is new gpkg;
use pkg.foo; -- foo might be a nested package instance within pkg
-- Should we consider lib.pkg a dependency here, what if pkg was a local constant?
Maybe it is ok to falsely introduce a dependency here to simplify the logic without any practical problem.
I see two solutions to making automatic analysis order work.
Have a separate pass to explicitly only compute the analysis order.
pros
Compute the analysis order concurrently with other analysis implicitly. If the use of an unanalysed unit is encountered we re-curse and analyse the unit. Units will have to be flagged to detect circular dependencies.
pros
cons
Being able to configure the language server to recursively include a directory would simplify project setup. A common practice is to have all RTL sources in a single directory and it should be possible to point to this directory in vhdl_ls.toml
. Implementing support for directory references would remove the need to update vhdl_ls.toml
when new files are added to a project.
To account for some variability, it could also be possible to configure include and exclude patterns for the files within the directory.
Suggestion to expand vhdl_ls.toml
to:
library.files
(glob::glob
?)**/*.vhd
)excludes
field to library to filter out unwanted files. Supports the same pattern styles as library.files
.Example:
[libraries]
lib1.files = [
'src',
'tests/*/tb_*.vhd',
]
lib1.exclude = [
'test/*/*_mock.vhd',
]
On https://crates.io/ there are two crates vhdl_parser and vhdl_lang. Both point to this repository. However there is no directory named vhdl_parser
, even when you grep for vhdl_parser you get nothing. Can you explain how vhdl_parser is created and what is the difference between vhdl_parser and vhdl_lang?
Implement conversion of the AST back into a VHDL text file.
There are a number of possible uses for having the ability to go back from the AST to VHDL.
But mostly I thought it was a reasonably independent project that I could work on and not get in your way too much :).
specifying a label for an end if clause is not supported.
mylabel: if condition then
call_function(a,b,c);
end if mylabel;
^^^^^
Expected ';'
Note that labelled if statement using a regular non-labelled end if is supported
@kraigher would you be so kind to explain what is the rationale for Message
and MessagePrinter
structs? Do I understand correctly, that Message has been introduced to abstract the message exchanged with LSP client, but later you wanted to also print some of the messages in the vhdl_lang
binary, so you have introduced MessagePrinter
?
Currently the parser/LSP panics when it encounters a non latin-1 character:
thread 'main' panicked at 'Source was not legal latin-1: Custom { kind: Other, error: "Invalid latin-1 character � in -- ...
VSCode will attempt to restart the server leading to a new crash and after 5 crashes within 3 minutes VCode will no longer try to start the server. To start the server again, VSCode must be restarted.
It would be better if an error was emitted for the location of the non valid character. The parser can then either stop parsing the file or try to recover.
Create VSCode integration of VHDL language server and publish instructions on how to use it.
@kraigher Is there anywhere information about what you are working on right now, and what are the plans for near future?
myproc: postponed process(i_clk, i_rst) is
begin
-- blablabla
end postponed process;
The parser will break at the end postponed process line because it isn't expecting the postponed keyword
Currently Rust HDL looks for the config file when the LSP is started, so any changes to it (for example adding files, fixing libraries, etc) require the server/editor to be restarted.
Ideally, I think restarting or refreshing the server should happen transparently to the user, so that the process of changing and fixing the config file is faster.
The current documentation is almost non-existing. @kraigher could you please document at least vhdl_lang
crate public items?
I am trying to use vhdl_lang
as dependency, but I stuck analyzing the internals. I guess the point of sharing code via crate is a bit different.
With the VHDL LS plugin (0.3.1) of VisualStudioCode (1.47.3) the VHDL keyword pure is flagged as unexpected before the function keyword. A list of expected keywords is shown. The keyword impure is among the ones suggest, which I consider mildly ironic.
The keyword pure is optional but correct in this position (the only position where pure can actually appear at all I guess) and should not be flagged.
According to the plugin's description "As VHDL LS uses the Rust HDL language server, issues related to syntax and semantic checks should be reported directly to Rust HDL".
I would like to implement support for precompiled libraries. Precompiled libraries would speed up the initial analysis of a project, because std
and ieee
would not have to be parsed and analyzed every time.
My thoughts on the subject:
vhdl_lang
should not prescribe a file format, nor should it be involved in locating precompiled libraries on disk.vhdl_lang
should probably not include a reference implementation of the (de)serialization step. vhdl_ls
is a different matter though, and should probably provide such an implementation. Not sure on that point...vhdl_lang
isn't able to locate, load, and deserialize a precompiled library, and programs using vhdl_lang
should, performance reasons, not be forced to load and deserialize every available library beforehand, the following procedure would make sense:
Project
and parse the source files.Project::get_missing_libs()
which returns the set of library names which are referenced in the library clauses of any parsed design unit, minus the set of library names which are already known to the Project
instance.Library
instances are added to the Project
.Project::analyse()
. No changes should be needed anywhere in the analysis code.Regarding the concrete (de)serialization implementation:
SrcPos
instances of declarations would probably be stored as some form of "named entity #17259 in library 'xyz'".SrcPos
data. Typical scenario: when working with command line tools like ModelSim's vcom
and vsim
, which incrementally add source files to a working library.Reference
type? Currently it uses SrcPos
as a way to identify declarations, which is okay for a language server, but suboptimal for a simulator or synthesis tool. It's also a bit problematic when serializing a black box library.@kraigher I struggle to analyze how the declaration position is determined when LSP client asks for Go to Declaration
. Everything is clear until search_reference
method is called on DesignRoot
pub fn search_reference(&self, source: &Source, cursor: Position) -> Option<SrcPos> {
ItemAtCursor::search(self, source, cursor)
}
If I understand correctly, first the item under the cursor is found, what is done with ItemAtCursor
. By the way, I have temporarily modified is_inside
function to print some debug info, and it looks like this function returns true
twice for the same cursor
and pos
values during the single Go to Declaration
procedure.
fn is_inside(&self, pos: &SrcPos) -> bool {
// cursor is the gap between character cursor and cursor + 1
// Thus cursor will match character cursor and cursor + 1
if pos.start() <= self.cursor && self.cursor <= pos.end() {
error!("{:#?}", self.cursor);
error!("{:#?}", pos);
return true
} else {
return false
}
}
Output:
[2020-08-03T13:35:53Z ERROR vhdl_lang::ast::search] Position {
line: 91,
character: 18,
}
[2020-08-03T13:35:53Z ERROR vhdl_lang::ast::search] SrcPos {
source: Source {
source: Source {file_name: "/home/mkru/workspace/smx/rcv_frames_shuffler/rtl/rcv_frames_shuffler.vhd"},
},
range: Range {
start: Position {
line: 91,
character: 12,
},
end: Position {
line: 91,
character: 20,
},
},
}
[2020-08-03T13:35:53Z ERROR vhdl_lang::ast::search] Position {
line: 91,
character: 18,
}
[2020-08-03T13:35:53Z ERROR vhdl_lang::ast::search] SrcPos {
source: Source {
source: Source {file_name: "/home/mkru/workspace/smx/rcv_frames_shuffler/rtl/rcv_frames_shuffler.vhd"},
},
range: Range {
start: Position {
line: 91,
character: 12,
},
end: Position {
line: 91,
character: 20,
},
},
}
Nevertheless, what I do not understand is what happens when the item under the cursor is already found. How is the actual declaration found? The whole search logic looks a bit distributed to me, and I find it hard to get the overall view.
@kraigher I wanted to ask one question, as you have probably already considered this issue. Have you thought about structuring project in the same way the standard is structured.
For example each section would be a module, and subsection would be represented by structs, enums, traits, etc.
At first it looks like a great idea, but maybe there is something that I have overlooked.
I would really appreciate you experience as you have probably tackled a lot of problems during implementation.
I've started to implement support for standard libraries, vendor libraries and some of the more prominent open source libraries (e.g. VUnit) in VHDL LS. I would like to do this by letting the extension maintain its own vhdl_ls.toml file. The user would be able to select which libraries to enable (including install dirs in some cases) and the client then generates a vhdl_ls.toml pointing out the standard/vendor libraries. The generated file would be local to the project (stored somewhere in .vscode). To do this I need a way to pass this file to vhdl_ls. I believe that the easiest way is as a command line parameter to vhdl_ls but I'm open for any suggestions.
Suggestion:
Add a command line parameter to vhdl_ls to point out a specific vhdl_ls.toml, priority according to list below:
@kraigher I was loosely thinking about adding support for sources formatting.
Right now it does not look like an easy task, because the syntax tree looks more like an Abstract Syntax Tree, not Concrete Syntax Tree and there is no easy access to separators (spaces, tabs, newlines). Such information of course could be retrieved based on the positions, however it wouldn't look like a clean solution (at least for me).
I am not a compilers expert, but do you think this is pure AST? It holds information about positions, and AST usually does not contain such information.
Have you ever been thinking about reformatting functionality? Have you had any idea how you would like to achieve this functionality?
Hi
It seems that lsp-define-stdio-client is deprecated and unless I'm missing something has been completely removed. I was trying to follow your example for lsp-mode in Emacs but got an error that lsp-define-stdio-client cannot be found.
I'm using Emacs 26.2 and latest version of the elp-mode package.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.