1
0
Fork 0
forked from wry/wry

workspace: move crates under crates

This commit is contained in:
kossLAN 2026-05-29 18:55:59 -04:00
parent 0016bc8cf0
commit 6393fdf3c0
No known key found for this signature in database
354 changed files with 102 additions and 102 deletions

View file

@ -0,0 +1,11 @@
mod spanned_ext;
#[cfg(test)]
mod tests;
mod toml_lexer;
pub mod toml_parser;
pub mod toml_span;
pub mod toml_value;
mod value_ext;
pub mod value_parser;
pub use spanned_ext::SpannedErrorExt;

View file

@ -0,0 +1,64 @@
use crate::{
toml_span::Spanned,
toml_value::Value,
value_parser::{ParseResult, Parser},
};
impl Spanned<&Value> {
pub fn parse<P: Parser>(&self, parser: &mut P) -> ParseResult<P> {
self.value.parse(self.span, parser)
}
pub fn parse_map<P: Parser, E>(
&self,
parser: &mut P,
) -> Result<<P as Parser>::Value, Spanned<E>>
where
<P as Parser>::Error: Into<E>,
{
self.parse(parser).map_spanned_err(|e| e.into())
}
}
impl Spanned<Value> {
pub fn parse<P: Parser>(&self, parser: &mut P) -> ParseResult<P> {
self.as_ref().parse(parser)
}
pub fn parse_map<P: Parser, E>(
&self,
parser: &mut P,
) -> Result<<P as Parser>::Value, Spanned<E>>
where
<P as Parser>::Error: Into<E>,
{
self.as_ref().parse_map(parser)
}
}
pub trait SpannedErrorExt {
type T;
type E;
fn map_spanned_err<U, F>(self, f: F) -> Result<Self::T, Spanned<U>>
where
F: FnOnce(Self::E) -> U;
}
impl<T, E> SpannedErrorExt for Result<T, Spanned<E>> {
type T = T;
type E = E;
fn map_spanned_err<U, F>(self, f: F) -> Result<Self::T, Spanned<U>>
where
F: FnOnce(Self::E) -> U,
{
match self {
Ok(v) => Ok(v),
Err(e) => Err(Spanned {
span: e.span,
value: f(e.value),
}),
}
}
}

View file

@ -0,0 +1,136 @@
use {
crate::{
toml_parser::{ErrorHandler, ParserError, parse},
toml_span::{Span, Spanned, SpannedExt},
toml_value::Value,
},
bstr::{BStr, ByteSlice},
std::{
os::unix::ffi::OsStrExt,
panic::{AssertUnwindSafe, catch_unwind},
str::FromStr,
},
walkdir::WalkDir,
};
#[test]
fn test() {
let mut have_failures = false;
let mut num = 0;
let tests = std::path::Path::new(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../toml-config/toml-test/tests/valid"
));
if !tests.exists() {
eprintln!("skipping TOML conformance tests because fixtures are not present");
return;
}
for path in WalkDir::new(tests) {
let path = path.unwrap();
if let Some(prefix) = path.path().as_os_str().as_bytes().strip_suffix(b".toml") {
num += 1;
let res = catch_unwind(AssertUnwindSafe(|| {
have_failures |= run_test(prefix.as_bstr());
}));
if res.is_err() {
eprintln!("panic while running {}", prefix.as_bstr());
}
}
}
if have_failures {
panic!("There were test failures");
}
eprintln!("ran {num} tests");
}
fn run_test(prefix: &BStr) -> bool {
let toml = std::fs::read(&format!("{}.toml", prefix)).unwrap();
let json = std::fs::read_to_string(&format!("{}.json", prefix)).unwrap();
let json: serde_json::Value = serde_json::from_str(&json).unwrap();
let json_as_toml = json_to_value(json);
let toml = match parse(toml.as_bytes(), &NoErrorHandler(prefix)) {
Ok(t) => t,
Err(e) => {
eprintln!("toml could not be parsed in test {}", prefix);
NoErrorHandler(prefix).handle(e);
return true;
}
};
if toml != json_as_toml {
eprintln!("toml and json differ in test {}", prefix);
eprintln!("toml: {:#?}", toml);
eprintln!("json: {:#?}", json_as_toml);
true
} else {
false
}
}
fn json_to_value(json: serde_json::Value) -> Spanned<Value> {
let span = Span { lo: 0, hi: 0 };
let val = match json {
serde_json::Value::String(_)
| serde_json::Value::Number(_)
| serde_json::Value::Null
| serde_json::Value::Bool(_) => panic!("Unexpected type"),
serde_json::Value::Array(v) => Value::Array(v.into_iter().map(json_to_value).collect()),
serde_json::Value::Object(v) => {
if v.len() == 2 && v.contains_key("type") && v.contains_key("value") {
let ty = v.get("type").unwrap().as_str().unwrap();
let val = v.get("value").unwrap().as_str().unwrap();
match ty {
"string" => Value::String(val.to_owned()),
"integer" => Value::Integer(i64::from_str(val).unwrap()),
"float" => Value::Float(f64::from_str(val).unwrap()),
"bool" => Value::Boolean(bool::from_str(val).unwrap()),
_ => panic!("unexpected type {}", ty),
}
} else {
Value::Table(
v.into_iter()
.map(|(k, v)| (k.spanned(span), json_to_value(v)))
.collect(),
)
}
}
};
val.spanned(span)
}
struct NoErrorHandler<'a>(&'a BStr);
impl<'a> ErrorHandler for NoErrorHandler<'a> {
fn handle(&self, err: Spanned<ParserError>) {
eprintln!(
"{}: An error occurred during validation: {}",
self.0,
FormatError(err.span, Some(err.value)),
);
}
fn redefinition(&self, err: Spanned<ParserError>, prev: Span) {
eprintln!(
"{}: Redefinition: {}",
self.0,
FormatError(err.span, Some(err.value)),
);
eprintln!(
"{}: Previous: {}",
self.0,
FormatError(prev, None),
);
}
}
struct FormatError(Span, Option<ParserError>);
impl std::fmt::Display for FormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(cause) = &self.1 {
write!(f, "{cause}: ")?;
}
write!(f, "span {}..{}", self.0.lo, self.0.hi)
}
}

View file

@ -0,0 +1,191 @@
use crate::toml_span::{Span, Spanned, SpannedExt};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Token<'a> {
Dot,
Equals,
Comma,
LeftBracket,
RightBracket,
LeftBrace,
RightBrace,
LiteralString(&'a [u8]),
CookedString(&'a [u8]),
Literal(&'a [u8]),
}
impl Token<'_> {
pub fn name(self, value_context: bool) -> &'static str {
match self {
Token::Dot => "`.`",
Token::Equals => "`=`",
Token::Comma => "`,`",
Token::LeftBracket => "`[`",
Token::RightBracket => "`]`",
Token::LeftBrace => "`{`",
Token::RightBrace => "`}`",
Token::LiteralString(_) | Token::CookedString(_) => "a string",
Token::Literal(_) if value_context => "a literal",
Token::Literal(_) => "a key",
}
}
}
pub struct Lexer<'a> {
input: &'a [u8],
pos: usize,
peek: Option<Spanned<Token<'a>>>,
peek_value_context: bool,
}
impl<'a> Lexer<'a> {
pub fn new(input: &'a [u8]) -> Self {
Self {
input,
pos: 0,
peek: None,
peek_value_context: false,
}
}
pub fn pos(&mut self) -> usize {
self.skip_ws();
self.pos
}
fn skip_ws(&mut self) {
while let Some(char) = self.input.get(self.pos).copied() {
match char {
b' ' | b'\t' | b'\n' => self.pos += 1,
b'#' => {
self.pos += 1;
while let Some(char) = self.input.get(self.pos).copied() {
self.pos += 1;
if char == b'\n' {
break;
}
}
}
_ => break,
}
}
}
pub fn peek(&mut self, value_context: bool) -> Option<Spanned<Token<'a>>> {
let next = self.next(value_context);
self.peek = next;
self.peek_value_context = value_context;
next
}
pub fn next(&mut self, value_context: bool) -> Option<Spanned<Token<'a>>> {
if let Some(peek) = self.peek.take() {
if self.peek_value_context == value_context {
return Some(peek);
}
self.pos = peek.span.lo;
}
use Token::*;
macro_rules! get {
($off:expr) => {
self.input.get(self.pos + $off).copied()
};
}
self.skip_ws();
let c = get!(0)?;
let pos = self.pos;
macro_rules! span {
() => {
Span {
lo: pos,
hi: self.pos,
}
};
}
'simple: {
let t = match c {
b'.' => Dot,
b',' => Comma,
b'=' => Equals,
b'[' => LeftBracket,
b']' => RightBracket,
b'{' => LeftBrace,
b'}' => RightBrace,
_ => break 'simple,
};
self.pos += 1;
return Some(t.spanned(span!()));
}
macro_rules! try_string {
($delim:expr, $escaping:expr, $ident:ident) => {
if c == $delim {
'ml_string: {
let delim = ($delim, Some($delim), Some($delim));
if (c, get!(1), get!(2)) != delim {
break 'ml_string;
}
self.pos += 3;
if get!(0) == Some(b'\n') {
self.pos += 1;
}
let start = self.pos;
let end = loop {
let c = match get!(0) {
Some(c) => c,
_ => break self.pos,
};
self.pos += 1;
if $escaping && c == b'\\' {
self.pos += 1;
} else if c == $delim {
if (c, get!(0), get!(1)) == delim && get!(2) != Some($delim) {
self.pos += 2;
break self.pos - 3;
}
}
};
return Some($ident(&self.input[start..end]).spanned(span!()));
}
self.pos += 1;
let start = self.pos;
let end = loop {
let c = match get!(0) {
Some(c) => c,
_ => break self.pos,
};
self.pos += 1;
if $escaping && c == b'\\' {
self.pos += 1;
} else if c == $delim {
break self.pos - 1;
}
};
return Some($ident(&self.input[start..end]).spanned(span!()));
}
};
}
try_string!(b'\'', false, LiteralString);
try_string!(b'"', true, CookedString);
let start = self.pos;
while let Some(c) = get!(0) {
match c {
b' ' | b'\t' | b'\n' | b'#' | b',' | b'=' | b'{' | b'}' | b'[' | b']' => break,
b'.' if !value_context => break,
_ => {}
}
self.pos += 1;
}
let end = self.pos;
Some(Literal(&self.input[start..end]).spanned(span!()))
}
}

View file

@ -0,0 +1,530 @@
use {
crate::{
toml_lexer::{Lexer, Token},
toml_span::{Span, Spanned, SpannedExt},
toml_value::Value,
},
bstr::ByteSlice,
indexmap::{
IndexMap,
map::{RawEntryApiV1, raw_entry_v1::RawEntryMut},
},
std::{collections::VecDeque, mem, str::FromStr},
thiserror::Error,
};
pub trait ErrorHandler {
fn handle(&self, err: Spanned<ParserError>);
fn redefinition(&self, err: Spanned<ParserError>, prev: Span);
}
#[derive(Debug, Error)]
pub enum ParserError {
#[error("Unexpected end of file")]
UnexpectedEof,
#[error("Expected a key")]
MissingKey,
#[error("Expected {0} but found {1}")]
Expected(&'static str, &'static str),
#[error("Duplicate key overwrites the previous definition")]
Redefined,
#[error("Literal is not valid UTF-8")]
NonUtf8Literal,
#[error("Could not parse the literal")]
UnknownLiteral,
#[error("Ignoring key due to following error")]
IgnoringKey,
#[error("Unnecessary comma")]
UnnecessaryComma,
}
pub fn parse(
input: &[u8],
error_handler: &dyn ErrorHandler,
) -> Result<Spanned<Value>, Spanned<ParserError>> {
let parser = Parser {
lexer: Lexer::new(input),
error_handler,
last_span: None,
};
parser.parse()
}
struct Parser<'a, 'b> {
lexer: Lexer<'a>,
error_handler: &'b dyn ErrorHandler,
last_span: Option<Span>,
}
type Key = VecDeque<Spanned<String>>;
impl<'a> Parser<'a, '_> {
fn parse(mut self) -> Result<Spanned<Value>, Spanned<ParserError>> {
self.parse_document()
}
fn unexpected_eof(&self) -> Spanned<ParserError> {
let span = self.last_span.unwrap_or(Span { lo: 0, hi: 0 });
ParserError::UnexpectedEof.spanned(span)
}
fn next(&mut self, value_context: bool) -> Result<Spanned<Token<'a>>, Spanned<ParserError>> {
match self.lexer.next(value_context) {
Some(t) => {
self.last_span = Some(t.span);
Ok(t)
}
_ => Err(self.unexpected_eof()),
}
}
fn peek(&mut self, value_context: bool) -> Result<Spanned<Token<'a>>, Spanned<ParserError>> {
match self.lexer.peek(value_context) {
Some(t) => Ok(t),
_ => Err(self.unexpected_eof()),
}
}
fn parse_value(&mut self) -> Result<Spanned<Value>, Spanned<ParserError>> {
let token = self.peek(true)?;
match token.value {
Token::LiteralString(s) => self.parse_literal_string(s),
Token::CookedString(s) => self.parse_cooked_string(s),
Token::LeftBracket => self.parse_array(),
Token::Literal(l) => self.parse_literal_value(l),
Token::LeftBrace => self.parse_inline_table(),
Token::Dot | Token::Equals | Token::Comma | Token::RightBrace | Token::RightBracket => {
Err(ParserError::Expected("a value", token.value.name(true)).spanned(token.span))
}
}
}
fn parse_literal_value(
&mut self,
literal: &[u8],
) -> Result<Spanned<Value>, Spanned<ParserError>> {
let span = self.next(true)?.span;
let Ok(s) = std::str::from_utf8(literal) else {
return Err(ParserError::NonUtf8Literal.spanned(span));
};
if s == "true" {
return Ok(Value::Boolean(true).spanned(span));
}
if s == "false" {
return Ok(Value::Boolean(false).spanned(span));
}
let s = s.replace('_', "");
if let Ok(n) = i64::from_str(&s) {
return Ok(Value::Integer(n).spanned(span));
}
'radix: {
let b = s.as_bytes();
if b.len() >= 2 && b[0] == b'0' {
let radix = match b[1] {
b'x' => 16,
b'o' => 8,
b'b' => 2,
_ => break 'radix,
};
if let Ok(n) = i64::from_str_radix(&s[2..], radix) {
return Ok(Value::Integer(n).spanned(span));
}
}
}
if let Ok(n) = f64::from_str(&s) {
return Ok(Value::Float(n).spanned(span));
}
Err(ParserError::UnknownLiteral.spanned(span))
}
fn parse_literal_string(&mut self, s: &[u8]) -> Result<Spanned<Value>, Spanned<ParserError>> {
let span = self.next(true)?.span;
let s = s.as_bstr().to_string();
Ok(Value::String(s).spanned(span))
}
fn parse_cooked_string(&mut self, s: &[u8]) -> Result<Spanned<Value>, Spanned<ParserError>> {
let span = self.next(true)?.span;
let s = self.cook_string(s);
Ok(Value::String(s).spanned(span))
}
fn cook_string(&self, s: &[u8]) -> String {
use std::io::Write;
if !s.contains(&b'\\') {
return s.as_bstr().to_string();
}
let mut res = vec![];
let mut pos = 0;
while pos < s.len() {
let c = s[pos];
pos += 1;
match c {
b'\\' => {
let c = s[pos];
pos += 1;
match c {
b'\\' => res.push(b'\\'),
b'"' => res.push(b'"'),
b'b' => res.push(0x8),
b'f' => res.push(0xc),
b'n' => res.push(b'\n'),
b'r' => res.push(b'\r'),
b't' => res.push(b'\t'),
b'e' => res.push(0x1b),
b'x' | b'u' | b'U' => 'unicode: {
let len = match c {
b'x' => 2,
b'u' => 4,
_ => 8,
};
if s.len() - pos >= len
&& let Ok(s) = std::str::from_utf8(&s[pos..pos + len])
&& let Ok(n) = u32::from_str_radix(s, 16)
&& let Some(c) = char::from_u32(n)
{
pos += len;
let _ = write!(res, "{c}");
break 'unicode;
}
res.extend_from_slice(&s[pos - 2..]);
}
b' ' | b'\t' | b'\n' => {
let mut t = pos;
let mut saw_nl = c == b'\n';
while t < s.len() && matches!(s[t], b' ' | b'\t' | b'\n') {
saw_nl |= s[t] == b'\n';
t += 1;
}
if saw_nl {
pos = t;
} else {
res.extend_from_slice(&[b'\\', c]);
}
}
_ => {
res.extend_from_slice(&[b'\\', c]);
}
}
}
_ => res.push(c),
}
}
res.as_bstr().to_string()
}
fn parse_array(&mut self) -> Result<Spanned<Value>, Spanned<ParserError>> {
let lo = self.next(true)?.span.lo;
let mut entries = vec![];
let mut consumed_comma = false;
loop {
if let Some(v) = self.lexer.peek(true) {
if v.value == Token::RightBracket {
let _ = self.next(true);
let hi = v.span.hi;
let span = Span { lo, hi };
return Ok(Value::Array(entries).spanned(span));
}
if entries.len() > 0 && !mem::take(&mut consumed_comma) {
self.error_handler.handle(
ParserError::Expected("`,` or `]`", v.value.name(true)).spanned(v.span),
);
}
}
match self.parse_value() {
Ok(v) => {
entries.push(v);
consumed_comma = self.skip_comma(true);
}
Err(e) => {
self.skip_tree(Token::LeftBracket, Token::RightBracket);
return Err(e);
}
}
}
}
fn parse_inline_table(&mut self) -> Result<Spanned<Value>, Spanned<ParserError>> {
let lo = self.next(true)?.span.lo;
let mut map = IndexMap::new();
let mut consumed_comma = false;
loop {
let token = match self.peek(false) {
Ok(t) => t,
Err(e) => {
self.error_handler.handle(e);
break;
}
};
if token.value == Token::RightBrace {
let _ = self.next(false);
break;
}
if !map.is_empty() && !mem::take(&mut consumed_comma) {
self.error_handler.handle(
ParserError::Expected("`,` or `}`", token.value.name(false))
.spanned(token.span),
);
}
let res = match self.parse_key_value_with_recovery() {
Ok(res) => res,
Err(e) => {
self.skip_tree(Token::LeftBrace, Token::RightBrace);
return Err(e);
}
};
if let Some((mut key, value)) = res {
self.insert(&mut map, &mut key, value, false, false);
};
consumed_comma = self.skip_comma(false);
}
let hi = self.last_span().hi;
let span = Span { lo, hi };
Ok(Value::Table(map).spanned(span))
}
fn skip_comma(&mut self, value_context: bool) -> bool {
if let Some(token) = self.lexer.peek(value_context) {
if token.value != Token::Comma {
return false;
}
let _ = self.next(value_context);
}
while let Some(token) = self.lexer.peek(value_context) {
if token.value != Token::Comma {
break;
}
let _ = self.next(value_context);
self.error_handler
.handle(ParserError::UnnecessaryComma.spanned(token.span));
}
true
}
fn parse_document(&mut self) -> Result<Spanned<Value>, Spanned<ParserError>> {
let mut map = IndexMap::new();
self.parse_table_body(&mut map)?;
while self.lexer.peek(false).is_some() {
let (mut key, append) = self.parse_table_header()?;
let mut inner_map = IndexMap::new();
self.parse_table_body(&mut inner_map)?;
let value = Value::Table(inner_map).spanned(key.span);
self.insert(&mut map, &mut key.value, value, true, append);
}
let hi = self.last_span().hi;
let span = Span { lo: 0, hi };
Ok(Value::Table(map).spanned(span))
}
fn parse_table_header(&mut self) -> Result<(Spanned<Key>, bool), Spanned<ParserError>> {
let lo = self.next(false)?.span.lo;
let mut append = false;
if let Some(token) = self.lexer.peek(false)
&& token.value == Token::LeftBracket
{
let _ = self.next(false);
append = true;
}
let key = self.parse_key()?;
let mut hi = self.parse_exact(Token::RightBracket, false)?.hi;
if append {
hi = self.parse_exact(Token::RightBracket, false)?.hi;
}
let span = Span { lo, hi };
Ok((key.spanned(span), append))
}
fn parse_table_body(
&mut self,
dst: &mut IndexMap<Spanned<String>, Spanned<Value>>,
) -> Result<(), Spanned<ParserError>> {
while let Some(e) = self.lexer.peek(false) {
if e.value == Token::LeftBracket {
return Ok(());
}
let Some((mut key, value)) = self.parse_key_value_with_recovery()? else {
continue;
};
self.insert(dst, &mut key, value, false, false);
}
Ok(())
}
fn insert(
&self,
dst: &mut IndexMap<Spanned<String>, Spanned<Value>>,
keys: &mut Key,
value: Spanned<Value>,
modify_array_element: bool,
append_last: bool,
) {
let key = keys.pop_front().unwrap();
if keys.is_empty() {
if let RawEntryMut::Occupied(mut old) =
dst.raw_entry_mut_v1().from_key(key.value.as_str())
{
if append_last && let Value::Array(array) = &mut old.get_mut().value {
array.push(value);
return;
}
if let Value::Table(old) = &mut old.get_mut().value
&& let Value::Table(new) = value.value
{
for (k, v) in new {
let mut keys = Key::new();
keys.push_back(k);
self.insert(old, &mut keys, v, false, false);
}
return;
}
self.error_handler
.redefinition(ParserError::Redefined.spanned(key.span), old.key().span);
old.shift_remove();
}
let span = value.span;
let value = match append_last {
true => Value::Array(vec![value]).spanned(span),
false => value,
};
dst.insert(key, value);
} else {
if let RawEntryMut::Occupied(mut o) = dst.raw_entry_mut_v1().from_key(&key) {
match &mut o.get_mut().value {
Value::Table(dst) => {
self.insert(dst, keys, value, modify_array_element, append_last);
return;
}
Value::Array(array) if modify_array_element => {
if let Some(Value::Table(dst)) =
array.last_mut().as_mut().map(|v| &mut v.value)
{
self.insert(dst, keys, value, modify_array_element, append_last);
return;
}
}
_ => {}
}
self.error_handler
.redefinition(ParserError::Redefined.spanned(key.span), o.key().span);
o.shift_remove();
}
let mut map = IndexMap::new();
let span = value.span;
self.insert(&mut map, keys, value, modify_array_element, append_last);
dst.insert(key, Value::Table(map).spanned(span));
}
}
fn parse_key_value_with_recovery(
&mut self,
) -> Result<Option<(Key, Spanned<Value>)>, Spanned<ParserError>> {
let pos = self.lexer.pos();
match self.parse_key_value() {
Ok(kv) => Ok(Some(kv)),
Err((e, key)) => {
if let Some(key) = key {
let span = key.back().unwrap().span;
self.error_handler
.handle(ParserError::IgnoringKey.spanned(span));
}
if self.lexer.pos() == pos {
Err(e)
} else {
self.error_handler.handle(e);
Ok(None)
}
}
}
}
#[expect(clippy::type_complexity)]
fn parse_key_value(
&mut self,
) -> Result<(Key, Spanned<Value>), (Spanned<ParserError>, Option<Key>)> {
let key = self.parse_key();
let eq = self.parse_exact(Token::Equals, true);
let value = self.parse_value();
let key = match key {
Ok(k) => k,
Err(e) => return Err((e, None)),
};
if let Err(e) = eq {
return Err((e, Some(key)));
}
let value = match value {
Ok(v) => v,
Err(e) => return Err((e, Some(key))),
};
Ok((key, value))
}
fn parse_key(&mut self) -> Result<Key, Spanned<ParserError>> {
let mut parts = Key::new();
loop {
if parts.len() > 0 {
if self.parse_exact(Token::Dot, false).is_err() {
break;
}
}
let Some(token) = self.lexer.peek(false) else {
break;
};
let s = match token.value {
Token::LiteralString(s) => s.as_bstr().to_string(),
Token::CookedString(s) => self.cook_string(s),
Token::Literal(l) => l.as_bstr().to_string(),
_ => break,
};
parts.push_back(s.spanned(token.span));
let _ = self.next(false);
}
if parts.is_empty() {
Err(ParserError::MissingKey.spanned(self.next_span()))
} else {
Ok(parts)
}
}
fn parse_exact(
&mut self,
token: Token<'a>,
value_context: bool,
) -> Result<Span, Spanned<ParserError>> {
let actual = match self.peek(value_context) {
Ok(t) if t.value == token => {
let _ = self.next(value_context);
return Ok(t.span);
}
Ok(t) => t.value.name(value_context),
Err(_) => "end of file",
};
let span = self.next_span();
Err(ParserError::Expected(token.name(value_context), actual).spanned(span))
}
fn last_span(&self) -> Span {
self.last_span.unwrap_or(Span { lo: 0, hi: 0 })
}
fn next_span(&mut self) -> Span {
self.lexer.peek(false).map(|v| v.span).unwrap_or_else(|| {
let hi = self.last_span().hi;
Span { lo: hi, hi }
})
}
fn skip_tree(&mut self, start: Token, end: Token) {
let mut depth = 1;
while let Ok(next) = self.next(false) {
if next.value == start {
depth += 1;
} else if next.value == end {
depth -= 1;
if depth == 0 {
return;
}
}
}
}
}

View file

@ -0,0 +1,108 @@
use std::{
borrow::Borrow,
fmt::{Debug, Formatter},
hash::{Hash, Hasher},
};
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Span {
pub lo: usize,
pub hi: usize,
}
impl Debug for Span {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}..{}", self.lo, self.hi)
}
}
#[derive(Copy, Clone, Eq)]
pub struct Spanned<T> {
pub span: Span,
pub value: T,
}
impl<T> Spanned<T> {
pub fn as_ref(&self) -> Spanned<&T> {
Spanned {
span: self.span,
value: &self.value,
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Spanned<U> {
Spanned {
span: self.span,
value: f(self.value),
}
}
pub fn into<U>(self) -> Spanned<U>
where
T: Into<U>,
{
Spanned {
span: self.span,
value: self.value.into(),
}
}
}
impl<T: Debug> Debug for Spanned<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)?;
write!(f, " @ {:?}", self.span)
}
}
impl<T: PartialEq> PartialEq for Spanned<T> {
fn eq(&self, other: &Self) -> bool {
self.value.eq(&other.value)
}
}
impl<T: Hash> Hash for Spanned<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state)
}
}
impl Borrow<str> for Spanned<String> {
fn borrow(&self) -> &str {
&self.value
}
}
pub trait SpannedExt: Sized {
fn spanned(self, span: Span) -> Spanned<Self>;
}
impl<T> SpannedExt for T {
fn spanned(self, span: Span) -> Spanned<Self> {
Spanned { span, value: self }
}
}
pub trait DespanExt: Sized {
type T;
fn despan(self) -> Option<Self::T>;
fn despan_into<U>(self) -> Option<U>
where
Self::T: Into<U>;
}
impl<T> DespanExt for Option<Spanned<T>> {
type T = T;
fn despan(self) -> Option<Self::T> {
self.map(|v| v.value)
}
fn despan_into<U>(self) -> Option<U>
where
Self::T: Into<U>,
{
self.map(|v| v.value.into())
}
}

View file

@ -0,0 +1,63 @@
use {
crate::toml_span::Spanned,
indexmap::IndexMap,
std::{
cmp::Ordering,
fmt::{Debug, Formatter},
},
};
pub enum Value {
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Array(Vec<Spanned<Value>>),
Table(IndexMap<Spanned<String>, Spanned<Value>>),
}
impl Value {
pub fn name(&self) -> &'static str {
match self {
Value::String(_) => "a string",
Value::Integer(_) => "an integer",
Value::Float(_) => "a float",
Value::Boolean(_) => "a boolean",
Value::Array(_) => "an array",
Value::Table(_) => "a table",
}
}
}
impl Debug for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::String(v) => v.fmt(f),
Value::Integer(v) => v.fmt(f),
Value::Float(v) => v.fmt(f),
Value::Boolean(v) => v.fmt(f),
Value::Array(v) => v.fmt(f),
Value::Table(v) => v.fmt(f),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::String(v1), Value::String(v2)) => v1 == v2,
(Value::Integer(v1), Value::Integer(v2)) => v1 == v2,
(Value::Float(v1), Value::Float(v2)) => {
if v1.is_nan() && v2.is_nan() {
true
} else {
v1.total_cmp(v2) == Ordering::Equal
}
}
(Value::Boolean(v1), Value::Boolean(v2)) => v1 == v2,
(Value::Array(v1), Value::Array(v2)) => v1 == v2,
(Value::Table(v1), Value::Table(v2)) => v1 == v2,
_ => false,
}
}
}

View file

@ -0,0 +1,18 @@
use crate::{
toml_span::Span,
toml_value::Value,
value_parser::{ParseResult, Parser},
};
impl Value {
pub fn parse<P: Parser>(&self, span: Span, parser: &mut P) -> ParseResult<P> {
match self {
Value::String(a) => parser.parse_string(span, a),
Value::Integer(a) => parser.parse_integer(span, *a),
Value::Float(a) => parser.parse_float(span, *a),
Value::Boolean(a) => parser.parse_bool(span, *a),
Value::Array(a) => parser.parse_array(span, a),
Value::Table(a) => parser.parse_table(span, a),
}
}
}

View file

@ -0,0 +1,118 @@
use {
crate::{
toml_span::{Span, Spanned, SpannedExt},
toml_value::Value,
},
indexmap::IndexMap,
std::{
error::Error,
fmt::{self, Display, Formatter},
},
};
#[derive(Copy, Clone, Debug)]
pub enum DataType {
String,
Integer,
Float,
Boolean,
Array,
Table,
}
impl Display for DataType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = match self {
DataType::String => "a string",
DataType::Integer => "an integer",
DataType::Float => "a float",
DataType::Boolean => "a bool",
DataType::Array => "an array",
DataType::Table => "a table",
};
f.write_str(s)
}
}
pub struct DataTypes(&'static [DataType]);
impl Display for DataTypes {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let d = self.0;
match d.len() {
0 => Ok(()),
1 => d[0].fmt(f),
2 => write!(f, "{} or {}", d[0], d[1]),
_ => {
let mut first = true;
#[expect(clippy::needless_range_loop)]
for i in 0..d.len() - 1 {
if !first {
f.write_str(", ")?;
}
first = false;
d[i].fmt(f)?;
}
write!(f, ", or {}", d[d.len() - 1])
}
}
}
}
#[derive(Debug)]
pub struct UnexpectedDataType(&'static [DataType], DataType);
impl Display for UnexpectedDataType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Expected {} but found {}", DataTypes(self.0), self.1)
}
}
impl Error for UnexpectedDataType {}
pub type ParseResult<P> = Result<<P as Parser>::Value, Spanned<<P as Parser>::Error>>;
pub trait Parser {
type Value;
type Error: From<UnexpectedDataType>;
const EXPECTED: &'static [DataType];
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
let _ = string;
expected(self, span, DataType::String)
}
fn parse_integer(&mut self, span: Span, integer: i64) -> ParseResult<Self> {
let _ = integer;
expected(self, span, DataType::Integer)
}
fn parse_float(&mut self, span: Span, float: f64) -> ParseResult<Self> {
let _ = float;
expected(self, span, DataType::Float)
}
fn parse_bool(&mut self, span: Span, bool: bool) -> ParseResult<Self> {
let _ = bool;
expected(self, span, DataType::Boolean)
}
fn parse_array(&mut self, span: Span, array: &[Spanned<Value>]) -> ParseResult<Self> {
let _ = array;
expected(self, span, DataType::Array)
}
fn parse_table(
&mut self,
span: Span,
table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> {
let _ = table;
expected(self, span, DataType::Table)
}
}
fn expected<P: Parser + ?Sized>(_p: &P, span: Span, actual: DataType) -> ParseResult<P> {
Err(P::Error::from(UnexpectedDataType(P::EXPECTED, actual)).spanned(span))
}