Skip to content

Commit

Permalink
Added multi-level accessor
Browse files Browse the repository at this point in the history
  • Loading branch information
valkjsaaa committed Nov 30, 2023
1 parent 89a968d commit 606e8ba
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 83 deletions.
6 changes: 3 additions & 3 deletions lib/__test__/example_descriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ export class Restaurant extends DataClass {
(Order.all().length + 1).toString(),
DateTime.CreateObject({
year: 2020,
month: 1,
month: 2,
day: 1,
hour: 13,
minute: 0,
Expand All @@ -526,7 +526,7 @@ export class Restaurant extends DataClass {
DateTime.CreateObject({
year: 2020,
month: 2,
day: 1,
day: 3,
hour: 14,
minute: 0,
}),
Expand All @@ -541,7 +541,7 @@ export class Restaurant extends DataClass {
DateTime.CreateObject({
year: 2020,
month: 1,
day: 1,
day: 4,
hour: 15,
minute: 0,
}),
Expand Down
12 changes: 12 additions & 0 deletions lib/dsl/__test__/dsl-interpreter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ test("Array between one side", async () => {
});
});

test("Array between one side deep accessor", async () => {
const interpreter = new DslInterpreter(allDescriptors);
const funcCallResult = await interpreter.interpret(
"Order.all().between(field: .dateTime.day, from: 2, to: 3)[-1].dateTime.day"
);
expect(funcCallResult).toEqual({
"objectType": "int",
"type": "object",
"value": 3
});
});

test("Array equals", async () => {
const interpreter = new DslInterpreter(allDescriptors);
const funcCallResult = await interpreter.interpret(
Expand Down
172 changes: 95 additions & 77 deletions lib/dsl/dsl-interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ import {
ParamDescriptor,
} from "../dsl-descriptor";

async function promiseOneByOne(promiseList: Promise<any>[]) {
let result = [];
for (const p of promiseList) {
result.push(await p);
}
return result;
}

function parseType(value_type: string): {
is_array: boolean;
original_type: string;
Expand Down Expand Up @@ -61,6 +53,11 @@ export class FunctionStaticError extends InterpreterError {
}
}

type ElementWithField = {
element: any,
fieldValue: any,
}

export class DslInterpreter {
// region array functions

Expand Down Expand Up @@ -179,51 +176,45 @@ export class DslInterpreter {
];
// implementations of functions
private arrayFunctionImplementations: {
[name: string]: (...args: any[]) => any;
[name: string]: (...args: any[]) => Promise<any>;
} = {
matching: ({
field,
matching: async ({
value,
array,
}: {
field: any;
value: any;
array: any[];
array: ElementWithField[];
}) => {
console.assert(field.type === "accessor");
const fieldName = field.field;
return array.filter((v) => this.matching(v[fieldName], value));
return array.filter((element) => {
return this.matching(element.fieldValue, value);
}).map((v) => v.element);
},
contains: ({
field,
contains: async({
value,
array,
}: {
field: any;
field: (element: any) => Promise<any>;
value: any;
array: any[];
array: ElementWithField[];
}) => {
console.assert(field.type === "accessor");
const fieldName = field.field;
return array.filter((v) => this.contains(v[fieldName], value));
return array.filter((element) => {
return this.contains(element.fieldValue, value);
}).map((v) => v.element);
},
between: ({
field,
between: async ({
from,
to,
array,
}: {
field: any;
from?: any;
to?: any;
array: any[];
array: ElementWithField[];
}) => {
console.assert(field.type === "accessor");
const fieldName = field.field;
return array.filter((v) => {
let newArray = [];
for (const element of array) {
let first: boolean = true;
let second: boolean = true;
let value = v[fieldName];
let value = element.fieldValue;
let valueIsPrimitive =
typeof value === "string" ||
typeof value === "number" ||
Expand Down Expand Up @@ -258,61 +249,58 @@ export class DslInterpreter {
second = to.constructor.compare(value, to) <= 0;
}
}
return first && second;
});
if (first && second) {
newArray.push(element.element);
}
}
return newArray;
},
equals: ({
field,
equals: async ({
value,
array,
}: {
field: any;
value: any;
array: any[];
array: ElementWithField[];
}) => {
console.assert(field.type === "accessor");
const fieldName = field.field;
return array.filter((v) => this.equals(v[fieldName], value));
return array.filter((element) => {
return this.equals(element.fieldValue, value);
}).map((v) => v.element);
},
sort: ({
field,
sort: async ({
ascending,
array,
}: {
field: any;
ascending: boolean;
array: any[];
array: ElementWithField[];
}) => {
console.assert(field.type === "accessor");
const fieldName = field.field;
return array.sort((a, b) => {
if (ascending) {
if (a[fieldName] < b[fieldName]) {
if (a.fieldValue < b.fieldValue) {
return -1;
} else if (a[fieldName] > b[fieldName]) {
} else if (a.fieldValue > b.fieldValue) {
return 1;
} else {
return 0;
}
} else {
if (a[fieldName] < b[fieldName]) {
if (a.fieldValue < b.fieldValue) {
return 1;
} else if (a[fieldName] > b[fieldName]) {
} else if (a.fieldValue > b.fieldValue) {
return -1;
} else {
return 0;
}
}
});
}).map((v) => v.element);
},
index: ({ index, array }: { index: any; array: any[] }) => {
index: async ({ index, array }: { index: any; array: any[] }) => {
console.assert(typeof index === "number");
if (index < 0) {
index = array.length + index;
}
return array[index];
},
length: ({ array }: { array: any[] }) => {
length: async ({ array }: { array: any[] }) => {
return array.length;
}
};
Expand Down Expand Up @@ -720,15 +708,6 @@ export class DslInterpreter {
}
}

if (isArray) {
for (const element of env.value) {
if ((element as any).type == "object" && !this.dry_run) {
await (element as any).value.update();
}
}
matchedParameters["array"] = this.strip(env);
}

// function is a class constructor
if (env === null) {
const classDescriptor = this.classDescriptors.find(
Expand All @@ -751,27 +730,66 @@ export class DslInterpreter {

const returnType = parseType(funcDescriptor.returnType);

const targetImplementation = isArray
? this.arrayFunctionImplementations
: this.strip(env);

if (isArray) {
if (ast.func_name != "index" && ast.func_name != "length") {
const classDescriptor = this.classDescriptors.find(
(c) => c.className === env.value[0].objectType
);
if (classDescriptor === undefined) {
throw new ClassMissingError(env.value[0].objectType)
for (const element of env.value) {
if ((element as any).type == "object" && !this.dry_run) {
await (element as any).value.update();
}
const fieldDescriptor = Array.from(classDescriptor.fields).find(
(f) => f.field === matchedParameters["field"].field
);
if (fieldDescriptor === undefined) {
throw new FieldMissingError(env.value[0].objectType, matchedParameters["field"].field)
}
matchedParameters["array"] = this.strip(env);
if (matchedParameters["field"] !== undefined) {
function constructAccess(accessor: any, parent: any) {
if (typeof accessor == "string") {
return {
type: "access",
parent: parent,
access: accessor,
};
} else {
if (accessor.type == "access") {
return {
type: "access",
parent: constructAccess(accessor.parent, parent),
access: accessor.access,
};
}
}
}
const field = matchedParameters["field"].field;
if (this.dry_run) {
// dry run for accessor
await this.resolve(constructAccess(field, {
type: "object",
value: null,
objectType: returnType.original_type,
}), null)
} else {
if (ast.func_name != "index" && ast.func_name != "length") {
const originalArray = matchedParameters["array"];
const array = [];
for (const element of originalArray) {
const fieldValue = await this.strip(await this.resolve(
constructAccess(field, {
type: "object",
value: element,
objectType: returnType.original_type,
})
));
array.push({
element: element,
fieldValue: fieldValue,
});
}
matchedParameters["array"] = array;
}
}
}
}

const targetImplementation = isArray
? this.arrayFunctionImplementations
: this.strip(env);

// call the function
if (returnType.is_array) {
if (this.dry_run) {
Expand Down
8 changes: 5 additions & 3 deletions lib/dsl/parser.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ parameter_list = pp:parameter_pair pl:(SP* ',' SP* parameter_pair)* {
}, [pp])};
}

parameter_pair = sy:symbol SP* ':' SP* v:value {
parameter_pair = sy:symbol SP* ':' SP* v:(value / accessor) {
return {"parameter": sy, "value": v};
}

Expand All @@ -95,14 +95,16 @@ value = 'true' {
return {"type": "int", "value": il};
} / s: all_symbol {
return s;
} / '.' s: symbol {
return {"type": "accessor", "field": s};
} / '"' s: [^"]* '"' {
return { type: "string", value: s.join("") };
} / '[' SP* s: array_value SP* ']' {
return { type: "array", value: s };
}

accessor = '.' s: all_symbol {
return {"type": "accessor", "field": s};
}

float_literal = s:("+" / "-")? i:[0-9]+ "." f:[0-9]+ {
return parseFloat((s === "-" ? "-" : "") + i.join("") + "." + f.join(""));
}
Expand Down

0 comments on commit 606e8ba

Please sign in to comment.