Question

I'm creating some expert system with Clojure and I need to develop Recursive descent parser for reading rules from a text file and creating clojure functions from it. I have written function which checks if text file is ok with my grammar and it gives me list of strings with element such as function names, numbers, names of facts for my system, arithmetic and logical operators. That is how my grammar look like:

 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]*

And that's my function for checking grammar:

(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")
  )

Now I would like to create a clojure function from my list of string which above function gives to me. Do you have any idea how to do that?

UPDATE Here is an example of rule and hard coded version of it:

( ROC > 100 ) >> buy

(fn
  (cond
    (> (ROC) 100) "buy"
    :else
    ()
  )
)
Was it helpful?

Solution

First, I'd agree with @Arthur concerning the use of Instaparse for generating your grammer.

The next step is to write a function which transform your grammar into Clojure data structures which represent code.

For example, if the grammar parses to

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

your function needs to take the appropriate parts and translate them into the corresponding code. The expression would probably be parsed into a (> (ROC) 100) for example, though it's hard to tell without example inputs and expected outputs.

Generating the functions is just like normal Clojure data manipulation. Take the result of the parser, turn it into code. Then use that generated code in a macro. Here's a simplified example for working with just what this example needs to handle.

(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) ()))))

Try running

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

and

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

to get a better grasp of what I'm referring to for translating the data into code.

OTHER TIPS

Have you tried instaparse?:

It produces parsers from a context free grammer

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

(def as-and-bs
  (insta/parser
    "S = AB*
     AB = A B
     A = 'a'+
     B = 'b'+"))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top