From 93466defd0cc84fc48dc19c80a22c98841c6c78b Mon Sep 17 00:00:00 2001 From: notafile Date: Thu, 30 Dec 2021 20:46:28 +0100 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 39 +++++++++++++ Cargo.toml | 9 +++ identity.fut | 4 ++ src/literals.rs | 46 ++++++++++++++++ src/main.rs | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 identity.fut create mode 100644 src/literals.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fed877d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,39 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "futilehdl" +version = "0.1.0" +dependencies = [ + "nom", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..35a1bc0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "futilehdl" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nom = "7.1.0" diff --git a/identity.fut b/identity.fut new file mode 100644 index 0000000..8f35fd2 --- /dev/null +++ b/identity.fut @@ -0,0 +1,4 @@ +module top ( input wire x, output wire y ) +{ + assign x = y; +} diff --git a/src/literals.rs b/src/literals.rs new file mode 100644 index 0000000..4897277 --- /dev/null +++ b/src/literals.rs @@ -0,0 +1,46 @@ +use nom::{ + bytes::complete::tag, + character::complete::{char, one_of}, + combinator::{map_res, recognize}, + multi::{many0, many1}, + sequence::{preceded, terminated}, +}; + +use crate::IResult; + +pub fn decimal(input: &str) -> IResult<&str, u64> { + map_res( + recognize(many1(terminated(one_of("0123456789"), many0(char('_'))))), + |out: &str| out.parse::(), + )(input) +} + +pub fn hexadecimal(input: &str) -> IResult<&str, u64> { + map_res( + preceded( + tag("h"), + recognize(many1(terminated( + one_of("0123456789abcdefABCDEF"), + many0(char('_')), + ))), + ), + |out: &str| u64::from_str_radix(&str::replace(&out, "_", ""), 16), + )(input) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_dec() { + assert_eq!(decimal("123").unwrap().1, 123); + assert_eq!(decimal("0123").unwrap().1, 123); + } + + #[test] + fn test_hex() { + assert_eq!(hexadecimal("hfF").unwrap().1, 0xff); + assert_eq!(hexadecimal("hF").unwrap().1, 0xf); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..df91ef7 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,144 @@ +mod literals; + +use nom::{ + self, + branch::alt, + bytes::complete::tag, + character::complete::{alpha1, alphanumeric1, char, multispace0}, + combinator::{map, opt, recognize}, + error::ParseError, + error::{context, convert_error, VerboseError}, + multi::{many0, separated_list1}, + sequence::{delimited, pair, preceded, terminated, tuple}, +}; + +use crate::literals::{decimal, hexadecimal}; + +const REG_EXAMPLE: &'static str = "reg [0:15] msg = 16'hAAAA;"; + +// custom IResult type for verboseerror +pub type IResult> = nom::IResult; + +fn ws0<'a, F: 'a, O, E: ParseError<&'a str>>( + inner: F, +) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> +where + F: FnMut(&'a str) -> IResult<&'a str, O, E>, +{ + delimited(multispace0, inner, multispace0) +} +fn identifier(input: &str) -> IResult<&str, &str> { + recognize(pair( + alt((alpha1, tag("_"))), + many0(alt((alphanumeric1, tag("_")))), + ))(input) +} + +fn widthspec(input: &str) -> IResult<&str, u64> { + delimited(char('['), ws0(decimal), char(']'))(input) +} + +fn intliteral(input: &str) -> IResult<&str, (u64, u64)> { + tuple((terminated(decimal, char('\'')), alt((decimal, hexadecimal))))(input) +} + +#[derive(Debug)] +struct NetDecl { + name: String, + width: Option, + value: Option<(u64, u64)>, +} + +#[derive(Debug)] +enum PortDirection { + Input, + Output, +} + +#[derive(Debug)] +struct PortDecl { + direction: PortDirection, + net: NetDecl, +} + +#[derive(Debug)] +struct Module { + name: String, + ports: Vec, +} + +fn declaration(i: &str) -> IResult<&str, NetDecl> { + let (i, (_, width, ident, value)) = tuple(( + ws0(alt((tag("reg"), tag("wire")))), + opt(ws0(widthspec)), + identifier, + opt(preceded(ws0(char('=')), intliteral)), + ))(i)?; + Ok(( + i, + NetDecl { + name: ident.into(), + width, + value, + }, + )) +} + +fn port_decl(i: &str) -> IResult<&str, PortDecl> { + map( + tuple(( + alt(( + map(tag("input"), |_| PortDirection::Input), + map(tag("output"), |_| PortDirection::Output), + )), + declaration, + )), + |(direction, net)| PortDecl { direction, net }, + )(i) +} + +fn ports_list(input: &str) -> IResult<&str, Vec> { + context( + "port declaration", + separated_list1(ws0(char(',')), ws0(port_decl)), + )(input) +} + +fn module(input: &str) -> IResult<&str, Module> { + context( + "module", + map( + tuple(( + tag("module"), + ws0(identifier), + ws0(delimited(char('('), ws0(ports_list), char(')'))), + )), + |(_, name, ports)| Module { + name: name.into(), + ports, + }, + ), + )(input) +} + +fn main() { + let input = include_str!("../identity.fut"); + let parsed = module(input); + match parsed { + Err(nom::Err::Error(err) | nom::Err::Failure(err)) => { + print!("{}", convert_error(input, err)) + } + Err(_) => (), + Ok(res) => println!("{:#?}", res), + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_decl() { + declaration("input reg abcd").unwrap(); + } +}