xlsynth-driver 0.61.0

Binary that integrates XLS capabilities into a driver program
Documentation
// SPDX-License-Identifier: Apache-2.0

use crate::common::parse_bool_flag_or;
use crate::common::write_stdout_line;
use crate::report_cli_error::report_cli_error_and_exit;
use crate::toolchain_config::ToolchainConfig;
use clap::ArgMatches;
use xlsynth_pir::ir;
use xlsynth_pir::ir_parser;
use xlsynth_pir::ir_query;

fn extract_error_byte_offset(msg: &str) -> Option<usize> {
    let (_prefix, suffix) = msg.rsplit_once(" at byte ")?;
    suffix.parse::<usize>().ok()
}

pub fn handle_ir_query(matches: &ArgMatches, _config: &Option<ToolchainConfig>) {
    let input_file = matches
        .get_one::<String>("ir_input_file")
        .expect("ir_input_file is required");
    let query_text = matches
        .get_one::<String>("query")
        .expect("query is required");

    // Parse the query up-front so malformed queries fail fast (before we do any IR
    // I/O).
    let query = match ir_query::parse_query(query_text) {
        Ok(query) => query,
        Err(e) => {
            let msg = if let Some(pos) = extract_error_byte_offset(&e) {
                let clamped = std::cmp::min(pos, query_text.len());
                format!(
                    "Failed to parse query: {e}\n  {query_text}\n  {}^",
                    " ".repeat(clamped)
                )
            } else {
                format!("Failed to parse query: {}", e)
            };
            report_cli_error_and_exit(&msg, Some("ir-query"), vec![]);
        }
    };

    if parse_bool_flag_or(matches, "check_query", false) {
        return;
    }

    let show_file = parse_bool_flag_or(matches, "show-file", false);
    let show_ret = parse_bool_flag_or(matches, "show-ret", true);

    let file_content = std::fs::read_to_string(input_file)
        .unwrap_or_else(|e| panic!("Failed to read {}: {}", input_file, e));
    let mut parser = ir_parser::Parser::new(&file_content);
    let mut pkg = parser
        .parse_and_validate_package()
        .unwrap_or_else(|e| panic!("Failed to parse/validate IR package: {}", e));

    if let Some(top) = matches.get_one::<String>("ir_top") {
        pkg.set_top_fn(top)
            .unwrap_or_else(|e| panic!("Failed to set --top: {}", e));
    }
    let top_fn = pkg
        .get_top_fn()
        .unwrap_or_else(|| panic!("No top function found in package"));

    let matches = ir_query::find_matching_nodes(top_fn, &query);

    for node_ref in matches {
        let node = top_fn.get_node(node_ref);
        let mut line = if let Some(line) = node.to_string(top_fn) {
            line
        } else {
            ir::node_textual_id(top_fn, node_ref)
        };
        if show_ret && top_fn.ret_node_ref == Some(node_ref) {
            line = format!("ret {}", line);
        }
        if show_file {
            write_stdout_line(&format!("{}: {}", input_file, line));
        } else {
            write_stdout_line(&line);
        }
    }
}