use sim_kernel::{Expr, Symbol};
#[derive(Clone, Debug, PartialEq)]
pub enum DomainValue {
Form(DomainForm),
List(Vec<DomainValue>),
String(String),
Atom(String),
}
#[derive(Clone, Debug, PartialEq)]
pub struct DomainForm {
pub name: String,
pub fields: Vec<(String, DomainValue)>,
pub positional: Vec<DomainValue>,
}
impl DomainForm {
pub fn field(&self, key: &str) -> Option<&DomainValue> {
self.fields
.iter()
.find_map(|(name, value)| (name == key).then_some(value))
}
pub fn atom(&self, key: &str) -> Result<&str, DomainFormError> {
match self.field(key) {
Some(DomainValue::Atom(value)) => Ok(value),
Some(_) => Err(DomainFormError::WrongFieldKind(key.to_owned())),
None => Err(DomainFormError::MissingField(key.to_owned())),
}
}
pub fn string(&self, key: &str) -> Result<&str, DomainFormError> {
match self.field(key) {
Some(DomainValue::String(value)) => Ok(value),
Some(_) => Err(DomainFormError::WrongFieldKind(key.to_owned())),
None => Err(DomainFormError::MissingField(key.to_owned())),
}
}
pub fn list(&self, key: &str) -> Result<&[DomainValue], DomainFormError> {
match self.field(key) {
Some(DomainValue::List(items)) => Ok(items),
Some(_) => Err(DomainFormError::WrongFieldKind(key.to_owned())),
None => Err(DomainFormError::MissingField(key.to_owned())),
}
}
pub fn form(&self, key: &str) -> Result<&DomainForm, DomainFormError> {
match self.field(key) {
Some(DomainValue::Form(value)) => Ok(value),
Some(_) => Err(DomainFormError::WrongFieldKind(key.to_owned())),
None => Err(DomainFormError::MissingField(key.to_owned())),
}
}
pub fn field_atom_or_string(&self, name: &str) -> Result<&str, DomainFormError> {
match self.field(name) {
Some(DomainValue::Atom(value) | DomainValue::String(value)) => Ok(value),
Some(_) => Err(DomainFormError::WrongFieldKind(name.to_owned())),
None => Err(DomainFormError::MissingField(name.to_owned())),
}
}
pub fn field_text(&self, name: &str) -> Result<String, DomainFormError> {
match self.field(name) {
Some(value) => Ok(value.render_text()),
None => Err(DomainFormError::MissingField(name.to_owned())),
}
}
pub fn to_expr_map(&self) -> Expr {
let mut entries = vec![(
Expr::Symbol(Symbol::new("form")),
Expr::String(self.name.clone()),
)];
if !self.positional.is_empty() {
entries.push((
Expr::Symbol(Symbol::new("args")),
Expr::List(self.positional.iter().map(DomainValue::to_expr).collect()),
));
}
entries.extend(
self.fields
.iter()
.map(|(key, value)| (Expr::Symbol(Symbol::new(key.clone())), value.to_expr())),
);
Expr::Map(entries)
}
}
impl DomainValue {
pub fn as_form(&self) -> Result<&DomainForm, DomainFormError> {
match self {
DomainValue::Form(form) => Ok(form),
_ => Err(DomainFormError::WrongValueKind),
}
}
pub fn atom_or_string(&self) -> Result<&str, DomainFormError> {
match self {
DomainValue::Atom(value) | DomainValue::String(value) => Ok(value),
_ => Err(DomainFormError::WrongValueKind),
}
}
pub fn render_text(&self) -> String {
match self {
DomainValue::Form(form) => format_domain_form(form),
DomainValue::List(items) => format!(
"[{}]",
items
.iter()
.map(DomainValue::render_text)
.collect::<Vec<_>>()
.join(",")
),
DomainValue::String(value) => {
format!("\"{}\"", value.replace('\\', "\\\\").replace('"', "\\\""))
}
DomainValue::Atom(value) => value.clone(),
}
}
pub fn to_expr(&self) -> Expr {
match self {
DomainValue::Form(form) => form.to_expr_map(),
DomainValue::List(items) => {
Expr::List(items.iter().map(DomainValue::to_expr).collect())
}
DomainValue::String(value) | DomainValue::Atom(value) => Expr::String(value.clone()),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DomainFormError {
ExpectedForm,
UnexpectedEof,
InvalidToken,
DuplicateField(String),
TrailingInput,
MissingField(String),
WrongFieldKind(String),
WrongValueKind,
}
pub fn parse_domain_form(input: &str) -> Result<DomainForm, DomainFormError> {
let mut parser = Parser { input, index: 0 };
parser.skip_ws();
if !parser.consume_str("#(") {
return Err(DomainFormError::ExpectedForm);
}
let form = parser.parse_form_body()?;
parser.skip_ws();
if parser.index != parser.input.len() {
return Err(DomainFormError::TrailingInput);
}
Ok(form)
}
pub fn format_domain_form(form: &DomainForm) -> String {
let mut out = String::from("#(");
out.push_str(&form.name);
for value in &form.positional {
out.push(' ');
format_value(value, &mut out);
}
for (key, value) in &form.fields {
out.push(' ');
out.push_str(key);
out.push('=');
format_value(value, &mut out);
}
out.push(')');
out
}
fn format_value(value: &DomainValue, out: &mut String) {
match value {
DomainValue::Form(form) => out.push_str(&format_domain_form(form)),
DomainValue::List(items) => {
out.push('[');
for (index, item) in items.iter().enumerate() {
if index > 0 {
out.push(',');
}
format_value(item, out);
}
out.push(']');
}
DomainValue::String(text) => {
out.push('"');
for ch in text.chars() {
if ch == '\\' || ch == '"' {
out.push('\\');
}
out.push(ch);
}
out.push('"');
}
DomainValue::Atom(text) => out.push_str(text),
}
}
struct Parser<'a> {
input: &'a str,
index: usize,
}
impl Parser<'_> {
fn parse_form_body(&mut self) -> Result<DomainForm, DomainFormError> {
let name = self.parse_ident()?;
let mut fields: Vec<(String, DomainValue)> = Vec::new();
let mut positional = Vec::new();
loop {
self.skip_ws();
if self.consume_char(')') {
break;
}
match self.peek_char() {
Some('#') | Some('[') | Some('"') => positional.push(self.parse_value()?),
_ => {
let atom = self.parse_atom()?;
if self.consume_char('=') {
if fields.iter().any(|(key, _)| key == &atom) {
return Err(DomainFormError::DuplicateField(atom));
}
fields.push((atom, self.parse_value()?));
} else {
positional.push(DomainValue::Atom(atom));
}
}
}
}
Ok(DomainForm {
name,
fields,
positional,
})
}
fn parse_value(&mut self) -> Result<DomainValue, DomainFormError> {
self.skip_ws();
if self.consume_str("#(") {
return self.parse_form_body().map(DomainValue::Form);
}
if self.consume_char('[') {
return self.parse_list();
}
if self.peek_char() == Some('"') {
return self.parse_string().map(DomainValue::String);
}
self.parse_atom().map(DomainValue::Atom)
}
fn parse_list(&mut self) -> Result<DomainValue, DomainFormError> {
let mut items = Vec::new();
loop {
self.skip_ws();
if self.consume_char(']') {
break;
}
items.push(self.parse_value()?);
self.skip_ws();
if self.consume_char(',') {
continue;
}
self.expect_char(']')?;
break;
}
Ok(DomainValue::List(items))
}
fn parse_string(&mut self) -> Result<String, DomainFormError> {
self.expect_char('"')?;
let mut out = String::new();
while let Some(ch) = self.next_char() {
match ch {
'"' => return Ok(out),
'\\' => out.push(self.next_char().ok_or(DomainFormError::UnexpectedEof)?),
other => out.push(other),
}
}
Err(DomainFormError::UnexpectedEof)
}
fn parse_atom(&mut self) -> Result<String, DomainFormError> {
let start = self.index;
while let Some(ch) = self.peek_char() {
if ch.is_whitespace() || [',', ')', ']', '='].contains(&ch) {
break;
}
self.index += ch.len_utf8();
}
if self.index == start {
return Err(DomainFormError::UnexpectedEof);
}
Ok(self.input[start..self.index].to_owned())
}
fn parse_ident(&mut self) -> Result<String, DomainFormError> {
let atom = self.parse_atom()?;
if atom
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || ch == '_' || ch == '-')
{
Ok(atom)
} else {
Err(DomainFormError::InvalidToken)
}
}
fn expect_char(&mut self, expected: char) -> Result<(), DomainFormError> {
match self.next_char() {
Some(ch) if ch == expected => Ok(()),
Some(_) => Err(DomainFormError::InvalidToken),
None => Err(DomainFormError::UnexpectedEof),
}
}
fn consume_char(&mut self, expected: char) -> bool {
if self.peek_char() == Some(expected) {
self.index += expected.len_utf8();
true
} else {
false
}
}
fn consume_str(&mut self, expected: &str) -> bool {
if self.input[self.index..].starts_with(expected) {
self.index += expected.len();
true
} else {
false
}
}
fn skip_ws(&mut self) {
while let Some(ch) = self.peek_char() {
if ch.is_whitespace() {
self.index += ch.len_utf8();
} else {
break;
}
}
}
fn peek_char(&self) -> Option<char> {
self.input[self.index..].chars().next()
}
fn next_char(&mut self) -> Option<char> {
let ch = self.peek_char()?;
self.index += ch.len_utf8();
Some(ch)
}
}