| var List = require('../common/List'); | 
| var hasOwnProperty = Object.prototype.hasOwnProperty; | 
|   | 
| function isValidNumber(value) { | 
|     // Number.isInteger(value) && value >= 0 | 
|     return ( | 
|         typeof value === 'number' && | 
|         isFinite(value) && | 
|         Math.floor(value) === value && | 
|         value >= 0 | 
|     ); | 
| } | 
|   | 
| function isValidLocation(loc) { | 
|     return ( | 
|         Boolean(loc) && | 
|         isValidNumber(loc.offset) && | 
|         isValidNumber(loc.line) && | 
|         isValidNumber(loc.column) | 
|     ); | 
| } | 
|   | 
| function createNodeStructureChecker(type, fields) { | 
|     return function checkNode(node, warn) { | 
|         if (!node || node.constructor !== Object) { | 
|             return warn(node, 'Type of node should be an Object'); | 
|         } | 
|   | 
|         for (var key in node) { | 
|             var valid = true; | 
|   | 
|             if (hasOwnProperty.call(node, key) === false) { | 
|                 continue; | 
|             } | 
|   | 
|             if (key === 'type') { | 
|                 if (node.type !== type) { | 
|                     warn(node, 'Wrong node type `' + node.type + '`, expected `' + type + '`'); | 
|                 } | 
|             } else if (key === 'loc') { | 
|                 if (node.loc === null) { | 
|                     continue; | 
|                 } else if (node.loc && node.loc.constructor === Object) { | 
|                     if (typeof node.loc.source !== 'string') { | 
|                         key += '.source'; | 
|                     } else if (!isValidLocation(node.loc.start)) { | 
|                         key += '.start'; | 
|                     } else if (!isValidLocation(node.loc.end)) { | 
|                         key += '.end'; | 
|                     } else { | 
|                         continue; | 
|                     } | 
|                 } | 
|   | 
|                 valid = false; | 
|             } else if (fields.hasOwnProperty(key)) { | 
|                 for (var i = 0, valid = false; !valid && i < fields[key].length; i++) { | 
|                     var fieldType = fields[key][i]; | 
|   | 
|                     switch (fieldType) { | 
|                         case String: | 
|                             valid = typeof node[key] === 'string'; | 
|                             break; | 
|   | 
|                         case Boolean: | 
|                             valid = typeof node[key] === 'boolean'; | 
|                             break; | 
|   | 
|                         case null: | 
|                             valid = node[key] === null; | 
|                             break; | 
|   | 
|                         default: | 
|                             if (typeof fieldType === 'string') { | 
|                                 valid = node[key] && node[key].type === fieldType; | 
|                             } else if (Array.isArray(fieldType)) { | 
|                                 valid = node[key] instanceof List; | 
|                             } | 
|                     } | 
|                 } | 
|             } else { | 
|                 warn(node, 'Unknown field `' + key + '` for ' + type + ' node type'); | 
|             } | 
|   | 
|             if (!valid) { | 
|                 warn(node, 'Bad value for `' + type + '.' + key + '`'); | 
|             } | 
|         } | 
|   | 
|         for (var key in fields) { | 
|             if (hasOwnProperty.call(fields, key) && | 
|                 hasOwnProperty.call(node, key) === false) { | 
|                 warn(node, 'Field `' + type + '.' + key + '` is missed'); | 
|             } | 
|         } | 
|     }; | 
| } | 
|   | 
| function processStructure(name, nodeType) { | 
|     var structure = nodeType.structure; | 
|     var fields = { | 
|         type: String, | 
|         loc: true | 
|     }; | 
|     var docs = { | 
|         type: '"' + name + '"' | 
|     }; | 
|   | 
|     for (var key in structure) { | 
|         if (hasOwnProperty.call(structure, key) === false) { | 
|             continue; | 
|         } | 
|   | 
|         var docsTypes = []; | 
|         var fieldTypes = fields[key] = Array.isArray(structure[key]) | 
|             ? structure[key].slice() | 
|             : [structure[key]]; | 
|   | 
|         for (var i = 0; i < fieldTypes.length; i++) { | 
|             var fieldType = fieldTypes[i]; | 
|             if (fieldType === String || fieldType === Boolean) { | 
|                 docsTypes.push(fieldType.name); | 
|             } else if (fieldType === null) { | 
|                 docsTypes.push('null'); | 
|             } else if (typeof fieldType === 'string') { | 
|                 docsTypes.push('<' + fieldType + '>'); | 
|             } else if (Array.isArray(fieldType)) { | 
|                 docsTypes.push('List'); // TODO: use type enum | 
|             } else { | 
|                 throw new Error('Wrong value `' + fieldType + '` in `' + name + '.' + key + '` structure definition'); | 
|             } | 
|         } | 
|   | 
|         docs[key] = docsTypes.join(' | '); | 
|     } | 
|   | 
|     return { | 
|         docs: docs, | 
|         check: createNodeStructureChecker(name, fields) | 
|     }; | 
| } | 
|   | 
| module.exports = { | 
|     getStructureFromConfig: function(config) { | 
|         var structure = {}; | 
|   | 
|         if (config.node) { | 
|             for (var name in config.node) { | 
|                 if (hasOwnProperty.call(config.node, name)) { | 
|                     var nodeType = config.node[name]; | 
|   | 
|                     if (nodeType.structure) { | 
|                         structure[name] = processStructure(name, nodeType); | 
|                     } else { | 
|                         throw new Error('Missed `structure` field in `' + name + '` node type definition'); | 
|                     } | 
|                 } | 
|             } | 
|         } | 
|   | 
|         return structure; | 
|     } | 
| }; |