var fs = require('fs'),
  path = require('path');

function unescape(key) {
  return key
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&amp;/g, '&');
}

function processLines(file, baseDir, lineNumber, key, lines, language, docs) {
  if (key.indexOf('# type:') === 0) {
    key = unescape(key.substr(7).trim());

    var type = language.types[key];
    if (!type) {
      throw new Error('In file ' + file + ' line ' + lineNumber +
        ': type "' + key + '" does not exist in the language.');
    }

    if (docs.types[key]) {
      throw new Error('In file ' + file + ' line ' + lineNumber +
        ': duplicate documentation for type "' + key + '"');
    }

    docs.types[key] = lines.join('\n');
    docs.source.types[key] = path.relative(baseDir, file);
  } else {
    key = unescape(key.substr(1).trim());

    var property = language.properties[key];
    if (!property) {
      throw new Error('In file ' + file + ' line ' + lineNumber +
        ': property "' + key + '" does not exist in the language.');
    }

    if (docs.properties[key]) {
      throw new Error('In file ' + file + ' line ' + lineNumber +
        ': duplicate documentation for property "' + key + '"');
    }

    docs.properties[key] = lines.join('\n').trim();
    docs.source.properties[key] = path.relative(baseDir, file);
  }
}

function parseFile(file, baseDir, language, docs) {
  var lineNumber, key, lines;

  fs.readFileSync(file).toString().split('\n').forEach(function(line, index) {
    if (line[0] === '#') {
      if (key) {
        processLines(file, baseDir, lineNumber, key, lines, language, docs);
      }

      key = line;
      lines = [];
      lineNumber = index + 1;
    } else {
      lines.push(line);
    }
  });

  if (key) {
    processLines(file, baseDir, lineNumber, key, lines, language, docs);
  }
}

function parseDir(folder, baseDir, language, docs) {
  var children = fs.readdirSync(folder).map(function(child) {
    return path.join(folder, child);
  });

  children.forEach(function(child) {
    var stats = fs.statSync(child);

    if (stats.isFile()) {
      parseFile(child, baseDir, language, docs);
    } else if (stats.isDirectory()) {
      parseDir(child, baseDir, language, docs);
    }
  });
}

function parse(folder, language) {
  var docs = {
    types: {},
    properties: {}
  };

  docs.source = {
    types: {},
    properties: {}
  };

  parseDir(folder, folder, language, docs);
  return docs;
}

module.exports = parse;

@@ -2240,24 +2240,705 @@
  %7D%0A  %7D);%0A%7D%0A%0A
+/**%0A * Check that every type and property is listed in the documentation.%0A */%0Afunction checkComplete(docs, language) %7B%0A  var complete = true;%0A%0A  Object.keys(language.types).forEach(function(type) %7B%0A    if (!docs.types%5Btype%5D) %7B%0A      console.error('No documentation found for type: ' + type);%0A      complete = false;%0A    %7D%0A  %7D);%0A%0A  Object.keys(language.properties).forEach(function(key) %7B%0A    if (!docs.properties%5Bkey%5D) %7B%0A      console.error('No documentation found for property: ' + key);%0A      complete = false;%0A    %7D%0A  %7D);%0A%0A  if (!complete) %7B%0A    throw new Error('Documentation is incomplete');%0A  %7D%0A%7D%0A%0A/**%0A * Parse the documentation files so that we can build the reference.%0A */%0A
 function par
@@ -3117,16 +3117,49 @@
  docs);%0A
+  checkComplete(docs, language);%0A
   return

Check that everything is listed in the reference. Fixes #8.