use core::{num::ParseIntError, str::FromStr};
use crate::{
arg_err,
attribute::AttrObj,
basic_block::BasicBlock,
combine::{
Parser, Stream, any, between, many, many1, none_of,
parser::char::{digit, spaces},
sep_by, token,
},
context::Ptr,
debug_info::set_operation_result_name,
identifier::Identifier,
location::{Located, Location},
operation::Operation,
parsable::{IntoParseResult, Parsable, ParseResult, StateStream, parser_combinator},
result::Result,
r#type::TypeHandle,
value::Value,
};
use alloc::{boxed::Box, string::String, vec::Vec};
pub fn spaced<Input: Stream<Token = char>, Output>(
parser: impl Parser<Input, Output = Output>,
) -> impl Parser<Input, Output = Output> {
combine::between(spaces(), spaces(), parser)
}
pub fn location<'a>() -> Box<dyn Parser<StateStream<'a>, Output = Location, PartialState = ()> + 'a>
{
combine::parser(|parsable_state: &mut StateStream<'a>| {
combine::ParseResult::PeekOk(parsable_state.loc()).into()
})
.boxed()
}
pub fn type_parse<'a>(state_stream: &mut StateStream<'a>) -> ParseResult<'a, TypeHandle> {
TypeHandle::parse(state_stream, ())
}
pub fn type_parser<'a>()
-> Box<dyn Parser<StateStream<'a>, Output = TypeHandle, PartialState = ()> + 'a> {
TypeHandle::parser(())
}
pub fn int_parse<'a, IntT>(state_stream: &mut StateStream<'a>, _arg: ()) -> ParseResult<'a, IntT>
where
IntT: FromStr,
IntT::Err: core::error::Error + Send + Sync + 'static,
{
many1::<String, _, _>(digit())
.and_then(|digits| digits.parse::<IntT>())
.parse_stream(state_stream)
.into()
}
pub fn int_parser<'a, IntT>()
-> Box<dyn Parser<StateStream<'a>, Output = IntT, PartialState = ()> + 'a>
where
IntT: FromStr + 'a,
IntT::Err: core::error::Error + Send + Sync + 'static,
{
parser_combinator(int_parse, ())
}
pub trait FromStrRadix: Sized {
fn from_str_radix(src: &str, radix: u32) -> core::result::Result<Self, ParseIntError>;
}
macro_rules! impl_from_str_radix_for_int {
($($ty:ty),*) => {
$(
impl FromStrRadix for $ty {
fn from_str_radix(src: &str, radix: u32) -> core::result::Result<Self, ParseIntError> {
<$ty>::from_str_radix(src, radix)
}
}
)*
};
}
impl_from_str_radix_for_int!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
pub fn hex_int_parse<'a, IntT>(
state_stream: &mut StateStream<'a>,
_arg: (),
) -> ParseResult<'a, IntT>
where
IntT: FromStrRadix,
{
combine::parser::char::string("0x")
.with(many1::<String, _, _>(combine::parser::char::hex_digit()))
.and_then(|digits| IntT::from_str_radix(&digits, 16))
.parse_stream(state_stream)
.into()
}
pub fn hex_int_parser<'a, IntT>()
-> Box<dyn Parser<StateStream<'a>, Output = IntT, PartialState = ()> + 'a>
where
IntT: FromStrRadix + 'a,
{
parser_combinator(hex_int_parse, ())
}
pub fn quoted_string_parse<'a>(
state_stream: &mut StateStream<'a>,
_arg: (),
) -> ParseResult<'a, String> {
let escaped_char = combine::parser(move |parsable_state: &mut StateStream<'a>| {
let loc = parsable_state.loc();
let mut escaped_char = token('\\').with(any()).then(move |c: char| {
let loc = loc.clone();
combine::parser(move |_parsable_state: &mut StateStream<'a>| {
let result = match c {
'\\' => Ok('\\'),
'\"' => Ok('\"'),
_ => arg_err!(loc.clone(), "Unexpected escaped character \\{}", c),
};
result.into_parse_result()
})
});
escaped_char.parse_stream(parsable_state).into()
});
let quoted_string = between(
token('"'),
token('"'),
many(escaped_char.or(none_of("\"".chars()))),
);
quoted_string
.map(|chars: Vec<char>| {
chars.into_iter().collect::<String>()
})
.parse_stream(state_stream)
.into()
}
pub fn quoted_string_parser<'a>()
-> Box<dyn Parser<StateStream<'a>, Output = String, PartialState = ()> + 'a> {
parser_combinator(quoted_string_parse, ())
}
pub fn attr_parse<'a>(state_stream: &mut StateStream<'a>) -> ParseResult<'a, AttrObj> {
AttrObj::parse(state_stream, ())
}
pub fn attr_parser<'a>()
-> Box<dyn Parser<StateStream<'a>, Output = AttrObj, PartialState = ()> + 'a> {
AttrObj::parser(())
}
pub fn delimited_list_parser<Input: Stream<Token = char>, Output>(
open: char,
close: char,
sep: char,
parser: impl Parser<Input, Output = Output>,
) -> impl Parser<Input, Output = Vec<Output>> {
between(
token(open).skip(spaces()),
spaces().with(token(close)),
list_parser(sep, parser),
)
}
pub fn list_parser<Input: Stream<Token = char>, Output>(
sep: char,
parser: impl Parser<Input, Output = Output>,
) -> impl Parser<Input, Output = Vec<Output>> {
sep_by::<Vec<_>, _, _, _>(parser.skip(spaces()), token(sep).skip(spaces()))
}
pub fn zero_or_more_parser<Input: Stream<Token = char>, Output>(
parser: impl Parser<Input, Output = Output>,
) -> impl Parser<Input, Output = Vec<Output>> {
many::<Vec<_>, _, _>(spaces().with(parser.skip(spaces())))
}
pub fn ssa_opd_parse<'a>(state_stream: &mut StateStream<'a>, _arg: ()) -> ParseResult<'a, Value> {
Identifier::parser(())
.parse_stream(state_stream)
.map(|opd| {
state_stream
.state
.name_tracker
.ssa_use(state_stream.state.ctx, &opd)
})
.into()
}
pub fn ssa_opd_parser<'a>()
-> Box<dyn Parser<StateStream<'a>, Output = Value, PartialState = ()> + 'a> {
parser_combinator(ssa_opd_parse, ())
}
pub fn block_opd_parse<'a>(
state_stream: &mut StateStream<'a>,
_arg: (),
) -> ParseResult<'a, Ptr<BasicBlock>> {
token('^')
.with(Identifier::parser(()))
.parse_stream(state_stream)
.map(|opd| {
state_stream
.state
.name_tracker
.block_use(state_stream.state.ctx, &opd)
})
.into()
}
pub fn block_opd_parser<'a>()
-> Box<dyn Parser<StateStream<'a>, Output = Ptr<BasicBlock>, PartialState = ()> + 'a> {
parser_combinator(block_opd_parse, ())
}
pub fn process_parsed_ssa_defs(
state_stream: &mut StateStream,
results: &[(Identifier, Location)],
op: Ptr<Operation>,
) -> Result<()> {
let ctx = &mut state_stream.state.ctx;
assert!(
results.len() == op.deref(ctx).get_num_results(),
"Error processing parsed SSA definitions. Result count mismatch"
);
let name_tracker = &mut state_stream.state.name_tracker;
for (idx, name_loc) in results.iter().enumerate() {
let res = op.deref(ctx).get_result(idx);
name_tracker.ssa_def(ctx, name_loc, res)?;
set_operation_result_name(ctx, op, idx, Some(name_loc.0.clone()));
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use alloc::{format, string::ToString};
use expect_test::expect;
use crate::{
context::Context, parsable::parse_from_str, printable::Printable, result::ExpectOk,
};
#[test]
fn test_parse_type() {
let mut ctx = Context::new();
let err_msg = format!(
"{}",
parse_from_str(type_parser(), &mut ctx, "builtin.some").unwrap_err()
);
let expected_err_msg = expect![[r#"
Compilation error: invalid input program.
Parse error at line: 1, column: 1
Unregistered type builtin.some
"#]];
expected_err_msg.assert_eq(&err_msg);
let err_msg = format!(
"{}",
parse_from_str(type_parser(), &mut ctx, "builtin.integer a").unwrap_err()
);
let expected_err_msg = expect![[r#"
Compilation error: invalid input program.
Parse error at line: 1, column: 17
Unexpected `a`
Expected whitespaces, si, ui or i
"#]];
expected_err_msg.assert_eq(&err_msg);
let parsed =
parse_from_str(type_parser(), &mut ctx, "builtin.integer si32").expect_ok(&ctx);
assert_eq!(parsed.disp(&ctx).to_string(), "builtin.integer si32");
}
#[test]
fn test_hex_int_parser() {
use crate::{context::Context, parsable::parse_from_str, result::ExpectOk};
let mut ctx = Context::new();
let parsed: u64 = parse_from_str(hex_int_parser(), &mut ctx, "0xff").expect_ok(&ctx);
assert_eq!(parsed, 0xff);
let parsed: u64 = parse_from_str(hex_int_parser(), &mut ctx, "0xDEAD").expect_ok(&ctx);
assert_eq!(parsed, 0xDEAD);
let parsed: u32 = parse_from_str(hex_int_parser(), &mut ctx, "0xCAFE").expect_ok(&ctx);
assert_eq!(parsed, 0xCAFEu32);
let parsed: u8 = parse_from_str(hex_int_parser(), &mut ctx, "0x7f").expect_ok(&ctx);
assert_eq!(parsed, 0x7fu8);
let parsed: i64 = parse_from_str(hex_int_parser(), &mut ctx, "0x1234").expect_ok(&ctx);
assert_eq!(parsed, 0x1234i64);
let parsed: usize = parse_from_str(hex_int_parser(), &mut ctx, "0xABCDEF").expect_ok(&ctx);
assert_eq!(parsed, 0xABCDEFusize);
{
let res = parse_from_str(hex_int_parser::<u8>(), &mut ctx, "0x100");
assert!(res.is_err());
}
{
let res = parse_from_str(hex_int_parser::<u16>(), &mut ctx, "0x10000");
assert!(res.is_err());
}
{
let res = parse_from_str(hex_int_parser::<u64>(), &mut ctx, "ff");
assert!(res.is_err());
}
{
let res = parse_from_str(hex_int_parser::<u64>(), &mut ctx, "0x");
assert!(res.is_err());
}
}
}