paralegal_compiler/
main.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use std::path::PathBuf;
use std::{fs, process::exit};

use crate::parsers::parse;
use clap::Parser;
use std::io::Result;

mod common;
mod compile;
mod initialization_typ;
mod optimizer;
mod parsers;

use common::{verify_scope::*, Policy};

/// A compiler for Paralegal's Controlled Natural Language (CNL) policies.
///
/// By default this generates a Rust namespace (`mod`) that contains a function
/// `check` which, when called, enforces your policy. You should use the
/// framework in
/// [`paralegal_policy`](https://brownsys.github.io/paralegal/libs/paralegal_policy/)
/// to create a suitable binary that calls this function. It depends on the
/// crates `paralegal_policy` and `anyhow`.
///
/// Alternatively you may use the `--bin` argument, which will instead create a
/// Rust source file with a `main` function and necessary boilerplate to call
/// the policy. The file created with this method additionally depends on the
/// `clap` crate with the `derive` feature enabled.
#[derive(clap::Parser)]
struct Args {
    /// Path to the policy file.
    path: PathBuf,
    /// Also generate boilerplate for a runnable policy. The output file
    /// contains a `main` function that runs both the PDG generation as well as
    /// the policy.
    #[clap(long)]
    bin: bool,
    /// Name of the output file.
    #[clap(short, long, default_value = "compiled_policy.rs")]
    out: PathBuf,
}

fn check_environment(policy: &Policy) {
    let mut env = Environment::new();
    verify_definitions_scope(&policy.definitions, &mut env);
    verify_scope(&policy.body, &mut env);
}

fn run(args: &Args) -> Result<()> {
    let policy = fs::read_to_string(&args.path).expect("Could not read policy file");

    let res = parse(&policy);
    match res {
        Ok(mut policy) => {
            // Verify that variables in definitions & policy are properly scoped.
            // If this fails, then the user made a mistake writing their policy.
            check_environment(&policy);
            crate::optimizer::optimize(&mut policy);
            compile::compile(
                policy,
                args.path
                    .file_name()
                    .map_or("<unnamed>", |n| n.to_str().unwrap()),
                &args.out,
                args.bin,
            )
        }
        Err(e) => {
            eprintln!("Error: Policy file {} did not parse", &args.path.display());
            eprintln!("{e}");
            exit(1);
        }
    }
}

fn main() -> Result<()> {
    let args = Args::parse();
    run(&args)
}