yanais/rust/crates/yanais-syntax/src/record.rs
2023-11-03 20:22:47 +01:00

69 lines
2.3 KiB
Rust

/*
* SPDX-FileCopyrightText: 2023 Alain Zscheile <fogti+devel@ytrizja.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
use std::sync::Arc;
use crate::{
lex::{Token, TokenKind as Tok},
none_up, Env as ParseEnv, Error as Perr, ErrorCtx as PeCtx, ErrorKind as Pek, EvEqSourceSpan,
MaybeParse, Parse, Result as Pres,
};
#[derive(Clone, Debug, PartialEq)]
pub struct Record<V>(pub Vec<(EvEqSourceSpan, Option<Arc<str>>, V)>);
impl<V: Parse> MaybeParse for Record<V> {
const DFL_CTX: PeCtx = PeCtx::Record;
fn maybe_parse(env: &mut ParseEnv<'_>) -> Pres<Option<Self>> {
none_up!(env.lxr.got(Tok::LBrace));
let mut fields: Vec<(_, Option<Arc<str>>, _)> = Vec::new();
// warning: this code executes in O(|fields|²)
loop {
let span_start;
let name = {
let mut lxrnxt = env.lxr.clone();
let Token { kind, span } = match lxrnxt.next_in_noeof(PeCtx::Record) {
Err(e) => {
env.lxr = lxrnxt;
return Err(e);
}
Ok(x) => x,
};
span_start = span.offset();
match kind {
Tok::DotIdent(i) => {
if env.lxr.expect(Tok::Assign, PeCtx::Record).is_ok() {
if fields.iter().any(|(_, j, _)| j.as_ref() == Some(&i)) {
return Err(Perr {
span,
kind: Pek::RecordDupIdent(i),
});
}
// "fronttrack"
env.lxr = lxrnxt;
Some(i)
} else {
None
}
}
Tok::RBrace => break,
_ => None,
}
};
let expr = V::parse(env)?;
let span_end = env.lxr.offset();
env.lxr.expect(Tok::SemiColon, PeCtx::Record)?;
fields.push(((span_start..span_end).into(), name, expr));
}
env.lxr.expect(Tok::RBrace, PeCtx::Record)?;
Ok(Some(Record(fields)))
}
}