From 759c05f04830a6a0b15f3537ace3fda0533b0885 Mon Sep 17 00:00:00 2001 From: Kirigaya <1193466151@qq.com> Date: Sun, 22 Sep 2024 21:20:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=89=8D=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=20|=20=E5=AE=8C=E6=88=90=20sv=5Fparser=20?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/sv_parser.rs | 44 +++++++++++++++++++ src/custom_request.rs | 85 ++++++++++++++++++++++++++++--------- src/definition.rs | 2 +- src/definition/def_types.rs | 13 ------ src/main.rs | 6 ++- src/sources.rs | 2 + sv-parser | 2 +- testfiles/child_1.v | 19 +++++++++ testfiles/child_2.v | 9 ++++ testfiles/head_1.v | 28 ++++++++++++ testfiles/hello.v | 11 +++++ testfiles/parent.v | 79 ++++++++++++++++++++++++++++++++++ testfiles/test copy.v | 47 ++++++++++++++++++++ testfiles/test.sv | 82 +++++++++++++++++++++++++++++++++++ testfiles/test.v | 16 +++++++ testfiles/test.vhd | 42 ++++++++++++++++++ 16 files changed, 452 insertions(+), 35 deletions(-) create mode 100644 testfiles/child_1.v create mode 100644 testfiles/child_2.v create mode 100644 testfiles/head_1.v create mode 100644 testfiles/hello.v create mode 100644 testfiles/parent.v create mode 100644 testfiles/test copy.v create mode 100644 testfiles/test.sv create mode 100644 testfiles/test.v create mode 100644 testfiles/test.vhd diff --git a/src/core/sv_parser.rs b/src/core/sv_parser.rs index 15aa000..af609a0 100644 --- a/src/core/sv_parser.rs +++ b/src/core/sv_parser.rs @@ -511,3 +511,47 @@ fn get_column_by_offset(content: &[String], offset: usize) -> usize { 1 // if offset over the file lentgh,return 1 } + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::Path; + + use super::sv_parser; + + const testfiles_dir: &str = "testfiles"; + + macro_rules! unwrap_result { + ($expr:expr) => { + match $expr { + Ok(e) => e, + Err(_) => return + } + }; + } + + macro_rules! unwrap_option { + ($expr:expr) => { + match $expr { + Some(e) => e, + None => return + } + }; + } + + #[test] + fn test_sv_parse() { + let entries = unwrap_result!(fs::read_dir(testfiles_dir)); + for entry in entries { + let entry = unwrap_result!(entry); + let file_path = entry.path(); + if file_path.is_file() { + let extension = unwrap_option!(file_path.extension()); + let file_path = unwrap_option!(file_path.to_str()); + if extension == "v" || extension == "sv" { + let _ = sv_parser(file_path); + } + } + } + } +} \ No newline at end of file diff --git a/src/custom_request.rs b/src/custom_request.rs index b3ca646..55b1846 100644 --- a/src/custom_request.rs +++ b/src/custom_request.rs @@ -1,26 +1,20 @@ -use std::future; +use std::{fs, future}; use std::path::PathBuf; use std::sync::Arc; use log::info; use ropey::Rope; -use serde::Deserialize; -use serde_json::Value; +use serde::{Deserialize, Serialize}; use tower_lsp::jsonrpc::Result; -use tower_lsp::lsp_types::*; +use tower_lsp::{lsp_types::*, LanguageServer}; use crate::core::fast_hdlparam::FastHdlparam; use crate::core::sv_parser::make_fast_from_syntaxtree; +use crate::definition::get_language_id_by_uri; use crate::server::Backend; use crate::sources::parse; -#[derive(Debug, Deserialize)] -pub struct CustomRequestParams { - // 定义你需要的参数 - pub data: Value, -} - #[derive(Clone)] pub struct CustomRequest; @@ -37,24 +31,77 @@ pub fn custom_request() -> Result { Ok(123) } +#[derive(Deserialize, Serialize, Debug)] +pub struct CustomParamRequestParams { + param: String, +} + #[derive(Clone)] -pub struct DoFastApi; +pub struct CustomParamRequest; -impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (TextDocumentItem, ), Result> for DoFastApi { - type Future = future::Ready>; +impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (CustomParamRequestParams, ), Result> for CustomParamRequest { + type Future = future::Ready>; - fn invoke(&self, _server: &'a Arc, _params: (TextDocumentItem, )) -> Self::Future { - let doc = _params.0; - future::ready(do_fast(doc)) + fn invoke(&self, _server: &'a Arc, _params: (CustomParamRequestParams, )) -> Self::Future { + future::ready(custom_param_request(_params.0.param)) } } +pub fn custom_param_request(param: String) -> Result { + info!("receive param: {:?}", param); + Ok(123) +} + + +#[derive(Deserialize, Serialize, Debug)] +pub struct DoFastApiRequestParams { + path: String, +} + + +#[derive(Clone)] +pub struct DoFastApi; + +impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (DoFastApiRequestParams, ), Result> for DoFastApi { + type Future = future::Ready>; + + fn invoke(&self, _server: &'a Arc, _params: (DoFastApiRequestParams, )) -> Self::Future { + let request_param = _params.0; + let path = request_param.path; + future::ready(do_fast(path)) + } +} + +fn make_textdocumenitem_from_path(path_buf: &PathBuf) -> Option { + if let Ok(url) = Url::from_file_path(path_buf) { + if let Ok(text) = fs::read_to_string(path_buf) { + let language_id = get_language_id_by_uri(&url); + return Some(TextDocumentItem::new(url, language_id, -1, text)); + } + } + None +} + /// 前端交互接口: do_fast,输入文件路径,计算出对应的 fast 结构 -pub fn do_fast(doc: TextDocumentItem) -> Result { - info!("lsp get doc: {:?}", doc); +pub fn do_fast(path: String) -> Result { + info!("parse hdl path: {:?}", path); + let path_buf = PathBuf::from(&path); + + let doc = match make_textdocumenitem_from_path(&path_buf) { + Some(doc) => doc, + None => { + let api_error = tower_lsp::jsonrpc::Error { + code: tower_lsp::jsonrpc::ErrorCode::InvalidParams, + message: std::borrow::Cow::Owned(format!("cannot make doc from path : {path}")), + data: None + }; + + return Err(api_error); + } + }; let uri = doc.uri; - let text = Rope::from_str(&doc.text); + let text = Rope::from(doc.text); // fast 解析不需要 include let includes: Vec = Vec::new(); diff --git a/src/definition.rs b/src/definition.rs index f6196d2..51fddeb 100644 --- a/src/definition.rs +++ b/src/definition.rs @@ -147,7 +147,7 @@ fn get_definition_token(line: RopeSlice, pos: Position) -> String { } -fn get_language_id_by_uri(uri: &Url) -> String { +pub fn get_language_id_by_uri(uri: &Url) -> String { let path = uri.path(); let ext_name = std::path::Path::new(path) .extension() diff --git a/src/definition/def_types.rs b/src/definition/def_types.rs index b7eb7c1..0b0b0cf 100644 --- a/src/definition/def_types.rs +++ b/src/definition/def_types.rs @@ -224,19 +224,6 @@ pub trait Scope: std::fmt::Debug + Definition + Sync + Send { } - /// 计算并得到有关 macro 的定义,保证 token 是以 ` 开头的 - fn get_macro_definition(&self, token: &str, byte_idx: usize, url: &Url) -> Option { - let mut definition: Option = None; - - if !token.starts_with("`") { - return definition; - } - - // 计算 macro 的位置,需要 global scope 下的所有 defs 加上 current scope 上的位置 - - definition - } - /// returns all symbols in a document fn document_symbols(&self, uri: &Url, doc: &Rope) -> Vec { let mut symbols: Vec = Vec::new(); diff --git a/src/main.rs b/src/main.rs index 19b094f..b44d9df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![recursion_limit = "256"] -use custom_request::CustomRequest; +use custom_request::{ CustomParamRequest, CustomRequest, DoFastApi }; + use log::info; use std::sync::Arc; use structopt::StructOpt; @@ -14,6 +15,7 @@ mod format; mod server; mod sources; mod custom_request; + #[cfg(test)] mod support; use server::Backend; @@ -38,6 +40,8 @@ async fn main() { let (service, socket) = LspService::build(|client| Arc::new(Backend::new(client, log_handle))) .custom_method("custom/request", CustomRequest) + .custom_method("custom/paramRequest", CustomParamRequest) + .custom_method("api/fast", DoFastApi) .finish(); Server::new(stdin, stdout, socket) diff --git a/src/sources.rs b/src/sources.rs index b1aaf65..166ec50 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -2,6 +2,7 @@ use crate::definition::def_types::*; use crate::definition::get_scopes; use crate::diagnostics::{get_diagnostics, is_hidden}; use crate::server::LSPServer; +use log::info; use log::{debug, error}; use pathdiff::diff_paths; use ropey::{Rope, RopeSlice}; @@ -170,6 +171,7 @@ impl Sources { } // find and add all source/header files recursively from configured include and source directories let src_paths = find_src_paths(&paths); + for path in src_paths { if let Ok(url) = Url::from_file_path(&path) { if let Ok(text) = fs::read_to_string(&path) { diff --git a/sv-parser b/sv-parser index 4960ea3..af11f5f 160000 --- a/sv-parser +++ b/sv-parser @@ -1 +1 @@ -Subproject commit 4960ea3fb7c848462ce6e78dbae454ffc775803f +Subproject commit af11f5ff1ef091562d2b17cdf4de3614aedf2286 diff --git a/testfiles/child_1.v b/testfiles/child_1.v new file mode 100644 index 0000000..bbb5a74 --- /dev/null +++ b/testfiles/child_1.v @@ -0,0 +1,19 @@ +module dependence_1 ( + // this is a test + input a, b, c, + // a test + output Result // balabalabala for result +); + + // a & b | ((b & c) & (b | c)) + // &=*, |=+ AB + BC(B+C) + // Distribute AB + BBC + BCC + // Simplify AA = A AB + BC + BC + // Simplify A + A = A AB + BC + // Factor B(A+C) + + assign Result = a & (b | c); + assign a = 11; + + +endmodule diff --git a/testfiles/child_2.v b/testfiles/child_2.v new file mode 100644 index 0000000..d4d746c --- /dev/null +++ b/testfiles/child_2.v @@ -0,0 +1,9 @@ +module dependence_2 ( +input a, b, c, +output Q +); + +assign Q = a & b | ((b & c) & (b | c)); + + +endmodule \ No newline at end of file diff --git a/testfiles/head_1.v b/testfiles/head_1.v new file mode 100644 index 0000000..8e1429b --- /dev/null +++ b/testfiles/head_1.v @@ -0,0 +1,28 @@ +`define cow 34 + +module dependence_1 ( + input port_a, port_b, port_c, + output out_q +); + // a & b | ((b & c) & (b | c)) + // &=*, |=+ AB + BC(B+C) + // Distribute AB + BBC + BCC + // Simplify AA = A AB + BC + BC + // Simplify A + A = A AB + BC + // Factor B(A+C) + + assign out_q = port_b & (port_a | port_c); +endmodule + + +module test_1 ( + input port_a, port_b, + output Q +); + assign Q = port_b & port_a; +endmodule + +module helloTest(); + assign a = 123; + +endmodule //helloTest \ No newline at end of file diff --git a/testfiles/hello.v b/testfiles/hello.v new file mode 100644 index 0000000..d3d9407 --- /dev/null +++ b/testfiles/hello.v @@ -0,0 +1,11 @@ +`define awda adwwa + +module hello; + + initial begin + $display("hello world"); + $finish; + end + + +endmodule \ No newline at end of file diff --git a/testfiles/parent.v b/testfiles/parent.v new file mode 100644 index 0000000..111eaa2 --- /dev/null +++ b/testfiles/parent.v @@ -0,0 +1,79 @@ +/* + * EN: A simple demo to test search order of dependence + * current file -> macro include -> whole project + * expect dependence_1 from child_1.v (macro include) + * expect dependence_2 from child_2.v (whole project) + * cannot find dependence_3 `main + */ + +`include "child_1.v" +`include "child_2.v" +`define main out +`define simple ss + + +module Main ( + // Main input + input a, b, c, + // Main output + output Qus, Qs, `main +); + + +initial begin + $display("hello world"); +end + + +dependence_1 u_dependence_1_1( + .a(a), + .b(b), + .c(c), + .Result(Qus) +); + +dependence_1 u_dependence_1_2( + .a(a), + .b(b), + .c(c), + .Result(Qus) +); + +dependence_3 u_dependence_3( + .a(a), + .b(b), + .c(c), + .Q(Qs) +); + + +endmodule + +/* @wavedrom this is wavedrom demo1 +{ + signal : [ + { name: "clk", wave: "p......" }, + { name: "bus", wave: "x.34.5x", data: "head body tail" }, + { name: "wire", wave: "0.1..0." } + ] +} +*/ + + +/* @wavedrom this is wavedrom demo2 +{ + signal: [ + { name: "pclk", wave: "p......." }, + { name: "Pclk", wave: "P......." }, + { name: "nclk", wave: "n......." }, + { name: "Nclk", wave: "N......." }, + {}, + { name: "clk0", wave: "phnlPHNL" }, + { name: "clk1", wave: "xhlhLHl." }, + { name: "clk2", wave: "hpHplnLn" }, + { name: "clk3", wave: "nhNhplPl" }, + { name: "clk4", wave: "xlh.L.Hx" }, +]} +*/ + + diff --git a/testfiles/test copy.v b/testfiles/test copy.v new file mode 100644 index 0000000..483b600 --- /dev/null +++ b/testfiles/test copy.v @@ -0,0 +1,47 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 2024 年 05 月 15 日 星期三 +// Design Name: test +// Module Name: test.v +// Project Name: +// Target Devices: +// Tool Versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +////////////////////////////////////////////////////////////////////////////////// +module TOP +( + +); +//============================================================================ +//Input and output declaration. +//============================================================================ +output spi_adc_csn; +output spi_dac_csn; +// output spi_iop_csn; +input spi_iop_csn; +// output spi_mul_sdt; +output spi_mul_sdi; +input spi_mul_sdo; +//============================================================================ +//Wire and reg declaration. +//============================================================================ + +//============================================================================ +//Wire and reg in this module. +//============================================================================ + +//============================================================================ +//logic. +//============================================================================ + + +endmodule \ No newline at end of file diff --git a/testfiles/test.sv b/testfiles/test.sv new file mode 100644 index 0000000..ea5dca1 --- /dev/null +++ b/testfiles/test.sv @@ -0,0 +1,82 @@ +// Test Scenario to validate correct translation +// Refer: https://github.com/Nic30/hdlConvertor/issues/173 +`define ADD 64 +module dummy #( + parameter reg ADDR_WIDTH = 4, + parameter interger ADDER_OOO = 25, + parameter [63:0] ADDER_OOO_1 = (1 << ADDER_OOO) + 1, + parameter logic [63:0] ADD_OTHER = `ADD'hff1 +) (c, a, b, d, f); + output reg c; + output logic [61:0] f; + input [ADDR_WIDTH_ANSI - 1:0] d; + input a, b; + and a1(c, a, b); +endmodule + +module d2(y, a); + output [3:0] y; + input reg [2:0] a; + + not n1 (y, a); +endmodule + +module example (a, b, c, d); + input a, b, c; + output d; + + wire tmp; + wire tmp2; + wire tmp3; + and a1 (tmp, a, b); + dummy du( + .c(tmp2), .a(b), .b(c)); + dummy #( + .ADDR_WIDTH(8) + ) duu(tmp3, b, c); + or o1 (d, tmp, tmp2); +endmodule + +// test dummy comment +module dummy_ansi #( + parameter ADDR_WIDTH = 4, + parameter interger ADDER_OOO = 25, + parameter [63:0] ADDER_OOO_1 = (1 << ADDER_OOO) + 1, + parameter logic [63:0] ADD_OTHER = `ADD'hff1 +) ( + output [ADDR_WIDTH_ANSI - 1:0] c, + input [ADDR_WIDTH_ANSI-1: 0] a, + input [ADDR_WIDTH_ANSI-1:0] b +); + and a1(c, a, b); + assign c = a & b; +endmodule + +module d2_ansi( + output [3:0] y, + input reg [2:0] a +); + + not n1 (y, a); +endmodule + +module example_ansi ( + input a, b, c, + output d +); + + wire tmp; + wire tmp2; + wire tmp3; + and a1 (tmp, a, b); + dummy_ansi du_ansi( + .c(tmp2), .b(b), .a(c)); + dummy_ansi #( + .ADDR_WIDTH_ANSI(8) + ) duu_ansi( + tmp3, + b, + c + ); + or o1 (d, tmp, tmp2); +endmodule diff --git a/testfiles/test.v b/testfiles/test.v new file mode 100644 index 0000000..c30b555 --- /dev/null +++ b/testfiles/test.v @@ -0,0 +1,16 @@ +module testModule(); + reg clk, rst; + reg port_1; + + + always @(posedge clk or negedge rst) begin + + end + + + + always @(posedge clk or negedge rst) begin + + end + +endmodule //testModule diff --git a/testfiles/test.vhd b/testfiles/test.vhd new file mode 100644 index 0000000..b4ea2d9 --- /dev/null +++ b/testfiles/test.vhd @@ -0,0 +1,42 @@ +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +entity top_level is + generic ( + WIDTH : integer := 4 + ); + port ( + A : in STD_LOGIC_VECTOR(WIDTH-1 downto 0); + B : in STD_LOGIC_VECTOR(WIDTH-1 downto 0); + Cin : in STD_LOGIC; + Sum : out STD_LOGIC_VECTOR(WIDTH-1 downto 0); + Cout : out STD_LOGIC + ); +end top_level; + +architecture Behavioral of top_level is + component adder is + generic ( + WIDTH : integer := 4 + ); + port ( + A : in STD_LOGIC_VECTOR(WIDTH-1 downto 0); + B : in STD_LOGIC_VECTOR(WIDTH-1 downto 0); + Cin : in STD_LOGIC; + Sum : out STD_LOGIC_VECTOR(WIDTH-1 downto 0); + Cout : out STD_LOGIC + ); + end component; +begin + adder_instance: adder + generic map ( + WIDTH => X"100c00" + ); + port map ( + A => A, + B => B, + Cin => Cin, + Sum => Sum, + Cout => Cout + ); +end Behavioral; \ No newline at end of file