How to define multiple character parenthesis in SMIE - emacs

I copied the the code in the GNU manual about SMIE. The indent of begin and end works fine.
(defvar sample-smie-grammar
(smie-prec2->grammar
(smie-bnf->prec2
'((id)
(inst ("begin" insts "end")
("if" exp "then" inst "else" inst)
(id ":=" exp)
(exp))
(insts (insts ";" insts) (inst))
(exp (exp "+" exp)
(exp "*" exp)
("(" exps ")"))
(exps (exps "," exps) (exp)))
'((assoc ";"))
'((assoc ","))
'((assoc "+") (assoc "*")))))
But when I try to add class/endclass, module/endmodule as below, the new added pairs do not indent at all. When I enable show-paren-mode. They are not highlighted as begin and end does.
(defvar sample-smie-grammar
(smie-prec2->grammar
(smie-bnf->prec2
'((id)
(inst ("begin" insts "end")
("module" insts "endmodule")
("class" insts "endclass")
("if" exp "then" inst "else" inst)
(id ":=" exp)
(exp))
(insts (insts ";" insts) (inst))
(exp (exp "+" exp)
(exp "*" exp)
("(" exps ")"))
(exps (exps "," exps) (exp)))
'((assoc ";"))
'((assoc ","))
'((assoc "+") (assoc "*")))))

Related

Error Bad argument type : fixnump : nil when calling second function

I'm trying to create an array of lists with its elements being: Surname , Name and Age.
Here is my code in AutoLISP:
(defun C:DP_ADINREG ( / prenume nume varsta inreg)
(initget 1)
(setq prenume (getstring "\nIntroduceti prenumele: "))
(initget 1)
(setq nume (getstring "\nIntroduceti numele: "))
(initget 7)
(setq varsta (getint "\nIntroduceti varsta: "))
(setq inreg (list (cons 'pn prenume) (cons 'nf nume)
(cons 'v varsta))
DP_DATA (append DP_DATA (list inreg))
)
(princ)
)
(defun C:DP_LISTARE ( / curent inreg n)
(setq curent DP_DATA
n 1)
(while curent
(setq inreg (car curent))
(princ (strcat "\nInregistrarea #" (itoa n)
": " (cdr (assoc 'pn inreg))
", " (cdr (assoc 'nf inreg))
". Varsta " (itoa (cdr (assoc 'v inreg)))
)
)
(setq curent (cdr curent)
n (1+ n)
)
)
(princ)
)
Problem is, when trying to access the second function, it gives me an error called:
Bad argument type : fixnump : nil.
Now I have no idea where the problem actually is.
Any ideas?
The error:
; error: bad argument type: fixnump: nil
Arises when a function requiring an integer argument is supplied with a value of nil. Hence, this error is arising from the evaluation of the second of your two itoa expressions:
(itoa (cdr (assoc 'v inreg)))
(Since the first itoa expression is being supplied with the variable n, which cannot be nil).
This implies that the following expression returns nil:
(cdr (assoc 'v inreg))
Which implies that one of the association lists within the list held by your global variable DP_DATA does not contain a dotted pair with key v. I would therefore suggest checking the value held by your global variable DP_DATA.
Aside: note that initget has no effect on a getstring prompt - you can achieve the same effect using a basic while loop, e.g.:
(while (= "" (setq prenume (getstring "\nIntroduceti prenumele: ")))
(princ "\nPlease enter a first name.")
)
(while (= "" (setq nume (getstring "\nIntroduceti numele: ")))
(princ "\nPlease enter a surname.")
)
You can account for null values in your association list using some basic error checking:
(defun C:DP_ADINREG ( / prenume nume varsta )
(while (= "" (setq prenume (getstring "\nIntroduceti prenumele: ")))
(princ "\nPlease enter a first name.")
)
(while (= "" (setq nume (getstring "\nIntroduceti numele: ")))
(princ "\nPlease enter a surname.")
)
(initget 7)
(setq varsta (getint "\nIntroduceti varsta: ")
DP_DATA (cons (list (cons 'pn prenume) (cons 'nf nume) (cons 'v varsta)) DP_DATA)
)
(princ)
)
(defun C:DP_LISTARE ( / n )
(setq n 0)
(foreach lst (reverse DP_DATA)
(princ
(strcat
"\nInregistrarea #" (itoa (setq n (1+ n)))
": " (cond ((cdr (assoc 'pn lst))) (""))
", " (cond ((cdr (assoc 'nf lst))) (""))
". Varsta " (itoa (cond ((cdr (assoc 'v lst))) (0)))
)
)
)
(princ)
)
The above will return a blank first name/surname where not present, and an age of 0 where not present; you could alternatively return an error if these values are not present, e.g.:
(defun C:DP_LISTARE ( / n nf pn v )
(setq n 1)
(foreach lst (reverse DP_DATA)
(if
(and
(setq pn (cdr (assoc 'pn lst)))
(setq nf (cdr (assoc 'nf lst)))
(setq v (cdr (assoc 'v lst)))
)
(princ (strcat "\nInregistrarea #" (itoa n) ": " pn ", " nf ". Varsta " (itoa v)))
(princ (strcat "\nMissing data for item " (itoa n)))
)
(setq n (1+ n))
)
(princ)
)

read words from a file into nested list in common lisp programming language

I have a file named test.txt, it contains
"hello this is a test file"
I want to read it from the file so that every word represents lists of character and every paragraph represents lists of words means that I want to store them into a nested list like:
(list(list (h e l l o)) (list(t h i s))(list(i s)) (list(a)) (list(t e s t)) (list(f i l e))))
I am totally new in lisp and have a lot of confusion about this problem.
Solution without any dependencies
(defun split (l &key (separators '(#\Space #\Tab #\Newline)) (acc '()) (tmp '()))
(cond ((null l) (nreverse (if tmp (cons (nreverse tmp) acc) acc)))
((member (car l) separators)
(split (cdr l) :separators separators
:acc (if tmp (cons (nreverse tmp) acc) acc)
:tmp '()))
(t
(split (cdr l) :separators separators
:acc acc
:tmp (cons (car l) tmp)))))
(defun read-file-lines (file-path)
(with-open-file (f file-path :direction :input)
(loop for line = (read-line f nil)
while line
collect line)))
(defun read-file-to-word-characters (file-path)
(mapcan (lambda (s) (split (coerce s 'list)))
(read-file-lines file-path)))
(read-file-to-word-characters "~/test.lisp.txt")
;; ((#\h #\e #\l #\l #\o) (#\t #\h #\i #\s) (#\i #\s) (#\a) (#\t #\e #\s #\t)
;; (#\f #\i #\l #\e))
Convert the characters to one-letter strings:
;; apply to elements of nested list (= a tree) the conversion function `string`
(defun map-tree (fn tree)
(cond ((null tree) '())
((atom tree) (funcall fn tree))
(t (mapcar (lambda (branch) (map-tree fn branch)) tree))))
(map-tree #'string (read-file-to-word-characters "~/test.lisp.txt"))
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))
Content of "~/test.lisp.txt":
hello this
is a test file
Solution using cl-ppcre (Edi Weitz's congenial regex package)
;; look here in an answer how to use cl-ppcre:split
;; https://stackoverflow.com/questions/15393797/lisp-splitting-input-into-separate-strings
(ql:quickload :cl-ppcre)
(defun read-file-lines (file-path)
(with-open-file (f file-path :direction :input)
(loop for line = (read-line f nil)
while line
collect line)))
(defun string-to-words (s) (cl-ppcre:split "\\s+" s))
(defun to-single-characters (s) (coerce s 'list))
(defun read-file-to-character-lists (file-path)
(mapcan (lambda (s)
(mapcar #'to-single-characters
(string-to-words s)))
(read-file-lines file-path)))
(read-file-to-character-lists "~/test.lisp.txt")
;; ((#\h #\e #\l #\l #\o) (#\t #\h #\i #\s) (#\i #\s) (#\a) (#\t #\e #\s #\t)
;; (#\f #\i #\l #\e))
;; or use above's function:
(map-tree #'string (read-file-to-character-lists "~/test.lisp.txt"))
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))
;; or:
(defun to-single-letter-strings (s) (cl-ppcre:split "\\s*" s))
(defun read-file-to-letter-lists (file-path)
(mapcan (lambda (s)
(mapcar #'to-single-letter-strings
(string-to-words s)))
(read-file-lines file-path)))
(read-file-to-letter-lists "~/test.lisp.txt")
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))

Function that returns #t if expr is a valid boolean expression in racket

expr = variable | bool-literal | not-expr | and-expr | or-expr .
variable = a symbol v for which (is-vble? v) returns #t .
bool-literal = "t" | "f" .
not-expr = "(" "not" expr ")" .
and-expr = "(" expr "and" expr ")" .
or-expr = "(" expr "or" expr ")" .
implication-expr = "(" expr "-->" expr ")" .
Implement the function (is-bool-expr? expr) that returns #t if expr is a valid boolean expression (as defined above), and #f otherwise. For example:
(is-bool-expr? 't)
t
(is-bool-expr? '(t or (not f)))
t
(is-bool-expr? '((p and (p --> f)) --> q))
t
(is-bool-expr? '((p and (p --> f)) --> q))
t
(is-bool-expr? '(cheese shoes string))
f

Convert all type of parentheses in emacs latex to \left \right form

In my .emacs I have the following function
;; Put the point immediately after a closing paren. replace-matching-parens will
;; replace the closing ) with \right) and the matching start paren ( with
;; \left(.
(defun replace-matching-parens ()
(interactive)
(save-excursion
(let ((end-point (point)))
(backward-list)
(let ((start-point (point)))
(goto-char end-point)
(re-search-backward ")" nil t)
(replace-match " \\\\right)" nil nil)
(goto-char start-point)
(re-search-forward "(" nil t)
(replace-match "\\\\left( " nil nil)))))
bound to a key to replace matching occurences of ( and ) with \left( and \right). How can I extend this to also work for pairs of [ ] and { } such that they are replaced by \left[ and \right] resp. \left{ and \right}.
This should get you started. Essentially, to use backward-list you need to give the other chars (][}{) the syntax of open and close parentheses temporarily. And then you need to adjust the search and replace to use those chars, not just regular parens.
(eval-when-compile (require 'cl)) ; case
(defun replace-matching-parens (char)
(interactive (list (char-before)))
(unless (memq char '(?\) ?\] ?\}))
(error "Cursor is not after `)', `]', or `}'"))
(save-excursion
(let ((syntable (copy-syntax-table (syntax-table)))
(end-point (point)))
(modify-syntax-entry ?\[ "(" syntable)
(modify-syntax-entry ?\] ")" syntable)
(modify-syntax-entry ?\{ "(" syntable)
(modify-syntax-entry ?\} ")" syntable)
(with-syntax-table syntable
(backward-list)
(let ((start-point (point)))
(goto-char end-point)
(search-backward (format "%c" char) nil t)
(replace-match (format " \\\\right%c" char) nil nil)
(goto-char start-point)
(search-forward (format "%c" (setq newchar (case char
(?\) ?\( )
(?\] ?\[ )
(?\} ?\{ ))))
nil t)
(replace-match (format "\\\\left%c " newchar) nil nil))))))

How to tell a lisp reader function to ignore errors during parsing

I need a way to suppress any error messages raised when parsing code using read-from-string so that I can read from Clojure code using something like this:
(let* ((string-with-code " '(func UpperCase \"string\")")
(brace-pos (position #\( string-with-code))
(end-of-token (+ brace-pos
(position #\Space (subseq string-with-code brace-pos))))
(first-token (subseq string-with-code (incf brace-pos) end-of-token)))
(format t "Found function: '~a'"
(read-from-string first-token)))
;; ==> Found function: 'func'
It basically prints the function name from the lisp code in the string. It works Ok until you try to use the dot operator(.) as the first item in the list. Clojure uses the . both to cons and to access classes in a Java package and hence valid code like:
(defmacro chain
([x form] `(. ~x ~form))
([x form & more] `(chain (. ~x ~form) ~#more)))
would cause an error:
*** - READ from #<INPUT STRING-INPUT-STREAM>: token "." not allowed here
if I were to walk it printing every function in the code. I want a way to ignore/suppress the error messages from read-from-string for this code to work preferably without modifying the way the lisp reader works.
EDIT :
A complete program:
(defvar string-with-code "(defmacro chain
([x form] `(d ~x ~form))
([x form & more] `(chain (. ~x ~form) ~#more)))
")
(defvar end-of-token 0)
(defvar first-token 0)
(defun functions-in-string (code)
(let ((bpos (position #\( code)))
(unless (null bpos)
(setq end-of-token (+ bpos (position #\Space (subseq code bpos))))
(setq first-token (subseq code (incf bpos) end-of-token))
(ignore-errors
(format t "Found function: '~(~A~)'~%" (read-from-string first-token)))
(functions-in-string (subseq code end-of-token)))))
;; (ignore-errors
;; (functions-in-string 0 code))
(functions-in-string string-with-code)
OUTPUT :
Found function: 'defmacro'
Found function: '[x'
Found function: 'd'
Found function: '[x'
Found function: 'chain'
;; You'll get the error below if ignore-errors wraps around the function call
;; *** - READ from #<INPUT STRING-INPUT-STREAM>: token "." not allowed here
Not clear what you are asking, but ignoring errors simply is:
CL-USER 37 > (ignore-errors (read-from-string "(. foo bar)"))
NIL
#<CONDITIONS:SIMPLE-READER-ERROR 402000243B>
In case of an error, IGNORE-ERRORS returns NIL and as the second return value the condition.
If you want more control, you would need to write an error handler.
Here's a start for Clojure yacc parsers. This needs more attention from you to deal with special Clojure reader macros and possibly ensure some other grammar aspects, but this is already a functioning start:
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun print-args (&rest args) (format nil "~{~a~^ ~}" args) ))
(defun clojure-lexer (stream)
(let ((digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
(translations (make-hash-table)))
(loop :for (key . value) :in
'((#\( . oparen)
(#\) . cparen)
(#\[ . obracket)
(#\] . cbracket)
(#\' . squote)
(#\` . accent)
(#\: . colon)
(#\, . comma)
(#\# . at)) :do
(setf (gethash key translations) value))
(labels ((%reverse-coerce (collected)
(coerce (nreverse collected) 'string))
(%read-token ()
(loop
:with collected := nil
:and stringp := nil
:and commentp := nil
:for token := (read-char stream nil nil) :do
(cond
((null token)
(return (and collected (%reverse-coerce collected))))
((char= token #\;)
(push token collected)
(setf commentp t))
((char= token #\")
(if commentp
(push token collected)
(if stringp
(progn
(push token collected)
(return (%reverse-coerce collected)))
(if collected
(progn
(unread-char token)
(return (%reverse-coerce collected)))
(progn
(push token collected)
(setf stringp t))))))
((gethash token translations)
(if (or stringp commentp)
(push token collected)
(if collected
(progn
(unread-char token stream)
(return (%reverse-coerce collected)))
(return (gethash token translations)))))
((member token '(#\Newline #\Rubout))
(if commentp
(return (and collected (%reverse-coerce collected)))
(if stringp
(push token collected)
(and collected (return (%reverse-coerce collected))))))
((member token '(#\Space #\Tab))
(if (or stringp commentp)
(push token collected)
(and collected (return (%reverse-coerce collected)))))
(t (push token collected))))))
(lambda ()
(let* ((key (%read-token))
(value (or (gethash key translations) key)))
(if (null key)
(values nil nil)
(let ((terminal
(cond
((member key '(oparen cparen squote colon accent
comma at obracket cbracket))
key)
((or (member (char key 0) digits)
(and (char= (char key 0) #\-)
(> (length key) 1)
(member (char key 1) digits)))
'number)
((char= (char key 0) #\") 'string)
((char= (char key 0) #\;) 'comment)
(t 'id))))
(values terminal value))))))))
(yacc:define-parser *clojure-parser*
(:start-symbol exp)
(:terminals (id oparen cparen squote colon accent
comma at obracket cbracket string number))
(exp
(oparen id exp-list cparen #'print-args)
(oparen id cparen #'print-args)
(obracket exp-list cbracket #'print-args)
(obracket cbracket #'print-args)
(comment #'print-args)
(accented-exp #'print-args)
(quoted-exp #'print-args)
(term #'print-args))
(term id string number)
(quoted-exp (quote exp))
(accented-exp (accent exp) (accent at exp))
(exp-list (exp exp-list) exp))
(defun parse-clojure (string)
(yacc:parse-with-lexer
(clojure-lexer (make-string-input-stream string)) *clojure-parser*))
(parse-clojure
"(defn str-invoke [instance method-str & args]
(clojure.lang.Reflector/invokeInstanceMethod
\"instance\" 123
method-str
(to-array args)))")
Results in:
;; "OPAREN defn (str-invoke
;; (OBRACKET (instance (method-str (& args))) CBRACKET
;; OPAREN clojure.lang.Reflector/invokeInstanceMethod (\"instance\"
;; (123
;; (method-str
;; OPAREN to-array args CPAREN))) CPAREN)) CPAREN"
Here's the BNF for the above grammar (not claiming it is the Clojure grammar, it only reflects the Lisp code above):
exp ::= '(' id exp-list ')'
| '(' id ')'
| '[' exp-list ']'
| '[' ']'
| ';' /[^\n]*/
| accented-exp
| quoted-exp
| term
term ::= id | '"' /[^"]*/ '"' | /-?[0-9][^\s]+/
quoted-exp ::= '\'' exp
accented-exp ::= '`' exp | '`' '#' exp
exp-list ::= exp exp-list | exp
id ::= /[^()[\]:,`#']+/
For simplicity, some parts are given as regular expressions, those are delimited by //.