Задача: токенайзер только для регулярных выражений для строк конфигурации, подобных назначению оболочки - PullRequest
1 голос
/ 28 декабря 2011

Я задал оригинальный вопрос здесь и получил практический ответ со смешанными Ruby и регулярными выражениями.Теперь пурист во мне хочет знать: Может ли это быть сделано в регулярных выражениях?Моя интуиция говорит, что может.Для bash 2.0 существует ABNF, хотя он не включает экранирование строк.

Спецификация

С учетом входной строки, которая является (1) переменной("ключ") из скрипта со вкусом bash или (2) установка значения ключа из типичного файла конфигурации, такого как postgresql.conf, это регулярное выражение (или пара регулярных выражений) должно захватывать ключ и значение таким образом, чтобыЯ могу использовать эти перехваты для замены нового значения для этого ключа.

Вы можете использовать другое регулярное выражение для строк со вкусом оболочки и со вкусом конфигурации;звонящий будет знать, что использовать.

Здесь будет вознаграждение в 50 баллов. Я не могу добавить вознаграждение в течение двух дней, поэтому я не приму ответ до этого, но вы можете начать отвечать немедленно,Вы зарабатываете очки за:

  • Удобочитаемость (именованные группы захвата, определения через? (DEFINE) или {0})
  • Использование одного регулярного выражения вместо двух
  • Обучите меня кое-чему о DFA
  • Производительность регулярных выражений, если применимо
  • Получение права голоса
  • Сначала используйте технику

Пример:

С учетом ввода

export RAILS_ENV=production

Я должен иметь возможность писать на Ruby:

match = THE_REGEX.match("export RAILS_ENV=production")
newline = "export #{match[:key]}=#{match[:value]}"

Контрольные примеры: стиль оболочки

RAILS_ENV=development     # Don't forget to change this for TechCrunch
HOSTNAME=`cat /etc/hostname`
plist=`cat "/Applications/Sublim\`e Text 2.app/Content's/Info.plist"`

# Optional bonus input: "#" present in the string
FORMAT="  ##0.00 passe\`" #comment

Контрольные примеры: стиль конфигурации

listen_addresses = 127.0.0.1 #localhost only by default
# listen_addresses = 0.0.0.0 commented out, should not match

Для целей данного задания «регулярное выражение» и «регулярное выражение» означают одно и то же, и оба могутобращайтесь к любому общему вкусу, который вам нравится, хотя я предпочитаю совместимый с Ruby 1.9.

1 Ответ

2 голосов
/ 28 декабря 2011

Я не уверен насчет полных спецификаций и того, что именно вы хотите в группе захвата значений, но это должно работать для ваших тестовых случаев:

/
^\s*+

(?:export\s++)?
(?<key>\w++)

\s*+
=
\s*+

(?<value>
  (?>  "(?:[^"\\]+|\\.)*+"
  |    '(?:[^'\\]+|\\.)*+'
  |    `(?:[^`\\]+|\\.)*+`
  |    [^#\n\r]++
  )
)

\s*+
(?:#.*+)?
$
/mx;

Обрабатывает комментарии и цитаты с экранированием.*

Аромат и цитирование Perl / PCRE.


Пример использования в Perl:

my $re = qr/
    ^\s*+

    (?:export\s++)?
    (?<key>\w++)

    \s*+
    =
    \s*+

    (?<value>
      (?>  "(?:[^"\\]+|\\.)*+"
      |    '(?:[^'\\]+|\\.)*+'
      |    `(?:[^`\\]+|\\.)*+`
      |    [^#\n\r]++
      )
    )

    \s*+
    (?:\#.*+)?
    $
/mx;

my $str = <<'_TESTS_';
RAILS_ENV=development     # Don't forget to change this for TechCrunch
HOSTNAME=`cat /etc/hostname`
plist=`cat "/Applications/Sublim\`e Text 2.app/Content's/Info.plist"`

# Optional bonus input: "#" present in the string
FORMAT="  ##0.00 passe\`" #comment

listen_addresses = 127.0.0.1 #localhost only by default
# listen_addresses = 0.0.0.0 commented out, should not match

TEST="foo'bar\"baz#"
TEST='foo\'bar"baz#\\'
_TESTS_


for(split /[\r\n]+/, $str){
    print "line: $_\n";
    print /$re/? "match: $1, $2\n": "no match\n";
    print "\n";
}

Вывод:

line: RAILS_ENV=development     # Don't forget to change this for TechCrunch
match: RAILS_ENV, development

line: HOSTNAME=`cat /etc/hostname`
match: HOSTNAME, `cat /etc/hostname`

line: plist=`cat "/Applications/Sublim\`e Text 2.app/Content's/Info.plist"`
match: plist, `cat "/Applications/Sublim\`e Text 2.app/Content's/Info.plist"`

line: # Optional bonus input: "#" present in the string
no match

line: FORMAT="  ##0.00 passe\`" #comment
match: FORMAT, "  ##0.00 passe\`"

line: listen_addresses = 127.0.0.1 #localhost only by default
match: listen_addresses, 127.0.0.1

line: # listen_addresses = 0.0.0.0 commented out, should not match
no match

line: TEST="foo'bar\"baz#"
match: TEST, "foo'bar\"baz#"

line: TEST='foo\'bar"baz#\\'
match: TEST, 'foo\'bar"baz#\\'
...