use crate::{
Result,
ast::{
ast::{Ast, AstInline, AstInlineKeyedValue, AstSumTypeConstructor},
parse::{Parser, Precedence},
},
bump::BumpBox,
token::{
operator::{
Operator,
Operator::{CloseCurly, Colon},
},
separator::Separator::Comma,
token::TokenKind,
},
};
impl<'bump> Parser<'bump> {
pub(crate) fn parse_inline(&mut self) -> Result<AstInline<'bump>> {
let token = self.consume_operator(Operator::OpenCurly)?;
let mut keyed_values = Vec::with_capacity(4);
loop {
self.skip_new_line()?;
if self.current()?.is_operator(CloseCurly) {
break;
}
let key = self.parse_identifier_with_hyphens()?;
self.consume_operator(Colon)?;
let mut value_ast = self.parse_node(Precedence::None)?;
if let Ast::Identifier(ref ident) = value_ast
&& !self.is_eof() && self.current()?.is_operator(Operator::OpenCurly)
{
let token = ident.token;
let variant_name = ident.token.fragment;
let columns = self.parse_inline()?;
value_ast = Ast::SumTypeConstructor(AstSumTypeConstructor {
token,
namespace: variant_name,
sumtype_name: variant_name,
variant_name,
columns,
});
}
let value = BumpBox::new_in(value_ast, self.bump());
keyed_values.push(AstInlineKeyedValue {
key,
value,
});
self.skip_new_line()?;
self.consume_if(TokenKind::Separator(Comma))?;
}
self.consume_operator(CloseCurly)?;
Ok(AstInline {
token,
keyed_values,
})
}
}
#[cfg(test)]
pub mod tests {
use crate::{
ast::{
ast::{
Ast::{Identifier, Literal},
AstLiteral::{Number, Text},
},
parse::parse,
},
bump::Bump,
token::tokenize,
};
#[test]
fn test_empty_inline() {
let bump = Bump::new();
let source = "{}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 0);
}
#[test]
fn test_single_keyed_value() {
let bump = Bump::new();
let source = "{id: 1}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 1);
let keyed_value = &inline[0];
assert_eq!(keyed_value.key.text(), "id");
let Literal(Number(value)) = keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(value.value(), "1");
}
#[test]
fn test_keyword() {
let bump = Bump::new();
let source = "{value: 1}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 1);
let keyed_value = &inline[0];
assert_eq!(keyed_value.key.text(), "value");
let Literal(Number(value)) = keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(value.value(), "1");
}
#[test]
fn test_text() {
let bump = Bump::new();
let source = r#"{text: 'Ada'}"#;
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 1);
let keyed_value = &inline[0];
assert_eq!(keyed_value.key.text(), "text");
let Literal(Text(value)) = keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(value.value(), "Ada");
}
#[test]
fn test_multiple_keyed_values() {
let bump = Bump::new();
let source = r#"{id: 1, name: 'Ada'}"#;
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 2);
let id_keyed_value = &inline[0];
assert_eq!(id_keyed_value.key.text(), "id");
let Literal(Number(value)) = id_keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(value.value(), "1");
let name_keyed_value = &inline[1];
assert_eq!(name_keyed_value.key.text(), "name");
let Literal(Text(value)) = name_keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(value.value(), "Ada");
}
#[test]
fn test_identifier_value() {
let bump = Bump::new();
let source = "{keyed_value: someVariable}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 1);
let keyed_value = &inline[0];
assert_eq!(keyed_value.key.text(), "keyed_value");
let Identifier(identifier) = keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(identifier.text(), "someVariable");
}
#[test]
fn test_multiline_inline() {
let bump = Bump::new();
let source = r#"{
id: 42,
name: 'Database',
active: true
}"#;
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 3);
let id_keyed_value = &inline[0];
assert_eq!(id_keyed_value.key.text(), "id");
let Literal(Number(value)) = id_keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(value.value(), "42");
let name_keyed_value = &inline[1];
assert_eq!(name_keyed_value.key.text(), "name");
let Literal(Text(value)) = name_keyed_value.value.as_ref() else {
panic!()
};
assert_eq!(value.value(), "Database");
let active_keyed_value = &inline[2];
assert_eq!(active_keyed_value.key.text(), "active");
assert!(active_keyed_value.value.is_literal_boolean());
}
#[test]
fn test_trailing_comma() {
let bump = Bump::new();
let source = "{id: 1, name: 'Test'}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 2);
let id_keyed_value = &inline[0];
assert_eq!(id_keyed_value.key.text(), "id");
let name_keyed_value = &inline[1];
assert_eq!(name_keyed_value.key.text(), "name");
}
#[test]
fn test_comptokenize_values() {
let bump = Bump::new();
let source = "{result: (1 + 2), enabled: !false}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 2);
let result_keyed_value = &inline[0];
assert_eq!(result_keyed_value.key.text(), "result");
assert!(result_keyed_value.value.is_tuple());
let enabled_keyed_value = &inline[1];
assert_eq!(enabled_keyed_value.key.text(), "enabled");
assert!(enabled_keyed_value.value.is_prefix());
}
#[test]
fn test_nested_inline() {
let bump = Bump::new();
let source = "{user: {id: 1, name: 'John'}}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let inline = result[0].first_unchecked().as_block();
assert_eq!(inline.len(), 1);
let user_keyed_value = &inline[0];
assert_eq!(user_keyed_value.key.text(), "user");
assert!(user_keyed_value.value.is_block());
let nested_inline = user_keyed_value.value.as_block();
assert_eq!(nested_inline.len(), 2);
assert_eq!(nested_inline[0].key.text(), "id");
assert_eq!(nested_inline[1].key.text(), "name");
}
}