/ compiler / ast / src / lib.rs
lib.rs
  1  // Copyright (C) 2019-2025 ADnet Contributors
  2  // This file is part of the ADL library.
  3  
  4  // The ADL library is free software: you can redistribute it and/or modify
  5  // it under the terms of the GNU General Public License as published by
  6  // the Free Software Foundation, either version 3 of the License, or
  7  // (at your option) any later version.
  8  
  9  // The ADL library is distributed in the hope that it will be useful,
 10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12  // GNU General Public License for more details.
 13  
 14  // You should have received a copy of the GNU General Public License
 15  // along with the ADL library. If not, see <https://www.gnu.org/licenses/>.
 16  
 17  //! The abstract syntax tree (ast) for a ADL program.
 18  //!
 19  //! This module contains the [`Ast`] type, a wrapper around the [`Program`] type.
 20  //! The [`Ast`] type is intended to be parsed and modified by different passes
 21  //! of the ADL compiler. The ADL compiler can generate a set of R1CS constraints from any [`Ast`].
 22  
 23  #![allow(ambiguous_glob_reexports)]
 24  
 25  mod composite;
 26  pub use self::composite::*;
 27  
 28  pub mod common;
 29  pub use self::common::*;
 30  
 31  pub mod constructor;
 32  pub use self::constructor::*;
 33  
 34  mod expressions;
 35  pub use self::expressions::*;
 36  
 37  mod functions;
 38  pub use self::functions::*;
 39  
 40  mod indent_display;
 41  use indent_display::*;
 42  
 43  pub mod interpreter_value;
 44  
 45  mod mapping;
 46  pub use self::mapping::*;
 47  
 48  mod module;
 49  pub use self::module::*;
 50  
 51  mod passes;
 52  pub use self::passes::*;
 53  
 54  mod program;
 55  pub use self::program::*;
 56  
 57  mod statement;
 58  pub use self::statement::*;
 59  
 60  mod storage;
 61  pub use self::storage::*;
 62  
 63  mod types;
 64  pub use self::types::*;
 65  
 66  mod stub;
 67  pub use self::stub::*;
 68  
 69  pub use common::node::*;
 70  
 71  use adl_errors::{AstError, Result};
 72  
 73  /// The abstract syntax tree (AST) for a ADL program.
 74  ///
 75  /// The [`Ast`] type represents a ADL program as a series of recursive data types.
 76  /// These data types form a tree that begins from a [`Program`] type root.
 77  #[derive(Clone, Debug, Default, Eq, PartialEq)]
 78  pub struct Ast {
 79      pub ast: Program,
 80  }
 81  
 82  impl Ast {
 83      /// Creates a new AST from a given program tree.
 84      pub fn new(program: Program) -> Self {
 85          Self { ast: program }
 86      }
 87  
 88      /// Returns a reference to the inner program AST representation.
 89      pub fn as_repr(&self) -> &Program {
 90          &self.ast
 91      }
 92  
 93      pub fn into_repr(self) -> Program {
 94          self.ast
 95      }
 96  
 97      /// Serializes the ast into a JSON string.
 98      pub fn to_json_string(&self) -> Result<String> {
 99          Ok(serde_json::to_string_pretty(&self.ast).map_err(|e| AstError::failed_to_convert_ast_to_json_string(&e))?)
100      }
101  
102      // Converts the ast into a JSON value.
103      // Note that there is no corresponding `from_json_value` function
104      // since we modify JSON values leaving them unable to be converted
105      // back into Programs.
106      pub fn to_json_value(&self) -> Result<serde_json::Value> {
107          Ok(serde_json::to_value(&self.ast).map_err(|e| AstError::failed_to_convert_ast_to_json_value(&e))?)
108      }
109  
110      /// Serializes the ast into a JSON file.
111      pub fn to_json_file(&self, mut path: std::path::PathBuf, file_name: &str) -> Result<()> {
112          path.push(file_name);
113          let file = std::fs::File::create(&path).map_err(|e| AstError::failed_to_create_ast_json_file(&path, &e))?;
114          let writer = std::io::BufWriter::new(file);
115          Ok(serde_json::to_writer_pretty(writer, &self.ast)
116              .map_err(|e| AstError::failed_to_write_ast_to_json_file(&path, &e))?)
117      }
118  
119      /// Serializes the ast into a JSON value and removes keys from object mappings before writing to a file.
120      pub fn to_json_file_without_keys(
121          &self,
122          mut path: std::path::PathBuf,
123          file_name: &str,
124          excluded_keys: &[&str],
125      ) -> Result<()> {
126          path.push(file_name);
127          let file = std::fs::File::create(&path).map_err(|e| AstError::failed_to_create_ast_json_file(&path, &e))?;
128          let writer = std::io::BufWriter::new(file);
129  
130          let mut value = self.to_json_value().unwrap();
131          for key in excluded_keys {
132              value = remove_key_from_json(value, key);
133          }
134          value = normalize_json_value(value);
135  
136          Ok(serde_json::to_writer_pretty(writer, &value)
137              .map_err(|e| AstError::failed_to_write_ast_to_json_file(&path, &e))?)
138      }
139  
140      /// Deserializes the JSON string into a ast.
141      pub fn from_json_string(json: &str) -> Result<Self> {
142          let ast: Program = serde_json::from_str(json).map_err(|e| AstError::failed_to_read_json_string_to_ast(&e))?;
143          Ok(Self { ast })
144      }
145  
146      /// Deserializes the JSON string into a ast from a file.
147      pub fn from_json_file(path: std::path::PathBuf) -> Result<Self> {
148          let data = std::fs::read_to_string(&path).map_err(|e| AstError::failed_to_read_json_file(&path, &e))?;
149          Self::from_json_string(&data)
150      }
151  }
152  
153  impl AsRef<Program> for Ast {
154      fn as_ref(&self) -> &Program {
155          &self.ast
156      }
157  }
158  
159  /// Helper function to recursively filter keys from AST JSON
160  pub fn remove_key_from_json(value: serde_json::Value, key: &str) -> serde_json::Value {
161      match value {
162          serde_json::Value::Object(map) => serde_json::Value::Object(
163              map.into_iter().filter(|(k, _)| k != key).map(|(k, v)| (k, remove_key_from_json(v, key))).collect(),
164          ),
165          serde_json::Value::Array(values) => {
166              serde_json::Value::Array(values.into_iter().map(|v| remove_key_from_json(v, key)).collect())
167          }
168          _ => value,
169      }
170  }
171  
172  /// Helper function to normalize AST JSON into a form compatible with tgc.
173  /// This function will traverse the original JSON value and produce a new
174  /// one under the following rules:
175  /// 1. Remove empty object mappings from JSON arrays
176  /// 2. If there are two elements in a JSON array and one is an empty object
177  ///    mapping and the other is not, then lift up the one that isn't
178  pub fn normalize_json_value(value: serde_json::Value) -> serde_json::Value {
179      match value {
180          serde_json::Value::Array(vec) => {
181              let orig_length = vec.len();
182              let mut new_vec: Vec<serde_json::Value> = vec
183                  .into_iter()
184                  .filter(|v| !matches!(v, serde_json::Value::Object(map) if map.is_empty()))
185                  .map(normalize_json_value)
186                  .collect();
187  
188              if orig_length == 2 && new_vec.len() == 1 {
189                  new_vec.pop().unwrap()
190              } else {
191                  serde_json::Value::Array(new_vec)
192              }
193          }
194          serde_json::Value::Object(map) => {
195              serde_json::Value::Object(map.into_iter().map(|(k, v)| (k, normalize_json_value(v))).collect())
196          }
197          _ => value,
198      }
199  }