// Blacksite modifications are documented in ../PATCH.md. use crate::*; #[apply(ast)] #[derive(Display)] pub enum Value<'s> { Int(Int<'s>), Float(Float<'s>), Str(Str<'s>), Char(Char<'s>), Bool(bool), List(List<'s>), Map(Map<'s>), Unit(&'s str), Tuple(Tuple<'s>), Struct(Struct<'s>), } pub(crate) fn value(input: &str) -> IResult { alt(( map(value_int, Value::Int), map(value_float, Value::Float), map(value_str, Value::Str), map(value_char, Value::Char), map(value_list, Value::List), map(value_map, Value::Map), map(value_struct, Value::Struct), map(value_tuple, Value::Tuple), map(ident, Value::Unit), ))(input) } #[apply(ast)] #[derive(Display)] pub struct Int<'s>(&'s str); fn value_int(s: &str) -> IResult { map( recognize(tuple(( sign, alt(( is_a("0123456789_"), preceded( char('0'), cut(preceded(is_a("box"), is_a("0123456789ABCDEFabcdef_"))), ), )), not(peek(one_of(".eE"))), ))), Int, )(s) } fn sign(s: &str) -> IResult> { opt(alt((char('+'), char('-'))))(s) } #[apply(ast)] #[derive(Display)] pub struct Float<'s>(&'s str); fn value_float(s: &str) -> IResult { map( // TODO test `double` recognize(tuple(( opt(alt((char('+'), char('-')))), alt(( tag("inf"), tag("NaN"), recognize(tuple(( alt(( recognize(tuple((digit1, opt(tuple((char('.'), digit0)))))), recognize(tuple((char('.'), digit1))), )), opt(tuple((alt((char('e'), char('E'))), sign, digit1))), ))), )), ))), Float, )(s) } #[apply(ast)] #[derive(Display)] pub enum Str<'s> { #[display(fmt = "\"{_0}\"")] Baked(&'s str), #[display(fmt = "r{0}\"{content}\"{0}", "\"#\".repeat(*pounds)")] Raw { pounds: usize, content: &'s str }, } fn value_str(s: &str) -> IResult { alt(( map( preceded( char::<&str, Error>('"'), cut(terminated( recognize(many0(alt(( tag("\\\""), tag("\\\\"), tag("\\"), is_not("\\\""), )))), char('"'), )), ), Str::Baked, ), map(raw_string, |(pounds, content)| Str::Raw { pounds, content }), ))(s) } fn raw_string(s: &str) -> IResult<(usize, &str)> { let (s, _) = char('r')(s)?; let (s, pounds) = many0_count(char('#'))(s)?; let terminator = format!("\"{}", "#".repeat(pounds)); let res = preceded( char('"'), cut(terminated( take_until(terminator.as_str()), tag(terminator.as_str()), )), ) .map(|content| (pounds, content)) .parse(s); res } #[apply(ast)] #[derive(Display)] #[display(fmt = "'{_0}'")] pub struct Char<'s>(pub &'s str); fn value_char(s: &str) -> IResult { map( preceded( char::<&str, Error>('\''), cut(terminated(tag("\\'").or(is_not("'")), char('\''))), ), Char, )(s) } #[apply(ast)] #[derive(Display)] #[display(fmt = "[{_0}]")] pub struct List<'s>(pub Separated<'s, Value<'s>>); fn value_list(s: &str) -> IResult { map( preceded(char('['), cut(terminated(separated(value), char(']')))), List, )(s) } #[apply(ast)] #[derive(Display)] #[display(fmt = "{key}{after_key}:{value}")] pub struct MapItem<'s> { pub key: Value<'s>, pub after_key: Whitespace<'s>, pub value: WsLead<'s, Value<'s>>, } #[apply(ast)] #[derive(Display)] #[display(fmt = "{{{_0}}}")] pub struct Map<'s>(pub Separated<'s, MapItem<'s>>); fn value_map(s: &str) -> IResult { context( "map", map( preceded( char('{'), cut(terminated( separated(tuple((value, ws, char(':'), ws_lead(value))).map( |(key, after_key, _, value)| MapItem { key, after_key, value, }, )), char('}'), )), ), Map, ), )(s) } #[apply(ast)] #[derive(Display)] #[display(fmt = "{}({fields})", "FormatOption(ident)")] pub struct Tuple<'s> { pub ident: Option>, pub fields: Separated<'s, Value<'s>>, } fn value_tuple(s: &str) -> IResult { map( pair( opt(ws_followed(ident)), delimited(char('('), separated(value), char(')')), ), |(ident, fields)| Tuple { ident, fields }, )(s) } #[apply(ast)] #[derive(Display)] #[display(fmt = "{key}{after_key}:{value}")] pub struct NamedField<'s> { pub key: &'s str, pub after_key: Whitespace<'s>, pub value: WsLead<'s, Value<'s>>, } #[apply(ast)] #[derive(Display)] #[display(fmt = "{}({fields})", "FormatOption(ident)")] pub struct Struct<'s> { pub ident: Option>, pub fields: Separated<'s, NamedField<'s>>, } fn value_struct(s: &str) -> IResult { context( "value struct", map( pair( opt(ws_followed(ident)), delimited( char('('), separated(map( tuple((ident, terminated(ws, char(':')), ws_lead(value))), |(key, after_key, value)| NamedField { key, after_key, value, }, )), char(')'), ), ), |(ident, fields)| Struct { ident, fields }, ), )(s) }