Re: rfc: curvbol - a lisp language for processing text (long)
Marco Baringer <[email protected]> Sat, 05 Jun 2004 13:15:37 +0200
| Newsgroups | gmane.lisp.clump |
|---|---|
| Message-ID | <[email protected]> |
Arthur Lemmens <[email protected]> writes: > Your specification looks interesting at first sight, but would be a lot > more readable if you included examples of each operator. Some kind of > motivation and a comparison with other systems (why/when is this better/ > worse than regular expressions, recursive decent parsers, parser generators) > would also help a lot. lisp already has regexps, recursive desecent parsers and parser generators, i'm not trying to replace/improve any of those, i'm attempting a different way looking at the problem. instead of describing the grammar of the file (which i many not know, or may not be expressable as a context free grammar) or mixing lisp code with regexp checks (which gets very messy if the logic is complicated) i want to express how to move around in the text and say what to do with certain sections of the text. basically this is another tool to put in the toolbox, not a replacement for the other techniques. Anyway here's the list of ops with examples. at the end of this (rather long) email i've put a real life curvbol program. it is in fact the only existing real life curvbol program :). Before i begin, curvbol programs are called via the curvbol macro: (curvbol subject &rest implicit-seq) - evaluate IMPLICIT-SEQ with the subject bound to SUBJECT, cursor starts at 0, direction starts as :forwards. All the examples look like this: [code] ==> [value], [env], [subject with cursor as #\|] The Examples: MATCH: (curvbol "abcd" (match "a")) ==> "a", NIL, a|bcd (curvbol "abcd" (match "a") (match "bc")) ==> "bc", NIL, abc|d SEQ: (curvbol "abcd" (seq (match "a") (match "bc"))) ==> "bc", NIL, abc|d (curvbol "abcd" (seq (match "a") (match "X"))) ==> NIL, NIL, |abcd LOOP: (curvbol "aa.aa.aa." (loop (0 nil t) (match "aa."))) ==> "aa.", NIL, aa.aa.aa.| (curvbol "aa.aa.aa." (loop (0 nil nil) (match "aa.")) (match "aa.")) ==> "aa.", NIL, aa.|aa.aa. (curvbol "aa.aa.aa." (loop (0 nil t) (match "aa.")) (match "aa.")) ==> "aa.", NIL, aa.aa.aa.| GOTO: (curvbol "abcd" (goto (match "bc")) (match "d")) ==> NIL, NIL, |abcd (curvbol "abcd" (goto (match "bc")) (match "bc")) ==> "bc", NIL, abc|d GOPAST: (curvbol "abcd" (gopast (match "bc"))) ==> "bc", NIL, abc|d OR: (curvbol "abcd" (or (match "X") (match "a"))) ==> "a", NIL, a|bcd (curvbol "aa" (or (match "aa") (match "a")) (match "a")) ==> "a", NIL, aa|bb (curvbol "abc" (or (seq (gopast (match "b")) (match "X")) (seq (gopast (match "b")) (match "c")))) ==> "c", NIL, abc| AND: (curvbol "abcd" (and (match "a") (match "b"))) ==> NIL, NIL, |abcd (curvbol "abcd" (and (goto (match "c")) (goto (match "b")) (gopast (match "a")))) ==> "a", NIL, a|bcd REVERSE: (curvbol "abcd" (goto (match "cd")) (reverse (match "ab"))) ==> "ab", NIL, |abcd (curvbol "abcd" (gopast (match "d")) (reverse (goto (match "b")) (match "a"))) ==> "a", NIL, |abcd BACKWARDS: (curvbol "abcd" (backwards (match "bcd"))) ==> "bcd", NIL, |abcd (curvbol "abcd" (backwards (match "d") (match "c")) (match "a")) ==> "a", NIL, a|bcd NOT: (curvbol "abcd" (not (match "X"))) ==> T, NIL, |abcd (curvbol "abcd" (not (goto (match "b")) (match "X"))) ==> T, NIL, ab|cd (curvbol "abcd" (not (goto (match "c")) (match "d"))) ==> NIL, NIL, |abcd TRY: (curvbol "abcd" (try (goto (match "c")) (match "X"))) ==> NIL, NIL, abc|d (curvbol "abcd" (match "a") (try (goto (match "c")) (match "d"))) ==> "d", NIL, a|bcd TEST: (curvbol "abcd" (match "a") (test (match "b"))) ==> "b", NIL, a|bcd (curvbol "abcd" (match "a") (test (match "X"))) ==> NIL, NIL, a|bcd HOP: (curvbol "abcd" (hop 2)) ==> 2, NIL, ab|cd (curvbol "abcd" (hop 5)) ==> NIL, NIL, |abcd LET: (curvbol "abcd" (let y "ab") (match y)) ==> "ab", NIL, ab|cd MAKE-SPLICE: (curvbol "abcd" (make-splice 1 3)) ==> "bc", NIL, |abcd (curvbol "abcd" (match "a") (make-splice (cursor) (seq (gopast (match "d")) (cursor)))) ==> "bc", NIL, abcd| A Real Life Example: This examples use these curvbol functions/macros which aren't part of the core: (chew-whitespace) - moves the cursor past any whitespace chars (#\Space, #\Tab, #\Newline, #\Linfeed, or #\Return). Perl equivalent: '\s*' (let-splice var &rest seq) - a macro which binds var to a splice which starts at cursor's value before the evaluation of SEQ and ends at cursor's value after the evaluation of SEQ. (defcurvbol parse-scheda () ;; the body of the curvbol function is an implicit SEQ, the ;; implication is that if any of these forms fail (return NIL) the ;; function will immediatly return NIL (gopast (match "Padiglione")) (gopast (match "<FONT color=\"red\">")) (chew-whitespace) (let-splice padiglione ;; move the cursor to the next non #\Space or digit char (goto (not (class " 0123456789")))) (gopast (match "<b><span style=\"background-color: #000000\"><font color=\"#FFFFFF\">") (chew-whitespace)) ;; the cursor is now located after any whitespace after the above string (let-splice ragione-sociale ;; move the cursor to the start of the next sequence of white ;; space chars followed by "<BR>" (goto (chew-whitespace) (match "<BR>"))) ;; find the address (gopast (match "<font >") (chew-whitespace)) (let-splice indirizzo (goto (chew-whitespace) (match "<BR>"))) ;; (or X t) is basically the '?' regepx operator (or (seq ;; move the cursor to the start of next sequence of digits (goto (class "0123456789")) (let-splice cap (goto (not (class "0123456789")))) t) (gopast (match "-") (chew-whitespace)) (or (let-splice citta (goto (match "-"))) t) (gopast (match "-") (chew-whitespace)) (let-splice paese (goto (chew-whitespace) (match "-"))) ;; in the subject we don't know which, if any, of these will appear ;; nor do we know in what order, but since we're using the AND op each ;; clause will be evaluated starting at the same cursor position. (and (seq (gopast (match "Tel.")) (let-splice tel (goto (not (class "0123456789-+ ()/"))))) (seq (gopast (match "Fax")) (let-splice fax (goto (not (class "0123456789-+ ()/"))))) (seq (gopast (match "<TD WIDTH =\"305\"> E-Mail: ")) (let-splice email (goto (match "</TD>"))))) (or (seq (gopast (match "Stand:")) (gopast (match "</b>")) (let-splice desc (goto (match "<a HREF=\"javascript:newpage('dettaglioAzienda.do")))) t)) well, at least this takes care of the docs :) -- -Marco Ring the bells that still can ring. Forget your perfect offering. There is a crack in everything. That's how the light gets in. -Leonard Cohen