Skip to main content

lex_file/
lex_file.rs

1//! Lex an LLVM IR `.ll` file from disk and print every token with its
2//! source location. The recommended I/O boundary pattern: read once into a
3//! `Vec<u8>`, hand the slice to the lexer, drive `next_token` to EOF.
4//!
5//! Usage:
6//!     cargo run -p llvmkit-asmparser --example lex_file -- path/to/file.ll
7//!
8//! On a lex error, the example prints a one-line diagnostic with `(line:col)`
9//! pulled from a [`SourceMap`] and exits with status 1.
10
11use std::fs::File;
12use std::path::PathBuf;
13use std::process::ExitCode;
14
15use llvmkit_asmparser::ll_lexer::{LexError, Lexer};
16use llvmkit_asmparser::read_to_owned;
17use llvmkit_support::SourceMap;
18
19fn main() -> ExitCode {
20    let Some(path) = std::env::args_os().nth(1).map(PathBuf::from) else {
21        eprintln!("usage: lex_file <path/to/file.ll>");
22        return ExitCode::from(2);
23    };
24
25    // Single I/O round-trip — every later operation works on the borrowed slice.
26    let bytes = match File::open(&path).and_then(read_to_owned) {
27        Ok(b) => b,
28        Err(e) => {
29            eprintln!("error: cannot read {}: {e}", path.display());
30            return ExitCode::from(1);
31        }
32    };
33
34    let source_map = SourceMap::new(&bytes);
35    let mut lexer = Lexer::new(&bytes);
36    let mut count = 0usize;
37
38    loop {
39        match lexer.next_token() {
40            Ok(spanned) => {
41                let (l, c) = source_map.line_col(spanned.span.start);
42                println!(
43                    "{path}:{l}:{c}  ({s_start}..{s_end})  {tok:?}",
44                    path = path.display(),
45                    l = l,
46                    c = c,
47                    s_start = spanned.span.start,
48                    s_end = spanned.span.end,
49                    tok = spanned.value,
50                );
51                if matches!(spanned.value, llvmkit_asmparser::ll_token::Token::Eof) {
52                    break;
53                }
54                count += 1;
55            }
56            Err(err) => {
57                report_error(&path, &source_map, &err);
58                return ExitCode::from(1);
59            }
60        }
61    }
62
63    eprintln!("{count} token(s) lexed");
64    ExitCode::SUCCESS
65}
66
67fn report_error(path: &std::path::Path, sm: &SourceMap<'_>, err: &LexError) {
68    let span = err.span();
69    let (l, c) = sm.line_col(span.start);
70    eprintln!("{path}:{l}:{c}: {err}", path = path.display());
71    if let Some(line) = sm.line_text(l) {
72        eprintln!("  | {}", String::from_utf8_lossy(line));
73        let underline_len = (span.end.saturating_sub(span.start) as usize).max(1);
74        eprintln!(
75            "  | {pad}{caret}",
76            pad = " ".repeat((c - 1) as usize),
77            caret = "^".repeat(underline_len)
78        );
79    }
80}