use core::convert::Infallible;
use macroonz_compiler::token::{
CaptureBound, CaptureBuildRefusal as HomeCaptureBuildRefusal,
CaptureBuilder as HomeCaptureBuilder, CapturedAtom, CapturedDelimiter, CapturedInput,
GeneratedDelimiter, GeneratedLiteral, GeneratedToken as HomeGeneratedToken,
GeneratedTree as HomeGeneratedTree, LiteralReadCause, TextLexicalCause, rust_keyword,
};
use macroonz_compiler::{
CaptureBuildRefusal, CaptureBuilder, GeneratedToken, GeneratedTree, encode_bytes,
};
fn token_bytes(slot: u8, material: &[u8], into: &mut Vec<u8>) {
into.push(slot);
encode_bytes(material, into);
}
#[test]
fn rust_keywords_are_one_exact_language_roster() {
let keywords = [
"abstract", "as", "async", "await", "become", "box", "break", "const", "continue", "crate",
"do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "gen", "if", "impl",
"in", "let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub",
"ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "try",
"type", "typeof", "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
];
assert!(keywords.into_iter().all(rust_keyword));
assert!(
[
"",
"_",
"State",
"async_task",
"selfish",
"yielded",
"r#type"
]
.into_iter()
.all(|spelling| !rust_keyword(spelling))
);
}
#[test]
fn capture_vocabulary_names_are_exact() {
let lexical = [
(
TextLexicalCause::BlockCommentNotTerminated,
"block-comment-not-terminated",
),
(TextLexicalCause::InvalidIdentifier, "invalid-identifier"),
(TextLexicalCause::UnknownPrefix, "unknown-prefix"),
(
TextLexicalCause::UnknownLifetimePrefix,
"unknown-lifetime-prefix",
),
(
TextLexicalCause::GuardedStringPrefix,
"guarded-string-prefix",
),
(TextLexicalCause::MalformedLiteral, "malformed-literal"),
(
TextLexicalCause::LifetimeStartsWithNumber,
"lifetime-starts-with-number",
),
(TextLexicalCause::Frontmatter, "frontmatter"),
(TextLexicalCause::UnknownToken, "unknown-token"),
];
let bounds = [
(CaptureBound::Depth, "depth"),
(CaptureBound::Level, "level"),
(CaptureBound::Tree, "tree"),
(CaptureBound::Work, "work"),
];
let literals = [
(LiteralReadCause::NotAKnownForm, "not-a-known-form"),
(LiteralReadCause::NotReadable, "not-readable"),
];
assert_eq!(TextLexicalCause::ALL, lexical.map(|(row, _name)| row));
assert_eq!(CaptureBound::ALL, bounds.map(|(row, _name)| row));
assert_eq!(LiteralReadCause::ALL, literals.map(|(row, _name)| row));
assert!(lexical.into_iter().all(|(row, name)| row.name() == name));
assert!(bounds.into_iter().all(|(row, name)| row.name() == name));
assert!(literals.into_iter().all(|(row, name)| row.name() == name));
}
fn captured(offset: u64) -> Option<CapturedInput> {
let mut builder = CaptureBuilder::declared();
let level = builder.open();
let level = level
.atom(offset, |_| {
Ok::<_, Infallible>(CapturedAtom::Word(String::from("w")))
})
.ok()?;
let level = level
.atom(offset.saturating_add(1), |_| {
Ok::<_, Infallible>(CapturedAtom::Punct(':'))
})
.ok()?;
let level = level
.atom(offset.saturating_add(2), |_| {
Ok::<_, Infallible>(CapturedAtom::Text(String::from("t")))
})
.ok()?;
let level = level
.atom(offset.saturating_add(3), |_| {
Ok::<_, Infallible>(CapturedAtom::Number(String::from("01")))
})
.ok()?;
let level = empty_group(
level,
offset.saturating_add(4),
CapturedDelimiter::Parenthesis,
)?;
let level = empty_group(level, offset.saturating_add(5), CapturedDelimiter::Brace)?;
let level = empty_group(level, offset.saturating_add(6), CapturedDelimiter::Bracket)?;
let level = empty_group(level, offset.saturating_add(7), CapturedDelimiter::Bare)?;
let level = level
.atom(offset.saturating_add(8), |_| {
Ok::<_, Infallible>(CapturedAtom::ByteText(vec![0, 0xff]))
})
.ok()?;
let level = level
.atom(offset.saturating_add(9), |_| {
Ok::<_, Infallible>(CapturedAtom::Character('é'))
})
.ok()?;
let level = level
.atom(offset.saturating_add(10), |_| {
Ok::<_, Infallible>(CapturedAtom::Byte(0xff))
})
.ok()?;
let level = level
.atom(offset.saturating_add(11), |_| {
Ok::<_, Infallible>(CapturedAtom::NulTerminatedText(vec![b'c']))
})
.ok()?;
let level = level
.atom(offset.saturating_add(12), |_| {
Ok::<_, Infallible>(CapturedAtom::RawIdentifier(String::from("type")))
})
.ok()?;
let level = level
.atom(offset.saturating_add(13), |_| {
Ok::<_, Infallible>(CapturedAtom::JointPunct('+'))
})
.ok()?;
Some(level.finish())
}
fn empty_group(
level: macroonz_compiler::CaptureLevel<'_, u64>,
position: u64,
delimiter: CapturedDelimiter,
) -> Option<macroonz_compiler::CaptureLevel<'_, u64>> {
level
.group(position, delimiter, |_span, inner| {
Ok::<_, CaptureBuildRefusal<u64, Infallible>>(inner)
})
.ok()
}
fn captured_receipt() -> Vec<u8> {
let mut bytes = Vec::new();
token_bytes(1, b"w", &mut bytes);
token_bytes(2, b":", &mut bytes);
token_bytes(3, b"t", &mut bytes);
token_bytes(4, b"01", &mut bytes);
for delimiter in 0u8..=3u8 {
bytes.extend_from_slice(&[5, delimiter]);
bytes.extend_from_slice(&0u64.to_be_bytes());
}
token_bytes(6, &[0, 0xff], &mut bytes);
token_bytes(7, "é".as_bytes(), &mut bytes);
bytes.extend_from_slice(&[8, 0xff]);
token_bytes(9, b"c", &mut bytes);
token_bytes(10, b"type", &mut bytes);
token_bytes(11, b"+", &mut bytes);
bytes
}
fn generated() -> Option<GeneratedTree> {
let tokens = vec![
GeneratedToken::word("word"),
GeneratedToken::joint(':'),
GeneratedToken::alone(':'),
GeneratedToken::text("a\"\\"),
GeneratedToken::group(GeneratedDelimiter::Parenthesis, Vec::new()).ok()?,
GeneratedToken::group(GeneratedDelimiter::Brace, Vec::new()).ok()?,
GeneratedToken::group(GeneratedDelimiter::Bracket, Vec::new()).ok()?,
GeneratedToken::group(
GeneratedDelimiter::Bare,
vec![GeneratedToken::word("inside")],
)
.ok()?,
GeneratedToken::byte_text(&[0, 0xff]),
GeneratedToken::number(0x0102_0304_0506_0708),
GeneratedToken::raw_identifier("type"),
GeneratedToken::literal(GeneratedLiteral::number("0xFFu8").ok()?),
GeneratedToken::literal(GeneratedLiteral::character('é')),
GeneratedToken::literal(GeneratedLiteral::byte(0xff)),
GeneratedToken::literal(GeneratedLiteral::nul_terminated_text(b"ab").ok()?),
];
GeneratedTree::assembled(tokens).ok()
}
fn generated_receipt() -> Vec<u8> {
let mut bytes = Vec::new();
token_bytes(1, b"word", &mut bytes);
bytes.extend_from_slice(&[2, 0]);
bytes.extend_from_slice(&1u64.to_be_bytes());
bytes.push(b':');
bytes.extend_from_slice(&[2, 1]);
bytes.extend_from_slice(&1u64.to_be_bytes());
bytes.push(b':');
token_bytes(3, b"a\"\\", &mut bytes);
for delimiter in 0u8..=2u8 {
bytes.extend_from_slice(&[4, delimiter]);
bytes.extend_from_slice(&0u64.to_be_bytes());
}
bytes.extend_from_slice(&[4, 3]);
bytes.extend_from_slice(&1u64.to_be_bytes());
token_bytes(1, b"inside", &mut bytes);
token_bytes(5, &[0, 0xff], &mut bytes);
bytes.push(6);
bytes.extend_from_slice(&0x0102_0304_0506_0708u64.to_be_bytes());
token_bytes(7, b"type", &mut bytes);
bytes.push(8);
token_bytes(0, b"0xFFu8", &mut bytes);
bytes.push(8);
token_bytes(1, "é".as_bytes(), &mut bytes);
bytes.extend_from_slice(&[8, 2, 0xff]);
bytes.push(8);
token_bytes(3, b"ab", &mut bytes);
bytes
}
#[test]
fn the_root_and_home_paths_name_one_public_surface() {
let root_token = GeneratedToken::word("same");
let home_token: HomeGeneratedToken = root_token;
let root_tree = GeneratedTree::assembled(vec![home_token]);
let home_tree: Result<HomeGeneratedTree, _> = root_tree;
let root_builder: CaptureBuilder<u64> = HomeCaptureBuilder::declared();
let home_builder: HomeCaptureBuilder<u64> = root_builder;
let refusal: Option<HomeCaptureBuildRefusal<u64, Infallible>> = None;
assert!(home_tree.is_ok());
assert!(home_builder.positions().is_empty());
assert!(refusal.is_none());
}
#[test]
fn captured_slots_are_exact_and_producer_coordinates_stay_out() -> Result<(), ()> {
let first = captured(10).ok_or(())?;
let moved = captured(1_000).ok_or(())?;
let expected = captured_receipt();
assert_eq!(first.canonical_bytes(), expected);
assert_eq!(moved.canonical_bytes(), expected);
assert_eq!(first.issued(), 14usize);
assert_eq!(moved.issued(), 14usize);
Ok(())
}
#[test]
fn generated_slots_and_readable_spelling_are_exact() -> Result<(), ()> {
let tree = generated().ok_or(())?;
assert_eq!(tree.canonical_bytes(), generated_receipt());
assert_eq!(
tree.inspected(),
r#"word :: "a\"\\" ( ) { } [ ] inside b"\x00\xFF" 72623859790382856 r#type 0xFFu8 '\u{e9}' b'\xFF' c"ab" "#
);
Ok(())
}
#[test]
fn a_keyword_cannot_name_a_rendered_item() {
assert!(macroonz_compiler::descriptor::ModuleName::declared("type").is_err());
assert!(macroonz_compiler::descriptor::SupportName::declared("mod").is_err());
assert!(macroonz_compiler::descriptor::TypeName::declared("gen").is_err());
assert!(macroonz_compiler::descriptor::FunctionName::declared("fn").is_err());
assert!(macroonz_compiler::descriptor::ModuleName::declared("lawful_name").is_ok());
assert!(macroonz_compiler::stamp::StampName::declared("type").is_err());
assert!(macroonz_compiler::support::SupportName::declared("loop").is_err());
assert!(macroonz_compiler::codec::ModuleSpelling::spelled("type").is_err());
assert!(macroonz_compiler::stamp::SiteRoot::spelled(vec!["crate".to_owned()]).is_ok());
assert!(
macroonz_compiler::stamp::SiteRoot::spelled(vec!["crate".to_owned(), "type".to_owned()])
.is_err()
);
assert!(
macroonz_compiler::stamp::SiteRoot::spelled(vec![
"super".to_owned(),
"super".to_owned(),
"stamps".to_owned(),
])
.is_ok()
);
assert!(
macroonz_compiler::stamp::SiteRoot::spelled(vec!["stamps".to_owned(), "self".to_owned()])
.is_err()
);
assert!(
macroonz_compiler::codec::CodecTypePath::spelled(
macroonz_compiler::codec::PathRooting::ParentScoped,
vec!["Thing".to_owned()],
)
.is_ok()
);
assert!(
macroonz_compiler::codec::CodecTypePath::spelled(
macroonz_compiler::codec::PathRooting::InScope,
vec!["self".to_owned(), "Thing".to_owned()],
)
.is_err()
);
}
#[test]
fn programmatic_item_names_share_one_identifier_alphabet() {
for invalid in ["7thing", "thing-name"] {
assert!(macroonz_compiler::descriptor::ModuleName::declared(invalid).is_err());
assert!(macroonz_compiler::stamp::StampName::declared(invalid).is_err());
assert!(macroonz_compiler::codec::ModuleSpelling::spelled(invalid).is_err());
}
for lawful in ["Thing", "_thing", "thing_7"] {
assert!(macroonz_compiler::descriptor::ModuleName::declared(lawful).is_ok());
assert!(macroonz_compiler::stamp::StampName::declared(lawful).is_ok());
assert!(macroonz_compiler::codec::ModuleSpelling::spelled(lawful).is_ok());
}
}