1 (in-package #:clnl-lexer)
3 ; I played around with using #'read for netlogo code, which would have been neat.
4 ; However, it provides too many instances where people could inject CL code
5 ; and I wanted to prevent that. In the end, writing my own lexer became kind of fun.
7 (defvar *state* :initial)
8 (defvar *states* :extension-literal)
9 (defparameter *lexes* nil)
11 (defun as-symbol (text) (intern (string-upcase text) :keyword))
13 (defmacro deflex (state match &optional func)
17 ((,scanner (when (stringp ,match) (cl-ppcre:create-scanner ,match))))
24 (and (symbolp text) (eql text ,match))
28 (multiple-value-bind (start end) (cl-ppcre:scan ,scanner text)
29 (and start end (= 0 start) (/= 0 end)))))))
30 (lambda (text) (second (multiple-value-list (cl-ppcre:scan ,scanner text))))
31 ,(or func #'as-symbol))
37 ((lex (find-if (lambda (f) (funcall f *state* :eof)) *lexes* :from-end t :key #'car)))
38 (when lex (list (funcall (third lex) :eof))))
40 ((lex (find-if (lambda (f) (funcall f *state* text)) *lexes* :from-end t :key #'car)))
41 (when (not lex) (error "Can't lex this: ~S" text))
43 ((val (funcall (third lex) (subseq text 0 (funcall (cadr lex) text)))))
45 (cons val (lex (subseq text (funcall (cadr lex) text))))
46 (lex (subseq text (funcall (cadr lex) text))))))))
48 (defun set-state (new-state)
49 (setf *state* new-state))
51 ; This part is the actual netlogo spec
53 (defvar *string-text* "(\\\"|\\r|\\n|\\t|\\\\|\\[^\"]|[^\r\n\"\\])*")
54 (defvar *nonnewline_white_space_char* "[ \\t\\b\\012]")
55 (defvar *letter* "\\w")
56 (defvar *digit* "\\d")
57 ;(defparameter *identifier-char* "[\\w\\d_\\.?=\*!<>:#\+/%\$\^\'&-]")
58 (defvar *identifier-char* "[\\w\\d-.]")
60 ;(defvar *extension-literal-depth* 0)
61 ;(defstruct extension-literal text)
63 ;(deflex :initial "{{"
65 ; (set-state :extension-literal)
69 ;(deflex :extension-literal "}}"
71 ; (if (= 0 *extension-literal-depth*)
72 ; (progn (set-state :initial) text)
73 ; (progn (decf *extension-literal-depth*) (as-symbol text)))))
75 ;(deflex :extension-literal "{{"
76 ; (lambda (text) (incf *extension-literal-depth*) text))
78 ;(deflex :extension-literal "\n|\r" (lambda () (error "End of line reached unexpectedly")))
79 ;(deflex :extension-literal :eof (lambda () (error "end of file reached unexpectedly")))
80 ;(deflex :extension-literal ".")
82 (deflex :initial "[,\\{\\}\\[\\]\\(\\)]" #'as-symbol)
83 (deflex :initial *nonnewline_white_space_char* (constantly nil))
84 (deflex :initial "\\n|\\r" (constantly nil))
85 ;(deflex :initial ";.*[\n\r]?" nil)
86 (deflex :initial (format nil "-?\.?[0-9]~A*" *identifier-char*)
91 ((*readtable* (copy-readtable nil))
93 (read-from-string text))))
94 (if (numberp num?) num? (error "Invalid number")))))
96 (deflex :initial (format nil "~A*" *identifier-char*) #'as-symbol)
97 ;(deflex :initial (format nil "\"~A*\"" *string-text*))
98 ;(deflex :initial (format nil "\"~A*" *string-text*) (lambda (text) (error "Closing double quote is missing")))
99 ;(deflex :initial "." (lambda (text) (error "Non standard character is not allowed")))