2013-06-17 3 views
4

Я создаю некоторую экспертную систему с Clojure, и мне нужно разработать парсер для рекурсивного спуска для чтения правил из текстового файла и создания из него функций clojure. Я написал функцию, которая проверяет, поддерживает ли текстовый файл мою грамматику, и дает мне список строк с такими элементами, как имена функций, числа, имена фактов для моей системы, арифметические и логические операторы. Вот как моя грамматика выглядит следующим образом:Анализатор рекурсивного спуска в Clojure

RULE := EXPR >> FACT 
EXPR := (WSK OpA NUM) || (FACT) || (EXPR OpL EXPR) || (WSK OpA WSK) 

OpL := AND || OR 
OpA := > || < || == 
WSK := [A-Z]+ 
FACT := [a-z]+ 
NUM := [0-9]+\.?[0-9]* 

И это моя функция для проверки грамматики:

(defn wyr 
    "new expression" 
    [przetworzone doPrzetworzenia] 
    (cond 
    (empty? doPrzetworzenia) przetworzone 
    (empty? przetworzone) (if (empty? (acceptLP (first doPrzetworzenia))) 
       "error-poczatek";todo - error 
       (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptLP (first przetworzone)))) (if (empty? (acceptFACT (first doPrzetworzenia))) 
          (if (empty? (acceptWSK (first doPrzetworzenia))) 
           (if (empty? (acceptLP (first doPrzetworzenia))) 
          "error-LP";todo - error 
          (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
           (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
          (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptFACT (first przetworzone)))) (if (empty? (acceptPP (first doPrzetworzenia))) 
           "error-FACT";todo - error 
           (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptWSK (first przetworzone)))) (if (empty? (acceptOpA (first doPrzetworzenia))) 
          (if (empty? (acceptPP (first doPrzetworzenia))) 
           "error-WSK";todo - error 
           (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
          (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptOpA (first przetworzone)))) (if (empty? (acceptNUM (first doPrzetworzenia))) 
          (if (empty? (acceptWSK (first doPrzetworzenia))) 
           "error-OpA";todo - error 
           (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
          (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptPP (first przetworzone)))) (if (empty? (acceptOpL (first doPrzetworzenia))) 
          (if (empty? (acceptImplication (first doPrzetworzenia))) 
           "error-PP";todo - error 
           (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
          (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptOpL (first przetworzone)))) (if (empty? (acceptLP (first doPrzetworzenia))) 
          "error-OpL";todo - error 
          (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptImplication (first przetworzone)))) (if (empty? (acceptFACT (first doPrzetworzenia))) 
           "error-Implication";todo - error 
           (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    (not (empty? (acceptNUM (first przetworzone)))) (if (empty? (acceptPP (first doPrzetworzenia))) 
          "error-NUM";todo - error 
          (wyr (cons (first doPrzetworzenia) przetworzone) (rest doPrzetworzenia))) 
    :else 
    "error") 
) 

Теперь я хотел бы создать функцию Clojure из моего списка строки, которая выше функции дает мне , У вас есть идеи, как это сделать?

UPDATE Вот пример правила и жестко ее вариант:

(ROC> 100) >> купить

(fn 
    (cond 
    (> (ROC) 100) "buy" 
    :else 
    () 
) 
) 
+0

Вы могли бы включить немного больше контекста (каламбур);) где приняты acceptFACT acceptLP и т. Д. –

+0

Откуда появляется ROC в вашем примере (существующая функция, аргумент, что-то еще)? Это похоже на функцию, учитывая, что вы ее вызываете. Кроме того, имеет ли внешняя функция какие-либо аргументы? На данный момент я предполагаю функцию и никаких аргументов. – deterb

+0

Правильно ли ваша грамматика?Он не поддерживает наличие более одного символа для WSK и FACT, а FACT - только в нижнем регистре, в то время как в примере, который вы указали, используется верхний регистр. Тот же вопрос касается BUY - я не знаю, откуда он. Поскольку вы не вызываете его, я собираюсь принять переменную, хотя она также выглядит как строка. – deterb

ответ

2

Во-первых, я согласен с @Arthur относительно использования Instaparse для генерации вашего грамматиста.

Следующий шаг - написать функцию, которая преобразует вашу грамматику в структуры данных Clojure, которые представляют код.

Например, если грамматика разбирает к

[:S [:EXPR [:WSK "ROC"] [:OpA ">"] [:NUM "100"]] :>> [:FCT "BUY"]] ; "ROC > 100 << BUY" 

ваша функция должна принять соответствующие части и перевести их в соответствующий код. Выражение, вероятно, будет проанализировано, например, на (> (ROC) 100), хотя трудно сказать без ввода и ожидаемых результатов.

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

(defn parse-expr [expr] 
    (let [[_ [part1-type part1-val] [part2-type part2-val] [part3-type part3-val]] expr] 
    (if (and (= :WSK part1-type) (= :OpA part2-type) (= :NUM part3-type)) 
     (let [wsk (variable part1-val) 
      opa (variable part2-val) 
      num (Integer/valueOf part3-val)] 
     (list opa (list wsk) num))))) 

(defmacro generate-funcs [parse-tree] 
    (let [[_ expr _ [_ fact]] parse-tree 
     expr (parse-expr expr) 
     fact (symbol fact)] 
    `(fn [] (if ~expr (str ~fact)())))) 

Попробуйте запустить

(parse-expr [:EXPR [:WSK "B"] [:OpA "<"] [:NUM "1"]]) 

и

(macroexpand-1 '(generate-funcs [:S [:EXPR [:WSK "B"] [:OpA "<"] [:NUM "1"]] :>> [:FCT "b"]])) 

, чтобы получить лучшее понимание того, что я имею в виду для перевода данных в коде.

+0

ok, но если я могу создать, например, строковое выражение с помощью функции str, используя соответствующие элементы анализируемой структуры данных, а затем eval? Я также хотел бы иметь свои правила в каком-то списке, чтобы затем проверить этот список, если я могу запускать некоторые правила и генерировать новые факты. – dzwonu

+0

Вам не нужно генерировать, а затем оценивать строку с помощью 'str'. Вместо этого я ожидал, что код будет генерировать нечто вроде '(list (symbol" <") (символ« A ») (Integer/valueOf« 1 »)), и это может быть частью содержимого функции Clojure. Несколько примеров правил и жестко закодированных версий этих правил были бы действительно полезны, трудно сказать, что вам нужно для генерируемого кода. – deterb

+0

Отсчет, пожалуйста, взгляните на обновление моего вопроса. – dzwonu

2

Вы пробовали instaparse:

Производит парсеры из контекстного свободного граммера

(ns example.core 
    (:require [instaparse.core :as insta]) 

(def as-and-bs 
    (insta/parser 
    "S = AB* 
    AB = A B 
    A = 'a'+ 
    B = 'b'+")) 
+0

Спасибо за ваш ответ. Я изучу его документацию и попытаюсь использовать в своем проекте. – dzwonu

+0

Не могли бы вы взглянуть на http://stackoverflow.com/questions/17432282/clojures-require-and-instaparse ?? – dzwonu

Смежные вопросы