241 lines
6.0 KiB
Rust
241 lines
6.0 KiB
Rust
// 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<Value> {
|
|
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<Int> {
|
|
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<Option<char>> {
|
|
opt(alt((char('+'), char('-'))))(s)
|
|
}
|
|
|
|
#[apply(ast)]
|
|
#[derive(Display)]
|
|
pub struct Float<'s>(&'s str);
|
|
fn value_float(s: &str) -> IResult<Float> {
|
|
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<Str> {
|
|
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<Char> {
|
|
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<List> {
|
|
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<Map> {
|
|
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<WsFollowed<'s, &'s str>>,
|
|
pub fields: Separated<'s, Value<'s>>,
|
|
}
|
|
fn value_tuple(s: &str) -> IResult<Tuple> {
|
|
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<WsFollowed<'s, &'s str>>,
|
|
pub fields: Separated<'s, NamedField<'s>>,
|
|
}
|
|
fn value_struct(s: &str) -> IResult<Struct> {
|
|
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)
|
|
}
|