1use 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 #[clap(long)]
18 pub show_tokens: bool,
19
20 #[clap(long)]
22 pub show_ast: bool,
23
24 #[clap(long)]
26 pub show_pre: bool,
27
28 #[clap(long)]
30 pub show_hlir: bool,
31
32 pub filename: String,
34
35 #[clap(value_enum, default_value_t = Target::Rust)]
37 pub target: Target,
38
39 #[clap(long)]
41 pub check: bool,
42
43 #[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}