Skip to content

Commit

Permalink
Add support for writing SExpression in code generation (#107)
Browse files Browse the repository at this point in the history
* Modifies `AbstractDataType::Sequence` to store sequence type (sexp or
list) and element type
* Adds changes for using a `SequenceType` enum in `AbstractDataType::Sequence`
* Adds `element_type()` and `sequence_type()` methods on
`AbstractDataType` to get element type and sequence type details
* Renames and modifies `verify_abstract_data_type_consistency` to
`verify_and_update_abstract_data_type`
* `JavaLanguage` and `RustLanguage` updated `target_type` to return
generic type for `List` and `SExp`
* Adds `wrapper_class` in `JavaLanguage` to be sued within `ArrayList`
* Adds template to generate `SerdeResult` in Rust
* Adds template file changes for SExp support
* Modifies class and struct template to add check for sequence type in
write_to API
* Adds new test and ISL files for SExp support
* Adds changes to Java setter tests
  • Loading branch information
desaikd authored May 9, 2024
1 parent 515bd90 commit 7f5a92c
Show file tree
Hide file tree
Showing 24 changed files with 436 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// nested struct with mismatched sequence type
{
A: "hello",
B: 12,
C: {
D: false,
E: (1 2 3) // expected list
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
B: 12,
C: {
D: 1e0, // expected type: bool
E: [1, 2, 3]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
A: "hello",
B: 12,
C: [1, 2, 3],
C: (1 2 3),
D: 10e2
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// simple struct with type mismatched sequence type
{
A: "hello",
B: 12,
C: ["foo", "bar", "baz"], // expected sexp
D: 10e2
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
A: "hello",
B: false, // expected field type: int
C: ["foo", "bar", "baz"],
C: ("foo" "bar" "baz"),
D: 10e2
}

3 changes: 2 additions & 1 deletion code-gen-projects/input/good/nested_struct/empty_values.ion
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// nested struct with empty string and zeros
// nested struct with empty string, list and zeros
{
C: {
D: false,
E: [],
},
A: "",
B: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
B: 12,
C: {
D: false,
E: [1, 2, 3]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
A: "hello",
C: {
D: false,
E: [1, 2, 3]
}
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// struct with empty list, empty string and zeros
{
C: [],
C: (),
A: "",
B: 0,
D: 0e0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
{
A: "hello",
B: 12,
C: ["foo", "bar", "baz"],
C: ("foo" "bar" "baz"),
D: 10e2
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

// struct with unordered fields
{
C: ["foo", "bar", "baz"],
C: ("foo" "bar" "baz"),
A: "hello",
B: 12,
D: 10e2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class CodeGenTest {
a.add("foo");
a.add("bar");
a.add("baz");
StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a), 10e2);
StructWithFields s = new StructWithFields("hello", 12, new AnonymousType3(a), 10e2);

// getter tests for `StructWithFields`
assertEquals("hello", s.getA(), "s.getA() should return \"hello\"");
Expand All @@ -39,18 +39,23 @@ class CodeGenTest {
assertEquals("hi", s.getA(), "s.getA() should return \"hi\"");
s.setB(6);
assertEquals(6, s.getB(), "s.getB() should return `6`");
s.setC(new AnonymousType2(new ArrayList<String>()));
s.setC(new AnonymousType3(new ArrayList<String>()));
assertEquals(true, s.getC().getValue().isEmpty(), "s.getC().isEmpty() should return `true`");
s.setD(11e3);
assertEquals(11e3 ,s.getD(), "s.getD() should return `11e3`");
}

@Test void getterAndSetterTestForNestedStruct() {
// getter tests for `NestedStruct`
NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false));
ArrayList<Integer> a = new ArrayList<Integer>();
a.add(1);
a.add(2);
a.add(3);
NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false, new AnonymousType2(a)));
assertEquals("hello", n.getA(), "n.getA() should return \"hello\"");
assertEquals(12, n.getB(), "n.getB() should return `12`");
assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`");
assertEquals(3, n.getC().getE().getValue().size(), "n.getC().getE().getValue().size() should return ArrayList fo size 3");

// setter tests for `NestedStruct`
n.setA("hi");
Expand Down
16 changes: 8 additions & 8 deletions code-gen-projects/rust/code-gen-demo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ mod tests {
}

#[test_resources("../../input/good/struct_with_fields/**/*.ion")]
fn roundtrip_good_test_generated_code_structs_with_fields(file_name: &str) -> IonResult<()> {
let ion_string = fs::read_to_string(file_name)?;
fn roundtrip_good_test_generated_code_structs_with_fields(file_name: &str) -> SerdeResult<()> {
let ion_string = fs::read_to_string(file_name).unwrap();
let mut reader = ReaderBuilder::new().build(ion_string.clone())?;
let mut buffer = Vec::new();
let mut text_writer = TextWriterBuilder::default().build(&mut buffer)?;
Expand All @@ -42,8 +42,8 @@ mod tests {
}

#[test_resources("../../input/bad/struct_with_fields/**/*.ion")]
fn roundtrip_bad_test_generated_code_structs_with_fields(file_name: &str) -> IonResult<()> {
let ion_string = fs::read_to_string(file_name)?;
fn roundtrip_bad_test_generated_code_structs_with_fields(file_name: &str) -> SerdeResult<()> {
let ion_string = fs::read_to_string(file_name).unwrap();
let mut reader = ReaderBuilder::new().build(ion_string.clone())?;
// read given Ion value using Ion reader
reader.next()?;
Expand All @@ -54,8 +54,8 @@ mod tests {
}

#[test_resources("../../input/good/nested_struct/**/*.ion")]
fn roundtrip_good_test_generated_code_nested_structs(file_name: &str) -> IonResult<()> {
let ion_string = fs::read_to_string(file_name)?;
fn roundtrip_good_test_generated_code_nested_structs(file_name: &str) -> SerdeResult<()> {
let ion_string = fs::read_to_string(file_name).unwrap();
let mut reader = ReaderBuilder::new().build(ion_string.clone())?;
let mut buffer = Vec::new();
let mut text_writer = TextWriterBuilder::default().build(&mut buffer)?;
Expand All @@ -75,8 +75,8 @@ mod tests {
}

#[test_resources("../../input/bad/nested_struct/**/*.ion")]
fn roundtrip_bad_test_generated_code_nested_structs(file_name: &str) -> IonResult<()> {
let ion_string = fs::read_to_string(file_name)?;
fn roundtrip_bad_test_generated_code_nested_structs(file_name: &str) -> SerdeResult<()> {
let ion_string = fs::read_to_string(file_name).unwrap();
let mut reader = ReaderBuilder::new().build(ion_string.clone())?;
// read given Ion value using Ion reader
reader.next()?;
Expand Down
3 changes: 2 additions & 1 deletion code-gen-projects/schema/nested_struct.isl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ type::{
B: int,
C: {
fields: {
D: bool
D: bool,
E: { type: list, element: int }
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion code-gen-projects/schema/struct_with_fields.isl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type::{
fields: {
A: string,
B: int,
C: { element: string },
C: { element: string, type: sexp },
D: float,
}
}
Expand Down
36 changes: 33 additions & 3 deletions src/bin/ion/commands/beta/generate/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ pub enum AbstractDataType {
// }
// ```
Value,
// A series of zero or more values whose type is described by the nested `String` (e.g. a list)
// A series of zero or more values whose type is described by the nested `element_type`
// and sequence type is described by nested `sequence_type` (e.g. List or SExp).
// If there is no `element` constraint present in schema type then `element_type` will be None.
// If there is no `type` constraint present in schema type then `sequence_type` will be None.
// e.g. Given below ISL,
// ```
// type::{
Expand All @@ -52,7 +55,10 @@ pub enum AbstractDataType {
// value: Vec<i64>
// }
// ```
Sequence(String),
Sequence {
element_type: Option<String>,
sequence_type: Option<SequenceType>,
},
// A collection of field name/value pairs (e.g. a map)
// the nested boolean represents whether the struct has closed fields or not
// e.g. Given below ISL,
Expand All @@ -75,16 +81,40 @@ pub enum AbstractDataType {
Structure(bool),
}

impl AbstractDataType {
pub fn element_type(&self) -> Option<String> {
match self {
AbstractDataType::Sequence { element_type, .. } => element_type.to_owned(),
_ => None,
}
}

pub fn sequence_type(&self) -> Option<SequenceType> {
match self {
AbstractDataType::Sequence { sequence_type, .. } => sequence_type.to_owned(),
_ => None,
}
}
}

impl Display for AbstractDataType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
AbstractDataType::Value => "single value struct",
AbstractDataType::Sequence(_) => "sequence value struct",
AbstractDataType::Sequence { .. } => "sequence value struct",
AbstractDataType::Structure(_) => "struct",
}
)
}
}

/// Represents a sequenced type which could either be a list or s-expression.
/// This is used by `AbstractDataType` to represent sequence type for `Sequence` variant.
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum SequenceType {
List,
SExp,
}
Loading

0 comments on commit 7f5a92c

Please sign in to comment.