diff options
Diffstat (limited to 'com/jeremyfa/yaml/YamlParser.js')
-rw-r--r-- | com/jeremyfa/yaml/YamlParser.js | 189 |
1 files changed, 145 insertions, 44 deletions
diff --git a/com/jeremyfa/yaml/YamlParser.js b/com/jeremyfa/yaml/YamlParser.js index 0c80d57..3346b48 100644 --- a/com/jeremyfa/yaml/YamlParser.js +++ b/com/jeremyfa/yaml/YamlParser.js @@ -1,11 +1,11 @@ /** * YamlParser parses YAML strings to convert them to JS objects - * (port of sfYaml Symfony Component) + * (port of Yaml Symfony Component) */ var YamlParser = function(offset /* Integer */) { - this.offset = this.isDefined(offset) ? offset : 0; + this.offset = (offset !== undefined) ? offset : 0; }; YamlParser.prototype = { @@ -29,7 +29,8 @@ YamlParser.prototype = this.lines = this.cleanup(value).split("\n"); var data = null; - + var context = null; + while ( this.moveToNextLine() ) { if ( this.isCurrentLineEmpty() ) @@ -38,9 +39,9 @@ YamlParser.prototype = } // tab? - if ( /^\t+/.test(this.currentLine) ) + if ( this.currentLine.charAt(0) == '\t' ) { - throw new InvalidArgumentException('A YAML file cannot contain tabs as indentation at line '+(this.getRealCurrentLineNb()+1)+' ('+this.currentLine+')'); + throw new YamlParseException('A YAML file cannot contain tabs as indentation.', this.getRealCurrentLineNb() + 1, this.currentLine); } var isRef = false; @@ -58,8 +59,14 @@ YamlParser.prototype = if ( values = /^\-((\s+)(.+?))?\s*$/.exec(this.currentLine) ) { + + if (context && 'mapping' == context) { + throw new YamlParseException('You cannot define a sequence item when in a mapping', this.getRealCurrentLineNb() + 1, this.currentLine); + } + context = 'sequence'; + if ( !this.isDefined(data) ) data = []; - if ( !(data instanceof Array) ) throw new InvalidArgumentException("Non array entry at line "+(this.getRealCurrentLineNb() + 1)+"."); + //if ( !(data instanceof Array) ) throw new YamlParseException("Non array entry", this.getRealCurrentLineNb() + 1, this.currentLine); values = {leadspaces: values[2], value: values[3]}; @@ -71,7 +78,7 @@ YamlParser.prototype = } // array - if ( !this.isDefined(values.value) || '' == values.value.split(' ').join('') || this.trim(values.value).charAt(0) == '#' ) + if ( !this.isDefined(values.value) || '' == this.trim(values.value) || values.value.replace(/^ +/,'').charAt(0) == '#' ) { c = this.getRealCurrentLineNb() + 1; parser = new YamlParser(c); @@ -81,8 +88,10 @@ YamlParser.prototype = } else { - if ( this.isDefined(values.leadspaces) && ' ' == values.leadspaces && ( matches = new RegExp('^('+YamlInline.REGEX_QUOTED_STRING+'|[^ \'"\{].*?) *\:(\\s+(.+?))?\\s*$').exec(values.value) ) ) - { + if ( this.isDefined(values.leadspaces) && + ' ' == values.leadspaces && + ( matches = new RegExp('^('+YamlInline.REGEX_QUOTED_STRING+'|[^ \'"\{\[].*?) *\:(\\s+(.+?))?\\s*$').exec(values.value) ) + ) { matches = {key: matches[1], value: matches[3]}; // this is a compact notation element, add to next block and parse c = this.getRealCurrentLineNb(); @@ -104,20 +113,37 @@ YamlParser.prototype = } } } - else if ( values = new RegExp('^('+YamlInline.REGEX_QUOTED_STRING+'|[^ \'"].*?) *\:(\\s+(.+?))?\\s*$').exec(this.currentLine) ) + else if ( values = new RegExp('^('+YamlInline.REGEX_QUOTED_STRING+'|[^ \'"\[\{].*?) *\:(\\s+(.+?))?\\s*$').exec(this.currentLine) ) { if ( !this.isDefined(data) ) data = {}; - if ( data instanceof Array ) throw new InvalidArgumentException("Non mapped entry at line "+(this.getRealCurrentLineNb() + 1)+"."); + if (context && 'sequence' == context) { + throw new YamlParseException('You cannot define a mapping item when in a sequence', this.getRealCurrentLineNb() + 1, this.currentLine); + } + context = 'mapping'; + //if ( data instanceof Array ) throw new YamlParseException("Non mapped entry", this.getRealCurrentLineNb() + 1, this.currentLine); values = {key: values[1], value: values[3]}; - key = (new YamlInline()).parseScalar(values.key); + try { + key = new YamlInline().parseScalar(values.key); + } catch (e) { + if ( e instanceof YamlParseException ) { + e.setParsedLine(this.getRealCurrentLineNb() + 1); + e.setSnippet(this.currentLine); + } + throw e; + } + if ( '<<' == key ) { if ( this.isDefined(values.value) && '*' == (values.value+'').charAt(0) ) { - isInPlace = values.value.substring(1); + isInPlace = values.value.substr(1); + if ( this.refs[isInPlace] == undefined ) + { + throw new YamlParseException('Reference "'+value+'" does not exist', this.getRealCurrentLineNb() + 1, this.currentLine); + } } else { @@ -139,7 +165,7 @@ YamlParser.prototype = var merged = []; if ( !this.isObject(parsed) ) { - throw new InvalidArgumentException("YAML merge keys used with a scalar value instead of an array at line "+(this.getRealCurrentLineNb() + 1)+" ("+this.currentLine+")"); + throw new YamlParseException("YAML merge keys used with a scalar value instead of an array", this.getRealCurrentLineNb() + 1, this.currentLine); } else if ( this.isDefined(parsed[0]) ) { @@ -151,7 +177,7 @@ YamlParser.prototype = var parsedItem = reverse[i]; if ( !this.isObject(reverse[i]) ) { - throw new InvalidArgumentException("Merge items must be arrays at line "+(this.getRealCurrentLineNb() + 1)+" ("+reverse[i]+")."); + throw new YamlParseException("Merge items must be arrays", this.getRealCurrentLineNb() + 1, this.currentLine); } merged = this.mergeObject(reverse[i], merged); } @@ -178,10 +204,10 @@ YamlParser.prototype = data = isProcessed; } // hash - else if ( !this.isDefined(values.value) || '' == values.value.split(' ').join('') || this.trim(values.value).charAt(0) == '#' ) + else if ( !this.isDefined(values.value) || '' == this.trim(values.value) || this.trim(values.value).charAt(0) == '#' ) { // if next line is less indented or equal, then it means that the current value is null - if ( this.isNextLineIndented() ) + if ( this.isNextLineIndented() && !this.isNextLineUnIndentedCollection() ) { data[key] = null; } @@ -211,18 +237,26 @@ YamlParser.prototype = // 1-liner followed by newline if ( 2 == this.lines.length && this.isEmpty(this.lines[1]) ) { - value = (new YamlInline()).load(this.lines[0]); + try { + value = new YamlInline().parse(this.lines[0]); + } catch (e) { + if ( e instanceof YamlParseException ) { + e.setParsedLine(this.getRealCurrentLineNb() + 1); + e.setSnippet(this.currentLine); + } + throw e; + } if ( this.isObject(value) ) { first = value[0]; - if ( '*' == (first+'').substr(0, 1) ) + if ( typeof(value) == 'string' && '*' == first.charAt(0) ) { data = []; len = value.length; for ( var i = 0; i < len; i++ ) { - data.push(this.refs[value[i].substring(1)]); + data.push(this.refs[value[i].substr(1)]); } value = data; } @@ -231,7 +265,7 @@ YamlParser.prototype = return value; } - throw new InvalidArgumentException('"'+this.currentLine+'" at line '+(this.getRealCurrentLineNb() + 1)); + throw new YamlParseException('Unable to parse.', this.getRealCurrentLineNb() + 1, this.currentLine); } if ( isRef ) @@ -279,6 +313,8 @@ YamlParser.prototype = * @param integer indentation The indent level at which the block is to be read, or null for default * * @return string A YAML string + * + * @throws YamlParseException When indentation problem are detected */ getNextEmbedBlock: function(indentation) { @@ -289,10 +325,12 @@ YamlParser.prototype = if ( !this.isDefined(indentation) ) { newIndent = this.getCurrentLineIndentation(); + + var unindentedEmbedBlock = this.isStringUnIndentedCollectionItem(this.currentLine); - if ( !this.isCurrentLineEmpty() && 0 == newIndent ) + if ( !this.isCurrentLineEmpty() && 0 == newIndent && !unindentedEmbedBlock ) { - throw new InvalidArgumentException('A Indentation problem at line '+(this.getRealCurrentLineNb() + 1)+' ('+this.currentLine+')'); + throw new YamlParseException('Indentation problem A', this.getRealCurrentLineNb() + 1, this.currentLine); } } else @@ -300,30 +338,38 @@ YamlParser.prototype = newIndent = indentation; } - var data = [this.currentLine.substring(newIndent)]; + var data = [this.currentLine.substr(newIndent)]; + + var isItUnindentedCollection = this.isStringUnIndentedCollectionItem(this.currentLine); while ( this.moveToNextLine() ) { + + if (isItUnindentedCollection && !this.isStringUnIndentedCollectionItem(this.currentLine)) { + this.moveToPreviousLine(); + break; + } + if ( this.isCurrentLineEmpty() ) { if ( this.isCurrentLineBlank() ) { - data.push(this.currentLine.substring(newIndent)); + data.push(this.currentLine.substr(newIndent)); } continue; } indent = this.getCurrentLineIndentation(); - var match; - if ( match = /^( *)$/.exec(this.currentLine) ) + var matches; + if ( matches = /^( *)$/.exec(this.currentLine) ) { // empty line - data.push(match[1]); + data.push(matches[1]); } else if ( indent >= newIndent ) { - data.push(this.currentLine.substring(newIndent)); + data.push(this.currentLine.substr(newIndent)); } else if ( 0 == indent ) { @@ -333,7 +379,7 @@ YamlParser.prototype = } else { - throw new InvalidArgumentException('B Indentation problem at line '+(this.getRealCurrentLineNb() + 1)+' ('+this.currentLine+')'); + throw new YamlParseException('Indentation problem B', this.getRealCurrentLineNb() + 1, this.currentLine); } } @@ -342,6 +388,8 @@ YamlParser.prototype = /** * Moves the parser to the next line. + * + * @return Boolean */ moveToNextLine: function() { @@ -371,6 +419,8 @@ YamlParser.prototype = * @param string value A YAML value * * @return mixed A JS value + * + * @throws YamlParseException When reference does not exist */ parseValue: function(value) { @@ -382,12 +432,12 @@ YamlParser.prototype = } else { - value = (value+'').substring(1); + value = (value+'').substr(1); } if ( this.refs[value] == undefined ) { - throw new InvalidArgumentException('Reference "'+value+'" does not exist ('+this.currentLine+').'); + throw new YamlParseException('Reference "'+value+'" does not exist', this.getRealCurrentLineNb() + 1, this.currentLine); } return this.refs[value]; } @@ -400,9 +450,14 @@ YamlParser.prototype = return this.parseFoldedScalar(matches.separator, modifiers.replace(/\d+/g, ''), Math.abs(parseInt(modifiers))); } - else - { - return (new YamlInline()).load(value); + try { + return new YamlInline().parse(value); + } catch (e) { + if ( e instanceof YamlParseException ) { + e.setParsedLine(this.getRealCurrentLineNb() + 1); + e.setSnippet(this.currentLine); + } + throw e; } }, @@ -472,7 +527,7 @@ YamlParser.prototype = } else if ( matches = /^( *)$/.exec(this.currentLine) ) { - text += matches[1].replace(new RegExp('^ {1,'+textIndent.length+'}','g'), '', matches[1])+"\n"; + text += matches[1].replace(new RegExp('^ {1,'+textIndent.length+'}','g'), '')+"\n"; } else { @@ -551,7 +606,7 @@ YamlParser.prototype = */ isCurrentLineBlank: function() { - return '' == this.currentLine.split(' ').join(''); + return '' == this.trim(this.currentLine); }, /** @@ -596,7 +651,7 @@ YamlParser.prototype = regex = /^(#.*?\n)+/; if ( regex.test(value) ) { - trimmedValue = value.replace(regex, ''); + var trimmedValue = value.replace(regex, ''); // items have been removed, update the offset this.offset += this.subStrCount(value, "\n") - this.subStrCount(trimmedValue, "\n"); @@ -619,6 +674,48 @@ YamlParser.prototype = return value; }, + + /** + * Returns true if the next line starts unindented collection + * + * @return Boolean Returns true if the next line starts unindented collection, false otherwise + */ + isNextLineUnIndentedCollection: function() + { + var currentIndentation = this.getCurrentLineIndentation(); + var notEOF = this.moveToNextLine(); + + while (notEOF && this.isCurrentLineEmpty()) { + notEOF = this.moveToNextLine(); + } + + if (false === notEOF) { + return false; + } + + var ret = false; + if ( + this.getCurrentLineIndentation() == currentIndentation + && + this.isStringUnIndentedCollectionItem(this.currentLine) + ) { + ret = true; + } + + this.moveToPreviousLine(); + + return ret; + }, + + /** + * Returns true if the string is unindented collection item + * + * @return Boolean Returns true if the string is unindented collection item, false otherwise + */ + isStringUnIndentedCollectionItem: function(string) + { + return (0 === this.currentLine.indexOf('- ')); + }, isObject: function(input) { @@ -650,16 +747,19 @@ YamlParser.prototype = merge: function(a /* Object */, b /* Object */) { var c = {}; + var i; for ( i in a ) { - if ( /^\d+$/.test(i) ) c.push(a); - else c[i] = a[i]; + if ( a.hasOwnProperty(i) ) + if ( /^\d+$/.test(i) ) c.push(a); + else c[i] = a[i]; } for ( i in b ) { - if ( /^\d+$/.test(i) ) c.push(b); - else c[i] = b[i]; + if ( b.hasOwnProperty(i) ) + if ( /^\d+$/.test(i) ) c.push(b); + else c[i] = b[i]; } return c; @@ -670,7 +770,7 @@ YamlParser.prototype = var i; var result = ''; for ( i = 0; i < count; i++ ) result += str; - return str; + return result; }, subStrCount: function(string, subString, start, length) @@ -689,6 +789,7 @@ YamlParser.prototype = { if ( subString == string.substr(i, sublen) ) c++; + i += sublen - 1; } return c; @@ -696,6 +797,6 @@ YamlParser.prototype = trim: function(str /* String */) { - return (str+'').replace(/^\s+/,'').replace(/\s+$/,''); + return (str+'').replace(/^ +/,'').replace(/ +$/,''); } }; |