@spiralup/jfl-parser
Version:
Parser for JHipster Form definition Language
416 lines (354 loc) • 12.3 kB
Plain Text
/**
* Grammar used to generate JHipster Form DSL.
*/
{
function addUniqueElements(array1, array2) {
if (array2) {
for (let i = 0; i < array2.length; i++) {
if (array1.indexOf(array2[i]) === -1) {
array1.push(array2[i]);
}
}
}
return array1;
}
function addElements(array1, array2) {
if (array2) {
for (let i = 0; i < array2.length; i++) {
array1.push(array2[i]);
}
}
return array1;
}
function flattenArray(array) {
var newArray = [];
for (let i = 0; i < array.length; i ++) {
if (!array[i].length) {
newArray.push(array[i]);
} else {
for (let j = 0; j < array[i].length; j++) {
newArray.push(array[i][j]);
}
}
}
return newArray;
}
function addCardinalityToRelationships(cardinality, relationships) {
if (!relationships) {
return;
}
for (let i = 0; i < relationships.length; i++) {
relationships[i].cardinality = cardinality;
}
}
function formatComment(comment) {
if (!comment) {
return undefined;
}
var parts = comment.trim().split('\n');
if (parts.length === 1 && parts[0].indexOf('*') !== 0) {
return parts[0];
}
return parts.reduce(function(previousValue, currentValue) {
// newlines in the middle of the comment should stay to achieve:
// multiline comments entered by user drive unchanged from JDL studio to generated domain class
var delimiter = '';
if (previousValue !== '') {
delimiter = '\n';
}
return previousValue.concat(delimiter, currentValue.trim().replace(/[*]*\s*/, ''));
}, '');
}
const parsed = {
forms: [],
menus: [],
};
}
start = p:prog { return p; }
prog
= SPACE* fd:formDecl SPACE* p:prog {
parsed.forms = addUniqueElements([fd], parsed.forms);
return parsed;
}
/ SPACE* md:menuDecl SPACE* p:prog {
parsed.menus = flattenArray(addUniqueElements([md], parsed.menus));
return parsed;
}
/ '' { return parsed; }
// forms
formDecl
= jd:comment?
SPACE* FORM SPACE* f:FORM_NAME
SPACE* t:optionalTitle?
SPACE* fb:formBody? SPACE* {
return { name: f, title: t, body: fb, comment: formatComment(jd) };
}
/ FORM SPACE* f:FORM_NAME SPACE* fb:formBody? { return { name: f, title: t, body: fb, comment: '' }; }
optionalTitle
= TITLE SPACE* t:title {return t}
formBody
= '{'
SPACE* e:formBodyEntity?
SPACE* t:formBodyTemplate?
SPACE* pfl:formBodyParentFields?
SPACE* lfl:formBodyLinkedFields?
SPACE* prop:formBodyProperties?
SPACE* fdl:formBodyFields?
SPACE* vdl:formBodyViews?
SPACE* sdl:subformDeclList?
SPACE* '}' { return {entity:e, template:t, properties:prop, parent:pfl, linked:lfl, fields:fdl, views:vdl, subforms:sdl} ; }
/ '' { return { entity:"", template:"", properties:[], parent:[], linked:[], fields:[], views:[], subforms:[] }; }
formBodyEntity
= ENTITY SPACE* e:ENTITY_NAME { return e; }
/ '' { return "" }
formBodyTemplate
= TEMPLATE SPACE* e:title { return e; }
/ '' { return "" }
formBodyParentFields
= PARENT SPACE* FIELDS SPACE* pfl:exactFieldAndConstantList { return pfl; }
/ '' { return []; }
formBodyLinkedFields
= LINKED SPACE* FIELDS SPACE* lfl:exactFieldList { return lfl; }
/ '' { return []; }
formBodyFields
= FIELDS SPACE* '{' fdl:fieldDeclList '}' SPACE* ','? { return fdl;}
/ '' { return []; }
formBodyProperties
= PROPERTIES SPACE* '{' prop:propertyDeclList '}' SPACE* ','? { return prop;}
/ '' { return []; }
formFieldProperties
= '{' prop:propertyDeclList '}' SPACE* ','? { return prop;}
/ '' { return []; }
formBodyViews
= VIEWS SPACE* '{'
SPACE* vd:viewDeclList?
'}' SPACE* ','? { return vd;}
/ '' { return []; }
viewDeclList
= SPACE* vd:viewDecl ','? SPACE* vdl:viewDeclList {
return addUniqueElements([vd], vdl );
}
/ '' { return []; }
subformDeclList
= SPACE* fd:formDecl ','? SPACE* fdl:subformDeclList {
return addUniqueElements([fd], fdl );
}
/ '' { return []; }
fieldDecl
= com:comment?
SPACE* f:FIELD_NAME
SPACE* t:optionalTitle?
SPACE* vl:validationList?
SPACE* dflt:fieldDefault?
SPACE* props:formFieldProperties?
SPACE_WITHOUT_NEWLINE* com2:comment?
{ return { name: f, title: t, validations: vl, default: dflt, properties:props, comment: formatComment( com || com2) }; }
fieldDeclList
= SPACE* fd:fieldDecl
SPACE_WITHOUT_NEWLINE* ','? SPACE* fdl:fieldDeclList
{
return addUniqueElements([fd], fdl );
}
/ '' { return []; }
validationList
= v:validation SPACE_WITHOUT_NEWLINE* vl:validationList { return addUniqueElements([v], vl); }
/ '' { return []; }
title
= t:StringLiteral { return t }
fieldDefault
= DEFAULT SPACE* d:StringLiteral {return d}
/ '' { return null;}
// common way to declare an form list
formList
= f:FORM_NAME SPACE* sub:formListTail? { return addUniqueElements([f], sub); }
/ STAR SPACE* { return ['*']; }
/ ALL SPACE* { return ['*']; }
formListTail
= SPACE* ',' SPACE* f:FORM_NAME SPACE* sub:formListTail?
{ return addUniqueElements([f], sub)}
// field list
fieldList
=
ALL SPACE* { return ['*']; }
/ STAR SPACE* { return ['*']; }
/ f:FIELD_NAME SPACE* sub:fieldListTail? { return addUniqueElements([f], sub); }
exactFieldList
= f:FIELD_NAME SPACE* sub:fieldListTail? { return addUniqueElements([f], sub); }
exactFieldAndConstantList
= f:FIELD_NAME SPACE* sub:fieldAndConstantListTail? { return addUniqueElements([f], sub); }
/ f:NUMBER SPACE* sub:fieldAndConstantListTail? { return addElements([f], sub); }
/ f:StringLiteral SPACE* sub:fieldAndConstantListTail? { return addElements([f], sub); }
fieldAndConstantListTail
= SPACE* ',' SPACE* f:FIELD_NAME SPACE* sub:fieldAndConstantListTail? { return addUniqueElements([f], sub); }
/ SPACE* ',' SPACE* f:NUMBER SPACE* sub:fieldAndConstantListTail? { return addElements([f], sub); }
/ SPACE* ',' SPACE* f:StringLiteral SPACE* sub:fieldAndConstantListTail? { return addElements([f], sub); }
fieldListTail
= SPACE* ',' SPACE* f:FORM_NAME SPACE* sub:fieldListTail?
{ return addUniqueElements([f], sub)}
// exclusion
exclusion
= EXCEPT SPACE+ sub:exclusionSub { return sub; }
/ '' { return [];}
exclusionSub
= e:FORM_NAME SPACE* ',' SPACE* sub:exclusionSub { return addUniqueElements([e], sub); }
/ e:FORM_NAME { return [e]; }
// declaration of fields in view
viewFieldsList
= FIELDS SPACE+ decl:fieldList SPACE* ex:exclusion? SPACE*
{ return {included:decl, excluded:ex};}
// declaration of roles in view
viewRolesList
= ROLES SPACE+ decl:fieldList SPACE* ex:exclusion? SPACE* {
return {included:decl, excluded:ex};
}
// view declaration
viewDecl
= SPACE* SPACE* n:VIEW_NAME
SPACE* '{'
SPACE* fl:viewFieldsList?
SPACE* rl:viewRolesList?
SPACE* '}' SPACE*
{return {view:n, fields:fl, roles:rl };}
// property declaration
propertyValue
= s:StringLiteral {
return s;
}
/ f:FIELD_NAME { return f;}
/ d:NUMBER_DECIMAL { return d;}
/ n:NUMBER { return n;}
propertyDecl
= SPACE* p:PROPERTY_NAME
SPACE* ':'
SPACE* v:propertyValue
SPACE_WITHOUT_NEWLINE* com2:comment?
{ return { name: p, value: v, comment: formatComment( com2) }; }
propertyDeclList
= SPACE* fd:propertyDecl
SPACE_WITHOUT_NEWLINE* ','? SPACE* fdl:propertyDeclList
{
return addUniqueElements([fd], fdl );
}
/ '' { return []; }
// menus
menuDecl
= jd:comment? SPACE* MENU SPACE* m:MENU_NAME
SPACE_WITHOUT_NEWLINE* TITLE SPACE_WITHOUT_NEWLINE* t:title
SPACE_WITHOUT_NEWLINE* FORM SPACE_WITHOUT_NEWLINE* f:FORM_NAME
SPACE* p: menuParentDecl?
SPACE* mb:menuBody? SPACE* {
return { name: m, title: t, form:f, parent: p, body: mb, comment: formatComment(jd) };
}
/ jd:comment? SPACE* MENU SPACE* m:MENU_NAME
SPACE_WITHOUT_NEWLINE* TITLE SPACE_WITHOUT_NEWLINE* t:title
SPACE* p: menuParentDecl?
SPACE* mb:menuBody? SPACE* {
return { name: m, title: t, form:null, parent: p, body: mb, comment: formatComment(jd) };
}
/ MENU SPACE* m:MENU_NAME
SPACE* TITLE SPACE* t:title
SPACE* FORM SPACE* f:FORM_NAME
SPACE* p: menuParentDecl?
SPACE* mb:menuBody?
{ return { name: m, title: t, form:f, parent: p, body: mb, comment: formatComment(jd) }; }
menuBody
= '{' SPACE* mdl:menuDeclList SPACE* '}' { return mdl; }
/ '' { return []; }
menuDeclList
= SPACE* md:menuDecl ','? SPACE* mdl:menuDeclList {
return addUniqueElements([md], mdl );
}
/ '' { return []; }
menuParentDecl
= PARENT SPACE_WITHOUT_NEWLINE* p:MENU_NAME
{return p;}
// ------------------
StringLiteral "string"
= '"' chars:DoubleStringCharacter* '"' {
return chars.join("");
}
/ "'" chars:SingleStringCharacter* "'" {
return chars.join("");
}
DoubleStringCharacter
= !('"' / "\\" / LineTerminator) SourceCharacter { return text(); }
SingleStringCharacter
= !("'" / "\\" / LineTerminator) SourceCharacter { return text(); }
SourceCharacter
= .
LineTerminator
= [\n\r\u2028\u2029]
validation
= REQUIRED { return { key: 'required', value: '' }; }
/ MINLENGTH SPACE* '(' SPACE* int:INTEGER SPACE* ')' { return { key: 'minlength', value: int }; }
/ MAXLENGTH SPACE* '(' SPACE* int:INTEGER SPACE* ')' { return { key: 'maxlength', value: int }; }
/ MINBYTES SPACE* '(' SPACE* int:INTEGER SPACE* ')' { return { key: 'minbytes', value: int }; }
/ MAXBYTES SPACE* '(' SPACE* int:INTEGER SPACE* ')' { return { key: 'maxbytes', value: int }; }
/ MAX SPACE* '(' SPACE* int:INTEGER SPACE* ')' { return { key: 'max', value: int };}
/ MIN SPACE* '(' SPACE* int:INTEGER SPACE* ')' { return { key: 'min', value: int };}
/ PATTERN SPACE* '(' APOSTROPHE regex:REGEX APOSTROPHE SPACE* ')' { return { key: 'pattern', value: regex }; }
// Comments
comment = commentStart notAComment:notAComment* commentStop { return notAComment.join(''); }
commentStart = '/*' [*]*
commentStop = [*]+ '/'
// a completely ignored comment, will not be a Javadoc comment
notAComment = !commentStop !commentStart char:. { return char; }
// Constants
ENTITY = 'entity'
TABLE = 'table'
FORM = 'form'
SUBFORM = 'subform'
FIELDS = 'fields'
MENU = 'menu'
TITLE = 'title'
PARENT = 'parent'
//RELATIONSHIP = 'relationship'
//ENUM = 'enum'
// Relationship types
//ONE_TO_ONE = 'OneToOne'
//ONE_TO_MANY = 'OneToMany'
//MANY_TO_ONE = 'ManyToOne'
//MANY_TO_MANY = 'ManyToMany'
// Options
ALL = 'all'
STAR = '*'
FOR = 'for'
WITH = 'with'
EXCEPT = 'except'
// validations
REQUIRED = 'required'
MINLENGTH = 'minlength'
MAXLENGTH = 'maxlength'
MINBYTES = 'minbytes'
MAXBYTES = 'maxbytes'
MAX = 'max'
MIN = 'min'
PATTERN = 'pattern'
LIST = 'list'
EDIT = 'edit'
SHOW = 'show'
VIEW = 'view'
TEMPLATE = 'template'
VIEWS = 'views'
ROLES = 'roles'
LINKED = 'linked'
DEFAULT = 'default'
PROPERTIES = 'properties'
ENUMNAME = head:[A-Z]tail:[A-z0-9]* { return `${head}${tail.join('')}`; }
ENUMPROP = underscore:[_]*head:[A-Z0-9]tail:[A-Z0-9_]* {
return `${underscore.join('')}${head}${tail.join('')}`;
}
INTEGER = negative:'-'?int:[0-9]+ { return parseInt(`${(negative ? negative : '') + int.join('')}`, 10); }
INJECTED_FIELD_NAME = head:[a-zA-Z]tail:[A-z0-9()]* { return `${head}${tail.join('')}`; }
ENTITY_NAME = head:[A-Z]tail:[A-z0-9]* { return `${head}${tail.join('')}`; }
FORM_NAME = head:[a-zA-Z]tail:[A-z0-9]* { return `${head}${tail.join('')}`; }
VIEW_NAME = head:[a-zA-Z]tail:[A-z0-9]* { return `${head}${tail.join('')}`; }
MENU_NAME = head:[a-zA-Z]tail:[A-z0-9]* { return `${head}${tail.join('')}`; }
FIELD_NAME = head:[a-zA-Z]tail:[A-z.0-9]* { return `${head}${tail.join('')}`; }
PROPERTY_NAME = head:[a-zA-Z]tail:[A-z.0-9]* { return `${head}${tail.join('')}`; }
NUMBER = head:[0-9]tail:[0-9]* { return `${head}${tail.join('')}`; }
NUMBER_DECIMAL = head:[0-9]tail:[0-9]*point:[.]tailDec:[0-9]* { return `${head}${tail.join('')}.${tailDec.join('')}`; }
SPACE = ['\n'|'\t'|'\r'|' '|\u2028|\u2029]
SPACE_WITHOUT_NEWLINE = ['\t'|' '|\u2028|\u2029]
REGEX = word:[A-z0-9!@#$%^&*()_+\-=\[\]{};':\\|,.<>\/? ]* { return word.join('') }
APOSTROPHE = ["|']