mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-02 03:19:41 +03:00
Implements working typed ast serialization
This commit is contained in:
parent
f6dcd7f9c2
commit
00c10870e2
@ -18,3 +18,5 @@ pest_derive = { version = "2.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
thiserror = { version = "1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
1
ast/tests/mod.rs
Normal file
1
ast/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
mod serialization;
|
91
ast/tests/serialization/expected_ast.json
Normal file
91
ast/tests/serialization/expected_ast.json
Normal file
@ -0,0 +1,91 @@
|
||||
{
|
||||
"imports": [],
|
||||
"circuits": [],
|
||||
"functions": [
|
||||
{
|
||||
"function_name": {
|
||||
"value": "main",
|
||||
"span": {
|
||||
"input": "main",
|
||||
"start": 9,
|
||||
"end": 13
|
||||
}
|
||||
},
|
||||
"parameters": [],
|
||||
"returns": [],
|
||||
"statements": [
|
||||
{
|
||||
"Return": {
|
||||
"return_": {
|
||||
"Single": {
|
||||
"Binary": {
|
||||
"operation": "Add",
|
||||
"left": {
|
||||
"Value": {
|
||||
"Implicit": {
|
||||
"number": {
|
||||
"value": "1",
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 29,
|
||||
"end": 30
|
||||
}
|
||||
},
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 29,
|
||||
"end": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"right": {
|
||||
"Value": {
|
||||
"Implicit": {
|
||||
"number": {
|
||||
"value": "1",
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 33,
|
||||
"end": 34
|
||||
}
|
||||
},
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 33,
|
||||
"end": 34
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"span": {
|
||||
"input": "1 + 1",
|
||||
"start": 29,
|
||||
"end": 34
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"span": {
|
||||
"input": "return 1 + 1",
|
||||
"start": 22,
|
||||
"end": 34
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"span": {
|
||||
"input": "function main() {\n return 1 + 1\n}\n",
|
||||
"start": 0,
|
||||
"end": 37
|
||||
}
|
||||
}
|
||||
],
|
||||
"tests": [],
|
||||
"eoi": null,
|
||||
"span": {
|
||||
"input": "function main() {\n return 1 + 1\n}\n",
|
||||
"start": 0,
|
||||
"end": 37
|
||||
}
|
||||
}
|
22
ast/tests/serialization/json.rs
Normal file
22
ast/tests/serialization/json.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use leo_ast::LeoAst;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
program_filepath.push("tests/serialization/main.leo");
|
||||
|
||||
let expected = include_str!("./expected_ast.json");
|
||||
|
||||
// Loads the Leo code as a string from the given file path.
|
||||
let program_string = LeoAst::load_file(&program_filepath).unwrap();
|
||||
|
||||
// Parses the Leo file and constructs an abstract syntax tree.
|
||||
let ast = LeoAst::new(&program_filepath, &program_string).unwrap();
|
||||
|
||||
// Serializes the abstract syntax tree into JSON format.
|
||||
let serialized_ast = LeoAst::to_json_string(&ast).unwrap();
|
||||
|
||||
assert_eq!(expected, serialized_ast);
|
||||
}
|
3
ast/tests/serialization/main.leo
Normal file
3
ast/tests/serialization/main.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() {
|
||||
return 1 + 1
|
||||
}
|
1
ast/tests/serialization/mod.rs
Normal file
1
ast/tests/serialization/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
mod json;
|
@ -1,11 +1,21 @@
|
||||
use crate::Span;
|
||||
use leo_ast::common::Identifier as AstIdentifier;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use serde::{
|
||||
de::{self, Visitor},
|
||||
Deserialize,
|
||||
Deserializer,
|
||||
Serialize,
|
||||
Serializer,
|
||||
};
|
||||
use std::{collections::BTreeMap, fmt};
|
||||
|
||||
/// An identifier in the constrained program.
|
||||
#[derive(Clone, Hash, Serialize, Deserialize)]
|
||||
///
|
||||
/// Attention - When adding or removing fields from this struct,
|
||||
/// please remember to update it's Serialize and Deserialize implementation
|
||||
/// to reflect the new struct instantiation.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct Identifier {
|
||||
pub name: String,
|
||||
pub span: Span,
|
||||
@ -44,3 +54,61 @@ impl PartialEq for Identifier {
|
||||
}
|
||||
|
||||
impl Eq for Identifier {}
|
||||
|
||||
impl Serialize for Identifier {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
// Converts an element that implements Serialize into a string.
|
||||
fn to_json_string<E: Serialize, Error: serde::ser::Error>(element: &E) -> Result<String, Error> {
|
||||
serde_json::to_string(&element).map_err(|e| Error::custom(e.to_string()))
|
||||
}
|
||||
|
||||
// Load the struct elements into a BTreeMap (to preserve serialized ordering of keys).
|
||||
let mut key: BTreeMap<String, String> = BTreeMap::new();
|
||||
key.insert("name".to_string(), self.name.clone());
|
||||
key.insert("span".to_string(), to_json_string(&self.span)?);
|
||||
|
||||
// Convert the serialized object into a string for use as a key.
|
||||
serializer.serialize_str(&to_json_string(&key)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Identifier {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct IdentifierVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for IdentifierVisitor {
|
||||
type Value = Identifier;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a string encoding the typed Identifier struct")
|
||||
}
|
||||
|
||||
/// Implementation for recovering a string that serializes Identifier.
|
||||
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
||||
// Converts a serialized string into an element that implements Deserialize.
|
||||
fn to_json_string<'a, D: Deserialize<'a>, Error: serde::de::Error>(
|
||||
serialized: &'a str,
|
||||
) -> Result<D, Error> {
|
||||
serde_json::from_str::<'a>(serialized).map_err(|e| Error::custom(e.to_string()))
|
||||
}
|
||||
|
||||
// Convert the serialized string into a BTreeMap to recover Identifier.
|
||||
let key: BTreeMap<String, String> = to_json_string(value)?;
|
||||
|
||||
let name = match key.get("name") {
|
||||
Some(name) => name.clone(),
|
||||
None => return Err(E::custom("missing 'name' in serialized Identifier struct")),
|
||||
};
|
||||
|
||||
let span: Span = match key.get("span") {
|
||||
Some(span) => to_json_string(span)?,
|
||||
None => return Err(E::custom("missing 'span' in serialized Identifier struct")),
|
||||
};
|
||||
|
||||
Ok(Identifier { name, span })
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(IdentifierVisitor)
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use leo_ast::imports::Import as AstImport;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Import {
|
||||
pub package: Package,
|
||||
pub span: Span,
|
||||
|
@ -4,7 +4,7 @@ use leo_ast::imports::ImportSymbol as AstImportSymbol;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ImportSymbol {
|
||||
pub symbol: Identifier,
|
||||
pub alias: Option<Identifier>,
|
||||
@ -31,7 +31,7 @@ impl fmt::Display for ImportSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
// todo: remove this
|
||||
// TODO (collin): remove this
|
||||
impl fmt::Debug for ImportSymbol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.alias.is_some() {
|
||||
|
@ -4,7 +4,7 @@ use leo_ast::imports::Package as AstPackage;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Package {
|
||||
pub name: Identifier,
|
||||
pub access: PackageAccess,
|
||||
|
@ -4,7 +4,7 @@ use leo_ast::imports::PackageAccess as AstPackageAccess;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PackageAccess {
|
||||
Star(Span),
|
||||
SubPackage(Box<Package>),
|
||||
|
@ -38,6 +38,7 @@ use leo_ast::LeoAst;
|
||||
|
||||
use serde_json;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct LeoTypedAst {
|
||||
typed_ast: Program,
|
||||
}
|
||||
@ -55,8 +56,14 @@ impl LeoTypedAst {
|
||||
self.typed_ast
|
||||
}
|
||||
|
||||
/// Serializes the abstract syntax tree into a JSON string.
|
||||
/// Serializes the typed syntax tree into a JSON string.
|
||||
pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
|
||||
Ok(serde_json::to_string_pretty(&self.typed_ast)?)
|
||||
}
|
||||
|
||||
/// Deserializes the JSON string into a typed syntax tree.
|
||||
pub fn from_json_string(json: &str) -> Result<Self, serde_json::Error> {
|
||||
let typed_ast: Program = serde_json::from_str(json)?;
|
||||
Ok(Self { typed_ast })
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A simple program with statement expressions, program arguments and program returns.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Program {
|
||||
pub name: String,
|
||||
pub expected_inputs: Vec<FunctionInput>,
|
||||
|
1
typed/tests/mod.rs
Normal file
1
typed/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
mod serialization;
|
66
typed/tests/serialization/expected_typed_ast.json
Normal file
66
typed/tests/serialization/expected_typed_ast.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": "leo_typed_tree",
|
||||
"expected_inputs": [],
|
||||
"imports": [],
|
||||
"circuits": {},
|
||||
"functions": {
|
||||
"{\"name\":\"main\",\"span\":\"{\\\"text\\\":\\\" function main() {\\\",\\\"line\\\":1,\\\"start\\\":10,\\\"end\\\":14}\"}": {
|
||||
"function_name": "{\"name\":\"main\",\"span\":\"{\\\"text\\\":\\\" function main() {\\\",\\\"line\\\":1,\\\"start\\\":10,\\\"end\\\":14}\"}",
|
||||
"inputs": [],
|
||||
"returns": [],
|
||||
"statements": [
|
||||
{
|
||||
"Return": [
|
||||
[
|
||||
{
|
||||
"Add": [
|
||||
{
|
||||
"Implicit": [
|
||||
"1",
|
||||
{
|
||||
"text": " return 1 + 1",
|
||||
"line": 2,
|
||||
"start": 12,
|
||||
"end": 13
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Implicit": [
|
||||
"1",
|
||||
{
|
||||
"text": " return 1 + 1",
|
||||
"line": 2,
|
||||
"start": 16,
|
||||
"end": 17
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": " return 1 + 1",
|
||||
"line": 2,
|
||||
"start": 12,
|
||||
"end": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
{
|
||||
"text": " return 1 + 1",
|
||||
"line": 2,
|
||||
"start": 5,
|
||||
"end": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"span": {
|
||||
"text": " function main() {",
|
||||
"line": 1,
|
||||
"start": 1,
|
||||
"end": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {}
|
||||
}
|
79
typed/tests/serialization/json.rs
Normal file
79
typed/tests/serialization/json.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use leo_ast::LeoAst;
|
||||
use leo_typed::LeoTypedAst;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn to_typed_ast(program_filepath: &PathBuf) -> LeoTypedAst {
|
||||
// Loads the Leo code as a string from the given file path.
|
||||
let program_string = LeoAst::load_file(program_filepath).unwrap();
|
||||
|
||||
// Parses the Leo file and constructs an abstract syntax tree.
|
||||
let ast = LeoAst::new(&program_filepath, &program_string).unwrap();
|
||||
|
||||
// Parse the abstract syntax tree and constructs a typed syntax tree.
|
||||
let typed_ast = LeoTypedAst::new("leo_typed_tree", &ast);
|
||||
|
||||
typed_ast
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize() {
|
||||
// Construct a typed syntax tree from the given test file.
|
||||
let typed_ast = {
|
||||
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
program_filepath.push("tests/serialization/main.leo");
|
||||
|
||||
to_typed_ast(&program_filepath)
|
||||
};
|
||||
|
||||
// Serializes the typed syntax tree into JSON format.
|
||||
let serialized_typed_ast = typed_ast.to_json_string().unwrap();
|
||||
|
||||
// Load the expected typed syntax tree.
|
||||
let expected = include_str!("expected_typed_ast.json");
|
||||
|
||||
println!("{}", serialized_typed_ast);
|
||||
assert_eq!(expected, serialized_typed_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize() {
|
||||
// Load the expected typed syntax tree.
|
||||
let expected_typed_ast = {
|
||||
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
program_filepath.push("tests/serialization/main.leo");
|
||||
|
||||
to_typed_ast(&program_filepath)
|
||||
};
|
||||
|
||||
// Construct a typed syntax tree by deserializing a typed syntax tree JSON file.
|
||||
let serialized_typed_ast = include_str!("expected_typed_ast.json");
|
||||
let typed_ast = LeoTypedAst::from_json_string(serialized_typed_ast).unwrap();
|
||||
|
||||
assert_eq!(expected_typed_ast, typed_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_deserialize_serialize() {
|
||||
// Construct a typed syntax tree from the given test file.
|
||||
let typed_ast = {
|
||||
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
program_filepath.push("tests/serialization/main.leo");
|
||||
|
||||
to_typed_ast(&program_filepath)
|
||||
};
|
||||
|
||||
// Serializes the typed syntax tree into JSON format.
|
||||
let serialized_typed_ast = typed_ast.to_json_string().unwrap();
|
||||
|
||||
// Deserializes the typed syntax tree into a LeoTypedAst.
|
||||
let typed_ast = LeoTypedAst::from_json_string(&serialized_typed_ast).unwrap();
|
||||
|
||||
// Reserializes the typed syntax tree into JSON format.
|
||||
let reserialized_typed_ast = typed_ast.to_json_string().unwrap();
|
||||
|
||||
// Load the expected typed syntax tree.
|
||||
let expected = include_str!("expected_typed_ast.json");
|
||||
|
||||
assert_eq!(serialized_typed_ast, reserialized_typed_ast);
|
||||
}
|
3
typed/tests/serialization/main.leo
Normal file
3
typed/tests/serialization/main.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() {
|
||||
return 1 + 1
|
||||
}
|
1
typed/tests/serialization/mod.rs
Normal file
1
typed/tests/serialization/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
mod json;
|
Loading…
Reference in New Issue
Block a user