UNPKG

7.72 kBJavaScriptView Raw
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14'use strict';
15
16const APModelManager = require('@accordproject/ergo-compiler').APModelManager;
17
18const Field = require('composer-concerto').Field;
19
20const ModelFile = require('composer-concerto').ModelFile;
21
22const RelationshipDeclaration = require('composer-concerto').RelationshipDeclaration;
23
24const EnumDeclaration = require('composer-concerto').EnumDeclaration;
25
26const EnumValueDeclaration = require('composer-concerto').EnumValueDeclaration;
27
28const ClassDeclaration = require('composer-concerto').ClassDeclaration;
29
30const util = require('util');
31
32const debug = require('debug')('cicero:grammarvisitor');
33/**
34 * Converts composer models and types to Nearley rules
35 *
36 * @private
37 * @class
38 * @memberof module:cicero-core
39 */
40
41
42class GrammarVisitor {
43 /**
44 * Visitor design pattern
45 * @param {Object} thing - the object being visited
46 * @param {Object} parameters - the parameter
47 * @return {Object} the result of visiting or null
48 * @private
49 */
50 visit(thing, parameters) {
51 if (thing instanceof APModelManager) {
52 return this.visitModelManager(thing, parameters);
53 } else if (thing instanceof ModelFile) {
54 return this.visitModelFile(thing, parameters);
55 } else if (thing instanceof EnumDeclaration) {
56 return this.visitEnumDeclaration(thing, parameters);
57 } else if (thing instanceof ClassDeclaration) {
58 return this.visitClassDeclaration(thing, parameters);
59 } else if (thing instanceof Field) {
60 return this.visitField(thing, parameters);
61 } else if (thing instanceof RelationshipDeclaration) {
62 return this.visitRelationshipDeclaration(thing, parameters);
63 } else if (thing instanceof EnumValueDeclaration) {
64 return this.visitEnumValueDeclaration(thing, parameters);
65 } else {
66 throw new Error('Unrecognised type: ' + typeof thing + ', value: ' + util.inspect(thing, {
67 showHidden: true,
68 depth: 1
69 }));
70 }
71 }
72 /**
73 * Visitor design pattern
74 * @param {ModelManager} modelManager - the object being visited
75 * @param {Object} parameters - the parameter
76 * @return {Object} the result of visiting or null
77 * @private
78 */
79
80
81 visitModelManager(modelManager, parameters) {
82 debug('entering visitModelManager'); // Save the model manager so that we have access to it later.
83
84 parameters.modelManager = modelManager; // Visit all of the files in the model manager.
85
86 modelManager.getModelFiles().forEach(modelFile => {
87 modelFile.accept(this, parameters);
88 });
89 return null;
90 }
91 /**
92 * Visitor design pattern
93 * @param {ModelFile} modelFile - the object being visited
94 * @param {Object} parameters - the parameter
95 * @return {Object} the result of visiting or null
96 * @private
97 */
98
99
100 visitModelFile(modelFile, parameters) {
101 debug('entering visitModelFile', modelFile.getNamespace()); // Save the model file so that we have access to it later.
102
103 parameters.modelFile = modelFile; // Visit all of class declarations, but ignore the abstract ones and system ones.
104
105 modelFile.getAllDeclarations().filter(declaration => {
106 return !(declaration.isAbstract() || declaration.isSystemType());
107 }).forEach(declaration => {
108 declaration.accept(this, parameters);
109 });
110 return null;
111 }
112 /**
113 * Visitor design pattern
114 * @param {EnumDeclaration} enumDeclaration - the object being visited
115 * @param {Object} parameters - the parameter
116 * @return {Object} the result of visiting or null
117 * @private
118 */
119
120
121 visitEnumDeclaration(enumDeclaration, parameters) {
122 let result = '';
123 enumDeclaration.getOwnProperties().forEach(property => {
124 if (result.length > 0) {
125 result += ' | ';
126 }
127
128 result += property.accept(this, parameters);
129 });
130 parameters.rules.push({
131 prefix: enumDeclaration.getName(),
132 symbols: [result],
133 properties: false
134 });
135 return result;
136 }
137 /**
138 * Visitor design pattern
139 * @param {ClassDeclaration} classDeclaration - the object being visited
140 * @param {Object} parameters - the parameter
141 * @return {Object} the result of visiting or null
142 * @private
143 */
144
145
146 visitClassDeclaration(classDeclaration, parameters) {
147 debug('entering visitClassDeclaration', classDeclaration.getName()); // do not visit the template model itself, as we need to generate
148 // that from the template grammar, including all the source text.
149
150 if (!classDeclaration.getDecorator('AccordTemplateModel') && // TODO (MCR) Why is the following line needed?
151 // Ignore classes that have no fields
152 classDeclaration.getProperties().length > 0) {
153 let result = []; // Walk over all of the properties of this class and its super classes.
154
155 classDeclaration.getProperties().forEach(property => {
156 if (result.length > 0) {
157 result.push(' __ ');
158 }
159
160 result.push(property.accept(this, parameters));
161 }); // populate all the properties
162
163 const properties = [];
164 classDeclaration.getProperties().forEach((property, index) => {
165 const sep = index < classDeclaration.getProperties().length - 1 ? ',' : '';
166 properties.push("".concat(property.getName(), " : data[").concat(index * 2, "]").concat(sep));
167 });
168 parameters.rules.push({
169 prefix: classDeclaration.getName(),
170 class: classDeclaration.getFullyQualifiedName(),
171 symbols: result,
172 properties
173 });
174 return null;
175 }
176 }
177 /**
178 * Visitor design pattern
179 * @param {Field} field - the object being visited
180 * @param {Object} parameters - the parameter
181 * @return {Object} the result of visiting or null
182 * @private
183 */
184
185
186 visitField(field, parameters) {
187 debug('entering visitField', field.getName());
188 let qualifier = '';
189
190 if (field.isArray()) {
191 if (field.isOptional()) {
192 qualifier = ':*';
193 } else {
194 qualifier = ':+';
195 }
196 } else {
197 if (field.isOptional()) {
198 qualifier = ':?';
199 }
200 }
201
202 return this.toGrammarType(field.getType()) + qualifier;
203 }
204 /**
205 * Visitor design pattern
206 * @param {EnumValueDeclaration} enumValueDeclaration - the object being visited
207 * @param {Object} parameters - the parameter
208 * @return {Object} the result of visiting or null
209 * @private
210 */
211
212
213 visitEnumValueDeclaration(enumValueDeclaration, parameters) {
214 debug('entering visitEnumValueDeclaration', enumValueDeclaration.getName());
215 return "\"".concat(enumValueDeclaration.getName(), "\" {% id %}");
216 }
217 /**
218 * Visitor design pattern
219 * @param {Relationship} relationship - the object being visited
220 * @param {Object} parameters - the parameter
221 * @return {Object} the result of visiting or null
222 * @private
223 */
224
225
226 visitRelationshipDeclaration(relationship, parameters) {
227 debug('entering visitRelationshipDeclaration', relationship.getName());
228 return 'String';
229 }
230 /**
231 * Converts a Composer type to a Nearley grammar type.
232 * @param {string} type - the composer type
233 * @return {string} the corresponding type to use for Nearley
234 * @private
235 */
236
237
238 toGrammarType(type) {
239 return type;
240 }
241
242}
243
244module.exports = GrammarVisitor;
\No newline at end of file