├── .gitignore ├── README.markdown ├── cl-punch-test.asd ├── cl-punch.asd ├── src └── cl-punch.lisp └── t └── cl-punch.lisp /.gitignore: -------------------------------------------------------------------------------- 1 | *.fasl 2 | *.dx32fsl 3 | *.dx64fsl 4 | *.lx32fsl 5 | *.lx64fsl 6 | *.x86f 7 | *~ 8 | .#* -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Cl-Punch - Scala-like anonymous lambda literal 2 | 3 | [![Quicklisp](http://quickdocs.org/badge/cl-punch.svg)](http://quickdocs.org/cl-punch/) 4 | 5 | ## Usage 6 | 7 | ``` 8 | ;; Enable syntax. 9 | (cl-punch:enable-punch-syntax) 10 | 11 | ;; ^() is converted into (lambda ...) . 12 | ;; Each underscore is converted into a lambda argument. 13 | 14 | (mapcar ^(* 2 _) '(1 2 3 4 5)) 15 | ;; => '(2 4 6 8 10) 16 | 17 | ;; One underscore corresponds one argument. 18 | 19 | (^(* _ _) 2 3) 20 | ;; => 6 21 | 22 | ;; <_ reuses last argument. 23 | 24 | (mapcar ^(if (oddp _) (* 2 <_) <_) '(1 2 3 4 5)) 25 | ;; => '(2 2 6 4 10) 26 | 27 | ;; _! corresponds one argument but it is brought to top of the argument list. 28 | ;; It can be useful when you want to change argument order. 29 | 30 | (^(cons _ _!) :a :b) 31 | ;; => (:b . :a) 32 | 33 | (^(list _! _! _!) 1 2 3) 34 | ;; => '(3 2 1) 35 | ``` 36 | 37 | ## Installation 38 | 39 | WIP: Register to quicklisp 40 | 41 | ## Author 42 | 43 | * Windymelt 44 | 45 | ## Copyright 46 | 47 | Copyright (c) 2018 Windymelt 48 | 49 | ## License 50 | 51 | Licensed under the MIT License. 52 | -------------------------------------------------------------------------------- /cl-punch-test.asd: -------------------------------------------------------------------------------- 1 | #| 2 | This file is a part of cl-punch project. 3 | Copyright (c) 2018 Windymelt 4 | |# 5 | 6 | (in-package :cl-user) 7 | (defpackage cl-punch-test-asd 8 | (:use :cl :asdf)) 9 | (in-package :cl-punch-test-asd) 10 | 11 | (defsystem cl-punch-test 12 | :author "Windymelt" 13 | :license "MIT" 14 | :depends-on (:cl-punch 15 | :prove) 16 | :components ((:module "t" 17 | :components 18 | ((:test-file "cl-punch")))) 19 | :description "Test system for cl-punch" 20 | 21 | :defsystem-depends-on (:prove-asdf) 22 | :perform (test-op :after (op c) 23 | (funcall (intern #.(string :run-test-system) :prove-asdf) c) 24 | (asdf:clear-system c))) 25 | -------------------------------------------------------------------------------- /cl-punch.asd: -------------------------------------------------------------------------------- 1 | #| 2 | This file is a part of cl-punch project. 3 | Copyright (c) 2018 Windymelt 4 | |# 5 | 6 | #| 7 | Scala-like anonymous lambda literal 8 | 9 | Author: Windymelt 10 | |# 11 | 12 | (in-package :cl-user) 13 | (defpackage cl-punch-asd 14 | (:use :cl :asdf)) 15 | (in-package :cl-punch-asd) 16 | 17 | (defsystem cl-punch 18 | :version "0.1" 19 | :author "Windymelt" 20 | :license "MIT" 21 | :depends-on (:cl-syntax) 22 | :components ((:module "src" 23 | :components 24 | ((:file "cl-punch")))) 25 | :description "Scala-like anonymous lambda literal" 26 | :long-description 27 | #.(with-open-file (stream (merge-pathnames 28 | #p"README.markdown" 29 | (or *load-pathname* *compile-file-pathname*)) 30 | :if-does-not-exist nil 31 | :direction :input) 32 | (when stream 33 | (let ((seq (make-array (file-length stream) 34 | :element-type 'character 35 | :fill-pointer t))) 36 | (setf (fill-pointer seq) (read-sequence seq stream)) 37 | seq))) 38 | :in-order-to ((test-op (test-op cl-punch-test)))) 39 | -------------------------------------------------------------------------------- /src/cl-punch.lisp: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | (defpackage cl-punch 3 | (:use :cl) 4 | (:export :punch-reader :enable-punch-syntax)) 5 | (in-package :cl-punch) 6 | 7 | (defun punch-reader (s c) 8 | "A reader which converts _ into lambda argument, <_ into last lambda argument." 9 | (declare (ignorable c)) 10 | (let ((form (read s nil nil t)) 11 | (arg-symbol-list nil)) 12 | (labels ((replace-underscore (x) 13 | (cond 14 | ((and (symbolp x) (string= (symbol-name x) "_!")) 15 | (let ((sym (gensym))) 16 | (if (null arg-symbol-list) 17 | (push sym arg-symbol-list) 18 | (push sym (cdr (last arg-symbol-list)))) 19 | sym)) 20 | ((and (symbolp x) (string= (symbol-name x) "_")) 21 | (let ((sym (gensym))) 22 | (push sym arg-symbol-list) 23 | sym)) 24 | ((and (symbolp x) (string= (symbol-name x) "<_")) 25 | (first arg-symbol-list)) 26 | ((listp x) (mapcar #'replace-underscore x)) 27 | ('otherwise x)))) 28 | (let ((result-inner-form (mapcar #'replace-underscore form))) 29 | `(lambda ,(nreverse arg-symbol-list) ,result-inner-form))))) 30 | 31 | (cl-syntax:defsyntax punch-syntax 32 | (:macro-char #\^ #'punch-reader)) 33 | 34 | (defmacro enable-punch-syntax () 35 | '(eval-when (:compile-toplevel :load-toplevel :execute) 36 | (cl-syntax:use-syntax punch-syntax))) 37 | -------------------------------------------------------------------------------- /t/cl-punch.lisp: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | (defpackage cl-punch-test 3 | (:use :cl 4 | :cl-punch 5 | :prove)) 6 | (in-package :cl-punch-test) 7 | 8 | ;; NOTE: To run this test file, execute `(asdf:test-system :cl-punch)' in your Lisp. 9 | 10 | (plan 5) 11 | 12 | (enable-punch-syntax) 13 | 14 | (is (^(* _ 2) 2) 4) 15 | 16 | (is (mapcar ^(* _ 3) '(1 2 3)) 17 | '(3 6 9)) 18 | 19 | (is (^(* _ _) 2 3) 6) 20 | 21 | (is (mapcar ^(if (oddp _) (* 2 <_) <_) 22 | '(1 2 3 4 5)) 23 | '(2 2 6 4 10)) 24 | 25 | (is (^(cons _ _!) :a :b) '(:b . :a)) 26 | 27 | (finalize) 28 | --------------------------------------------------------------------------------