Skip to content

Instantly share code, notes, and snippets.

@woodruffw
Last active December 23, 2017 21:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save woodruffw/7b1001c8a29ef4796c48b2ad59ba6bf7 to your computer and use it in GitHub Desktop.
Save woodruffw/7b1001c8a29ef4796c48b2ad59ba6bf7 to your computer and use it in GitHub Desktop.
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