jsonpiler/
impl_compiler.rs

1//! Implementation of the compiler inside the `Jsonpiler`.
2use super::{
3  BuiltinFunc, ErrOR, ErrorInfo, JFunc, JFuncResult, JObject, JResult, JValue, Json, Jsonpiler,
4  Section, functions::obj_json,
5};
6use core::fmt::Write as _;
7use std::{
8  collections::HashSet,
9  fs::File,
10  io::{self, BufWriter, Write as _},
11};
12/// Macro to include assembly files only once.
13macro_rules! include_once {
14  ($self:ident, $name:literal) => {{
15    if !$self.include_flag.contains($name) {
16      $self.include_flag.insert($name.into());
17      write!($self.sect.text, include_str!(concat!("asm/", $name, ".s")))?;
18    }
19  }};
20}
21impl Jsonpiler {
22  /// Assert condition.
23  fn assert(&self, cond: bool, text: &str, info: &ErrorInfo) -> ErrOR<()> {
24    cond.then_some(()).ok_or_else(|| self.fmt_err(text, info).into())
25  }
26  /// Builds the assembly code from the parsed JSON.
27  /// This function is the main entry point for the compilation process.
28  /// It takes the parsed JSON, sets up the initial function table,
29  /// evaluates the JSON, and writes the resulting assembly code to a file.
30  /// # Arguments
31  /// * `source` - The JSON String.
32  /// * `json_file` - The name of the original JSON file.
33  /// * `filename` - The name of the file to write the assembly code to.
34  /// # Returns
35  /// * `Ok(Json)` - The result of the evaluation.
36  /// * `Err(Box<dyn Error>)` - If an error occurred during the compilation process.
37  /// # Errors
38  /// * `Box<dyn Error>` - If an error occurred during the compilation process.
39  #[inline]
40  pub fn build(&mut self, source: String, json_file: &str, filename: &str) -> ErrOR<()> {
41    let json = self.parse(source)?;
42    self.include_flag = HashSet::new();
43    self.seed = 0;
44    self.register("=", true, Jsonpiler::f_local_set);
45    self.register("$", true, Jsonpiler::f_local_get);
46    self.register("+", true, Jsonpiler::f_plus);
47    self.register("-", true, Jsonpiler::f_minus);
48    self.register("message", true, Jsonpiler::f_message);
49    self.register("begin", true, Jsonpiler::f_begin);
50    let mut start = String::new();
51    self.sect = Section::default();
52    let result = self.eval(&json, &mut start)?;
53    writeln!(
54      start,
55      "  {}
56  call [qword ptr __imp_ExitProcess[rip]]
57  .seh_endproc",
58      if let JValue::LInt(int) = result.value {
59        format!("mov rcx, {int}")
60      } else if let JValue::VInt(var) = &result.value {
61        format!("mov rcx, qword ptr {var}[rip]")
62      } else {
63        "xor ecx, ecx".into()
64      }
65    )?;
66    self.write_file(&start, filename, json_file)?;
67    Ok(())
68  }
69  /// Evaluates a JSON object.
70  fn eval(&mut self, json: &Json, function: &mut String) -> JResult {
71    const ERR: &str = "Unreachable (eval)";
72    let JValue::LArray(list) = &json.value else {
73      let JValue::LObject(object) = &json.value else { return Ok(json.clone()) };
74      let mut evaluated = JObject::default();
75      for kv in object.iter() {
76        evaluated.insert(kv.0.clone(), self.eval(&kv.1, function)?);
77      }
78      return Ok(obj_json(JValue::LObject(evaluated), json.info.clone()));
79    };
80    let first_elem =
81      list.first().ok_or(self.fmt_err("An function call cannot be an empty list.", &json.info))?;
82    let first = &self.eval(first_elem, function)?;
83    if let JValue::LString(cmd) = &first.value {
84      if cmd == "lambda" {
85        let mut func_buffer = String::new();
86        let result = Ok(self.eval_lambda(json, &mut func_buffer)?);
87        self.sect.text.push_str(&func_buffer);
88        result
89      } else if self.f_table.contains_key(cmd.as_str()) {
90        let args = if self.f_table.get_mut(cmd.as_str()).ok_or(ERR)?.evaluated {
91          &self.eval_args(list.get(1..).unwrap_or(&[]), function)?
92        } else {
93          list.get(1..).unwrap_or(&[])
94        };
95        Ok(obj_json(
96          (self.f_table.get_mut(cmd.as_str()).ok_or(ERR)?.func)(self, first, args, function)?,
97          first.info.clone(),
98        ))
99      } else {
100        Err(self.fmt_err(&format!("Function '{cmd}' is undefined."), &first.info).into())
101      }
102    } else if let JValue::Function { name: n, ret: re, .. } = &first.value {
103      writeln!(function, "  call {n}")?;
104      if let JValue::VInt(_) | JValue::LInt(_) = **re {
105        let na = self.get_name()?;
106        writeln!(self.sect.bss, "  .lcomm {na}, 8")?;
107        writeln!(function, "  mov qword ptr {na}[rip], rax")?;
108        Ok(obj_json(JValue::VInt(na), first.info.clone()))
109      } else {
110        Ok(Json::default())
111      }
112    } else {
113      Err(self.fmt_err("Expected a function or lambda as the first element.", &json.info).into())
114    }
115  }
116  /// Evaluate arguments.
117  fn eval_args(&mut self, args: &[Json], function: &mut String) -> ErrOR<Vec<Json>> {
118    let mut result = vec![];
119    for arg in args {
120      result.push(self.eval(arg, function)?);
121    }
122    Ok(result)
123  }
124  /// Evaluates a lambda function definition.
125  fn eval_lambda(&mut self, func: &Json, function: &mut String) -> JResult {
126    const ERR: &str = "Unreachable (eval_lambda)";
127    let tmp = self.vars.clone();
128    let JValue::LArray(func_list) = &func.value else {
129      return Err(self.fmt_err("Invalid function definition.", &func.info).into());
130    };
131    self.assert(func_list.len() >= 3, "Invalid function definition.", &func.info)?;
132    let lambda = func_list.first().ok_or(ERR)?;
133    self.assert(
134      matches!(&lambda.value, JValue::LString(st) if st == "lambda"),
135      r#"The first element of a lambda list requires "lambda"."#,
136      &lambda.info,
137    )?;
138    let params_json = func_list.get(1).ok_or(ERR)?;
139    let JValue::LArray(params) = &params_json.value else {
140      return Err(
141        self
142          .fmt_err(
143            "The second element of a lambda list requires an argument list.",
144            &params_json.info,
145          )
146          .into(),
147      );
148    };
149    self.assert(params.is_empty(), "PARAMS ISN'T IMPLEMENTED.", &params_json.info)?;
150    let name = self.get_name()?;
151    writeln!(
152      function,
153      ".seh_proc {name}
154{name}:
155  push rbp
156  .seh_pushreg rbp
157  mov rbp, rsp
158  .seh_setframe rbp, 0
159  sub rsp, 32
160  .seh_stackalloc 32
161  .seh_endprologue
162  .seh_handler .L_SEH_HANDLER, @except",
163    )?;
164    let mut ret = JValue::Null;
165    for arg in func_list.get(2..).ok_or("Empty lambda body.")? {
166      ret = self.eval(arg, function)?.value;
167    }
168    writeln!(
169      function,
170      "  {}
171  add rsp, 32
172  leave
173  ret
174  .seh_endproc",
175      if let JValue::LInt(int) = ret {
176        format!("mov rax, {int}")
177      } else if let JValue::VInt(var) = &ret {
178        format!("mov rax, qword ptr {var}[rip]")
179      } else {
180        "xor eax, eax".into()
181      }
182    )?;
183    self.vars = tmp;
184    Ok(obj_json(
185      JValue::Function { name, params: params.clone(), ret: Box::new(ret) },
186      lambda.info.clone(),
187    ))
188  }
189  /// Evaluates a 'begin' block.
190  #[expect(clippy::single_call_fn, reason = "")]
191  fn f_begin(&mut self, first: &Json, args: &[Json], _: &mut String) -> JFuncResult {
192    args.last().map_or_else(
193      || Err(self.fmt_err("'begin' requires at least one arguments.", &first.info).into()),
194      |last| Ok(last.value.clone()),
195    )
196  }
197  /// Utility functions for binary operations
198  fn f_binary_op(
199    &mut self, first: &Json, args: &[Json], function: &mut String, mn: &str, op: &str,
200  ) -> JFuncResult {
201    let mut f_binary_mn = |json: &Json, mne: &str| -> ErrOR<()> {
202      if let JValue::LInt(int) = json.value {
203        Ok(writeln!(function, "  {mne} rax, {int}")?)
204      } else if let JValue::VInt(var) = &json.value {
205        Ok(writeln!(function, "  {mne} rax, qword ptr {var}[rip]")?)
206      } else {
207        Err(
208          self
209            .fmt_err(
210              &format!("'{op}' requires integer operands, but got {}", json.value),
211              &json.info,
212            )
213            .into(),
214        )
215      }
216    };
217    let operand_r = args
218      .first()
219      .ok_or(self.fmt_err(&format!("'{op}' requires at least one arguments."), &first.info))?;
220    f_binary_mn(operand_r, "mov")?;
221    for operand_l in args.get(1..).unwrap_or(&[]) {
222      f_binary_mn(operand_l, mn)?;
223    }
224    let ret = self.get_name()?;
225    writeln!(self.sect.bss, "  .lcomm {ret}, 8")?;
226    writeln!(function, "  mov qword ptr {ret}[rip], rax")?;
227    Ok(JValue::VInt(ret))
228  }
229  /// Gets the value of a local variable.
230  #[expect(clippy::single_call_fn, reason = "")]
231  fn f_local_get(&mut self, first: &Json, args: &[Json], _: &mut String) -> JFuncResult {
232    self.assert(args.len() == 1, "'$' requires one argument.", &first.info)?;
233    let Some(var) = args.first() else {
234      return Err(self.fmt_err("'$' requires one argument.", &first.info).into());
235    };
236    let JValue::LString(var_name) = &var.value else {
237      return Err(self.fmt_err("Variable name must be a string literal.", &var.info).into());
238    };
239    match self.vars.get(var_name) {
240      Some(value) => Ok(value.clone()),
241      None => Err(self.fmt_err(&format!("Undefined variables: '{var_name}'"), &var.info).into()),
242    }
243  }
244  /// Sets a local variable.
245  #[expect(clippy::single_call_fn, reason = "")]
246  fn f_local_set(&mut self, first: &Json, args: &[Json], _: &mut String) -> JFuncResult {
247    self.assert(args.len() == 2, "'=' requires two arguments.", &first.info)?;
248    let JValue::LString(variable) = &args.first().ok_or("Unreachable (f_set_local)")?.value else {
249      return Err(
250        self
251          .fmt_err(
252            "Variable name must be a string literal.",
253            &args.first().ok_or("Unreachable (f_set_local)")?.info,
254          )
255          .into(),
256      );
257    };
258    let value = args.get(1).ok_or("Unreachable (f_set_local)")?;
259    match &value.value {
260      JValue::LString(st) => {
261        let name = self.get_name()?;
262        writeln!(self.sect.data, "  {name}: .string \"{st}\"")?;
263        self.vars.insert(variable.clone(), JValue::VString(name.clone()))
264      }
265      JValue::Null => self.vars.insert(variable.clone(), JValue::Null),
266      JValue::LInt(int) => {
267        let name = self.get_name()?;
268        writeln!(self.sect.data, "  {name}: .quad 0x{int:x}")?;
269        self.vars.insert(variable.clone(), JValue::VInt(name.clone()))
270      }
271      JValue::VString(_)
272      | JValue::VInt(_)
273      | JValue::Function { .. }
274      | JValue::VArray(_)
275      | JValue::VBool(..)
276      | JValue::VFloat(_)
277      | JValue::VObject(_) => self.vars.insert(variable.clone(), value.value.clone()),
278      JValue::LArray(_) | JValue::LBool(_) | JValue::LFloat(_) | JValue::LObject(_) => {
279        return Err(self.fmt_err("Assignment to an unimplemented type.", &value.info).into());
280      }
281    }
282    .map_or(Ok(()), |_| Err(self.fmt_err("Reassignment not implemented.", &first.info)))?;
283    Ok(JValue::Null)
284  }
285  /// Displays a message box.
286  #[expect(clippy::single_call_fn, reason = "")]
287  fn f_message(&mut self, first: &Json, args: &[Json], function: &mut String) -> JFuncResult {
288    self.assert(args.len() == 2, "'message' requires two arguments.", &first.info)?;
289    let title = self.string2var(args.first().ok_or("Unreachable (f_message)")?, "title")?;
290    let msg = self.string2var(args.get(1).ok_or("Unreachable (f_message)")?, "text")?;
291    let wtitle = self.get_name()?;
292    let wmsg = self.get_name()?;
293    let ret = self.get_name()?;
294    for data in [&wtitle, &wmsg, &ret] {
295      writeln!(self.sect.bss, "  .lcomm {data}, 8")?;
296    }
297    include_once!(self, "func/U8TO16");
298    write!(
299      function,
300      include_str!("asm/message.s"),
301      msg, wmsg, title, wtitle, wmsg, wtitle, ret, wmsg, wtitle
302    )?;
303    Ok(JValue::VInt(ret))
304  }
305  /// Performs subtraction.
306  #[expect(clippy::single_call_fn, reason = "")]
307  fn f_minus(&mut self, first: &Json, args: &[Json], function: &mut String) -> JFuncResult {
308    self.f_binary_op(first, args, function, "sub", "-")
309  }
310  /// Performs addition.
311  #[expect(clippy::single_call_fn, reason = "")]
312  fn f_plus(&mut self, first: &Json, args: &[Json], function: &mut String) -> JFuncResult {
313    self.f_binary_op(first, args, function, "add", "+")
314  }
315  /// Generates a unique name for internal use.
316  fn get_name(&mut self) -> Result<String, &str> {
317    let Some(seed) = self.seed.checked_add(1) else { return Err("SeedOverflowError") };
318    self.seed = seed;
319    Ok(format!(".LC{seed:x}"))
320  }
321  /// Registers a function in the function table.
322  fn register(&mut self, name: &str, ev: bool, fu: JFunc) {
323    self.f_table.insert(name.into(), BuiltinFunc { evaluated: ev, func: fu });
324  }
325  /// Convert `JValue::` (`StringVar` or `String`) to `StringVar`, otherwise return `Err`
326  fn string2var(&mut self, json: &Json, ctx: &str) -> ErrOR<String> {
327    if let JValue::LString(st) = &json.value {
328      let name = self.get_name()?;
329      writeln!(self.sect.data, "  {name}: .string \"{st}\"")?;
330      Ok(name)
331    } else if let JValue::VString(var) = &json.value {
332      Ok(var.clone())
333    } else {
334      Err(self.fmt_err(&format!("'{ctx}' must be a string."), &json.info).into())
335    }
336  }
337  /// Writes the compiled assembly code to a file.
338  fn write_file(&self, start: &str, filename: &str, json_file: &str) -> io::Result<()> {
339    let mut writer = BufWriter::new(File::create(filename)?);
340    writer.write_all(format!(".file \"{json_file}\"\n.intel_syntax noprefix\n").as_bytes())?;
341    writer.write_all(include_bytes!("asm/data.s"))?;
342    writer.write_all(self.sect.data.as_bytes())?;
343    writer.write_all(include_bytes!("asm/bss.s"))?;
344    writer.write_all(self.sect.bss.as_bytes())?;
345    writer.write_all(include_bytes!("asm/start.s"))?;
346    writer.write_all(start.as_bytes())?;
347    writer.write_all(include_bytes!("asm/text.s"))?;
348    writer.write_all(self.sect.text.as_bytes())?;
349    writer.flush()?;
350    Ok(())
351  }
352}