use std::{num::ParseIntError, str::FromStr};
use crate::{
arg_err,
attribute::AttrObj,
basic_block::BasicBlock,
context::Ptr,
debug_info::set_operation_result_name,
identifier::Identifier,
location::{Located, Location},
operation::Operation,
parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
result::Result,
r#type::TypeObj,
value::Value,
};
use combine::{
Parser, Stream, any, between, many, many1, none_of,
parser::char::{digit, spaces},
sep_by, token,
};
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_parser<'a>()
-> Box<dyn Parser<StateStream<'a>, Output = Ptr<TypeObj>, PartialState = ()> + 'a> {
Ptr::<TypeObj>::parser(())
}
pub fn int_parse<'a, IntT>(state_stream: &mut StateStream<'a>, _arg: ()) -> ParseResult<'a, IntT>
where
IntT: FromStr,
IntT::Err: std::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,
IntT::Err: std::error::Error + Send + Sync + 'static,
{
combine::parser(move |parsable_state: &mut StateStream<'a>| int_parse(parsable_state, ()))
.boxed()
}
pub trait FromStrRadix: Sized {
fn from_str_radix(src: &str, radix: u32) -> std::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) -> std::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,
{
combine::parser(move |parsable_state: &mut StateStream<'a>| hex_int_parse(parsable_state, ()))
.boxed()
}
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> {
combine::parser(move |parsable_state: &mut StateStream<'a>| {
quoted_string_parse(parsable_state, ())
})
.boxed()
}
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> {
combine::parser(move |parsable_state: &mut StateStream<'a>| ssa_opd_parse(parsable_state, ()))
.boxed()
}
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> {
combine::parser(move |parsable_state: &mut StateStream<'a>| block_opd_parse(parsable_state, ()))
.boxed()
}
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 expect_test::expect;
use crate::{
context::Context,
location,
parsable::{self, state_stream_from_iterator},
printable::Printable,
};
#[test]
fn test_parse_type() {
let mut ctx = Context::new();
let state_stream = state_stream_from_iterator(
"builtin.some".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let res = type_parser().parse(state_stream);
let err_msg = format!("{}", res.err().unwrap());
let expected_err_msg = expect![[r#"
Parse error at line: 1, column: 1
Unregistered type builtin.some
"#]];
expected_err_msg.assert_eq(&err_msg);
let state_stream = state_stream_from_iterator(
"builtin.integer a".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let res = type_parser().parse(state_stream);
let err_msg = format!("{}", res.err().unwrap());
let expected_err_msg = expect![[r#"
Parse error at line: 1, column: 17
Unexpected `a`
Expected whitespaces, si, ui or i
"#]];
expected_err_msg.assert_eq(&err_msg);
let state_stream = state_stream_from_iterator(
"builtin.integer si32".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let parsed = type_parser().parse(state_stream).unwrap().0;
assert_eq!(parsed.disp(&ctx).to_string(), "builtin.integer si32");
}
#[test]
fn test_hex_int_parser() {
use crate::{
context::Context,
location,
parsable::{self, state_stream_from_iterator},
};
let mut ctx = Context::new();
let state_stream = state_stream_from_iterator(
"0xff".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let parsed: u64 = hex_int_parser().parse(state_stream).unwrap().0;
assert_eq!(parsed, 0xff);
let state_stream = state_stream_from_iterator(
"0xDEAD".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let parsed: u64 = hex_int_parser().parse(state_stream).unwrap().0;
assert_eq!(parsed, 0xDEAD);
let state_stream = state_stream_from_iterator(
"0xCAFE".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let parsed: u32 = hex_int_parser().parse(state_stream).unwrap().0;
assert_eq!(parsed, 0xCAFEu32);
let state_stream = state_stream_from_iterator(
"0x7f".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let parsed: u8 = hex_int_parser().parse(state_stream).unwrap().0;
assert_eq!(parsed, 0x7fu8);
let state_stream = state_stream_from_iterator(
"0x1234".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let parsed: i64 = hex_int_parser().parse(state_stream).unwrap().0;
assert_eq!(parsed, 0x1234i64);
let state_stream = state_stream_from_iterator(
"0xABCDEF".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let parsed: usize = hex_int_parser().parse(state_stream).unwrap().0;
assert_eq!(parsed, 0xABCDEFusize);
{
let state_stream = state_stream_from_iterator(
"0x100".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let res = hex_int_parser::<u8>().parse(state_stream);
assert!(res.is_err());
}
{
let state_stream = state_stream_from_iterator(
"0x10000".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let res = hex_int_parser::<u16>().parse(state_stream);
assert!(res.is_err());
}
{
let state_stream = state_stream_from_iterator(
"ff".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let res = hex_int_parser::<u64>().parse(state_stream);
assert!(res.is_err());
}
{
let state_stream = state_stream_from_iterator(
"0x".chars(),
parsable::State::new(&mut ctx, location::Source::InMemory),
);
let res = hex_int_parser::<u64>().parse(state_stream);
assert!(res.is_err());
}
}
}