thalir_parser/
lib.rs

1/*! Parse text IR into structured data.
2 *
3 * Round-tripping IR through text files enables version control, tool interop, and transformation
4 * validation. This parser reads IR back into memory so you can analyze it, transform it, or verify
5 * it matches expectations.
6 */
7
8#![allow(unreachable_patterns)]
9
10use pest::Parser;
11use pest_derive::Parser;
12use std::path::Path;
13
14pub mod annotations;
15
16#[derive(Parser)]
17#[grammar = "grammar.pest"]
18pub struct ThalirParser;
19
20pub type ParseResult<T> = Result<T, Box<pest::error::Error<Rule>>>;
21
22pub fn parse(input: &str) -> ParseResult<pest::iterators::Pairs<'_, Rule>> {
23    ThalirParser::parse(Rule::module, input).map_err(|e| Box::new(e))
24}
25
26pub fn parse_file<P: AsRef<Path>>(path: P) -> ParseResult<String> {
27    std::fs::read_to_string(path).map_err(|e| {
28        Box::new(pest::error::Error::new_from_pos(
29            pest::error::ErrorVariant::CustomError {
30                message: format!("Failed to read file: {}", e),
31            },
32            pest::Position::from_start(""),
33        ))
34    })
35}
36
37pub fn check(input: &str) -> bool {
38    parse(input).is_ok()
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn test_empty_module() {
47        let input = "";
48        assert!(check(input));
49    }
50
51    #[test]
52    fn test_simple_function() {
53        let input = r"
54function %test(i32, i32) -> i32 {
55block0(v0: i32, v1: i32):
56    v2 = iadd v0, v1
57    return v2
58}
59";
60        assert!(check(input));
61    }
62
63    #[test]
64    fn test_function_with_entities() {
65        let input = r"
66function %f(i64, i32) -> i32 {
67    gv0 = vmctx
68    gv1 = load.i64 notrap readonly aligned gv0+8
69    fn0 = %g(i64)
70
71block0(v0: i64, v1: i32):
72    v2 = global_value.i64 gv1
73    v3 = load.i32 v2+8
74    return v3
75}
76";
77        match parse(input) {
78            Ok(_) => {}
79            Err(e) => panic!("Parse error: {}", e),
80        }
81    }
82
83    #[test]
84    #[ignore]
85    fn test_branch_with_block_args() {
86        let input = r"
87function %f(i32, i32) -> i32 {
88block0(v0: i32, v1: i32):
89    v2 = iadd v0, v1
90    brif v2, block1(v0), block2(v1)
91
92block1(v3: i32):
93    return v3
94
95block2(v4: i32):
96    return v4
97}
98";
99        match parse(input) {
100            Ok(_) => {}
101            Err(e) => panic!("Parse error: {}", e),
102        }
103    }
104
105    #[test]
106    fn test_test_directives() {
107        let input = r"
108test optimize
109set opt_level=speed
110target x86_64
111
112function %f(i32) -> i32 {
113block0(v0: i32):
114    return v0
115}
116";
117        assert!(check(input));
118    }
119
120    #[test]
121    #[ignore]
122    fn test_floats_and_vectors() {
123        let input = r"
124function %f(f32x4, f64) -> f32x4 {
125block0(v0: f32x4, v1: f64):
126    v2 = fconst.f32 0x1.5p-3
127    v3 = fconst.f64 3.14159
128    v4 = fconst.f64 NaN
129    v5 = fconst.f64 Inf
130    return v0
131}
132";
133        assert!(check(input));
134    }
135}