x4c/
lib.rs

1// Copyright 2022 Oxide Computer Company
2
3use anyhow::{anyhow, Result};
4use clap::Parser;
5use p4::check::Diagnostics;
6use p4::{
7    ast::AST, check, error, error::SemanticError, lexer, parser, preprocessor,
8};
9use std::fs;
10use std::path::Path;
11use std::sync::Arc;
12
13#[derive(Parser)]
14#[clap(version = "0.1")]
15pub struct Opts {
16    /// Show parsed lexical tokens.
17    #[clap(long)]
18    pub show_tokens: bool,
19
20    /// Show parsed abstract syntax tree.
21    #[clap(long)]
22    pub show_ast: bool,
23
24    /// Show parsed preprocessor info.
25    #[clap(long)]
26    pub show_pre: bool,
27
28    /// Show high-level intermediate representation info.
29    #[clap(long)]
30    pub show_hlir: bool,
31
32    /// File to compile.
33    pub filename: String,
34
35    /// What target to generate code for.
36    #[clap(value_enum, default_value_t = Target::Rust)]
37    pub target: Target,
38
39    /// Just check code, do not compile.
40    #[clap(long)]
41    pub check: bool,
42
43    /// Filename to write generated code to.
44    #[clap(short, long, default_value = "out.rs")]
45    pub out: String,
46}
47
48#[derive(clap::ValueEnum, Clone)]
49pub enum Target {
50    Rust,
51    RedHawk,
52    Docs,
53}
54
55pub fn process_file(
56    filename: Arc<String>,
57    ast: &mut AST,
58    opts: &Opts,
59) -> Result<()> {
60    let contents = fs::read_to_string(&*filename)
61        .map_err(|e| anyhow!("read input: {}: {}", &*filename, e))?;
62
63    let ppr = preprocessor::run(&contents, filename.clone())?;
64    if opts.show_pre {
65        println!("{:#?}", ppr.elements);
66    }
67
68    for included in &ppr.elements.includes {
69        let path = Path::new(included);
70        if !path.is_absolute() {
71            let parent = Path::new(&*filename).parent().unwrap();
72            let joined = parent.join(included);
73            process_file(
74                Arc::new(joined.to_str().unwrap().to_string()),
75                ast,
76                opts,
77            )?
78        } else {
79            process_file(Arc::new(included.clone()), ast, opts)?
80        }
81    }
82
83    let lines: Vec<&str> = ppr.lines.iter().map(|x| x.as_str()).collect();
84
85    let mut lxr = lexer::Lexer::new(lines.clone(), filename);
86    lxr.show_tokens = opts.show_tokens;
87
88    let mut psr = parser::Parser::new(lxr);
89    psr.run(ast)?;
90    if opts.show_ast {
91        println!("{:#?}", ast);
92    }
93
94    let (hlir, diags) = check::all(ast);
95    check(&lines, &diags)?;
96
97    if opts.show_hlir {
98        println!("{:#?}", hlir);
99    }
100
101    Ok(())
102}
103
104fn check(lines: &[&str], diagnostics: &Diagnostics) -> Result<()> {
105    let errors = diagnostics.errors();
106    if !errors.is_empty() {
107        let mut err = Vec::new();
108        for e in errors {
109            err.push(SemanticError {
110                at: e.token.clone(),
111                message: e.message.clone(),
112                source: lines[e.token.line].into(),
113            });
114        }
115        Err(error::Error::Semantic(err))?;
116    }
117    Ok(())
118}