Autocad programming in visual lisp.
Autocad visual lisp has made available all (or maybe most) functionality of autocad exposed through the activex and vba interface. However the interface so provided exposes a lot of internal data structures probably for the sake of efficiency but make the API less productive for direct use. One should build more lisp-like abstraction on the top of the visual lisp API, so that lists can be used for most things (instead of idiosyncratic "safe-arrays" and "variants"). There is a nice book titled "
Visual Lisp: a guide to artful programming" by Phil Kreiker that presents such an abstraction layer. The software accompanying the book implements the abstraction layer.
Following are some usage examples of the artful programming API
;; setting and getting a variable that would be persistent with the drawing file.
(defun setp (varname value)
(eval (list 'setq varname (list 'quote value) ))
(ap-putdict varname))
(defun getp (varname)
(ap-getdict varname))
;; getting and setting a external string-valued property associated with an object
(defun set-prop (obj prop propvalue)
(regapp prop)
(apa-SetXData obj (list (cons 1001 prop) (cons 1000 propvalue))))
(defun get-prop (obj prop / xdata)
(setq xdata (apa-GetXData obj prop))
(cdr (assoc 1000 xdata)))
;;; aforementioned are two important primitives for building custom applications.
Parsing and lexical analysis using common lisp
Lexical analysis and parsing always seemed to be a demanding task (to me). That was until I used the common lisp based tools for the same. YYSTYPE and declaration of types of productions always seemed to be an extra baggage to bother about. Since I am not a language developer and only need to parse in odd problem solving scenarios, the perceived intellectual load of parsing makes me go for work-arounds when writing a formal parser was the right thing. An example of such work-arounds is using regexp substitutions untill the text is transformed into a program in an appropriate scripting language. With lex/yacc, adding a little parser for a little language lang1 to your project entails adding several files (lang1.tab.h, ang1.lex, lang1.yacc, lang1parser.c,lang1lexer.c may be more by way of AST builder and walker functions) in the code-base. With common lisp based parser/lexer generators, the usage is so compact that writing the lexer and parser seems like writing just another couple of functions. The AST-building is automatic.
Following is a sample parser+lexer written for cl-yacc and cl-lexer - an infix to prefix translator. Compare this with the number of files that a single parser will add to your project. It uses the
cl-lexer and the
cl-yacc libraries.
Now my approach to language processing is as follows.
1. If I am free to choose the syntax I would use syntact abstraction mechanisms of lisp (or tcl).
2. If it is somebody else's syntax and not reducible to syntactic abstration of lisp then parse using cl-yacc and (A) translate to C or lisp in which the semantics is implemented or (B) process as you translate.
3. If XML syntax use SAX
4. If natural language avoid doing it.
1
2 (deflexer common-lexer
3 ("[0-9]+([.][0-9]+([Ee][0-9]+)?)"
4 (return (values 'flt (read (make-string-input-stream %0))) ))
5 ("[0-9]+"
6 (return (values 'int (read (make-string-input-stream %0))) ))
7 ("[\+\\-\*\/\$%\^\&\<\>\=/]" (return (values (read (make-string-input-stream %0)) %0)))
8 ("\\(" (return (values 'lparen nil)))
9 ("\\)" (return (values 'rparen nil)))
10 ("[:alpha:][:alnum:]*"
11 (return (values (filter-keywords %0) %0)))
12 ("[:space:]+")
13 )
14
15 (define-parser *expression-parser*
16 (:start-symbol expression)
17 (:terminals (int flt id + - * / lparen rparen))
18 (:precedence ((:right * /) (:right + -)))
19
20 (expression
21 (expression + expression #'(lambda (a b c) (list '+ a c)))
22 (expression - expression #'(lambda (a b c) (list '- a c)))
23 (expression * expression #'(lambda (a b c) (list '* a c)))
24 (expression / expression #'(lambda (a b c) (list '/ a c)))
25 (id #'read-from-string)
26 flt
27 int
28 (lparen expression rparen #'(lambda (a b c) b )))
29 )
30
31 (defun parse (parser lexer str)
32 (let ((token-stream (funcall lexer str)))
33 (parse-with-lexer token-stream parser)))
34
35
36 (defun filter-keywords (x)
37 (let ((val (read (make-string-input-stream (format nil "~a" x))))
38 (keywords '(for with in loop on if while do continue)))
39 (if (member val keywords)
40 val
41 'id)))
42
43
44 (prin1 (parse *expression-parser* 'common-lexer "((x+((3.14))))+y*2.718*z" ))
45 ;;; the returned s-expression is : (+ (+ X 3.14) (* Y (* 2.718 Z)))