pub mod dialect;
pub mod ir;
pub mod lexer;
pub mod units;
use codehelion_core::discovery::Language;
use codehelion_core::frontend::{Frontend, LexedFile};
pub const C_FAMILY_LEXER_VERSION: &str = "c-family-lexer-v1";
pub const FRONTEND_VERSION: &str = "c-lexer-v1+c-family-lexer-v1";
#[derive(Debug, Clone, Copy, Default)]
pub struct CFrontend;
impl Frontend for CFrontend {
fn language(&self) -> Language {
Language::C
}
fn frontend_version(&self) -> &'static str {
FRONTEND_VERSION
}
fn lex(&self, source: &str) -> LexedFile {
let (tokens, mut diagnostics) = lexer::lex(source, &dialect::C);
let (units, unit_diagnostics) = units::detect(&tokens, &dialect::C);
diagnostics.extend(unit_diagnostics);
LexedFile {
language: Language::C,
frontend_version: FRONTEND_VERSION,
tokens,
units,
diagnostics,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use codehelion_core::ir::StructuralFrontend;
use proptest::prelude::*;
use std::time::{Duration, Instant};
#[test]
fn frontend_reports_language_and_version() {
let frontend = CFrontend;
assert_eq!(frontend.language(), Language::C);
assert_eq!(frontend.frontend_version(), FRONTEND_VERSION);
assert!(FRONTEND_VERSION.ends_with(C_FAMILY_LEXER_VERSION));
}
#[test]
fn lexing_a_file_yields_tokens_units_and_the_version() {
let lexed = CFrontend.lex("int main(void) { return 0; }");
assert_eq!(lexed.language, Language::C);
assert_eq!(lexed.frontend_version, FRONTEND_VERSION);
assert!(!lexed.tokens.is_empty());
assert_eq!(lexed.units.len(), 1);
assert!(lexed.diagnostics.is_empty());
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(64))]
#[test]
fn arbitrary_text_never_panics(source in proptest::collection::vec(any::<char>(), 0..1024)
.prop_map(|characters| characters.into_iter().collect::<String>())) {
let started = Instant::now();
let _ = CFrontend.lex(&source);
let _ = ir::CStructuralFrontend.parse(&source);
prop_assert!(
started.elapsed() < Duration::from_secs(1),
"a bounded frontend input took too long"
);
}
}
}