Создание дискриминированных данных объединения из файла / базы данных

У меня есть размеченный союз для таких выражений (EQ =; GT>; и т. Д.)

  (AND (OR (EQ X 0)
           (GT X 10))
       (OR (EQ Y 0)
           (GT Y 10)))

Я хочу создать экземпляры DU из таких выражений, сохраненных в файле / базе данных. Как это сделать? Если это невозможно, как лучше всего подойти к этому в F #?

Даниэль: эти выражения сохраняются в формате префикса (как указано выше) как текст и будут проанализированы в F #. Спасибо.


person huirong    schedule 28.01.2013    source источник
comment
Эти выражения хранятся как текст, который нужно проанализировать или как?   -  person Daniel    schedule 28.01.2013
comment
Вы хотите знать, как моделировать свой AST?   -  person Daniel    schedule 28.01.2013


Ответы (2)


Если вы просто хотите знать, как моделировать эти выражения с помощью DU, вот один способ:

type BinaryOp =
  | EQ
  | GT

type Expr =
  | And of Expr * Expr
  | Or of Expr * Expr
  | Binary of BinaryOp * Expr * Expr
  | Var of string
  | Value of obj

let expr = 
  And(
    Or(
      Binary(EQ, Var("X"), Value(0)),
      Binary(GT, Var("X"), Value(10))),
    Or(
      Binary(EQ, Var("Y"), Value(0)),
      Binary(GT, Var("Y"), Value(10))))

Теперь это может быть слишком "свободным", т. Е. Допускает выражения типа And(Value(1), Value(2)), которые могут быть недопустимыми в соответствии с вашей грамматикой. Но это должно дать вам представление о том, как к этому подойти.

Есть также несколько хороших примеров в вики-книге по программированию на F #.

Если вам нужно разобрать эти выражения, я настоятельно рекомендую FParsec.

person Daniel    schedule 28.01.2013
comment
Спасибо, Даниэль, но здесь экземпляр DU создается буквально, то есть в исходном коде. Мне нужна функция, которая может принимать такие строки, как (EQ X 10) и (OR (EQ Y 2) (LT A 3)), и возвращать экземпляр DU. - person huirong; 28.01.2013
comment
Вы просите полноценный парсер? Посетите FParsec или страницу вики-книг, на которую я дал ссылку. - person Daniel; 28.01.2013
comment
извините за плохое написание и плохое понимание F #: я могу разобрать синтаксис префикса, но какие функции вызывать для создания экземпляра DU. - person huirong; 28.01.2013
comment
Сами корпуса DU являются конструкторами. Мой пример кода не демонстрирует, как делать то, что вы хотите? - person Daniel; 28.01.2013
comment
Думаю, теперь я знаю, что искал: я только что понял, что в вашем ответе И ИЛИ и Двоичный ЯВЛЯЮТСЯ функциями для этой цели. я читал их только как названия типов. - person huirong; 28.01.2013
comment
Большое спасибо за ваше терпение! - person huirong; 28.01.2013
comment
Я допил весь свой скотч, и мои ногти исчезли ... но, пожалуйста. :) - person Daniel; 28.01.2013

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

type BinOp = | And | Or
type Comparison = | Gt | Eq

type Expr =
| BinOp of BinOp * Expr * Expr
| Comp of Comparison * string * int

module private Parsing = 
    // recognize and strip a leading literal 
    let (|Lit|_|) lit (s:string) =
        if s.StartsWith(lit) then Some(s.Substring lit.Length)
        else None

    // strip leading whitespace
    let (|NoWs|) (s:string) =
        s.TrimStart(' ', '\t', '\r', '\n')

    // parse a binary operator
    let (|BinOp|_|) = function
    | Lit "AND" r -> Some(And, r)
    | Lit "OR" r -> Some(Or, r)
    | _ -> None

    // parse a comparison operator
    let (|Comparison|_|) = function
    | Lit "GT" r -> Some(Gt, r)
    | Lit "EQ" r -> Some(Eq, r)
    | _ -> None

    // parse a variable (alphabetical characters only)
    let (|Var|_|) s =
        let m = System.Text.RegularExpressions.Regex.Match(s, "^[a-zA-Z]+")
        if m.Success then
            Some(m.Value, s.Substring m.Value.Length)
        else
            None

    // parse an integer
    let (|Int|_|) s =
        let m = System.Text.RegularExpressions.Regex.Match(s, @"^-?\d+")
        if m.Success then
            Some(int m.Value, s.Substring m.Value.Length)
        else
            None

    // parse an expression
    let rec (|Expr|_|) = function
    | NoWs (Lit "(" (BinOp (b, Expr(e1, Expr(e2, Lit ")" rest))))) -> 
        Some(BinOp(b, e1, e2), rest)
    | NoWs (Lit "(" (Comparison (c, NoWs (Var (v, NoWs (Int (i, Lit ")" rest))))))) ->
        Some(Comp(c, v, i), rest)
    | _ -> None

let parse = function
| Parsing.Expr(e, "") -> e
| s -> failwith (sprintf "Not a valid expression: %s" s)

let e = parse @"
    (AND (OR (EQ X 0)
             (GT X 10))
         (OR (EQ Y 0)
             (GT Y 10)))"
person kvb    schedule 28.01.2013
comment
Спасибо KVB, я только что прочитал про активный паттерн и нашел его интересным. Я воспользуюсь вашим ответом как шаблоном и попытаюсь развить его. - person huirong; 29.01.2013