require 'parslet' require 'figtree/ip_rules' module Figtree # ConFIG into a Tree :) class Parser < Parslet::Parser include IPv4 include IPv6 rule(:eof) { any.absent? } rule(:group_title) { match('[a-zA-Z]').repeat(1) } rule(:space) { match("\s").repeat(0) } rule(:newline) { match("\n") >> match("\r").maybe } rule(:grouper) do newline.maybe >> str('[') >> group_title.as(:group_title) >> str(']') end rule(:comment) do # comments go uncaptured (str(';') >> (newline.absent? >> any).repeat) >> newline.maybe end rule(:string) do str('"') >> ((str('\\') >> any) | (str('"').absent? >> any)).repeat.as(:string) >> str('"') end rule(:boolean) do # expand this check (str('no') | str('yes')).as(:boolean) end rule(:number) do match('[0-9]').repeat(1).as(:number) end rule(:ip_address) do (ipv4 | ipv6).as(:ip_address) end rule(:array) do (match('[a-zA-Z]').repeat(1) >> (str(',') >> match('[a-zA-Z]').repeat(1)).repeat.maybe).maybe.as(:array) >> (str(',') | newline | eof) end rule(:file_path) do match('[/a-z/]').repeat(1).as(:file_path) end rule(:snake_case_key) do match('[a-zA-Z0-9_]').repeat(1).as(:snake_case_key) end rule(:snakey_option_key) do snake_case_key.as(:key_to_be_overridden) >> str('<') >> snake_case_key.as(:optional_key) >> str('>') end rule(:assignment) do snake_case_key >> space >> str("=") >> space >> # this ordering matters # we are roughly moving from more # to less specific (ip_address | number | boolean | array | snake_case_key | file_path | string) end rule(:override_assignment) do snakey_option_key >> space >> str("=") >> space >> file_path end rule(:assignment_or_comment) do ( comment | assignment | override_assignment ) end rule(:group_member) do newline.maybe >> assignment_or_comment >> newline.maybe end rule(:group) do (grouper >> group_member.repeat.maybe).as(:group). repeat.maybe end rule(:comment_or_group) do comment.maybe >> newline.maybe >> group.maybe end root(:comment_or_group) end end