{"id":22318630,"url":"https://github.com/scymtym/trivial-with-current-source-form","last_synced_at":"2026-01-27T21:16:07.732Z","repository":{"id":136535913,"uuid":"240736251","full_name":"scymtym/trivial-with-current-source-form","owner":"scymtym","description":"Helps macro writers produce better errors for macro users","archived":false,"fork":false,"pushed_at":"2023-06-11T19:15:43.000Z","size":58,"stargazers_count":34,"open_issues_count":0,"forks_count":2,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-05-19T05:38:25.831Z","etag":null,"topics":["compatibility","macros"],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scymtym.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-02-15T15:22:33.000Z","updated_at":"2024-05-07T16:59:11.000Z","dependencies_parsed_at":"2024-01-05T22:14:24.399Z","dependency_job_id":null,"html_url":"https://github.com/scymtym/trivial-with-current-source-form","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Ftrivial-with-current-source-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Ftrivial-with-current-source-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Ftrivial-with-current-source-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Ftrivial-with-current-source-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scymtym","download_url":"https://codeload.github.com/scymtym/trivial-with-current-source-form/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245585811,"owners_count":20639671,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compatibility","macros"],"created_at":"2024-12-03T23:42:04.835Z","updated_at":"2026-01-27T21:16:07.722Z","avatar_url":"https://github.com/scymtym.png","language":"Common Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+OPTIONS: toc:nil num:nil\n\n* Introduction\n\n  This library allows macro writers to provide better feedback to\n  macro users when errors are signaled during macroexpansion.\n\n  #+BEGIN_QUOTE\n    Note: While this library can be loaded into and used in any Common\n    Lisp implementation, the improved behavior described below is only\n    available in [[https://github.com/clasp-developers/clasp][CLASP]] and [[http://www.sbcl.org][SBCL]] (only in versions 1.3.13 and newer).\n  #+END_QUOTE\n\n  For example, consider the following macro\n\n  #+BEGIN_SRC lisp\n    (defmacro even-number-case (value \u0026body clauses)\n      \"Like `cl:case' but each key has to be an even number.\"\n      (let ((value-var (gensym \"VALUE\")))\n        `(let ((,value-var ,value))\n           (cond ,@(mapcar (lambda (clause)\n                             (destructuring-bind (number \u0026rest body) clause\n                               (unless (evenp number)\n                                 (error \"Only even numbers are allowed.\"))\n                               `((= ,value-var ,number)\n                                 ,@body)))\n                           clauses)))))\n  #+END_SRC\n\n  This is fine if the expansion does not signal an error. If it does,\n  however, it is not immediately clear which of the clauses (if any)\n  caused the error:\n\n  #+BEGIN_SRC lisp\n    (defun foo (x)\n      (even-number-case x\n        (2 :two)\n        (4 :four)\n        (5 :fix)\n        (8 :eight)\n        (10 :ten)))\n  #+END_SRC\n\n  [[file:pictures/bad-expansion-error.png]]\n\n  The problem is not very hard to spot in the above code, but think of\n  macros for declaring complex things like ~cl:defpackage~ or\n  ~cl:defclass~ or macros for domain specific languages, and the\n  problem becomes more severe.\n\n  The mechanism provided by this library is the\n  ~with-current-source-form~ macro. The macro is intended to surround\n  parts of macro expanders that process certain sub-forms of the form\n  passed to the expander:\n\n  #+BEGIN_SRC lisp\n    (defmacro even-number-case (value \u0026body clauses)\n      \"Like `cl:case' but each key has to be an even number.\"\n      (let ((value-var (gensym \"VALUE\")))\n        `(let ((,value-var ,value))\n           (cond ,@(mapcar (lambda (clause)\n                             (trivial-with-current-source-form:with-current-source-form (clause)\n                               (destructuring-bind (number \u0026rest body) clause\n                                 (unless (evenp number)\n                                   (error \"Only even numbers are allowed.\"))\n                                 `((= ,value-var ,number)\n                                   ,@body))))\n                           clauses)))))\n  #+END_SRC\n\n  The effect of the above change is that the implementation can now\n  report a more useful location when reporting the error during macro\n  expansion. Other tools like SLIME benefit from this functionality as\n  well:\n\n  [[file:pictures/better-expansion-error.png]]\n\n* Tutorial\n\n  Since the example given in the [[*Introduction]] should explain the\n  basic usage of this library, here are just a few additional hints:\n\n  + ~trivial-with-current-source-form:with-current-source-form~\n    optionally accepts additional source forms besides the mandatory\n    one. The reason for this mechanism is that a Common Lisp\n    implementation may be unable to produce a source location for the\n    most specific source form, for example if that form is a symbol or\n    a number. In such cases, the client may be able to help the\n    implementation by providing additional, less specific source forms\n    which contain the first form as sub-forms.\n\n    For example, if a macro expansion function detects a problem with\n    ~foo~ (bound to, say, ~head~ in the expansion function) in ~(foo\n    bar baz)~ (bound to ~call~ in the expansion function), the\n    expansion function could provide the source information as\n\n    #+BEGIN_SRC\n      (trivial-with-current-source-form:with-current-source-form (head call)\n        …)\n    #+END_SRC\n\n    in case the implementation cannot handle just ~head~.\n\n* Reference\n\n  #+BEGIN_SRC lisp :results none :exports none\n    #.(progn\n        #1=(ql:quickload '(\"trivial-with-current-source-form\"))\n        '#1#)\n    (defun doc (symbol kind)\n      (let* ((lambda-list (sb-introspect:function-lambda-list symbol))\n             (string      (documentation symbol kind))\n             (lines       (loop :for start = 0 :then (1+ end)\n                                :for end = (position #\\Newline string :start start)\n                                :while end\n                                :collect (subseq string start end) :into lines\n                                :finally (return (nconc lines (list (subseq string start))))))\n             (trimmed     (mapcar (lambda (line) (string-left-trim '(#\\Space) line)) lines)))\n        (format nil \"~(~A~) ~\u003c~{~A~^ ~}~:@\u003e~2%~{~A~^~%~}\"\n                symbol (list lambda-list) trimmed)))\n  #+END_SRC\n\n  #+BEGIN_SRC lisp :results value :exports results\n    (doc 'trivial-with-current-source-form:with-current-source-form 'function)\n  #+END_SRC\n\n  #+RESULTS:\n  #+BEGIN_EXAMPLE\n  with-current-source-form (FORM \u0026REST FORMS) \u0026BODY BODY\n\n  In a macroexpander, indicate that FORM, FORMS are being processed by BODY.\n\n  FORMS are usually sub-forms of the whole form passed to the expander.\n\n  If more than one form is supplied, FORMS should be ordered by\n  specificity, with the most specific form first. This allows the\n  compiler to try and obtain a source path using subsequent elements of\n  FORMS if it fails for the first one.\n\n  Indicating the processing of sub-forms lets the compiler report\n  precise source locations in case conditions are signaled during the\n  execution of BODY.\n  #+END_EXAMPLE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscymtym%2Ftrivial-with-current-source-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscymtym%2Ftrivial-with-current-source-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscymtym%2Ftrivial-with-current-source-form/lists"}