F# fslex fsyacc подходит для производственного кода?

После прочтения двухлетней веб-страницы, которая по-настоящему разорвала fslex/fsyacc, глючила, работала медленно, тупо и т. Д. По сравнению с их коллегами из OCamel, я задалась вопросом, что будет лучшим выбором для нужд синтаксического анализа lexing?

Раньше я использовал ANTLR с привязками C#, но в настоящее время я нахожусь в процессе изучения F# и был взволнован, когда увидел, что он поставляется с генератором парсера. Поскольку F# официально выпущен, и похоже, что Microsoft действительно стремится поддерживать и развивать. Вы сказали бы, что fslex и fsyacc того стоят для производственного кода?

3 ответа

Fslex и fsyacc используются компилятором F#, поэтому они вроде работают. Я использовал их несколько лет назад, это было достаточно хорошо для моих нужд.

Однако мой опыт показывает, что lex/yacc гораздо менее зрел в F#, чем в OCaml. Многие люди из сообщества OCaml использовали их годами, в том числе многие студенты (кажется, что написание небольшого интерпретатора / компилятора с ними - обычное упражнение). Я не думаю, что многие разработчики F# использовали их, и я не думаю, что команда F# в последнее время проделала большую работу над этими инструментами (например, интеграция VS не была приоритетом). Если вы не очень требовательны, Fslex и fsyacc может быть достаточно для вас.

Решением может быть адаптация Menhir (замена camlyacc на несколько полезных функций) для использования его с F#. Я понятия не имею, сколько работы это будет.

Лично я теперь использую FParsec каждый раз, когда мне нужно написать парсер. Он совершенно другой в использовании, но он также гораздо более гибкий и генерирует хорошие сообщения об ошибках разбора. Я был очень счастлив с ним, и его автор всегда был очень полезным, когда у меня были вопросы.

Fslex и fsyacc, безусловно, готовы к использованию. В конце концов, они используются в Microsoft Visual Studio 2010, потому что лексер и синтаксический анализатор F# написаны с использованием их ( исходный код компилятора F# также является хорошим примером, демонстрирующим, как эффективно их использовать).

Я не уверен, как fslex/fsyacc сравниваются с их эквивалентами OCaml или с ANTLR. Однако у Фредерика Холмстрома есть статья, в которой сравнивается ANTLR с рукописным парсером, написанным на F#, который используется в IronJS. К сожалению, у него нет версии fslex/fsyacc, поэтому прямого сравнения нет.

Чтобы ответить на некоторые конкретные вопросы - вы можете получить задачи MSBUILD для запуска fslex/fsyacc как часть сборки, так что она достаточно хорошо интегрируется. Вы не получаете подсветку синтаксиса, но я не думаю, что это так важно. Это может быть медленнее, чем версия OCaml, но это влияет на компиляцию только при изменении парсера - я сделал некоторые изменения в парсере F# и не нашел время компиляции проблемой.

Инструменты fslex и fsyacc были специально написаны для компилятора F# и не были предназначены для более широкого использования. Тем не менее, мне удалось получить значительные базы кода, портированные с OCaml на F#, благодаря этим инструментам, но это было трудоемким из-за полного отсутствия интеграции VS на стороне F# (OCaml имеет отличную интеграцию с подсветкой синтаксиса, переходом к определению и ошибкой возврат). В частности, я переместил как можно большую часть кода F# из лексера и анализатора.

Нам часто приходилось писать парсеры и просили Microsoft добавить официальную поддержку fslex и fsyacc, но я не верю, что это произойдет.

Я бы посоветовал использовать fslex и fsyacc, только если вы сталкиваетесь с переводом большой устаревшей базы кода OCaml, в которой используются ocamllex и ocamlyacc. В противном случае напишите парсер с нуля.

Лично я не фанат библиотек комбинаторов парсеров и предпочитаю писать парсеры, используя активные шаблоны, которые выглядят примерно так: парсер s-выражений:

let alpha = set['A'..'Z'] + set['a'..'z']
let numeric = set['0'..'9']
let alphanumeric = alpha + numeric

let (|Empty|Next|) (s: string, i) =
  if i < s.Length then Next(s.[i], (s, i+1)) else Empty

let (|Char|_|) alphabet = function
  | Empty -> None
  | s, i when Set.contains s.[i] alphabet -> Some(s, i+1)
  | _ -> None

let rec (|Chars|) alphabet = function
  | Char alphabet (Chars alphabet it)
  | it -> it

let sub (s: string, i0) (_, i1) =
  s.Substring(i0, i1-i0)

let rec (|SExpr|_|) = function
  | Next ((' ' | '\n' | '\t'), SExpr(f, it)) -> Some(f, it)
  | Char alpha (Chars alphanumeric it1) as it0 -> Some(box(sub it0 it1), it1)
  | Next ('(', SExprs(fs, Next(')', it))) -> Some(fs, it)
  | _ -> None
and (|SExprs|) = function
  | SExpr(f, SExprs(fs, it)) -> box(f, fs), it
  | it -> null, it

Этот подход не требует интеграции VS, потому что это просто ванильный код F#. Я нахожу это легко читать и поддерживать. Производительность была более чем адекватной в моем рабочем коде.

Другие вопросы по тегам