Last active
December 23, 2017 21:34
-
-
Save woodruffw/7b1001c8a29ef4796c48b2ad59ba6bf7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "benchmark" | |
# Exception thrown on an INI parse error. | |
class ParseException < Exception | |
getter line_number : Int32 | |
getter column_number : Int32 | |
def initialize(message, @line_number, @column_number) | |
super "#{message} at #{@line_number}:#{@column_number}" | |
end | |
def location | |
{line_number, column_number} | |
end | |
end | |
def self.parse_re(str) : Hash(String, Hash(String, String)) | |
ini = {} of String => Hash(String, String) | |
section = "" | |
str.each_line do |line| | |
if line =~ /\s*(.*[^\s])\s*=\s*(.*[^\s])/ | |
ini[section] ||= {} of String => String if section == "" | |
ini[section][$1] = $2 | |
elsif line =~ /\[(.*)\]/ | |
section = $1 | |
ini[section] = {} of String => String | |
end | |
end | |
ini | |
end | |
def self.parse(str) : Hash(String, Hash(String, String)) | |
ini = {} of String => Hash(String, String) | |
current_section_name = "" | |
lineno = 0 | |
str.each_line do |line| | |
lineno += 1 | |
next if line.empty? | |
line, skip = strip_and_lskip(line) | |
case line[0]? | |
when '#', ';' | |
next | |
when '[' | |
end_idx = line.index(']') | |
raise ParseException.new("unterminated section", lineno, line.size + skip) unless end_idx | |
raise ParseException.new("data after section", lineno, end_idx + skip + 1) unless end_idx == line.size - 1 | |
current_section_name = line[1...end_idx] | |
ini[current_section_name] = {} of String => String | |
else | |
key, eq, value = line.partition('=') | |
raise ParseException.new("expected declaration", lineno, key.size + skip) if eq != "=" | |
section = ini[current_section_name]? || Hash(String, String).new | |
section[key.rstrip] = value.strip | |
ini[current_section_name] = section | |
end | |
end | |
ini | |
end | |
private def self.strip_and_lskip(str) | |
stripped = str.strip | |
{stripped, str.size - stripped.size} | |
end | |
DATA = <<-INI | |
top = level | |
key = value | |
[foo] | |
bar = baz | |
quux = zap | |
[1234567890] | |
skjfdhsfslkfdsfhkdsfsdf=324324324324 | |
1=2 | |
3=4 | |
INI | |
Benchmark.ips do |b| | |
b.report("INI parse w/ RE") { parse_re(DATA) } | |
b.report("INI parse w/o RE") { parse(DATA) } | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment