2014-09-03 18:48:37 +02:00
|
|
|
#|
|
|
|
|
This file is a part of LASS
|
|
|
|
(c) 2014 TymoonNET/NexT http://tymoon.eu (shinmera@tymoon.eu)
|
|
|
|
Author: Nicolas Hafner <shinmera@tymoon.eu>
|
|
|
|
|#
|
|
|
|
|
|
|
|
(in-package #:org.tymoonnext.lass)
|
|
|
|
|
2014-09-05 11:30:19 +02:00
|
|
|
(defvar *vars* (make-hash-table)
|
|
|
|
"Special variable containing LASS-environment variables.
|
|
|
|
|
|
|
|
See the definition of the LET block.")
|
|
|
|
|
|
|
|
(defgeneric resolve (thing)
|
|
|
|
(:documentation "Resolves THING to a value that makes sense for LASS.
|
|
|
|
|
|
|
|
By default the following types are handled:
|
|
|
|
NULL: NIL
|
|
|
|
STRING: the THING itself
|
|
|
|
ARRAY: the variable stored in *VARS* under THING
|
|
|
|
KEYWORD: Colon-prefixed, downcased symbol-name of THING
|
|
|
|
SYMBOL: Downcased symbol-name of THING
|
|
|
|
T: PRINC-TO-STRING of THING")
|
|
|
|
(:method ((thing null))
|
|
|
|
NIL)
|
|
|
|
(:method ((thing string))
|
|
|
|
thing)
|
|
|
|
(:method ((thing array))
|
|
|
|
(gethash (aref thing 0) *vars*))
|
|
|
|
(:method ((thing symbol))
|
|
|
|
(if (keywordp thing)
|
|
|
|
(format NIL ":~a" (string-downcase thing))
|
|
|
|
(string-downcase thing)))
|
|
|
|
(:method ((thing T))
|
|
|
|
(princ-to-string thing)))
|
2014-09-03 21:42:56 +02:00
|
|
|
|
2014-09-05 10:48:27 +02:00
|
|
|
(defun make-property (property &optional value)
|
2014-09-05 11:30:19 +02:00
|
|
|
"Creates a property object with PROPERTY as its key and VALUE as its value."
|
2014-09-05 10:48:27 +02:00
|
|
|
(list :property property value))
|
2014-09-05 09:21:15 +02:00
|
|
|
|
2014-09-05 10:48:27 +02:00
|
|
|
(defun make-block (selector values)
|
2014-09-05 11:30:19 +02:00
|
|
|
"Creates a block object with SELECTOR and VALUES."
|
2014-09-05 10:48:27 +02:00
|
|
|
(cons :block (cons selector values)))
|
2014-09-05 09:21:15 +02:00
|
|
|
|
2014-09-05 10:48:27 +02:00
|
|
|
(defgeneric compile-property (key value)
|
2014-09-05 11:30:19 +02:00
|
|
|
(:documentation "Compile a property of KEY and VALUE to a list of property objects.
|
|
|
|
By default, the following cases are handled:
|
|
|
|
|
|
|
|
(T LIST)
|
|
|
|
A list is created with one property object, wherein the property-value is the
|
|
|
|
Space-concatenated list of RESOLVEd VALUEs. The KEY is DOWNCASEd.
|
|
|
|
|
|
|
|
(T T)
|
|
|
|
A list is created with one property object, wherein the property-value is the
|
|
|
|
RESOLVEd VALUE. The KEY is DOWNCASEd.
|
|
|
|
|
|
|
|
|
|
|
|
Special handling of properties may occur.
|
|
|
|
See DEFINE-SPECIAL-PROPERTY")
|
2014-09-03 18:48:37 +02:00
|
|
|
(:method (key (value list))
|
2014-09-05 10:48:27 +02:00
|
|
|
(list (make-property
|
2014-09-05 11:30:19 +02:00
|
|
|
(string-downcase key)
|
|
|
|
(format NIL "~{~a~^ ~}" (mapcar #'resolve value)))))
|
2014-09-03 18:48:37 +02:00
|
|
|
|
|
|
|
(:method (key value)
|
2014-09-05 10:48:27 +02:00
|
|
|
(list (make-property
|
2014-09-05 11:30:19 +02:00
|
|
|
(string-downcase key)
|
|
|
|
(resolve value)))))
|
2014-09-03 18:48:37 +02:00
|
|
|
|
|
|
|
;; THIS IS SOME PRETTY SHODDY MAGIC CODE HERE
|
|
|
|
;; BEWARE OF DRAGONS AND ALL THAT
|
|
|
|
;; YOU HAVE BEEN WARNED.
|
|
|
|
;;
|
|
|
|
;; Can't wait for bugs about this to hit me down the line.
|
|
|
|
(defgeneric compile-constraint (func args)
|
2014-09-05 11:34:08 +02:00
|
|
|
(:documentation "Compiles a constraint of type FUNC with arguments ARGS to a list of alternative selectors.
|
2014-09-05 11:30:19 +02:00
|
|
|
By default, the following cases are handled:
|
|
|
|
|
|
|
|
(T T)
|
|
|
|
Concatenates its ARGS together with spaces.
|
|
|
|
Preserves OR combinations.
|
|
|
|
|
|
|
|
(NULL NULL)
|
|
|
|
Returns NIL
|
|
|
|
|
|
|
|
(T NULL)
|
|
|
|
Returns FUNC
|
|
|
|
|
|
|
|
(:OR T)
|
|
|
|
Passes all ARGS to COMPILE-SELECTOR individually and then APPENDS
|
|
|
|
all the results together.
|
|
|
|
|
|
|
|
(:AND T)
|
|
|
|
Concatenates its ARGS together without spaces.
|
|
|
|
Preserves OR combinations.
|
|
|
|
|
|
|
|
|
|
|
|
Special handling of constraints may occur.
|
2014-09-05 11:34:08 +02:00
|
|
|
See DEFINE-SPECIAL-SELECTOR.")
|
2014-09-03 18:48:37 +02:00
|
|
|
(:method (func args)
|
|
|
|
(let ((cfunc (cond ((listp func)
|
|
|
|
(if (symbolp (first func))
|
|
|
|
(compile-selector func)
|
|
|
|
func))
|
2014-09-03 21:42:56 +02:00
|
|
|
(T (list (resolve func)))))
|
2014-09-03 18:48:37 +02:00
|
|
|
(cargs (compile-selector (car args))))
|
|
|
|
(loop with result = ()
|
|
|
|
for func in cfunc
|
|
|
|
do (loop for arg in cargs
|
|
|
|
do (push (format NIL "~a ~a" func arg) result))
|
|
|
|
finally (return (compile-constraint (nreverse result) (cdr args))))))
|
|
|
|
(:method ((func null) (args null))
|
|
|
|
NIL)
|
|
|
|
(:method (func (args null))
|
|
|
|
func)
|
|
|
|
(:method ((func (eql :or)) args)
|
|
|
|
(apply #'append (mapcar #'compile-selector args)))
|
|
|
|
(:method ((func (eql :and)) args)
|
|
|
|
(when args
|
|
|
|
(if (cdr args)
|
|
|
|
(let ((cfunc (compile-selector (first args)))
|
|
|
|
(cargs (compile-selector (second args))))
|
|
|
|
(loop with result = ()
|
|
|
|
for func in cfunc
|
|
|
|
do (loop for arg in cargs
|
|
|
|
do (push (format NIL "~a~a" func arg) result))
|
2014-09-04 23:43:48 +02:00
|
|
|
finally (return (compile-constraint :and (cons (cons :OR (nreverse result)) (cddr args))))))
|
|
|
|
(if (and (listp (first args)) (eql (first (first args)) :OR))
|
|
|
|
(rest (first args))
|
|
|
|
args)))))
|
2014-09-03 18:48:37 +02:00
|
|
|
|
2014-09-05 11:30:19 +02:00
|
|
|
(defgeneric compile-selector (selector)
|
|
|
|
(:documentation "Compiles the SELECTOR form into a list of alternative selectors.
|
|
|
|
By default, the following cases are handled:
|
|
|
|
|
|
|
|
(NULL)
|
|
|
|
Returns NIL.
|
|
|
|
|
|
|
|
(LIST)
|
|
|
|
Calls COMPILE-CONSTRAINT with the SELECTOR's CAR and CDR.
|
|
|
|
|
|
|
|
(T)
|
|
|
|
Returns a list with the RESOLVEd SELECTOR.")
|
|
|
|
(:method ((selector null))
|
|
|
|
NIL)
|
|
|
|
(:method ((selector list))
|
|
|
|
(compile-constraint (car selector) (cdr selector)))
|
|
|
|
(:method ((selector T))
|
|
|
|
(list (resolve selector))))
|
2014-09-03 18:48:37 +02:00
|
|
|
|
2014-09-03 20:18:34 +02:00
|
|
|
(defgeneric compile-block (header fields)
|
2014-09-05 11:30:19 +02:00
|
|
|
(:documentation "Compiles the block with given HEADER and FIELDS list.
|
|
|
|
By default, the following case is handled:
|
|
|
|
|
|
|
|
(T T)
|
|
|
|
Blocks are handled in the following way:
|
|
|
|
The HEADER is used as a selector and compiled through COMPILE-SELECTOR.
|
|
|
|
Fields are semantically segregated through KEYWORDS and LISTS.
|
|
|
|
|
|
|
|
Every time a KEYWORD is encountered, it is taken as the current property
|
|
|
|
and all following objects until either a LIST or a KEYWORD is encountered
|
|
|
|
are gathered as the property's values.
|
|
|
|
|
|
|
|
Every time a LIST is encountered, it is taken as a SUB-BLOCK and is
|
|
|
|
passed to COMPILE-BLOCK with the HEADER being the current block's
|
|
|
|
selector prepended to the selector of the sub-block.
|
|
|
|
|
|
|
|
|
|
|
|
Special handling of blocks may occur.
|
|
|
|
See DEFINE-SPECIAL-BLOCK.")
|
2014-09-03 20:18:34 +02:00
|
|
|
(:method (selector fields)
|
|
|
|
(let ((selector (compile-selector selector))
|
2014-09-05 10:48:27 +02:00
|
|
|
(props ())
|
2014-09-03 20:18:34 +02:00
|
|
|
(subblocks ()))
|
2014-09-05 10:48:27 +02:00
|
|
|
;; compute props and subblocks
|
|
|
|
(flet ((add-prop (prop)
|
|
|
|
(when prop
|
|
|
|
(let ((prop (nreverse prop)))
|
|
|
|
(dolist (prop (compile-property (car prop) (cdr prop)))
|
|
|
|
(push prop props))))))
|
|
|
|
(loop with prop = ()
|
2014-09-03 20:30:16 +02:00
|
|
|
for field in fields
|
|
|
|
do (etypecase field
|
|
|
|
(keyword
|
2014-09-05 10:48:27 +02:00
|
|
|
(add-prop prop)
|
|
|
|
(setf prop (list field)))
|
2014-09-03 20:30:16 +02:00
|
|
|
(list
|
|
|
|
(dolist (subblock (compile-block (list selector (car field)) (cdr field)))
|
|
|
|
(push subblock subblocks)))
|
2014-09-05 10:48:27 +02:00
|
|
|
(T (push field prop)))
|
|
|
|
finally (add-prop prop)))
|
2014-09-03 20:30:54 +02:00
|
|
|
;; Returns list of blocks with ours consed to front
|
2014-09-05 10:48:27 +02:00
|
|
|
(cons (make-block selector (nreverse props))
|
2014-09-03 20:30:16 +02:00
|
|
|
(nreverse subblocks)))))
|
2014-09-03 18:48:37 +02:00
|
|
|
|
|
|
|
(defun compile-sheet (&rest blocks)
|
2014-09-05 11:30:19 +02:00
|
|
|
"Compiles a LASS sheet composed of BLOCKS.
|
|
|
|
Each BLOCK is passed to COMPILE-BLOCK. The results thereof are appended
|
|
|
|
together into one list of blocks and properties."
|
2014-09-03 20:30:16 +02:00
|
|
|
(let ((sheet ()))
|
2014-09-03 18:48:37 +02:00
|
|
|
(dolist (block blocks)
|
2014-09-03 20:30:16 +02:00
|
|
|
(dolist (resulting-block (compile-block (car block) (cdr block)))
|
|
|
|
(push resulting-block sheet)))
|
|
|
|
(nreverse sheet)))
|