Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(parse-dsl): fix issue with infinite loops triggered when parsing some models #76

Merged
merged 4 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 24 additions & 19 deletions src/openfga.ne
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
@preprocessor typescript

types -> (_newline):* (type (relations (define):+):*):* {%
types -> (_newline):* (type (_relations (_multiline_comment define):+):*):* {%
// @ts-ignore
(data) => {
// @ts-ignore
const types = data[1].map((datum) => {
const relations = datum[1][0] ? datum[1][0][1].flat() : [];
// @ts-ignore
const relations = (datum[1] && datum[1][0] && datum[1][0][1].map(innerDatum => innerDatum[1])) || [];

return { ...datum[0], relations }
})
Expand All @@ -18,35 +19,39 @@ types -> (_newline):* (type (relations (define):+):*):* {%
}
%}

type -> _multiline_comment _type _naming (_newline):+ {%
type -> _multiline_comment _type _naming (_newline):+{%
data => ({ comment: data[0], type: data[2] })
%}
relations -> _multiline_comment _relations {%
data => data[1]
relations -> _relations {%
data => data[0]
%}
define -> (_newline):+ _multiline_comment define_initial (_relation_types):? _as (define_base | define_or | define_and | define_but_not) (_newline):* {%
define -> _newline define_initial (_spacing | _relation_types) _as _spacing (define_base | define_or | define_and | define_but_not) (_newline):* {%
(data, _location, reject) => {
const relation = data[2];

const relation = data[1];
// Not supported yet
const comment = "";
const def = data[5][0];
const definition = def.type ? def :
{
type: 'single',
targets: [def]
}
const allowedTypes = data[3] ? data[3][0] : []
let allowedTypes = data[2] ? data[2][0] : [];
if (allowedTypes.length && typeof allowedTypes[0] !== "string") {
allowedTypes = [];
}

return { comment: data[1], allowedTypes, relation, definition };
return { comment, allowedTypes, relation, definition };
}
%}

define_initial -> _define _naming {%
data => data[1]
%}

define_base -> _optional_space (_naming | from_phrase) _optional_space {%
define_base -> (_naming | from_phrase) {%
data => {
const entry = data[1][0];
const entry = data[0][0];
let target, rewrite, from
if (typeof entry === "string") {
if (entry === "self") {
Expand All @@ -72,8 +77,8 @@ define_and -> define_base (_spacing _and _spacing define_base):+ {%
// @ts-ignore
data => ({ targets: [data[0], ...data[1].map((datum) => datum[3])], type: "intersection" })
%}
define_but_not -> define_base _but_not define_base {%
data => ({ base: data[0], diff: data[2], type: "exclusion" })
define_but_not -> define_base _spacing _but_not _spacing define_base {%
data => ({ base: data[0], diff: data[4], type: "exclusion" })
%}
from_phrase -> _naming _spacing _from _spacing _naming {%
data => ({ target: data[0], from: data[4] })
Expand All @@ -82,15 +87,15 @@ from_phrase -> _naming _spacing _from _spacing _naming {%
_multiline_comment -> (_comment):* {%
data => data.flat(3).join('\n')
%}
_comment -> " ":* "#" _spacing _naming (_spacing _word):* _newline {%
_comment -> (_newline):? _optional_space "#" _optional_space _word (_spacing _word):* (_newline):? {%
data => data.flat(3).join('').trim().substring(1).trim()
%}
_word -> ([a-z] | [A-Z] | [0-9] | "_" | "-" | "," | "&" | "+" | "/" | "$" ):+ _optional_space {%
data => data.flat(3).join('').trim()
%}

_relation_types -> ":" _optional_space "[" _array_of_types "]" _spacing {%
data => data[3]
_relation_types -> _optional_space ":" _optional_space "[" _array_of_types "]" _spacing {%
data => data[4]
%}

_array_of_types -> ("$"):? ([a-zA-Z0-9_#\-,\s]):+ {%
Expand All @@ -105,10 +110,10 @@ _but_not -> "but not"

_self -> "self"
_define -> " define" _spacing
_relations -> " relations" _optional_space
_relations -> " relations" (_newline):*
_type -> "type" _spacing
_no_relations -> "none" (_newline):*
_naming -> (("$"):? ( [a-z] | [A-Z] | [0-9] | "_" | "-" ):+) _optional_space {%
_naming -> (("$"):? ( [a-z] | [A-Z] | [0-9] | "_" | "-" ):+) {%
data => data.flat(3).join('').trim()
%}
_optional_space -> " ":*
Expand Down
15 changes: 12 additions & 3 deletions src/parse-dsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,19 @@ export interface ParserResult {
types: TypeDefParserResult[];
}

export const parseDSL = (code: string): ParserResult => {
export const innerParseDSL = (code: string): ParserResult[] => {
const parser = new Parser(Grammar.fromCompiled(grammar));
parser.feed(code.trim() + "\n");
return parser.results[0] || [];
const cleanedCode =
code
.split("\n")
.map((line) => line.trimEnd())
.join("\n") + "\n";
parser.feed(cleanedCode);
return parser.results;
};

export const parseDSL = (code: string): ParserResult => {
return innerParseDSL(code)[0] || [];
};

// The TransformedType allows us to quickly access the various relations unique by
Expand Down
8 changes: 4 additions & 4 deletions tests/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ exports[`apiSyntaxToFriendlySyntax should correctly read from \`or\` definition
"
`;

exports[`apiSyntaxToFriendlySyntax should read a complex definition 1`] = `
exports[`apiSyntaxToFriendlySyntax should read a complex definition 4 1`] = `
"type folder
relations
define deleter as self
Expand Down Expand Up @@ -212,7 +212,7 @@ exports[`friendlySyntaxToApiSyntax() should correctly read from \`or\` definitio
}
`;

exports[`friendlySyntaxToApiSyntax() should read a complex definition 1`] = `
exports[`friendlySyntaxToApiSyntax() should read a complex definition 1 1`] = `
{
"schema_version": "1.0",
"type_definitions": [
Expand Down Expand Up @@ -327,7 +327,7 @@ exports[`friendlySyntaxToApiSyntax() should read a complex definition 1`] = `
}
`;

exports[`friendlySyntaxToApiSyntax() should read a complex definition 2`] = `
exports[`friendlySyntaxToApiSyntax() should read a complex definition 2 1`] = `
{
"schema_version": "1.0",
"type_definitions": [
Expand Down Expand Up @@ -415,7 +415,7 @@ exports[`friendlySyntaxToApiSyntax() should read a complex definition 2`] = `
}
`;

exports[`friendlySyntaxToApiSyntax() should read a complex definition 3`] = `
exports[`friendlySyntaxToApiSyntax() should read a complex definition 3 1`] = `
{
"schema_version": "1.0",
"type_definitions": [
Expand Down
Loading