add match blocks and constant literals

main
NotAFile 2022-04-04 21:40:08 +02:00
parent 68be74d032
commit da5258a11a
10 changed files with 240 additions and 116 deletions

View File

@ -3,6 +3,7 @@ use std::collections::BTreeMap;
use std::fmt::Write;
use super::parser;
use super::parser::block_expression::BlockExpr;
use crate::rtlil;
pub use callable::{Callable, CallableContext, CallableId};
pub use types::{Type, TypeStruct, TypingContext};
@ -119,11 +120,8 @@ impl Context {
match &expr.kind {
typed_ir::ExprKind::Literal(lit) => Ok(lit.clone()),
typed_ir::ExprKind::Path(_) => todo!("evaluate path"),
typed_ir::ExprKind::Call {
called,
args,
genargs,
} => todo!("evaluate call"),
typed_ir::ExprKind::Call(_call) => todo!("evaluate call"),
typed_ir::ExprKind::Match(_) => todo!(),
}
}
@ -144,10 +142,10 @@ impl Context {
}
Expression::Literal(lit) => {
// TODO: make this a proper enum instead of having to match on everything
let data = match lit.kind() {
parser::tokens::TokenKind::Number => {
let num = lit.span().fragment().parse().unwrap();
self.types.make_elabnum_u32(num)
let data = match lit.kind {
parser::expression::LiteralKind::Num(num) => self.types.make_elabnum_u32(num),
parser::expression::LiteralKind::Const(width, val) => {
self.types.make_const_u32(width, val as u32)
}
_ => unreachable!("non-literal token in literal?"),
};
@ -161,11 +159,11 @@ impl Context {
let a = self.type_expression(&op.a)?;
typed_ir::Expr {
id,
kind: typed_ir::ExprKind::Call {
kind: typed_ir::ExprKind::Call(typed_ir::Call {
called: self.callables.builtins.bitnot,
args: vec![a],
genargs: vec![],
},
}),
typ: self.types.primitives.infer,
}
}
@ -173,11 +171,11 @@ impl Context {
let (a, b) = (self.type_expression(&op.a)?, self.type_expression(&op.b)?);
typed_ir::Expr {
id,
kind: typed_ir::ExprKind::Call {
kind: typed_ir::ExprKind::Call(typed_ir::Call {
called: self.callables.builtins.xor,
args: vec![a, b],
genargs: vec![],
},
}),
typ: self.types.primitives.infer,
}
}
@ -202,14 +200,34 @@ impl Context {
.collect();
typed_ir::Expr {
id,
kind: typed_ir::ExprKind::Call {
kind: typed_ir::ExprKind::Call(typed_ir::Call {
called,
args: args_resolved,
genargs: genargs_resolved,
},
}),
typ: self.types.primitives.infer,
}
}
Expression::BlockExpr(block) => match &**block {
BlockExpr::IfElse(_) => todo!(),
BlockExpr::Match(match_) => {
let expr = self.type_expression(&match_.expr)?;
let arms = match_
.arms
.iter()
.map(|(cond, val)| {
Ok((self.type_expression(cond)?, self.type_expression(val)?))
})
.collect::<Result<_, _>>()?;
let typed = typed_ir::Match { expr, arms };
typed_ir::Expr {
id,
kind: typed_ir::ExprKind::Match(Box::new(typed)),
typ: self.types.primitives.infer,
}
}
BlockExpr::Block(_) => todo!(),
},
};
Ok(t_expr)
}
@ -317,13 +335,13 @@ impl Context {
typed_ir::ExprKind::Literal(lit) => expr.clone().with_type(lit.typ),
// we can not see beyond this expression right now
typed_ir::ExprKind::Path(_) => expr.clone(),
typed_ir::ExprKind::Call {
called,
args,
genargs,
} => {
let args_typed: Vec<_> = args.iter().map(|ex| self.infer_expr_types(ex)).collect();
let callee_def = self.callables.get(*called);
typed_ir::ExprKind::Call(call) => {
let args_typed: Vec<_> = call
.args
.iter()
.map(|ex| self.infer_expr_types(ex))
.collect();
let callee_def = self.callables.get(call.called);
let param_types: Vec<_> = callee_def.args.iter().map(|param| param.1).collect();
let inferred_args: Vec<_> = param_types
@ -358,11 +376,27 @@ impl Context {
let mut new_expr = expr.clone();
new_expr.typ = new_type;
new_expr.kind = typed_ir::ExprKind::Call {
called: called.clone(),
new_expr.kind = typed_ir::ExprKind::Call(typed_ir::Call {
called: call.called.clone(),
args: args_typed,
genargs,
});
new_expr
}
typed_ir::ExprKind::Match(match_) => {
let new_arms: Vec<_> = match_
.arms
.iter()
.map(|(pat, val)| (self.infer_expr_types(pat), self.infer_expr_types(val)))
.collect();
// TODO: hacky hacky hacky
let res_type = new_arms.first().unwrap().1.typ;
let new_match = typed_ir::Match {
expr: self.infer_expr_types(&match_.expr),
arms: new_arms,
};
let mut new_expr = expr.clone().with_type(res_type);
new_expr.kind = typed_ir::ExprKind::Match(Box::new(new_match));
new_expr
}
}
@ -429,20 +463,18 @@ impl Context {
lit_str
}
typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0),
typed_ir::ExprKind::Call {
called,
args,
genargs,
} => {
let args = args
typed_ir::ExprKind::Call(call) => {
let args = call
.args
.iter()
.map(|arg| {
self.pretty_typed_expr(w, arg)?;
Ok(format!("_{}", arg.id.0))
})
.collect::<Result<Vec<_>, std::fmt::Error>>()?;
let callable = self.callables.get(*called);
let genargs = genargs
let callable = self.callables.get(call.called);
let genargs = call
.genargs
.iter()
.map(|param| {
let mut type_str = String::new();
@ -457,6 +489,23 @@ impl Context {
args.join(", ")
)
}
typed_ir::ExprKind::Match(match_) => {
self.pretty_typed_expr(w, &match_.expr)?;
let arms = match_
.arms
.iter()
.map(|(pat, val)| {
self.pretty_typed_expr(w, pat)?;
self.pretty_typed_expr(w, val)?;
Ok(format!(" _{} => _{}", pat.id.0, val.id.0))
})
.collect::<Result<Vec<_>, _>>()?;
format!(
"match (_{}) {{\n{}\n}}",
&match_.expr.id.0,
arms.join(",\n")
)
}
};
let mut type_pretty = String::new();
self.types.pretty_type(&mut type_pretty, expr.typ)?;

View File

@ -17,22 +17,19 @@ fn lower_expression(
module.add_wire(expr_wire);
match &expr.kind {
ExprKind::Path(def) => Ok(rtlil::SigSpec::Wire(format!("\\$sig_{}", def.0))),
ExprKind::Call {
called,
args,
genargs,
} => {
let args_resolved = args
ExprKind::Call(call) => {
let args_resolved = call
.args
.iter()
.map(|expr| lower_expression(ctx, module, expr))
.collect::<Result<Vec<_>, _>>()?;
let callable = ctx.callables.get(*called);
let callable = ctx.callables.get(call.called);
let cell_id = module.make_genid(callable.name());
if *called == ctx.callables.builtins.xor {
let a_width = ctx.types.get_width(args[0].typ).unwrap();
let b_width = ctx.types.get_width(args[1].typ).unwrap();
if call.called == ctx.callables.builtins.xor {
let a_width = ctx.types.get_width(call.args[0].typ).unwrap();
let b_width = ctx.types.get_width(call.args[1].typ).unwrap();
let y_width = ctx.types.get_width(expr.typ).unwrap();
let mut cell = rtlil::Cell::new(&cell_id, "$xor");
cell.add_param("\\A_SIGNED", "0");
@ -44,8 +41,8 @@ fn lower_expression(
cell.add_connection("\\B", &args_resolved[1]);
cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone()));
module.add_cell(cell);
} else if *called == ctx.callables.builtins.reduce_or {
let a_width = ctx.types.get_width(args[0].typ).unwrap();
} else if call.called == ctx.callables.builtins.reduce_or {
let a_width = ctx.types.get_width(call.args[0].typ).unwrap();
let y_width = ctx.types.get_width(expr.typ).unwrap();
let mut cell = rtlil::Cell::new(&cell_id, "$reduce_or");
cell.add_param("\\A_SIGNED", "0");
@ -54,8 +51,8 @@ fn lower_expression(
cell.add_connection("\\A", &args_resolved[0]);
cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone()));
module.add_cell(cell);
} else if *called == ctx.callables.builtins.bitnot {
let a_width = ctx.types.get_width(args[0].typ).unwrap();
} else if call.called == ctx.callables.builtins.bitnot {
let a_width = ctx.types.get_width(call.args[0].typ).unwrap();
let y_width = ctx.types.get_width(expr.typ).unwrap();
let mut cell = rtlil::Cell::new(&cell_id, "$not");
cell.add_param("\\A_SIGNED", "0");
@ -72,6 +69,7 @@ fn lower_expression(
todo!(),
ctx.types.get_width(expr.typ).expect("signal has no size"),
)),
ExprKind::Match(_) => todo!(),
}
}

View File

@ -28,15 +28,25 @@ pub struct Expr {
pub typ: Type,
}
#[derive(Debug, Clone)]
pub struct Call {
pub called: CallableId,
pub args: Vec<Expr>,
pub genargs: Vec<Type>,
}
#[derive(Debug, Clone)]
pub struct Match {
pub expr: Expr,
pub arms: Vec<(Expr, Expr)>,
}
#[derive(Debug, Clone)]
pub enum ExprKind {
Literal(ElabData),
Path(DefId),
Call {
called: CallableId,
args: Vec<Expr>,
genargs: Vec<Type>,
},
Call(Call),
Match(Box<Match>),
}
#[derive(Debug, Clone)]

View File

@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::fmt::Debug;
/// Alias for &TypeStruct to reduce repetition
/// and make futura migration to interning
@ -13,12 +14,12 @@ impl Debug for InternedType {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct TypeStruct {
kind: TypeKind,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
enum TypeKind {
/// Elaboration-time types
ElabType(ElabKind),
@ -62,7 +63,7 @@ enum ElabValueData {
}
/// Types that are only valid during Elaboration
#[derive(Debug)]
#[derive(Debug, Clone)]
enum ElabKind {
/// general, unsized number type
Num,
@ -95,7 +96,7 @@ pub struct PrimitiveTypes {
}
pub struct TypingContext {
types: Vec<TypeStruct>,
types: RefCell<Vec<TypeStruct>>,
pub primitives: PrimitiveTypes,
}
@ -107,7 +108,7 @@ impl TypingContext {
infer: InternedType(2),
};
Self {
types: vec![
types: RefCell::new(vec![
TypeStruct {
kind: TypeKind::ElabType(ElabKind::Num),
},
@ -120,19 +121,19 @@ impl TypingContext {
TypeStruct {
kind: TypeKind::Infer,
},
],
]),
primitives,
}
}
pub fn add(&mut self, typ: TypeStruct) -> Type {
let id = self.types.len();
self.types.push(typ);
pub fn add(&self, typ: TypeStruct) -> Type {
let id = self.types.borrow().len();
self.types.borrow_mut().push(typ);
InternedType(id)
}
pub fn get(&self, typ: Type) -> &TypeStruct {
&self.types[typ.0]
pub fn get(&self, typ: Type) -> TypeStruct {
self.types.borrow()[typ.0].clone()
}
pub fn make_elabnum_u32(&self, num: u32) -> ElabData {
@ -142,7 +143,14 @@ impl TypingContext {
}
}
pub fn make_logic_size(&mut self, width: u32) -> Type {
pub fn make_const_u32(&self, width: u32, num: u32) -> ElabData {
ElabData {
typ: self.make_logic_size(width),
value: ElabValue::Concrete(ElabValueData::U32(num)),
}
}
pub fn make_logic_size(&self, width: u32) -> Type {
let widthnum = self.make_elabnum_u32(width);
self.parameterize(self.primitives.logic, &[GenericArg::Elab(widthnum)])
.unwrap()
@ -171,7 +179,7 @@ impl TypingContext {
}
}
pub fn parameterize(&mut self, typ: Type, params: &[GenericArg]) -> Option<Type> {
pub fn parameterize(&self, typ: Type, params: &[GenericArg]) -> Option<Type> {
// TODO: return proper error type here
match &self.get(typ).kind {
// Elab types have no params yet

View File

@ -0,0 +1,57 @@
use nom::{
branch::alt,
combinator::map,
error::context,
multi::{many1, separated_list1},
sequence::{delimited, separated_pair, tuple},
};
use crate::parser::{
assign_statement, expression,
expression::{expression, Expression},
tokens::{token, TokenKind as tk, TokenSpan},
Assign, IResult, Span,
};
#[derive(Debug, Clone)]
pub enum BlockExpr<'a> {
IfElse(IfElseBlock),
Match(MatchBlock<'a>),
Block(Vec<BlockExpr<'a>>),
}
// TODO: postponed because annoying to implement
#[derive(Debug, Clone)]
pub struct IfElseBlock {}
#[derive(Debug, Clone)]
pub struct MatchBlock<'a> {
pub expr: Expression<'a>,
pub arms: Vec<(Expression<'a>, Expression<'a>)>,
}
fn match_arm(input: TokenSpan) -> IResult<TokenSpan, (Expression, Expression)> {
separated_pair(expression, token(tk::FatArrow), expression)(input)
}
fn match_block(input: TokenSpan) -> IResult<TokenSpan, MatchBlock> {
context(
"match block",
map(
tuple((
token(tk::Match),
delimited(token(tk::LParen), expression, token(tk::RParen)),
delimited(
token(tk::LBrace),
separated_list1(token(tk::Comma), match_arm),
token(tk::RBrace),
),
)),
|(_, expr, arms)| MatchBlock { expr, arms },
),
)(input)
}
pub fn block_expr(input: TokenSpan) -> IResult<TokenSpan, BlockExpr> {
alt((map(match_block, BlockExpr::Match),))(input)
}

View File

@ -1,3 +1,5 @@
use super::block_expression::{block_expr, BlockExpr};
use super::literals;
use super::tokens::{token, Token, TokenKind as tk, TokenSpan};
use super::{IResult, Span};
use nom::{
@ -7,6 +9,18 @@ use nom::{
sequence::{delimited, preceded, separated_pair, tuple},
};
#[derive(Debug, Clone)]
pub struct Literal<'a> {
pub tok: Token<'a>,
pub kind: LiteralKind,
}
#[derive(Debug, Clone)]
pub enum LiteralKind {
Num(u32),
Const(u32, u64),
}
#[derive(Debug, Clone)]
pub struct Call<'a> {
pub name: Span<'a>,
@ -42,10 +56,11 @@ pub struct UnOp<'a> {
#[derive(Debug, Clone)]
pub enum Expression<'a> {
Path(&'a str),
Literal(Token<'a>),
Literal(Literal<'a>),
UnOp(Box<UnOp<'a>>),
BinOp(Box<BinOp<'a>>),
Call(Box<Call<'a>>),
BlockExpr(Box<BlockExpr<'a>>),
}
/// expressions that can't be subdivided further
@ -55,7 +70,19 @@ fn atom(input: TokenSpan) -> IResult<TokenSpan, Expression> {
map(token(tk::Ident), |it| {
Expression::Path(it.span().fragment())
}),
map(token(tk::Number), Expression::Literal),
map(token(tk::Number), |tok| {
Expression::Literal(Literal {
tok,
kind: LiteralKind::Num(tok.span().parse().expect("invalid literal")),
})
}),
map(token(tk::Constant), |tok| {
let (_, (width, val)) = literals::const_bits(tok.span()).expect("invalid literal");
Expression::Literal(Literal {
tok,
kind: LiteralKind::Const(width, val),
})
}),
delimited(token(tk::LParen), expression, token(tk::RParen)),
))(input)
}
@ -119,11 +146,11 @@ pub fn call_item(input: TokenSpan) -> IResult<TokenSpan, Call> {
)(input)
}
/// parser combinators can not parse left-recursive grammars. To work around this, we split
/// expressions into a recursive and non-recursive portion.
/// Parsers reachable from this point must call expression_nonrecurse instead
pub fn expression(input: TokenSpan) -> IResult<TokenSpan, Expression> {
bitop(input)
alt((
bitop,
map(block_expr, |blk| Expression::BlockExpr(Box::new(blk))),
))(input)
}
#[cfg(test)]

View File

@ -1,15 +1,27 @@
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{alpha1, alphanumeric1, char, multispace0, one_of},
character::complete::{alpha1, alphanumeric1, char, digit1, multispace0, one_of},
combinator::{map, recognize},
error::ParseError,
multi::{many0, many1},
sequence::{delimited, pair, preceded, terminated},
sequence::{delimited, pair, preceded, separated_pair, terminated},
};
use crate::parser::{IResult, Span};
pub fn const_bits(input: Span) -> IResult<Span, (u32, u64)> {
map(
separated_pair(digit1, char('\''), digit1),
|(width, value): (Span, Span)| {
(
width.fragment().parse().expect("error parsing literal"),
value.fragment().parse().expect("error parsing literal"),
)
},
)(input)
}
pub fn hexadecimal(input: Span) -> IResult<Span, u64> {
map(
preceded(

View File

@ -1,4 +1,5 @@
pub mod adt;
pub mod block_expression;
pub mod comb;
pub mod declaration;
pub mod error;

View File

@ -3,12 +3,11 @@ use nom::{
combinator::map,
error::context,
multi::{many1, separated_list1},
sequence::{delimited, separated_pair, tuple},
sequence::{delimited, tuple},
};
use crate::parser::{
assign_statement, expression,
expression::{expression, Expression},
assign_statement,
tokens::{token, TokenKind as tk, TokenSpan},
Assign, IResult, Span,
};
@ -21,42 +20,7 @@ pub struct ProcBlock<'a> {
#[derive(Debug)]
pub enum ProcStatement<'a> {
IfElse(IfElseBlock),
Assign(Assign<'a>),
Match(MatchBlock<'a>),
Block(Vec<ProcStatement<'a>>),
}
// TODO: postponed because annoying to implement
#[derive(Debug)]
pub struct IfElseBlock {}
#[derive(Debug)]
pub struct MatchBlock<'a> {
pub expr: Expression<'a>,
pub arms: Vec<(Expression<'a>, ProcStatement<'a>)>,
}
fn match_arm(input: TokenSpan) -> IResult<TokenSpan, (Expression, ProcStatement)> {
separated_pair(expression, token(tk::FatArrow), proc_statement)(input)
}
fn match_block(input: TokenSpan) -> IResult<TokenSpan, MatchBlock> {
context(
"match block",
map(
tuple((
token(tk::Match),
delimited(token(tk::LParen), expression, token(tk::RParen)),
delimited(
token(tk::LBrace),
separated_list1(token(tk::Comma), match_arm),
token(tk::RBrace),
),
)),
|(_, expr, arms)| MatchBlock { expr, arms },
),
)(input)
}
fn statement_block(input: TokenSpan) -> IResult<TokenSpan, Vec<ProcStatement>> {
@ -69,11 +33,7 @@ fn statement_block(input: TokenSpan) -> IResult<TokenSpan, Vec<ProcStatement>> {
/// parse a statement that is valid inside a proc block
fn proc_statement(input: TokenSpan) -> IResult<TokenSpan, ProcStatement> {
alt((
map(match_block, ProcStatement::Match),
map(statement_block, ProcStatement::Block),
map(assign_statement, ProcStatement::Assign),
))(input)
alt((map(assign_statement, ProcStatement::Assign),))(input)
}
pub fn proc_block(input: TokenSpan) -> IResult<TokenSpan, ProcBlock> {

View File

@ -2,7 +2,7 @@
use super::{
error::{Error, InputPos},
literals::{identifier, ws0},
literals::{const_bits, identifier, ws0},
IResult, Span,
};
use nom::{
@ -77,6 +77,7 @@ pub enum TokenKind {
// Literals
Ident,
Number,
Constant,
// Keywords
Module,
Assign,
@ -186,6 +187,7 @@ fn lex_braces(input: Span) -> IResult<Span, Token> {
fn lex_literals(input: Span) -> IResult<Span, Token> {
map(
consumed(alt((
map(const_bits, |_| TokenKind::Constant),
map(identifier, |_| TokenKind::Ident),
map(digit1, |_| TokenKind::Number),
))),