dpscript/ir/compiler/
node.rs

1use once_cell::sync::Lazy;
2use regex::Regex;
3use std::{
4    fs::{self, File},
5    io::Write,
6    path::PathBuf,
7};
8
9use crate::{IRCheckerContext, IRLiteral, IRNode, Result, UnsourcedCompilerError};
10
11use super::TagData;
12
13pub const FIXER_REGEX: Lazy<Regex> =
14    Lazy::new(|| Regex::new(r"(?m)([^:\s]+:[^\s]+)\s([^\s]+)\s\[").unwrap());
15
16impl IRNode {
17    pub fn compile(&self, cx: &IRCheckerContext, dir: &PathBuf) -> Result<String> {
18        match self {
19            Self::Function(func) => {
20                let id = func.id.to_lowercase();
21                let mut split = id.split(":");
22                let ns = split.next().unwrap();
23                let name = split.next().unwrap();
24
25                let path = dir
26                    .join("data")
27                    .join(ns)
28                    .join("functions")
29                    .join(format!("{}.mcfunction", name));
30
31                let root = path.parent().unwrap();
32
33                if !root.exists() {
34                    fs::create_dir_all(root)?;
35                }
36
37                let mut file = File::create(path)?;
38
39                for item in &func.body {
40                    let data = item.compile(cx, dir)?;
41
42                    if data.is_empty() {
43                        continue;
44                    }
45
46                    writeln!(file, "{}", data)?;
47                }
48
49                Ok(String::new())
50            }
51
52            Self::Tag(tag) => {
53                let mut split = tag.name.split(":");
54                let ns = split.next().unwrap();
55                let name = split.next().unwrap();
56                let path = dir.join("data").join(ns).join(format!("{}.json", name));
57                let root = path.parent().unwrap();
58
59                if !root.exists() {
60                    fs::create_dir_all(root)?;
61                }
62
63                let mut values = Vec::new();
64
65                for item in &tag.entries {
66                    if !values.contains(item) {
67                        values.push(item.clone());
68                    }
69                }
70
71                let data = TagData {
72                    replace: false,
73                    values,
74                };
75
76                fs::write(path, serde_json::to_string_pretty(&data)?)?;
77
78                Ok(String::new())
79            }
80
81            Self::Literal(lit) => match lit {
82                IRLiteral::String(s) => Ok(s.clone()),
83                IRLiteral::PathOf(_) | IRLiteral::StoreOf(_) => Err(UnsourcedCompilerError {
84                    err: "path!() and store!() expressions are not allowed during compilation!"
85                        .into(),
86                }
87                .into()),
88            },
89
90            Self::Group(group) => {
91                let mut data = Vec::new();
92
93                for item in group {
94                    let out = item.compile(cx, dir)?;
95
96                    if out.is_empty() {
97                        continue;
98                    }
99
100                    data.push(out);
101                }
102
103                Ok(data.join("\n"))
104            }
105
106            Self::Command(cmd) => {
107                let mut buf = Vec::new();
108
109                for node in &cmd.cmd {
110                    buf.push(node.compile(cx, dir)?);
111                }
112
113                Ok(FIXER_REGEX.replace(&buf.join(" "), "$1 $2[").to_string())
114            }
115
116            Self::None => Ok(String::new()),
117            // These get ignored, they were only for checking and analysis
118            Self::Definition(_) => Ok(String::new()),
119
120            _ => Err(UnsourcedCompilerError {
121                err: format!("Unexpected node for compilation: {:?}", self),
122            }
123            .into()),
124        }
125    }
126}