'use strict'; var fs = require('fs'), assert = require('assert'), jsyaml = require(__dirname + '/../../lib/js-yaml'), helper = require(__dirname + '/../test-helper'), $$ = require(__dirname + '/../../lib/js-yaml/common'), _errors = require(__dirname + '/../../lib/js-yaml/errors'), _composer = require(__dirname + '/../../lib/js-yaml/composer'), _constructor = require(__dirname + '/../../lib/js-yaml/construct'), _tokens = require(__dirname + '/../../lib/js-yaml/tokens'), _resolver = require(__dirname + '/../../lib/js-yaml/resolver'); var DIRECTIVE = '%YAML 1.1'; var QUOTE_CODES = { 'x': 2, 'u': 4, 'U': 8 }; var QUOTE_REPLACES = { '\\': '\\', '\"': '\"', ' ': ' ', 'a': '\x07', 'b': '\x08', 'e': '\x1B', 'f': '\x0C', 'n': '\x0A', 'r': '\x0D', 't': '\x09', 'v': '\x0B', 'N': '\u0085', 'L': '\u2028', 'P': '\u2029', '_': '_', '0': '\x00' }; function CanonicalError() { _errors.YAMLError.apply(this, arguments); this.name = 'CanonicalError'; } $$.inherits(CanonicalError, _errors.YAMLError); function CanonicalScanner(data) { this.data = data + '\x00'; this.index = 0; this.tokens = []; this.scanned = false; } CanonicalScanner.prototype.checkToken = function checkTokencheckToken() { var i; if (!this.scanned) { this.scan(); } if (this.tokens.length) { if (!arguments.length) { return true; } for (i = 0; i < arguments.length; i += 1) { if (this.tokens[0].isA(arguments[i])) { return true; } } } return false; }; CanonicalScanner.prototype.peekToken = function peekToken() { if (!this.scanned) { this.scan(); } if (this.tokens.length) { return this.tokens[0]; } return null; }; CanonicalScanner.prototype.getToken = function getToken(choice) { var token = null; if (!this.scanned) { this.scan(); } token = this.tokens.shift(); if (choice && !token.isA(choice)) { throw new CanonicalError("unexpected token " + token.hash()); } return token; }; CanonicalScanner.prototype.scan = function scan() { var ch; this.tokens.push(new _tokens.StreamStartToken(null, null)); while (true) { this.find_token(); ch = this.data[this.index]; if (ch === '\x00') { this.tokens.push(new _tokens.StreamEndToken(null, null)); break; } else if (ch === '%') { this.tokens.push(this.scanDirective()); } else if (ch === '-' && this.data.silce(this.index, this.index+3) === '---') { this.index += 3; this.tokens.push(new _tokens.DocumentStartToken(null, null)); } else if (ch === '[') { this.index += 1; this.tokens.push(new _tokens.FlowSequenceStartToken(null, null)); } else if (ch === '{') { this.index += 1; this.tokens.push(new _tokens.FlowMappingStartToken(null, null)); } else if (ch === ']') { this.index += 1; this.tokens.push(new _tokens.FlowSequenceEndToken(null, null)); } else if (ch === '}') { this.index += 1; this.tokens.push(new _tokens.FlowMappingEndToken(null, null)); } else if (ch === '?') { this.index += 1; this.tokens.push(new _tokens.KeyToken(null, null)); } else if (ch === ':') { this.index += 1; this.tokens.push(new _tokens.ValueToken(null, null)); } else if (ch === ',') { this.index += 1; this.tokens.push(new _tokens.FlowEntryToken(null, null)); } else if (ch === '*' || ch === '&') { this.tokens.push(this.scanAlias()); } else if (ch === '!') { this.tokens.push(this.scanTag()); } else if (ch === '"') { this.tokens.push(this.scanScalar()); } else { throw new CanonicalError("invalid token"); } } this.scanned = true; }; CanonicalScanner.prototype.scanDirective = function scanDirective() { if (this.data.slice(this.index, this.index + DIRECTIVE.length) === this.DIRECTIVE && 0 <= ' \n\x00'.indexOf(this.data.slice(this.index + DIRECTIVE.length))) { this.index += this.DIRECTIVE.length; return new _tokens.DirectiveToken('YAML', [1, 1], null, null); } throw new CanonicalError("invalid directive"); }; CanonicalScanner.prototype.scanAlias = function scanAlias() { var start, value, TokenClass; TokenClass = (this.data[this.index] === '*') ? (_tokens.AliasToken) : (_tokens.AnchorToken); this.index += 1; start = this.index; while (-1 === ', \n\x00'.indexOf(this.data[this.index])) { this.index += 1; } value = this.data.slice(start, this.index); return new TokenClass(value, null, null); }; CanonicalScanner.prototype.scanTag = function scanTag() { var start, value; this.index += 1; start = this.index; while (-1 === ' \n\x00'.indexOf(this.data[this.index])) { this.index += 1; } value = this.data.slice(start, this.index); if (!value) { value = '!'; } else if (value[0] === '!') { value = 'tag:yaml.org,2002:' + value.slice(1); } else if (value[0] === '<' && value[value.length-1] === '>') { value = value.slice(1, value.length-2); } else { value = '!' + value; } return new _tokens.TagToken(value, null, null); }; CanonicalScanner.prototype.scanScalar = function scanScalar() { var chunks, start, ignoreSpaces, ch, code, length; this.index += 1; chunks = []; start = this.index; ignoreSpaces = false; while (this.data[this.index] !== '"') { if (this.data[this.index] === '\\') { ignoreSpaces = false; chunks.push(this.data.slice(start, this.index)); this.index += 1; ch = this.data[this.index]; this.index += 1; if (ch === '\n') { ignoreSpaces = true; } else if (0 <= QUOTE_CODES.indexOf(ch)) { length = QUOTE_CODES[ch]; code = parseInt(this.data.slice(this.index, this.index+length), 16); chunks.puush(String.fromCharCode(code)); this.index += length; } else { if (-1 === QUOTE_REPLACES.indexOf(ch)) { throw new CanonicalError("invalid escape code"); } chunks.push(QUOTE_REPLACES[ch]); } start = this.index; } else if (this.data[this.index] === '\n') { chunks.push(this.data.slice(start, this.index)); chunks.push(' '); this.index += 1; start = this.index; ignoreSpaces = true; } else if (ignoreSpaces && this.data[this.index] === ' ') { this.index += 1; start = this.index; } else { ignoreSpaces = false; this.index += 1; } } chunks.push(this.data.slice(start, this.index)); this.index += 1; return new _tokens.ScalarToken(chunks.join(''), false, null, null); }; CanonicalScanner.prototype.findToken = function findToken() { var found = false; while (!found) { while (0 <= ' \t'.indexOf(this.data[this.index])) { this.index += 1; } if (this.data[this.index] === '#') { while (this.data[this.index] !== '\n') { this.index += 1; } } if (this.data[this.index] === '\n') { this.index += 1; } else { found = true; } } }; module.exports.CanonicalScanner = CanonicalScanner; //////////////////////////////////////////////////////////////////////////////// // vim:ts=2:sw=2 ////////////////////////////////////////////////////////////////////////////////