Skip to content

Instantly share code, notes, and snippets.

@jphastings
Created September 22, 2010 22:41
Show Gist options
  • Save jphastings/592720 to your computer and use it in GitHub Desktop.
Save jphastings/592720 to your computer and use it in GitHub Desktop.
Got bored, wrote a Yaml parser (ish) in javascript.

Usage

console.log(YAML.eval("Yaml!"))

New features

  • Now does comments (& --- lines)
  • Deals with booleans (yes, no, true, false)
  • Handles multi-line strings (both types!)
  • Single-line hashes now supported
  • Knows about indentation

Limitations

  • Can't see how it'll ever get to dealing with pointers
  • Can't yet deal with escaping (eg "" around strings)
  • Won't do custom classes

Important Caveat!

If you're extending the Array class (eg. with jQuery, RightJS) then the single line array stuff (line 61 below, at the moment) will break!

Why? Well, some libraries extend the Array class in such a way that when you iterate over the array, not only the elements are iterated over, but also all of the extended functions. There's probably a way around this, but I haven't bothered implementing it yet.

Workaround:

In RightJS I simple swap out these lines

for(var i in contents) {
  data.push(this.parse([contents[i]]))
}

return data

For this one:

return contents.map(function(item) { this.parse(item) })
var YAML = {
parse: function(tokens) {
var data;
while (string = tokens.shift()) {
if (/^(?:---|#.*)?\s*$/.test(string)) { // Comments, empty lines etc
// do nothing!
} else if (/^\s*-\s+(.*)$/.test(string)) { // Arrays (longhand)
var this_item = [RegExp.$1]
data = []
while (string = tokens.shift()) {
if (/^\s*-\s+(.*)$/.test(string)) {
var new_item = RegExp.$1
data.push(this.parse(this_item))
this_item = [new_item]
} else if (/^\s+(.+)/.test(string)) {
this_item.push(RegExp.$1)
} else {
tokens.unshift(string)
break
}
}
data.push(this.parse(this_item))
return data
} else if (/^(\s*)[\w\-_]+:/.test(string)) { // Hash
var origin_indent = RegExp.$1
if (data == undefined)
data = {}
if (/^(\s*)([\w\-_]+):\s*([\|>]?)$/.test(string)) { // Hash contains multiline
var key = RegExp.$2
var multiline = []
if (RegExp.$3 != '')
multiline.push(RegExp.$3)
var origin_indent = RegExp.$1.replace('\t','\\t')
while (string = tokens.shift()) {
if (new RegExp('^'+origin_indent+'[\\w\\-_]+:').test(string)) {
// Make sure we leave the next one in place
tokens.unshift(string)
break
} else {
multiline.push(string)
}
}
data[key] = this.parse(multiline)
} else if (/^\s*([\w\-_]+):\s+(.+)$/.test(string)) { // Hash contains single line
data[RegExp.$1] = this.parse([RegExp.$2])
}
} else if (/^\[(.*?)\]$/.test(string)) { // Array (one line)
data = []
var contents = RegExp.$1.split(", ")
for(var i in contents) {
data.push(this.parse([contents[i]]))
}
return data
} else if (/^\{(.*?)\}$/.test(string)) { // Hash (one line)
data = {}
var contents = RegExp.$1.split(", ")
return this.parse(contents)
} else if (/^\-?\d+$/.test(string)) { // Integer
return parseInt(string)
} else if (/^\-?\d+\.\d+$/.test(string)) { // Float
return parseFloat(string)
} else if (/^true|yes$/i.test(string)) { // Boolean (true)
return true
} else if (/^false|no$/i.test(string)) { // Boolean (false)
return false
} else if (/^(?:---\ )?\|$/.test(string)) { // Multiline string, newlines preserved
tokens.shift().match(/^(\s*)(.*)$/)
var multiline = [RegExp.$2]
var origin_indent = RegExp.$1.replace('\t','\\t')
while (string = tokens.shift()) {
if (new RegExp('^'+origin_indent+'(.*)$').test(string)) {
multiline.push(RegExp.$1)
} else {
// Not strictly necessary, as tokens should only contain stuff for the string
tokens.unshift(string)
break
}
}
return multiline.join("\n")
} else if (/^(?:---\ )?>$/.test(string)) { // Multiline string, newlines folded
var multiline = []
while (string = tokens.shift()) {
multiline.push(string.trim())
}
return multiline.join(' ')
} else { // String
return string
}
}
return data
},
eval:function(string) {
return this.parse(string.replace('\r','\n').replace(/\n\s*\n/,'\n').split("\n"))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment