tiger_lib/data/
data_binding.rs1use std::mem::take;
2use std::path::PathBuf;
3
4use crate::block::Block;
5use crate::data::localization::LocaValue;
6use crate::datatype::{Code, CodeArg, CodeChain};
7use crate::everything::Everything;
8use crate::fileset::{FileEntry, FileHandler};
9use crate::helpers::{TigerHashMap, dup_error};
10use crate::parse::ParserMemory;
11use crate::parse::localization::ValueParser;
12use crate::pdxfile::PdxFile;
13use crate::report::{ErrorKey, err, warn};
14use crate::token::Token;
15use crate::validator::Validator;
16
17#[derive(Clone, Debug, Default)]
18pub struct DataBindings {
19 bindings: TigerHashMap<&'static str, DataBinding>,
20}
21
22impl DataBindings {
23 fn load_macro(&mut self, block: Block) {
24 let key;
25 if let Some(def) = block.get_field_value("definition") {
26 if let Some((splitdef, _)) = def.split_once('(') {
27 key = splitdef;
28 } else {
29 key = def.clone();
30 }
31 } else {
32 warn(ErrorKey::ParseError).msg("missing field `definition`").loc(block).push();
33 return;
34 }
35 if let Some(other) = self.bindings.get(key.as_str())
36 && other.key.loc.kind >= key.loc.kind
37 {
38 dup_error(&key, &other.key, "data binding");
39 }
40 self.bindings.insert(key.as_str(), DataBinding::new(key, block));
41 }
42
43 pub fn get(&self, key: &str) -> Option<&DataBinding> {
44 self.bindings.get(key)
45 }
46
47 pub fn validate(&self, data: &Everything) {
48 for item in self.bindings.values() {
49 item.validate(data);
50 }
51 }
52}
53
54impl FileHandler<Block> for DataBindings {
55 fn subpath(&self) -> PathBuf {
56 PathBuf::from("data_binding")
57 }
58
59 fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
60 if !entry.filename().to_string_lossy().ends_with(".txt") {
61 return None;
62 }
63
64 PdxFile::read(entry, parser)
65 }
66
67 fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
68 for (key, block) in block.drain_definitions_warn() {
69 if key.is("macro") {
70 self.load_macro(block);
71 } else {
72 let msg = format!("unexpected key {key} in data_binding");
73 warn(ErrorKey::ParseError).msg(msg).loc(key).push();
74 }
75 }
76 }
77}
78
79#[derive(Clone, Debug)]
80pub struct DataBinding {
81 key: Token,
82 block: Block,
83 params: Vec<Token>,
84 body: Option<CodeChain>,
85}
86
87impl DataBinding {
88 fn new(key: Token, block: Block) -> Self {
89 let mut params = Vec::new();
90 if let Some(def) = block.get_field_value("definition")
91 && let Some((_, paramsx)) = def.split_once('(')
92 && let Some((arguments, _)) = paramsx.split_once(')')
93 {
94 for param in arguments.split(',') {
95 params.push(param);
96 }
97 }
98 let mut body = None;
99 if let Some(rep) = block.get_field_value("replace_with") {
100 let open_bracket = Token::from_static_str("[", rep.loc);
103 let close_bracket = Token::from_static_str("]", rep.loc);
104 let to_parse = vec![&open_bracket, rep, &close_bracket];
105 let value = ValueParser::new(to_parse).parse();
106 if let LocaValue::Code(mut chain, _) = value {
107 body = Some(take(&mut chain));
108 } else {
109 let msg = "could not parse macro replacement";
110 err(ErrorKey::Datafunctions).msg(msg).loc(rep).push();
111 }
112 }
113 Self { key, block, params, body }
114 }
115
116 pub fn replace(&self, call: &Code) -> Option<CodeChain> {
117 if call.arguments.len() != self.params.len() {
118 let msg = "wrong number of arguments for macro";
119 err(ErrorKey::Datafunctions).msg(msg).loc(&call.name).push();
120 return None;
121 }
122 match self.replace_chain(self.body.as_ref()?, call)? {
123 CodeArg::Chain(chain) => Some(chain),
124 CodeArg::Literal(token) => {
125 let msg = "cannot substitute a literal here";
126 err(ErrorKey::Datafunctions).msg(msg).loc(token).push();
127 None
128 }
129 }
130 }
131
132 fn replace_chain(&self, body_chain: &CodeChain, call: &Code) -> Option<CodeArg> {
133 let mut result = Vec::new();
134 for body_code in &body_chain.codes {
135 if body_code.arguments.is_empty() {
136 if let Some(idx) = self.params.iter().position(|param| param == &body_code.name) {
140 match &call.arguments[idx] {
141 CodeArg::Literal(caller_token) => {
142 if body_chain.codes.len() != 1 {
145 let msg = "cannot substitute a literal here";
146 err(ErrorKey::Datafunctions).msg(msg).loc(caller_token).push();
147 return None;
148 }
149 return Some(call.arguments[idx].clone());
150 }
151 CodeArg::Chain(caller_chain) => {
152 result.extend_from_slice(&caller_chain.codes);
153 }
154 }
155 } else {
156 result.push(body_code.clone());
157 }
158 } else {
159 result.push(Code {
160 name: body_code.name.clone(),
161 arguments: body_code
162 .arguments
163 .iter()
164 .map(|arg| self.replace_arg(arg, call))
165 .collect::<Option<Vec<_>>>()?,
166 });
167 }
168 }
169 Some(CodeArg::Chain(CodeChain { codes: result.into_boxed_slice() }))
170 }
171
172 fn replace_arg(&self, body_arg: &CodeArg, call: &Code) -> Option<CodeArg> {
173 match body_arg {
174 CodeArg::Chain(body_chain) => self.replace_chain(body_chain, call),
175 CodeArg::Literal(_) => Some(body_arg.clone()),
176 }
177 }
178
179 fn validate(&self, data: &Everything) {
180 let mut vd = Validator::new(&self.block, data);
181 vd.req_field("replace_with");
182 vd.field_value("description");
183 vd.field_value("definition");
184 vd.field_value("replace_with");
185 }
186}