use ilex::rule::*; use ilex::testing::Matcher; use ilex::token::Content as C; use ilex::Lexeme; #[ilex::spec] struct Llvm { #[rule(";")] comment: Lexeme, #[rule('(', ')')] parens: Lexeme, #[rule('[', ']')] brackets: Lexeme, #[rule('<', '>')] vector: Lexeme, #[rule('{', '}')] braces: Lexeme, #[rule("<{", "}>")] packed: Lexeme, #[rule("!{", "}")] meta: Lexeme, #[rule(',')] comma: Lexeme, #[rule('=')] equal: Lexeme, #[rule('*')] star: Lexeme, #[rule('x')] times: Lexeme, br: Lexeme, call: Lexeme, icmp: Lexeme, #[rule("eq")] icmp_eq: Lexeme, ret: Lexeme, unreachable: Lexeme, constant: Lexeme, declare: Lexeme, define: Lexeme, global: Lexeme, label: Lexeme, null: Lexeme, ptr: Lexeme, #[rule(Digital::new(10).prefix("i"))] int: Lexeme, void: Lexeme, private: Lexeme, unnamed_addr: Lexeme, nocapture: Lexeme, nounwind: Lexeme, #[named] #[rule(Quoted::new('"') .fixed_length_escape(r"\", 2) .prefixes(["", "c"]))] string: Lexeme, #[named("identifier")] #[rule(Ident::new() .ascii_only() .extra_starts(".0123456789".chars()) .suffix(":"))] label_ident: Lexeme, #[named("identifier")] #[rule(Ident::new() .ascii_only() .extra_starts(".0123456789".chars()) .prefixes(["!", "@", "%"]))] bare: Lexeme, #[named("quoted identifier")] #[rule(Quoted::new('"') .fixed_length_escape(r"\", 2) .prefixes(["!", "@", "%"]))] quoted: Lexeme, #[named = "number"] #[rule(Digital::new(10) .minus() .point_limit(0..2) .exponents(["e", "E"], Digits::new(10).plus().minus()))] dec: Lexeme, #[named = "number"] #[rule(Digital::new(16).minus().prefix("0x"))] hex: Lexeme, } #[test] fn llvm() { let text = r#" ; Declare the string constant as a global constant. @.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00" ; External declaration of the puts function declare i32 @"non trivial name"(ptr nocapture) nounwind ; Definition of main function define i32 @main(i32 %0, ptr %1) { ; Call puts function to write out the string to stdout. call i32 @"non trivial name"(ptr @.str) ret i32 0 } ; Named metadata !0 = !{i32 42, null, !"string"} !foo = !{!0} @glb = global i8 0 define void @f(ptr %a) { %c = icmp eq ptr %a, @glb br i1 %c, label %BB_EXIT, label %BB_CONTINUE ; escapes %a BB_EXIT: call void @exit() unreachable BB_CONTINUE: ret void } "#; let llvm = Llvm::get(); let ctx = ilex::Context::new(); let _u = ctx.use_for_debugging_spans(); let report = ctx.new_report(); let tokens = ctx .new_file("test.file", text) .lex(llvm.spec(), &report) .unwrap(); eprintln!("stream: {tokens:#?}"); Matcher::new() .prefix1(llvm.bare, "@", ".str") .comments(["; Declare the string constant as a global constant.\n"]) .then1(llvm.equal, "=") .then1(llvm.private, "private") .then1(llvm.unnamed_addr, "unnamed_addr") .then1(llvm.constant, "constant") .then2( llvm.brackets, ("[", "]"), Matcher::new() .then2(llvm.dec, 10, ["13"]) .then1(llvm.times, "x") .prefix2(llvm.int, "i", 10, ["8"]), ) .prefix2( llvm.string, "c", ('"', '"'), [ C::lit("hello world"), C::esc_with_data(r"\", "0A"), C::esc_with_data(r"\", "00"), ], ) // .then1(llvm.declare, "declare") .comments(["; External declaration of the puts function\n"]) .prefix2(llvm.int, "i", 10, ["32"]) .prefix2(llvm.quoted, "@", ('"', '"'), ["non trivial name"]) .then2( llvm.parens, ("(", ")"), Matcher::new() .then1(llvm.ptr, "ptr") .then1(llvm.nocapture, "nocapture"), ) .then1(llvm.nounwind, "nounwind") // .then1(llvm.define, "define") .comments(["; Definition of main function\n"]) .prefix2(llvm.int, "i", 10, ["32"]) .prefix1(llvm.bare, "@", "main") .then2( llvm.parens, ("(", ")"), Matcher::new() .prefix2(llvm.int, "i", 10, ["32"]) .prefix1(llvm.bare, "%", "0") .then1(llvm.comma, ",") .then1(llvm.ptr, "ptr") .prefix1(llvm.bare, "%", "1"), ) .then2( llvm.braces, ("{", "}"), Matcher::new() .then1(llvm.call, "call") .comments(["; Call puts function to write out the string to stdout.\n"]) .prefix2(llvm.int, "i", 10, ["32"]) .prefix2(llvm.quoted, "@", ('"', '"'), ["non trivial name"]) .then2( llvm.parens, ("(", ")"), Matcher::new() .then1(llvm.ptr, "ptr") .prefix1(llvm.bare, "@", ".str"), ) .then1(llvm.ret, "ret") .prefix2(llvm.int, "i", 10, ["32"]) .then2(llvm.dec, 10, ["0"]), ) // .prefix1(llvm.bare, "!", "0") .comments(["; Named metadata\n"]) .then1(llvm.equal, "=") .then2( llvm.meta, ("!{", "}"), Matcher::new() .prefix2(llvm.dec, "i", 10, ["32"]) .then2(llvm.dec, 10, ["42"]) .then1(llvm.comma, ",") .then1(llvm.null, "null") .then1(llvm.comma, ",") .prefix2(llvm.quoted, "!", ('"', '"'), ["string"]), ) .prefix1(llvm.bare, "!", "foo") .then1(llvm.equal, "=") .then2(llvm.meta, ("!{", "}"), Matcher::new().prefix1(llvm.bare, "!", "0")) // .prefix1(llvm.bare, "@", "glb") .then1(llvm.equal, "=") .then1(llvm.global, "global") .prefix2(llvm.int, "i", 10, ["8"]) .then2(llvm.dec, 10, ["0"]) // .then1(llvm.define, "define") .then1(llvm.void, "void") .prefix1(llvm.bare, "@", "f") .then2( llvm.parens, ("(", ")"), Matcher::new() .then1(llvm.ptr, "ptr") .prefix1(llvm.bare, "%", "a"), ) .then2( llvm.braces, ("{", "}"), Matcher::new() .prefix1(llvm.bare, "%", "c") .then1(llvm.equal, "=") .then1(llvm.icmp, "icmp") .then1(llvm.icmp_eq, "eq") .then1(llvm.ptr, "ptr") .prefix1(llvm.bare, "%", "a") .then1(llvm.comma, ",") .prefix1(llvm.bare, "@", "glb") // .then1(llvm.br, "br") .prefix2(llvm.int, "i", 10, ["1"]) .prefix1(llvm.bare, "%", "c") .then1(llvm.comma, ",") .then1(llvm.label, "label") .prefix1(llvm.bare, "%", "BB_EXIT") .then1(llvm.comma, ",") .then1(llvm.label, "label") .prefix1(llvm.bare, "%", "BB_CONTINUE") // .suffix1(llvm.label_ident, "BB_EXIT", ":") .comments(["; escapes %a\n"]) // .then1(llvm.call, "call") .then1(llvm.void, "void") .prefix1(llvm.bare, "@", "exit") .then2(llvm.parens, ("(", ")"), Matcher::new()) // .then1(llvm.unreachable, "unreachable") // .suffix1(llvm.label_ident, "BB_CONTINUE", ":") .then1(llvm.ret, "ret") .then1(llvm.void, "void"), ) .eof() .assert_matches(&ctx, &tokens) }