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 if other.key.loc.kind >= key.loc.kind {
37 dup_error(&key, &other.key, "data binding");
38 }
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 if let Some((_, paramsx)) = def.split_once('(') {
92 if let Some((arguments, _)) = paramsx.split_once(')') {
93 for param in arguments.split(',') {
94 params.push(param);
95 }
96 }
97 }
98 }
99 let mut body = None;
100 if let Some(rep) = block.get_field_value("replace_with") {
101 let open_bracket = Token::from_static_str("[", rep.loc);
104 let close_bracket = Token::from_static_str("]", rep.loc);
105 let to_parse = vec![&open_bracket, rep, &close_bracket];
106 let value = ValueParser::new(to_parse).parse();
107 if let LocaValue::Code(mut chain, _) = value {
108 body = Some(take(&mut chain));
109 } else {
110 let msg = "could not parse macro replacement";
111 err(ErrorKey::Datafunctions).msg(msg).loc(rep).push();
112 }
113 }
114 Self { key, block, params, body }
115 }
116
117 pub fn replace(&self, call: &Code) -> Option<CodeChain> {
118 if call.arguments.len() != self.params.len() {
119 let msg = "wrong number of arguments for macro";
120 err(ErrorKey::Datafunctions).msg(msg).loc(&call.name).push();
121 return None;
122 }
123 match self.replace_chain(self.body.as_ref()?, call)? {
124 CodeArg::Chain(chain) => Some(chain),
125 CodeArg::Literal(token) => {
126 let msg = "cannot substitute a literal here";
127 err(ErrorKey::Datafunctions).msg(msg).loc(token).push();
128 None
129 }
130 }
131 }
132
133 fn replace_chain(&self, body_chain: &CodeChain, call: &Code) -> Option<CodeArg> {
134 let mut result = Vec::new();
135 for body_code in &body_chain.codes {
136 if body_code.arguments.is_empty() {
137 if let Some(idx) = self.params.iter().position(|param| param == &body_code.name) {
141 match &call.arguments[idx] {
142 CodeArg::Literal(caller_token) => {
143 if body_chain.codes.len() != 1 {
146 let msg = "cannot substitute a literal here";
147 err(ErrorKey::Datafunctions).msg(msg).loc(caller_token).push();
148 return None;
149 }
150 return Some(call.arguments[idx].clone());
151 }
152 CodeArg::Chain(caller_chain) => {
153 result.extend_from_slice(&caller_chain.codes);
154 }
155 }
156 } else {
157 result.push(body_code.clone());
158 }
159 } else {
160 result.push(Code {
161 name: body_code.name.clone(),
162 arguments: body_code
163 .arguments
164 .iter()
165 .map(|arg| self.replace_arg(arg, call))
166 .collect::<Option<Vec<_>>>()?,
167 });
168 }
169 }
170 Some(CodeArg::Chain(CodeChain { codes: result.into_boxed_slice() }))
171 }
172
173 fn replace_arg(&self, body_arg: &CodeArg, call: &Code) -> Option<CodeArg> {
174 match body_arg {
175 CodeArg::Chain(body_chain) => self.replace_chain(body_chain, call),
176 CodeArg::Literal(_) => Some(body_arg.clone()),
177 }
178 }
179
180 fn validate(&self, data: &Everything) {
181 let mut vd = Validator::new(&self.block, data);
182 vd.req_field("replace_with");
183 vd.field_value("description");
184 vd.field_value("definition");
185 vd.field_value("replace_with");
186 }
187}