#![allow(clippy::upper_case_acronyms)]
use super::ast::{
self, BitNum, Control, GuardComp as GC, GuardExpr, NumType, StaticGuardExpr,
};
use super::Attributes;
use crate::{Attribute, Direction, PortDef, Primitive, Width};
use calyx_utils::{self, CalyxResult, Id};
use calyx_utils::{FileIdx, GPosIdx, GlobalPositionTable};
use pest::pratt_parser::{Assoc, Op, PrattParser};
use pest_consume::{match_nodes, Error, Parser};
use std::convert::TryInto;
use std::fs;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;
type ParseResult<T> = Result<T, Error<Rule>>;
type ComponentDef = ast::ComponentDef;
#[derive(Clone)]
struct UserData {
pub file: FileIdx,
}
type Node<'i> = pest_consume::Node<'i, Rule, UserData>;
const _GRAMMAR: &str = include_str!("syntax.pest");
lazy_static::lazy_static! {
static ref PRATT: PrattParser<Rule> =
PrattParser::new()
.op(Op::infix(Rule::guard_or, Assoc::Left))
.op(Op::infix(Rule::guard_and, Assoc::Left));
}
#[derive(Parser)]
#[grammar = "syntax.pest"]
pub struct CalyxParser;
impl CalyxParser {
pub fn parse_file(path: &Path) -> CalyxResult<ast::NamespaceDef> {
let time = std::time::Instant::now();
let content = &fs::read(path).map_err(|err| {
calyx_utils::Error::invalid_file(format!(
"Failed to read {}: {err}",
path.to_string_lossy(),
))
})?;
let string_content = std::str::from_utf8(content)?.to_string();
let file = GlobalPositionTable::as_mut()
.add_file(path.to_string_lossy().to_string(), string_content);
let user_data = UserData { file };
let content = GlobalPositionTable::as_ref().get_source(file);
let inputs =
CalyxParser::parse_with_userdata(Rule::file, content, user_data)
.map_err(|e| e.with_path(&path.to_string_lossy()))
.map_err(|e| {
calyx_utils::Error::misc(format!(
"Failed to parse `{}`: {err}",
path.to_string_lossy(),
err = e
))
})?;
let input = inputs.single().map_err(|e| {
calyx_utils::Error::misc(format!(
"Failed to parse `{}`: {err}",
path.to_string_lossy(),
err = e
))
})?;
let out = CalyxParser::file(input).map_err(|e| {
calyx_utils::Error::misc(format!(
"Failed to parse `{}`: {err}",
path.to_string_lossy(),
err = e
))
})?;
log::info!(
"Parsed `{}` in {}ms",
path.to_string_lossy(),
time.elapsed().as_millis()
);
Ok(out)
}
pub fn parse<R: Read>(mut r: R) -> CalyxResult<ast::NamespaceDef> {
let mut buf = String::new();
r.read_to_string(&mut buf).map_err(|err| {
calyx_utils::Error::invalid_file(format!(
"Failed to parse buffer: {err}",
))
})?;
let file =
GlobalPositionTable::as_mut().add_file("<stdin>".to_string(), buf);
let user_data = UserData { file };
let contents = GlobalPositionTable::as_ref().get_source(file);
let inputs =
CalyxParser::parse_with_userdata(Rule::file, contents, user_data)
.map_err(|e| {
calyx_utils::Error::misc(
format!("Failed to parse buffer: {e}",),
)
})?;
let input = inputs.single().map_err(|e| {
calyx_utils::Error::misc(format!("Failed to parse buffer: {e}",))
})?;
let out = CalyxParser::file(input).map_err(|e| {
calyx_utils::Error::misc(format!("Failed to parse buffer: {e}",))
})?;
Ok(out)
}
fn get_span(node: &Node) -> GPosIdx {
let ud = node.user_data();
let sp = node.as_span();
let pos = GlobalPositionTable::as_mut().add_pos(
ud.file,
sp.start(),
sp.end(),
);
GPosIdx(pos)
}
#[allow(clippy::result_large_err)]
fn guard_expr_helper(
ud: UserData,
pairs: pest::iterators::Pairs<Rule>,
) -> ParseResult<Box<GuardExpr>> {
PRATT
.map_primary(|primary| match primary.as_rule() {
Rule::term => {
Self::term(Node::new_with_user_data(primary, ud.clone()))
.map(Box::new)
}
x => unreachable!("Unexpected rule {:?} for guard_expr", x),
})
.map_infix(|lhs, op, rhs| {
Ok(match op.as_rule() {
Rule::guard_or => Box::new(GuardExpr::Or(lhs?, rhs?)),
Rule::guard_and => Box::new(GuardExpr::And(lhs?, rhs?)),
_ => unreachable!(),
})
})
.parse(pairs)
}
#[allow(clippy::result_large_err)]
fn static_guard_expr_helper(
ud: UserData,
pairs: pest::iterators::Pairs<Rule>,
) -> ParseResult<Box<StaticGuardExpr>> {
PRATT
.map_primary(|primary| match primary.as_rule() {
Rule::static_term => Self::static_term(
Node::new_with_user_data(primary, ud.clone()),
)
.map(Box::new),
x => unreachable!(
"Unexpected rule {:?} for static_guard_expr",
x
),
})
.map_infix(|lhs, op, rhs| {
Ok(match op.as_rule() {
Rule::guard_or => Box::new(StaticGuardExpr::Or(lhs?, rhs?)),
Rule::guard_and => {
Box::new(StaticGuardExpr::And(lhs?, rhs?))
}
_ => unreachable!(),
})
})
.parse(pairs)
}
}
#[allow(clippy::large_enum_variant)]
enum ExtOrComp {
Ext((Option<String>, Vec<Primitive>)),
Comp(ComponentDef),
PrimInline(Primitive),
}
#[pest_consume::parser]
impl CalyxParser {
fn EOI(_input: Node) -> ParseResult<()> {
Ok(())
}
fn semi(_input: Node) -> ParseResult<()> {
Ok(())
}
fn comb(_input: Node) -> ParseResult<()> {
Ok(())
}
fn static_word(_input: Node) -> ParseResult<()> {
Ok(())
}
fn reference(_input: Node) -> ParseResult<()> {
Ok(())
}
fn identifier(input: Node) -> ParseResult<Id> {
Ok(Id::new(input.as_str()))
}
fn bitwidth(input: Node) -> ParseResult<u64> {
input
.as_str()
.parse::<u64>()
.map_err(|_| input.error("Expected valid bitwidth"))
}
fn bad_num(input: Node) -> ParseResult<u64> {
Err(input.error("Expected number with bitwidth (like 32'd10)."))
}
fn hex(input: Node) -> ParseResult<u64> {
u64::from_str_radix(input.as_str(), 16)
.map_err(|_| input.error("Expected hexadecimal number"))
}
fn decimal(input: Node) -> ParseResult<u64> {
#[allow(clippy::from_str_radix_10)]
u64::from_str_radix(input.as_str(), 10)
.map_err(|_| input.error("Expected decimal number"))
}
fn octal(input: Node) -> ParseResult<u64> {
u64::from_str_radix(input.as_str(), 8)
.map_err(|_| input.error("Expected octal number"))
}
fn binary(input: Node) -> ParseResult<u64> {
u64::from_str_radix(input.as_str(), 2)
.map_err(|_| input.error("Expected binary number"))
}
fn num_lit(input: Node) -> ParseResult<BitNum> {
let span = Self::get_span(&input);
let num = match_nodes!(
input.clone().into_children();
[bitwidth(width), decimal(val)] => BitNum {
width,
num_type: NumType::Decimal,
val,
span
},
[bitwidth(width), hex(val)] => BitNum {
width,
num_type: NumType::Hex,
val,
span
},
[bitwidth(width), octal(val)] => BitNum {
width,
num_type: NumType::Octal,
val,
span
},
[bitwidth(width), binary(val)] => BitNum {
width,
num_type: NumType::Binary,
val,
span
},
);
if num.width == 0
|| (num.width < 64 && u64::pow(2, num.width as u32) <= num.val)
{
let lit_str = match num.num_type {
NumType::Binary => format!("{:b}", num.val),
NumType::Decimal => format!("{}", num.val),
NumType::Octal => format!("{:o}", num.val),
NumType::Hex => format!("{:x}", num.val),
};
let bit_plural = if num.width == 1 { "bit" } else { "bits" };
Err(input.error(format!(
"Cannot represent given literal '{}' in {} {}",
lit_str, num.width, bit_plural
)))
} else {
Ok(num)
}
}
fn char(input: Node) -> ParseResult<&str> {
Ok(input.as_str())
}
fn string_lit(input: Node) -> ParseResult<String> {
Ok(match_nodes!(
input.into_children();
[char(c)..] => c.collect::<Vec<_>>().join("")
))
}
fn attribute(input: Node) -> ParseResult<(Attribute, u64)> {
match_nodes!(
input.clone().into_children();
[string_lit(key), bitwidth(num)] => Attribute::from_str(&key).map(|attr| (attr, num)).map_err(|e| input.error(format!("{:?}", e)))
)
}
fn attributes(input: Node) -> ParseResult<Attributes> {
match_nodes!(
input.clone().into_children();
[attribute(kvs)..] => kvs.collect::<Vec<_>>().try_into().map_err(|e| input.error(format!("{:?}", e)))
)
}
fn name_with_attribute(input: Node) -> ParseResult<(Id, Attributes)> {
Ok(match_nodes!(
input.into_children();
[identifier(name), attributes(attrs)] => (name, attrs),
[identifier(name)] => (name, Attributes::default()),
))
}
fn block_char(input: Node) -> ParseResult<&str> {
Ok(input.as_str())
}
fn block_string(input: Node) -> ParseResult<String> {
Ok(match_nodes!(
input.into_children();
[block_char(c)..] => c.collect::<String>()
))
}
fn attr_val(input: Node) -> ParseResult<u64> {
Ok(match_nodes!(
input.into_children();
[bitwidth(num)] => num
))
}
fn latency_annotation(input: Node) -> ParseResult<std::num::NonZeroU64> {
let num = match_nodes!(
input.clone().into_children();
[bitwidth(value)] => value,
);
if num == 0 {
Err(input.error("latency annotation of 0"))
} else {
Ok(std::num::NonZeroU64::new(num).unwrap())
}
}
fn at_attribute(input: Node) -> ParseResult<(Attribute, u64)> {
match_nodes!(
input.clone().into_children();
[identifier(key), attr_val(num)] => Attribute::from_str(key.as_ref()).map_err(|e| input.error(format!("{:?}", e))).map(|attr| (attr, num)),
[identifier(key)] => Attribute::from_str(key.as_ref()).map_err(|e| input.error(format!("{:?}", e))).map(|attr| (attr, 1)),
)
}
fn at_attributes(input: Node) -> ParseResult<Attributes> {
match_nodes!(
input.clone().into_children();
[at_attribute(kvs)..] => kvs.collect::<Vec<_>>().try_into().map_err(|e| input.error(format!("{:?}", e)))
)
}
fn params(input: Node) -> ParseResult<Vec<Id>> {
Ok(match_nodes!(
input.into_children();
[identifier(id)..] => id.collect()
))
}
fn args(input: Node) -> ParseResult<Vec<u64>> {
Ok(match_nodes!(
input.into_children();
[bitwidth(bw)..] => bw.collect(),
[] => vec![]
))
}
fn io_port(input: Node) -> ParseResult<(Id, Width, Attributes)> {
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), identifier(id), bitwidth(value)] =>
(id, Width::Const { value }, attrs),
[at_attributes(attrs), identifier(id), identifier(value)] =>
(id, Width::Param { value }, attrs)
))
}
fn inputs(input: Node) -> ParseResult<Vec<PortDef<Width>>> {
Ok(match_nodes!(
input.into_children();
[io_port(ins)..] => {
ins.map(|(name, width, attributes)| PortDef {
name, width, direction: Direction::Input, attributes
}).collect()
}
))
}
fn outputs(input: Node) -> ParseResult<Vec<PortDef<Width>>> {
Ok(match_nodes!(
input.into_children();
[io_port(outs)..] => {
outs.map(|(name, width, attributes)| PortDef {
name, width, direction: Direction::Output, attributes
}).collect()
}
))
}
fn signature(input: Node) -> ParseResult<Vec<PortDef<Width>>> {
Ok(match_nodes!(
input.into_children();
[] => Vec::with_capacity(4),
[inputs(ins)] => ins ,
[outputs(outs)] => outs ,
[inputs(ins), outputs(outs)] => {
ins.into_iter().chain(outs.into_iter()).collect()
},
))
}
fn sig_with_params(
input: Node,
) -> ParseResult<(Vec<Id>, Vec<PortDef<Width>>)> {
Ok(match_nodes!(
input.into_children();
[params(p), signature(s)] => (p, s),
[signature(s)] => (vec![], s),
))
}
fn primitive(input: Node) -> ParseResult<Primitive> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[name_with_attribute((name, attrs)), sig_with_params((p, s))] => Primitive {
name,
params: p,
signature: s,
attributes: attrs.add_span(span),
is_comb: false,
body: None,
},
[comb(_), name_with_attribute((name, attrs)), sig_with_params((p, s))] => Primitive {
name,
params: p,
signature: s,
attributes: attrs.add_span(span),
is_comb: true,
body: None,
},
))
}
fn cell_without_semi(input: Node) -> ParseResult<ast::Cell> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), reference(_), identifier(id), identifier(prim), args(args)] =>
ast::Cell::from(id, prim, args, attrs.add_span(span),true),
[at_attributes(attrs), identifier(id), identifier(prim), args(args)] =>
ast::Cell::from(id, prim, args, attrs.add_span(span),false)
))
}
fn cell(input: Node) -> ParseResult<ast::Cell> {
match_nodes!(
input.clone().into_children();
[cell_without_semi(_)] =>
Err(input.error("Declaration is missing `;`")),
[cell_without_semi(node), semi(_)] => Ok(node),
)
}
fn cells(input: Node) -> ParseResult<Vec<ast::Cell>> {
Ok(match_nodes!(
input.into_children();
[cell(cells)..] => cells.collect()
))
}
fn port(input: Node) -> ParseResult<ast::Port> {
Ok(match_nodes!(
input.into_children();
[identifier(component), identifier(port)] =>
ast::Port::Comp { component, port },
[identifier(port)] => ast::Port::This { port }
))
}
fn hole(input: Node) -> ParseResult<ast::Port> {
Ok(match_nodes!(
input.into_children();
[identifier(group), identifier(name)] => ast::Port::Hole { group, name }
))
}
#[allow(clippy::upper_case_acronyms)]
fn LHS(input: Node) -> ParseResult<ast::Port> {
Ok(match_nodes!(
input.into_children();
[port(port)] => port,
[hole(hole)] => hole
))
}
fn expr(input: Node) -> ParseResult<ast::Atom> {
match_nodes!(
input.into_children();
[LHS(port)] => Ok(ast::Atom::Port(port)),
[num_lit(num)] => Ok(ast::Atom::Num(num)),
[bad_num(_)] => unreachable!("bad_num returned non-error result"),
)
}
fn guard_eq(_input: Node) -> ParseResult<()> {
Ok(())
}
fn guard_neq(_input: Node) -> ParseResult<()> {
Ok(())
}
fn guard_leq(_input: Node) -> ParseResult<()> {
Ok(())
}
fn guard_geq(_input: Node) -> ParseResult<()> {
Ok(())
}
fn guard_lt(_input: Node) -> ParseResult<()> {
Ok(())
}
fn guard_gt(_input: Node) -> ParseResult<()> {
Ok(())
}
fn cmp_expr(input: Node) -> ParseResult<ast::CompGuard> {
Ok(match_nodes!(
input.into_children();
[expr(l), guard_eq(_), expr(r)] => (GC::Eq, l, r),
[expr(l), guard_neq(_), expr(r)] => (GC::Neq, l, r),
[expr(l), guard_geq(_), expr(r)] => (GC::Geq, l, r),
[expr(l), guard_leq(_), expr(r)] => (GC::Leq, l, r),
[expr(l), guard_gt(_), expr(r)] => (GC::Gt, l, r),
[expr(l), guard_lt(_), expr(r)] => (GC::Lt, l, r),
))
}
fn guard_not(_input: Node) -> ParseResult<()> {
Ok(())
}
fn guard_expr(input: Node) -> ParseResult<Box<GuardExpr>> {
let ud = input.user_data().clone();
Self::guard_expr_helper(ud, input.into_pair().into_inner())
}
fn static_guard_expr(input: Node) -> ParseResult<Box<StaticGuardExpr>> {
let ud = input.user_data().clone();
Self::static_guard_expr_helper(ud, input.into_pair().into_inner())
}
fn term(input: Node) -> ParseResult<ast::GuardExpr> {
Ok(match_nodes!(
input.into_children();
[guard_expr(guard)] => *guard,
[cmp_expr((gc, a1, a2))] => ast::GuardExpr::CompOp((gc, a1, a2)),
[expr(e)] => ast::GuardExpr::Atom(e),
[guard_not(_), expr(e)] => {
ast::GuardExpr::Not(Box::new(ast::GuardExpr::Atom(e)))
},
[guard_not(_), cmp_expr((gc, a1, a2))] => {
ast::GuardExpr::Not(Box::new(ast::GuardExpr::CompOp((gc, a1, a2))))
},
[guard_not(_), guard_expr(e)] => {
ast::GuardExpr::Not(e)
},
[guard_not(_), expr(e)] =>
ast::GuardExpr::Not(Box::new(ast::GuardExpr::Atom(e)))
))
}
fn static_term(input: Node) -> ParseResult<ast::StaticGuardExpr> {
Ok(match_nodes!(
input.into_children();
[static_timing_expr(interval)] => ast::StaticGuardExpr::StaticInfo(interval),
[static_guard_expr(guard)] => *guard,
[cmp_expr((gc, a1, a2))] => ast::StaticGuardExpr::CompOp((gc, a1, a2)),
[expr(e)] => ast::StaticGuardExpr::Atom(e),
[guard_not(_), expr(e)] => {
ast::StaticGuardExpr::Not(Box::new(ast::StaticGuardExpr::Atom(e)))
},
[guard_not(_), cmp_expr((gc, a1, a2))] => {
ast::StaticGuardExpr::Not(Box::new(ast::StaticGuardExpr::CompOp((gc, a1, a2))))
},
[guard_not(_), static_guard_expr(e)] => {
ast::StaticGuardExpr::Not(e)
},
[guard_not(_), expr(e)] =>
ast::StaticGuardExpr::Not(Box::new(ast::StaticGuardExpr::Atom(e)))
))
}
fn switch_stmt(input: Node) -> ParseResult<ast::Guard> {
Ok(match_nodes!(
input.into_children();
[guard_expr(guard), expr(expr)] => ast::Guard { guard: Some(*guard), expr },
))
}
fn static_switch_stmt(input: Node) -> ParseResult<ast::StaticGuard> {
Ok(match_nodes!(
input.into_children();
[static_guard_expr(guard), expr(expr)] => ast::StaticGuard { guard: Some(*guard), expr },
))
}
fn wire(input: Node) -> ParseResult<ast::Wire> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), LHS(dest), expr(expr)] => ast::Wire {
src: ast::Guard { guard: None, expr },
dest,
attributes: attrs.add_span(span),
},
[at_attributes(attrs), LHS(dest), switch_stmt(src)] => ast::Wire {
src,
dest,
attributes: attrs.add_span(span),
}
))
}
fn static_wire(input: Node) -> ParseResult<ast::StaticWire> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), LHS(dest), expr(expr)] => ast::StaticWire {
src: ast::StaticGuard { guard: None, expr },
dest,
attributes: attrs.add_span(span),
},
[at_attributes(attrs), LHS(dest), static_switch_stmt(src)] => ast::StaticWire {
src,
dest,
attributes: attrs.add_span(span),
}
))
}
fn static_timing_expr(input: Node) -> ParseResult<(u64, u64)> {
Ok(match_nodes!(
input.into_children();
[bitwidth(single_num)] => (single_num, single_num+1),
[bitwidth(start_interval), bitwidth(end_interval)] => (start_interval, end_interval)
))
}
fn group(input: Node) -> ParseResult<ast::Group> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[name_with_attribute((name, attrs)), wire(wire)..] => ast::Group {
name,
attributes: attrs.add_span(span),
wires: wire.collect(),
is_comb: false,
},
[comb(_), name_with_attribute((name, attrs)), wire(wire)..] => ast::Group {
name,
attributes: attrs.add_span(span),
wires: wire.collect(),
is_comb: true,
}
))
}
fn static_group(input: Node) -> ParseResult<ast::StaticGroup> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[static_word(_), latency_annotation(latency), name_with_attribute((name, attrs)), static_wire(wire)..] => ast::StaticGroup {
name,
attributes: attrs.add_span(span),
wires: wire.collect(),
latency,
}
))
}
fn connections(
input: Node,
) -> ParseResult<(Vec<ast::Wire>, Vec<ast::Group>, Vec<ast::StaticGroup>)>
{
let mut wires = Vec::new();
let mut groups = Vec::new();
let mut static_groups = Vec::new();
for node in input.into_children() {
match node.as_rule() {
Rule::wire => wires.push(Self::wire(node)?),
Rule::group => groups.push(Self::group(node)?),
Rule::static_group => {
static_groups.push(Self::static_group(node)?)
}
_ => unreachable!(),
}
}
Ok((wires, groups, static_groups))
}
fn invoke_arg(input: Node) -> ParseResult<(Id, ast::Atom)> {
Ok(match_nodes!(
input.into_children();
[identifier(name), port(p)] => (name, ast::Atom::Port(p)),
[identifier(name), num_lit(bn)] => (name, ast::Atom::Num(bn))
))
}
fn invoke_args(input: Node) -> ParseResult<Vec<(Id, ast::Atom)>> {
Ok(match_nodes!(
input.into_children();
[invoke_arg(args)..] => args.collect()
))
}
fn invoke_ref_arg(input: Node) -> ParseResult<(Id, Id)> {
Ok(match_nodes!(
input.into_children();
[identifier(outcell), identifier(incell)] => (outcell, incell)
))
}
fn invoke_ref_args(input: Node) -> ParseResult<Vec<(Id, Id)>> {
Ok(match_nodes!(
input.into_children();
[invoke_ref_arg(args)..] => args.collect(),
[] => Vec::new()
))
}
fn invoke(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), identifier(comp), invoke_ref_args(cells),invoke_args(inputs), invoke_args(outputs)] =>
ast::Control::Invoke {
comp,
inputs,
outputs,
attributes: attrs.add_span(span),
comb_group: None,
ref_cells: cells
},
[at_attributes(attrs), identifier(comp), invoke_ref_args(cells),invoke_args(inputs), invoke_args(outputs), identifier(group)] =>
ast::Control::Invoke {
comp,
inputs,
outputs,
attributes: attrs.add_span(span),
comb_group: Some(group),
ref_cells: cells
},
))
}
fn empty(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs)] => ast::Control::Empty {
attributes: attrs.add_span(span)
}
))
}
fn enable(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), identifier(name)] => ast::Control::Enable {
comp: name,
attributes: attrs.add_span(span)
}
))
}
fn seq(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), stmt(stmt)..] => ast::Control::Seq {
stmts: stmt.collect(),
attributes: attrs.add_span(span),
}
))
}
fn static_seq(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), static_word(_), latency_annotation(latency) ,stmt(stmt)..] => ast::Control::StaticSeq {
stmts: stmt.collect(),
attributes: attrs.add_span(span),
latency: Some(latency),
},
[at_attributes(attrs), static_word(_) ,stmt(stmt)..] => ast::Control::StaticSeq {
stmts: stmt.collect(),
attributes: attrs.add_span(span),
latency: None,
},
))
}
fn par(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), stmt(stmt)..] => ast::Control::Par {
stmts: stmt.collect(),
attributes: attrs.add_span(span),
}
))
}
fn static_par(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), static_word(_), latency_annotation(latency) ,stmt(stmt)..] => ast::Control::StaticPar {
stmts: stmt.collect(),
attributes: attrs.add_span(span),
latency: Some(latency),
},
[at_attributes(attrs), static_word(_), stmt(stmt)..] => ast::Control::StaticPar {
stmts: stmt.collect(),
attributes: attrs.add_span(span),
latency: None,
},
))
}
fn port_with(input: Node) -> ParseResult<(ast::Port, Option<Id>)> {
Ok(match_nodes!(
input.into_children();
[port(port), identifier(cond)] => (port, Some(cond)),
[port(port)] => (port, None),
))
}
fn if_stmt(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), port_with((port, cond)), block(stmt)] => ast::Control::If {
port,
cond,
tbranch: Box::new(stmt),
fbranch: Box::new(ast::Control::Empty { attributes: Attributes::default() }),
attributes: attrs.add_span(span),
},
[at_attributes(attrs), port_with((port, cond)), block(tbranch), block(fbranch)] =>
ast::Control::If {
port,
cond,
tbranch: Box::new(tbranch),
fbranch: Box::new(fbranch),
attributes: attrs.add_span(span),
},
[at_attributes(attrs), port_with((port, cond)), block(tbranch), if_stmt(fbranch)] =>
ast::Control::If {
port,
cond,
tbranch: Box::new(tbranch),
fbranch: Box::new(fbranch),
attributes: attrs.add_span(span),
},
))
}
fn static_if_stmt(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), static_word(_), latency_annotation(latency), port(port), block(stmt)] => ast::Control::StaticIf {
port,
tbranch: Box::new(stmt),
fbranch: Box::new(ast::Control::Empty { attributes: Attributes::default() }),
attributes: attrs.add_span(span),
latency: Some(latency),
},
[at_attributes(attrs), static_word(_), port(port), block(stmt)] => ast::Control::StaticIf {
port,
tbranch: Box::new(stmt),
fbranch: Box::new(ast::Control::Empty { attributes: Attributes::default() }),
attributes: attrs.add_span(span),
latency: None,
},
[at_attributes(attrs), static_word(_), latency_annotation(latency), port(port), block(tbranch), block(fbranch)] =>
ast::Control::StaticIf {
port,
tbranch: Box::new(tbranch),
fbranch: Box::new(fbranch),
attributes: attrs.add_span(span),
latency: Some(latency),
},
[at_attributes(attrs), static_word(_), port(port), block(tbranch), block(fbranch)] =>
ast::Control::StaticIf {
port,
tbranch: Box::new(tbranch),
fbranch: Box::new(fbranch),
attributes: attrs.add_span(span),
latency: None,
},
[at_attributes(attrs), static_word(_), latency_annotation(latency), port(port), block(tbranch), if_stmt(fbranch)] =>
ast::Control::StaticIf {
port,
tbranch: Box::new(tbranch),
fbranch: Box::new(fbranch),
attributes: attrs.add_span(span),
latency: Some(latency),
},
[at_attributes(attrs), static_word(_), port(port), block(tbranch), if_stmt(fbranch)] =>
ast::Control::StaticIf {
port,
tbranch: Box::new(tbranch),
fbranch: Box::new(fbranch),
attributes: attrs.add_span(span),
latency: None,
},
))
}
fn while_stmt(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), port_with((port, cond)), block(stmt)] => ast::Control::While {
port,
cond,
body: Box::new(stmt),
attributes: attrs.add_span(span),
}
))
}
fn repeat_stmt(input: Node) -> ParseResult<ast::Control> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[at_attributes(attrs), static_word(_), bitwidth(num_repeats) , block(stmt)] => ast::Control::StaticRepeat {
num_repeats,
body: Box::new(stmt),
attributes: attrs.add_span(span),
}
))
}
fn stmt(input: Node) -> ParseResult<ast::Control> {
Ok(match_nodes!(
input.into_children();
[enable(data)] => data,
[empty(data)] => data,
[invoke(data)] => data,
[seq(data)] => data,
[static_seq(data)] => data,
[par(data)] => data,
[static_par(data)] => data,
[if_stmt(data)] => data,
[static_if_stmt(data)] => data,
[while_stmt(data)] => data,
[repeat_stmt(data)] => data,
))
}
fn block(input: Node) -> ParseResult<ast::Control> {
Ok(match_nodes!(
input.into_children();
[stmt(stmt)] => stmt,
[stmts_without_block(seq)] => seq,
))
}
fn stmts_without_block(input: Node) -> ParseResult<ast::Control> {
match_nodes!(
input.clone().into_children();
[stmt(stmt)..] => Ok(ast::Control::Seq {
stmts: stmt.collect(),
attributes: Attributes::default(),
})
)
}
fn control(input: Node) -> ParseResult<ast::Control> {
Ok(match_nodes!(
input.into_children();
[block(stmt)] => stmt,
[] => ast::Control::empty()
))
}
fn component(input: Node) -> ParseResult<ComponentDef> {
let span = Self::get_span(&input);
match_nodes!(
input.clone().into_children();
[
comb(_),
name_with_attribute((name, attributes)),
signature(sig),
cells(cells),
connections(connections)
] => {
let (continuous_assignments, groups, static_groups) = connections;
let sig = sig.into_iter().map(|PortDef { name, width, direction, attributes }| {
if let Width::Const { value } = width {
Ok(PortDef {
name,
width: value,
direction,
attributes
})
} else {
Err(input.error("Components cannot use parameters"))
}
}).collect::<Result<_, _>>()?;
Ok(ComponentDef {
name,
signature: sig,
cells,
groups,
static_groups,
continuous_assignments,
control: Control::empty(),
attributes: attributes.add_span(span),
is_comb: true,
})
},
[
name_with_attribute((name, attributes)),
signature(sig),
cells(cells),
connections(connections),
control(control)
] => {
let (continuous_assignments, groups, static_groups) = connections;
let sig = sig.into_iter().map(|PortDef { name, width, direction, attributes }| {
if let Width::Const { value } = width {
Ok(PortDef {
name,
width: value,
direction,
attributes
})
} else {
Err(input.error("Components cannot use parameters"))
}
}).collect::<Result<_, _>>()?;
Ok(ComponentDef {
name,
signature: sig,
cells,
groups,
static_groups,
continuous_assignments,
control,
attributes: attributes.add_span(span),
is_comb: false,
})
})
}
fn imports(input: Node) -> ParseResult<Vec<String>> {
Ok(match_nodes!(
input.into_children();
[string_lit(path)..] => path.collect()
))
}
fn ext(input: Node) -> ParseResult<(Option<String>, Vec<Primitive>)> {
Ok(match_nodes!(
input.into_children();
[string_lit(file), primitive(prims)..] => (Some(file), prims.collect())
))
}
fn prim_inline(input: Node) -> ParseResult<Primitive> {
let span = Self::get_span(&input);
Ok(match_nodes!(
input.into_children();
[name_with_attribute((name, attrs)), sig_with_params((p, s)), block_string(b)] => {
Primitive {
name,
params: p,
signature: s,
attributes: attrs.add_span(span),
is_comb: false,
body: Some(b),
}},
[comb(_), name_with_attribute((name, attrs)), sig_with_params((p, s)), block_string(b)] => Primitive {
name,
params: p,
signature: s,
attributes: attrs.add_span(span),
is_comb: true,
body: Some(b),
},
))
}
fn extern_or_component(input: Node) -> ParseResult<ExtOrComp> {
Ok(match_nodes!(
input.into_children();
[component(comp)] => ExtOrComp::Comp(comp),
[ext(ext)] => ExtOrComp::Ext(ext),
[prim_inline(prim_inline)] => ExtOrComp::PrimInline(prim_inline),
))
}
fn externs_and_comps(
input: Node,
) -> ParseResult<impl Iterator<Item = ExtOrComp>> {
Ok(match_nodes!(input.into_children();
[extern_or_component(e)..] => e
))
}
fn any_char(input: Node) -> ParseResult<String> {
Ok(input.as_str().into())
}
fn metadata_char(input: Node) -> ParseResult<String> {
Ok(match_nodes!(input.into_children();
[any_char(c)] => c,
))
}
fn metadata(input: Node) -> ParseResult<String> {
Ok(match_nodes!(input.into_children();
[metadata_char(c)..] => c.collect::<String>().trim().into()
))
}
fn file(input: Node) -> ParseResult<ast::NamespaceDef> {
Ok(match_nodes!(
input.into_children();
[imports(imports), externs_and_comps(mixed), metadata(m), EOI(_)] => {
let mut namespace =
ast::NamespaceDef {
imports,
components: Vec::new(),
externs: Vec::new(),
metadata: if m != *"" { Some(m) } else { None }
};
for m in mixed {
match m {
ExtOrComp::Ext(ext) => namespace.externs.push(ext),
ExtOrComp::Comp(comp) => namespace.components.push(comp),
ExtOrComp::PrimInline(prim) => {
if let Some((_, prim_inlines)) = namespace.externs.iter_mut().find(|(filename, _)| filename.is_none()) {
prim_inlines.push(prim)
}
else{
namespace.externs.push((None, vec![prim]));
}
},
}
}
namespace
},
[imports(imports), externs_and_comps(mixed), EOI(_)] => {
let mut namespace =
ast::NamespaceDef {
imports,
components: Vec::new(),
externs: Vec::new(),
metadata: None
};
for m in mixed {
match m {
ExtOrComp::Ext(ext) => namespace.externs.push(ext),
ExtOrComp::Comp(comp) => namespace.components.push(comp),
ExtOrComp::PrimInline(prim) => {
if let Some((_, prim_inlines)) = namespace.externs.iter_mut().find(|(filename, _)| filename.is_none()) {
prim_inlines.push(prim)
}
else{
namespace.externs.push((None, vec![prim]));
}
},
}
}
namespace
},
))
}
}