Skip to content

Instantly share code, notes, and snippets.

@tarcieri
Created May 7, 2012 21:35
Show Gist options
  • Save tarcieri/2630622 to your computer and use it in GitHub Desktop.
Save tarcieri/2630622 to your computer and use it in GitHub Desktop.
How not to write a YAML parser (for https://github.com/KeeperPat/music-yaml-parsing)
Dash=(Dedent=-(Indent=1.0/0)).to_s[0...1]
def scan(file)
File.readlines(file).inject([[],[]]) do |(tokens,levels),line|
case (levels.last || 0) <=> (level = line[/^\s*(-\s*)?/].length)
when -1 then tokens << Indent; levels << level
when 1 then while (levels.last || 0) > level; levels.pop; tokens << Dedent; end
end
if matches = line.match(/^\s*(-\s*)?(\w+):\s*(.*)$/)
tokens << Dash if matches[1]
tokens << matches[2].intern
tokens << matches[3] unless matches[3].empty?
end
next tokens, levels
end.first
end
def compile(tokens)
"".tap do |str|
case token = tokens.shift
when Symbol, Dash
dash = token == Dash
str << "#{dash ? '' : 'H'}["
begin str << (dash ? '' : "#{token}:") << compile(tokens); token = tokens.shift
end while (dash ? token == Dash : token.is_a?(Symbol)) && str << ","
tokens.unshift(token)
str << "]"
when Indent
while tokens.first != Dedent && !tokens.empty?; str << compile(tokens); end
tokens.shift
when String then str << token.dump
end
end
end
class H < Hash
def [](key) super(key.intern) end
def method_missing(key) self[key] end
end
data = eval compile scan "music.yaml"
p data["genres"].last["artists"].first["albums"].first["tracks"].last["name"],
data.genres.last.artists.first.albums.first.tracks.last.name
@kares
Copy link

kares commented May 8, 2012

immediately reminded me of "how not to write a YAML emitter" I came across a while back
https://github.com/spree/spree_i18n/blob/00cd5ed517fd608d8df41964383726035bed2203/lib/spree/i18n_utils.rb#L34-43

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment