diff --git a/404.html b/404.html index 3e195fbe..db4da6e3 100644 --- a/404.html +++ b/404.html @@ -4,7 +4,7 @@ Page Not Found | LIPS Scheme - + diff --git a/assets/js/aa178e2b.f31d8ce2.js b/assets/js/aa178e2b.b3cb7e23.js similarity index 69% rename from assets/js/aa178e2b.f31d8ce2.js rename to assets/js/aa178e2b.b3cb7e23.js index 860e2d2e..489d5e55 100644 --- a/assets/js/aa178e2b.f31d8ce2.js +++ b/assets/js/aa178e2b.b3cb7e23.js @@ -1 +1 @@ -"use strict";(self.webpackChunknew_docs=self.webpackChunknew_docs||[]).push([[7868],{3649:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>h,frontMatter:()=>a,metadata:()=>o,toc:()=>l});var i=s(5893),t=s(1151);const a={sidebar_position:6,description:"A way to extends LIPS syntax, not only with macros"},r="Extending LIPS",o={id:"lips/extension",title:"Extending LIPS",description:"A way to extends LIPS syntax, not only with macros",source:"@site/docs/lips/extension.md",sourceDirName:"lips",slug:"/lips/extension",permalink:"/docs/lips/extension",draft:!1,unlisted:!1,editUrl:"https://github.com/jcubic/lips/tree/master/docs/docs/lips/extension.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6,description:"A way to extends LIPS syntax, not only with macros"},sidebar:"tutorialSidebar",previous:{title:"Functional and other utils",permalink:"/docs/lips/functional-helpers"},next:{title:"REPL",permalink:"/docs/lips/REPL"}},c={},l=[{value:"Macros",id:"macros",level:2},{value:"Hygienic macros",id:"hygienic-macros",level:3},{value:"Macroexpand",id:"macroexpand",level:3},{value:"Syntax extensions",id:"syntax-extensions",level:2},{value:"Splice syntax extensions",id:"splice-syntax-extensions",level:3},{value:"Symbol syntax extensions",id:"symbol-syntax-extensions",level:3},{value:"Autogensyms",id:"autogensyms",level:3},{value:"String interpolation",id:"string-interpolation",level:3},{value:"Accessing parser",id:"accessing-parser",level:3},{value:"Standard input",id:"standard-input",level:3},{value:"Limitations",id:"limitations",level:3},{value:"New Homoiconic data types",id:"new-homoiconic-data-types",level:2},{value:"Combining with syntax extensions",id:"combining-with-syntax-extensions",level:3}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.a)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"extending-lips",children:"Extending LIPS"}),"\n",(0,i.jsxs)(n.p,{children:["There are two ways to extend LIPS Scheme, one is through ",(0,i.jsx)(n.a,{href:"#macros",children:"macros"})," and the other ways is with\n",(0,i.jsx)(n.a,{href:"#syntax-extensions",children:"syntax extensions"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"macros",children:"Macros"}),"\n",(0,i.jsxs)(n.p,{children:["LIPS allow creating Lisp macros and Scheme hygienic macros. Right now the limitations of macros is\nthat they are runtime. There are no expansion time. Macros act like function calls, but they\ntransform the code and the interpreter evaluates the code that is returned by the macro. They ware\nimplemented like this, because this is how I understood the macros when they first got\nimplemented. There is a ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/169",children:"plan to create proper macro\nexpansion"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Quasiquote works with object literals, like with vectors:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(let* ((x 10)\n (y 20)\n (obj `&(:x ,x :y ,y)))\n (print obj))\n"})}),"\n",(0,i.jsxs)(n.p,{children:["to define a lisp macro, you use syntax defined in ",(0,i.jsx)(n.a,{href:"/docs/scheme-intro/macros",children:"Scheme Tutorial about Macros"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(define-macro (for var start end . body)\n `(for-each (lambda (,var)\n ,@body)\n (range ,start ,(+ end 1))))\n\n(let ((result (vector)))\n (for i 10 20\n (result.push i))\n (print result))\n;; ==> #(10 11 12 13 14 15 16 17 18 19 20)\n"})}),"\n",(0,i.jsx)(n.p,{children:"You can define macro that create shorthand syntax like in JavaScript:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-javascript",children:"const x = 10;\nconst y = 20;\nconst obj = { x, y };\nconsole.log(obj);\n// { x: 10, y: 20 }\n"})}),"\n",(0,i.jsx)(n.p,{children:"You can create macro that will work the same in LIPS Scheme:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(define (symbol->key symbol)\n (string->symbol (string-append ":" (symbol->string symbol))))\n\n(define-macro (expand . args)\n `(object ,@(reduce (lambda (symbol acc)\n (let ((key (symbol->key symbol)))\n (append acc (list key symbol))))\n \'()\n args)))\n(let* ((x 10)\n (y 20)\n (obj (expand x y)))\n (print obj))\n;; ==> &(:x 10 :y 20)\n'})}),"\n",(0,i.jsx)(n.h3,{id:"hygienic-macros",children:"Hygienic macros"}),"\n",(0,i.jsxs)(n.p,{children:["LIPS define hygienic macros in form of standard ",(0,i.jsx)(n.code,{children:"syntax-rules"})," expression. Note that there are know\nbugs in ",(0,i.jsx)(n.code,{children:"syntax-rules"})," see ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/43",children:"issue #43 on GitHub"})," and ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/blob/devel/tests/syntax.scm",children:"unit\ntests"})," that have tests marked as\nfailing."]}),"\n",(0,i.jsx)(n.p,{children:"If you find a case of failing macro, don't hessitate to create an issue. You can also check if your\ncase is not already listed on above links. You can also just create a comment on issue #43 with your\nbroken test case."}),"\n",(0,i.jsx)(n.p,{children:"LIPS Scheme define those extensions to syntax-rules macros:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-46/srfi-46.html",children:"SRFI-46"})," (changing ellipsis symbol: see\n",(0,i.jsx)(n.a,{href:"/docs/scheme-intro/macros#nested-hygienic-macros",children:"Nested Hygienic Macros"})]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-139/srfi-139.html",children:"SRFI-139"})," see\n",(0,i.jsx)(n.a,{href:"/docs/scheme-intro/macros#anaphoric-hygienic-macros",children:"Syntax Parameters"})]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-147/srfi-147.html",children:"SRFI 147"})," allow defining new syntax-rules transformers"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"macroexpand",children:"Macroexpand"}),"\n",(0,i.jsxs)(n.p,{children:["LIPS define ",(0,i.jsx)(n.code,{children:"macroexpand"})," and ",(0,i.jsx)(n.code,{children:"macroexpand-1"})," but they are macros and the expression don't need to be quoted.\nThere is an ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/323",children:"issue to change those expressions into functions"})," like\nin ",(0,i.jsx)(n.a,{href:"http://clhs.lisp.se/Body/f_mexp_.htm",children:"Common Lisp"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"syntax-extensions",children:"Syntax extensions"}),"\n",(0,i.jsx)(n.p,{children:"Syntax extensions are a way to add new syntax to LIPS Scheme. They are executed at parse time. Object literals and\nvector literals are added using syntax extensions. Syntax extension modify the Parser and allow to add new behavior at\nparse time."}),"\n",(0,i.jsx)(n.p,{children:"To add syntax extension you use:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "##" \'my-function lips.specials.LITERAL)\n'})}),"\n",(0,i.jsx)(n.p,{children:"The syntax extension can point to a macro or a function. When extension is a function it's invoked and the result data\nis returned from the parser:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(define (my-function number)\n `(list ,number ,number))\n"})}),"\n",(0,i.jsx)(n.p,{children:"if you define the function like this and execute:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"##10\n;; ==> (10 10)\n"})}),"\n",(0,i.jsxs)(n.p,{children:["To see the expansion of syntax extension you can use ",(0,i.jsx)(n.code,{children:"lips.parse"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(lips.parse "##10")\n;; ==> #((list 10 10))\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"NOTE"}),": The ",(0,i.jsx)(n.code,{children:"lips.parse"})," function return array/vector of parsed expressions."]}),"\n",(0,i.jsxs)(n.p,{children:["There are 3 types of syntax extensions ",(0,i.jsx)(n.code,{children:"SPLICE"}),", ",(0,i.jsx)(n.code,{children:"LITERAL"}),", and ",(0,i.jsx)(n.code,{children:"SYMBOL"}),". You define them using\nconstants defined in ",(0,i.jsx)(n.code,{children:"lips.specials"})," object."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"LITERAL"})," - used above pass it's argument as is, with literal syntax extension you can execute it\non any argument. This is default when no constant in ",(0,i.jsx)(n.code,{children:"set-special!"})," is used."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"SPLICE"})," - if you execute syntax ",(0,i.jsx)(n.code,{children:"##(1 2 3)"})," the arguments will be spliced, so the function or a\nmacro needs to use improper list. Or use named arguments if syntax accept fixed amount of arguments."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"SYMBOL"})," - this type of extensions don't accept any arguments and can be used to define parser constants."]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"splice-syntax-extensions",children:"Splice syntax extensions"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "##" \'complex lips.specials.SPLICE)\n\n(define (complex real imag)\n (make-rectangular real imag))\n'})}),"\n",(0,i.jsx)(n.p,{children:"This syntax extension will define complex numbers and will work only on lists:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"##(10 20)\n;; ==> 10+20i\n"})}),"\n",(0,i.jsx)(n.p,{children:"Since it's a macro it evaluate at parse time:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(lips.parse "##(10 20)")\n;; ==> #(10+20i)\n'})}),"\n",(0,i.jsxs)(n.p,{children:["With splice syntax extension you can limit the number of arguments (remember that LIPS don't check\n",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Arity",children:"arity"}),")."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(define (complex . args)\n (if (not (= (length args) 2))\n (throw "Invalid invocation of ## syntax extension")\n (apply make-rectangular args)))\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(lips.parse "##(10 20)")\n;; ==> #(10+20i)\n(lips.parse "##(1 2 3)")\n;; ==> Invalid invocation of ## syntax extension\n'})}),"\n",(0,i.jsx)(n.h3,{id:"symbol-syntax-extensions",children:"Symbol syntax extensions"}),"\n",(0,i.jsx)(n.p,{children:"The last type of syntax extensions are symbols they don't accept any arguments and can be used to\ndefine parser constants."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(set-special! \"nil\" 'nil-fn lips.specials.SYMBOL)\n(define (nil-fn) '())\n"})}),"\n",(0,i.jsxs)(n.p,{children:["This will define constant ",(0,i.jsx)(n.code,{children:"#nil"}),". It's different from ",(0,i.jsx)(n.code,{children:"nil"})," variable:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(define nil '())\n\n(eq? nil #nil)\n;; ==> #t\n(eq? (car '(nil)) (car '(#nil)))\n;; ==> #f\n(symbol? (car '(nil)))\n;; ==> #f\n(symbol? (car '(#nil)))\n;; ==> #f\n(eq? (car '(#nil)) '())\n;; ==> #t\n"})}),"\n",(0,i.jsx)(n.h3,{id:"autogensyms",children:"Autogensyms"}),"\n",(0,i.jsx)(n.p,{children:"With syntax extensions you can define autogensyms expressions:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(set-special! \"#:\" 'keyword lips.specials.LITERAL)\n\n(define (keyword symbol)\n `(gensym ',symbol))\n\n(let ((x #:foo))\n (write x))\n;; ==> #:foo\n"})}),"\n",(0,i.jsxs)(n.p,{children:["This allow to create named ",(0,i.jsx)(n.a,{href:"/docs/lips/intro#gensyms",children:"gensyms"})," that are unique:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(eq? #:foo #:foo)\n;; ==> #f\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can use them with lisp macros instead of ",(0,i.jsx)(n.code,{children:"gensym"})," expressions. The autogensyms are actually part\nof the standard library."]}),"\n",(0,i.jsx)(n.h3,{id:"string-interpolation",children:"String interpolation"}),"\n",(0,i.jsx)(n.p,{children:"With syntax extensions you can create string interpolation that expand into a Scheme code:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "$" \'interpolate)\n\n(define (interpolate str)\n (typecheck "interpolate" str "string")\n (let* ((re #/(\\$\\{[^\\}]+\\})/)\n (parts (--\x3e str (split re) (filter Boolean))))\n `(string-append ,@(map (lambda (part)\n (if (not (null? (part.match re)))\n (let* ((expr (part.replace #/(^\\$\\{)|(\\}$)/g ""))\n (port (open-input-string expr))\n (value (with-input-from-port port read)))\n `(repr ,value))\n part))\n (vector->list parts)))))\n\n(pprint (macroexpand-1 (let ((x 10)) $"x = ${(+ x 2)}")))\n;; ==> (let ((x 10))\n;; ==> (string-append "x = " (repr (+ x 2))))\n\n(let ((x 10))\n $"x = ${(+ x 2)}")\n;; ==> "x = 12"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The limitation of this solution is that you can't use strings inside ",(0,i.jsx)(n.code,{children:"${ ... }"}),". It will break the\nLexer. In the future there may be a way to define such syntax extensions (See ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/321",children:"Add full string\ninterpolation as syntax extension"}),")."]}),"\n",(0,i.jsx)(n.h3,{id:"accessing-parser",children:"Accessing parser"}),"\n",(0,i.jsx)(n.p,{children:"In LIPS syntax extensions you can access the parser instance, so you can implement syntax\nextension that return line number:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "#:num" \'line-num lips.specials.SYMBOL)\n\n(define (line-num)\n (let* ((lexer lips.__parser__.__lexer__)\n (token lexer.__token__))\n (write token)\n (newline)\n ;; line number start from 0\n (+ token.line 1)))\n\n(print (list\n #:num\n #:num))\n;; ==> &(:token "#:num" :col 8 :offset 260 :line 11)\n;; ==> &(:token "#:num" :col 10 :offset 274 :line 12)\n;; ==> (12 13)\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"NOTE"}),": The provided output will be exactly the same, when the code will be put into a single file\nand executed."]}),"\n",(0,i.jsx)(n.h3,{id:"standard-input",children:"Standard input"}),"\n",(0,i.jsxs)(n.p,{children:["In syntax extensions ",(0,i.jsx)(n.code,{children:"current-input-port"})," points into the parser stream. So you can implement\nyour own parser logic. The best way to implement custom syntax extension (that works similar to\ncommon lips reader macros)."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "$" \'raw-string lips.specials.SYMBOL)\n\n(define (raw-string)\n (if (char=? (peek-char) #\\")\n (begin\n (read-char)\n (let loop ((result (vector)) (char (peek-char)))\n (read-char)\n (if (char=? char #\\")\n (apply string (vector->list result))\n (loop (vector-append result (vector char)) (peek-char)))))))\n\n(print $"foo \\ bar")\n;; ==> "foo \\\\ bar"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["This extension implements raw string, like in Python, where you don't need to escape the characters that are thread literally.\nSimilarly, you can implement strings that use backticks, you only need to replace ",(0,i.jsx)(n.code,{children:'#\\"'})," with ",(0,i.jsx)(n.code,{children:"#\\`"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "$" \'raw-string lips.specials.SYMBOL)\n\n(define (raw-string)\n (if (char=? (peek-char) #\\`)\n (begin\n (read-char)\n (let loop ((result (vector)) (char (peek-char)))\n (read-char)\n (if (char=? char #\\`)\n (apply string (vector->list result))\n (loop (vector-append result (vector char)) (peek-char)))))))\n\n(print $`foo \\ bar`)\n;; ==> "foo \\\\ bar"\n'})}),"\n",(0,i.jsx)(n.p,{children:"With this feature in hand you can implement full string interpolation (that will probably be part of\nLIPS Scheme in the future)."}),"\n",(0,i.jsx)(n.h3,{id:"limitations",children:"Limitations"}),"\n",(0,i.jsx)(n.p,{children:"The limitation of syntax extensions is that you can't define a variable that starts with the\nsame characters as syntax extension. This may be a benefit and not a limitation:"}),"\n",(0,i.jsx)(n.h2,{id:"new-homoiconic-data-types",children:"New Homoiconic data types"}),"\n",(0,i.jsx)(n.p,{children:"With LIPS, you can define representation of custom data types that are the same when printed and read."}),"\n",(0,i.jsxs)(n.p,{children:["To create custom representation of new data type you can use ",(0,i.jsx)(n.code,{children:"set-repr!"})," expression. It only works\nwith JavaScript classes. But Scheme records in LIPS define new JavaScript class. So you can create\nnew records and create different representation for them."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(define-record-type :Person\n (make-person name age)\n person?\n (name person-name set-name!)\n (age person-age set-age!))\n\n(set-repr! :Person (lambda (obj quot)\n (string-append "(make-person "\n (repr (person-name obj) quot)\n " "\n (repr (person-age obj) quot)\n ")")))\n\n(write (make-person "Mick Jagger" 80))\n;; ==> (make-person "Mick Jagger" 80)\n(display (make-person "Mick Jagger" 80))\n;; ==> (make-person Mick Jagger 80)\n'})}),"\n",(0,i.jsxs)(n.p,{children:["As you can see the ",(0,i.jsx)(n.code,{children:"display"})," don't quote the strings because of ",(0,i.jsx)(n.code,{children:"repr"})," expression that use ",(0,i.jsx)(n.code,{children:"quot"}),"\nargument to the ",(0,i.jsx)(n.code,{children:"set-repr!"})," handler."]}),"\n",(0,i.jsx)(n.h3,{id:"combining-with-syntax-extensions",children:"Combining with syntax extensions"}),"\n",(0,i.jsx)(n.p,{children:"You can combine syntax extensions with custom representation:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! ":P" \'make-person lips.specials.SPLICE)\n\n(set-repr! :Person (lambda (obj quot)\n (string-append ":P("\n (repr (person-name obj) quot)\n " "\n (repr (person-age obj) quot)\n ")")))\n\n(write :P("Mick Jagger" 80))\n;; ==> :P("Mick Jagger" 80)\n'})})]})}function h(e={}){const{wrapper:n}={...(0,t.a)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},1151:(e,n,s)=>{s.d(n,{Z:()=>o,a:()=>r});var i=s(7294);const t={},a=i.createContext(t);function r(e){const n=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),i.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunknew_docs=self.webpackChunknew_docs||[]).push([[7868],{3649:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>h,frontMatter:()=>a,metadata:()=>o,toc:()=>l});var i=s(5893),t=s(1151);const a={sidebar_position:6,description:"A way to extends LIPS syntax, not only with macros"},r="Extending LIPS",o={id:"lips/extension",title:"Extending LIPS",description:"A way to extends LIPS syntax, not only with macros",source:"@site/docs/lips/extension.md",sourceDirName:"lips",slug:"/lips/extension",permalink:"/docs/lips/extension",draft:!1,unlisted:!1,editUrl:"https://github.com/jcubic/lips/tree/master/docs/docs/lips/extension.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6,description:"A way to extends LIPS syntax, not only with macros"},sidebar:"tutorialSidebar",previous:{title:"Functional and other utils",permalink:"/docs/lips/functional-helpers"},next:{title:"REPL",permalink:"/docs/lips/REPL"}},c={},l=[{value:"Macros",id:"macros",level:2},{value:"Hygienic macros",id:"hygienic-macros",level:3},{value:"Macroexpand",id:"macroexpand",level:3},{value:"Syntax extensions",id:"syntax-extensions",level:2},{value:"Splice syntax extensions",id:"splice-syntax-extensions",level:3},{value:"Symbol syntax extensions",id:"symbol-syntax-extensions",level:3},{value:"Autogensyms",id:"autogensyms",level:3},{value:"String interpolation",id:"string-interpolation",level:3},{value:"Accessing parser",id:"accessing-parser",level:3},{value:"Standard input",id:"standard-input",level:3},{value:"Limitations",id:"limitations",level:3},{value:"New Homoiconic data types",id:"new-homoiconic-data-types",level:2},{value:"Combining with syntax extensions",id:"combining-with-syntax-extensions",level:3}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.a)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"extending-lips",children:"Extending LIPS"}),"\n",(0,i.jsxs)(n.p,{children:["There are two ways to extend LIPS Scheme, one is through ",(0,i.jsx)(n.a,{href:"#macros",children:"macros"})," and the other ways is with\n",(0,i.jsx)(n.a,{href:"#syntax-extensions",children:"syntax extensions"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"macros",children:"Macros"}),"\n",(0,i.jsxs)(n.p,{children:["LIPS allow creating Lisp macros and Scheme hygienic macros. Right now the limitations of macros is\nthat they are runtime. There are no expansion time. Macros act like function calls, but they\ntransform the code and the interpreter evaluates the code that is returned by the macro. They ware\nimplemented like this, because this is how I understood the macros when they first got\nimplemented. There is a ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/169",children:"plan to create proper macro\nexpansion"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Quasiquote works with object literals, like with vectors:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(let* ((x 10)\n (y 20)\n (obj `&(:x ,x :y ,y)))\n (print obj))\n"})}),"\n",(0,i.jsxs)(n.p,{children:["to define a lisp macro, you use syntax defined in ",(0,i.jsx)(n.a,{href:"/docs/scheme-intro/macros",children:"Scheme Tutorial about Macros"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(define-macro (for var start end . body)\n `(for-each (lambda (,var)\n ,@body)\n (range ,start ,(+ end 1))))\n\n(let ((result (vector)))\n (for i 10 20\n (result.push i))\n (print result))\n;; ==> #(10 11 12 13 14 15 16 17 18 19 20)\n"})}),"\n",(0,i.jsx)(n.p,{children:"You can define macro that create shorthand syntax like in JavaScript:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-javascript",children:"const x = 10;\nconst y = 20;\nconst obj = { x, y };\nconsole.log(obj);\n// { x: 10, y: 20 }\n"})}),"\n",(0,i.jsx)(n.p,{children:"You can create macro that will work the same in LIPS Scheme:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(define (symbol->key symbol)\n (string->symbol (string-append ":" (symbol->string symbol))))\n\n(define-macro (expand . args)\n `(object ,@(reduce (lambda (symbol acc)\n (let ((key (symbol->key symbol)))\n (append acc (list key symbol))))\n \'()\n args)))\n(let* ((x 10)\n (y 20)\n (obj (expand x y)))\n (print obj))\n;; ==> &(:x 10 :y 20)\n'})}),"\n",(0,i.jsx)(n.h3,{id:"hygienic-macros",children:"Hygienic macros"}),"\n",(0,i.jsxs)(n.p,{children:["LIPS define hygienic macros in form of standard ",(0,i.jsx)(n.code,{children:"syntax-rules"})," expression. Note that there are know\nbugs in ",(0,i.jsx)(n.code,{children:"syntax-rules"})," see ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/43",children:"issue #43 on GitHub"})," and ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/blob/devel/tests/syntax.scm",children:"unit\ntests"})," that have tests marked as\nfailing."]}),"\n",(0,i.jsx)(n.p,{children:"If you find a case of failing macro, don't hessitate to create an issue. You can also check if your\ncase is not already listed on above links. You can also just create a comment on issue #43 with your\nbroken test case."}),"\n",(0,i.jsx)(n.p,{children:"LIPS Scheme define those extensions to syntax-rules macros:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-46/srfi-46.html",children:"SRFI-46"})," (changing ellipsis symbol: see\n",(0,i.jsx)(n.a,{href:"/docs/scheme-intro/macros#nested-hygienic-macros",children:"Nested Hygienic Macros"})]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-139/srfi-139.html",children:"SRFI-139"})," see\n",(0,i.jsx)(n.a,{href:"/docs/scheme-intro/macros#anaphoric-hygienic-macros",children:"Syntax Parameters"})]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-147/srfi-147.html",children:"SRFI 147"})," allow defining new syntax-rules transformers"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"macroexpand",children:"Macroexpand"}),"\n",(0,i.jsxs)(n.p,{children:["LIPS define ",(0,i.jsx)(n.code,{children:"macroexpand"})," and ",(0,i.jsx)(n.code,{children:"macroexpand-1"})," but they are macros and the expression don't need to be quoted.\nThere is an ",(0,i.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/323",children:"issue to change those expressions into functions"})," like\nin ",(0,i.jsx)(n.a,{href:"http://clhs.lisp.se/Body/f_mexp_.htm",children:"Common Lisp"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"syntax-extensions",children:"Syntax extensions"}),"\n",(0,i.jsx)(n.p,{children:"Syntax extensions are a way to add new syntax to LIPS Scheme. They are executed at parse time. Object literals and\nvector literals are added using syntax extensions. Syntax extension modify the Parser and allow to add new behavior at\nparse time."}),"\n",(0,i.jsx)(n.p,{children:"To add syntax extension you use:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "##" \'my-function lips.specials.LITERAL)\n'})}),"\n",(0,i.jsx)(n.p,{children:"The syntax extension can point to a macro or a function. When extension is a function it's invoked and the result data\nis returned from the parser:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(define (my-function number)\n `(list ,number ,number))\n"})}),"\n",(0,i.jsx)(n.p,{children:"if you define the function like this and execute:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"##10\n;; ==> (10 10)\n"})}),"\n",(0,i.jsxs)(n.p,{children:["To see the expansion of syntax extension you can use ",(0,i.jsx)(n.code,{children:"lips.parse"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(lips.parse "##10")\n;; ==> #((list 10 10))\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"NOTE"}),": The ",(0,i.jsx)(n.code,{children:"lips.parse"})," function return array/vector of parsed expressions."]}),"\n",(0,i.jsxs)(n.p,{children:["There are 3 types of syntax extensions ",(0,i.jsx)(n.code,{children:"SPLICE"}),", ",(0,i.jsx)(n.code,{children:"LITERAL"}),", and ",(0,i.jsx)(n.code,{children:"SYMBOL"}),". You define them using\nconstants defined in ",(0,i.jsx)(n.code,{children:"lips.specials"})," object."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"LITERAL"})," - used above pass it's argument as is, with literal syntax extension you can execute it\non any argument. This is default when no constant in ",(0,i.jsx)(n.code,{children:"set-special!"})," is used."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"SPLICE"})," - if you execute syntax ",(0,i.jsx)(n.code,{children:"##(1 2 3)"})," the arguments will be spliced, so the function or a\nmacro needs to use improper list. Or use named arguments if syntax accept fixed amount of arguments."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"SYMBOL"})," - this type of extensions don't accept any arguments and can be used to define parser constants."]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"splice-syntax-extensions",children:"Splice syntax extensions"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "##" \'complex lips.specials.SPLICE)\n\n(define (complex real imag)\n (make-rectangular real imag))\n'})}),"\n",(0,i.jsx)(n.p,{children:"This syntax extension will define complex numbers and will work only on lists:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"##(10 20)\n;; ==> 10+20i\n"})}),"\n",(0,i.jsx)(n.p,{children:"Since it's a macro it evaluate at parse time:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(lips.parse "##(10 20)")\n;; ==> #(10+20i)\n'})}),"\n",(0,i.jsxs)(n.p,{children:["With splice syntax extension you can limit the number of arguments (remember that LIPS don't check\n",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Arity",children:"arity"}),")."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(define (complex . args)\n (if (not (= (length args) 2))\n (throw "Invalid invocation of ## syntax extension")\n (apply make-rectangular args)))\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(lips.parse "##(10 20)")\n;; ==> #(10+20i)\n(lips.parse "##(1 2 3)")\n;; ==> Invalid invocation of ## syntax extension\n'})}),"\n",(0,i.jsx)(n.h3,{id:"symbol-syntax-extensions",children:"Symbol syntax extensions"}),"\n",(0,i.jsx)(n.p,{children:"The last type of syntax extensions are symbols they don't accept any arguments and can be used to\ndefine parser constants."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(set-special! \"nil\" 'nil-fn lips.specials.SYMBOL)\n(define (nil-fn) '())\n"})}),"\n",(0,i.jsxs)(n.p,{children:["This will define constant ",(0,i.jsx)(n.code,{children:"#nil"}),". It's different from ",(0,i.jsx)(n.code,{children:"nil"})," variable:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(define nil '())\n\n(eq? nil #nil)\n;; ==> #t\n(eq? (car '(nil)) (car '(#nil)))\n;; ==> #f\n(symbol? (car '(nil)))\n;; ==> #f\n(symbol? (car '(#nil)))\n;; ==> #f\n(eq? (car '(#nil)) '())\n;; ==> #t\n"})}),"\n",(0,i.jsx)(n.h3,{id:"autogensyms",children:"Autogensyms"}),"\n",(0,i.jsx)(n.p,{children:"With syntax extensions you can define autogensyms expressions:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(set-special! \"#:\" 'keyword lips.specials.LITERAL)\n\n(define (keyword symbol)\n `(gensym ',symbol))\n\n(let ((x #:foo))\n (write x))\n;; ==> #:foo\n"})}),"\n",(0,i.jsxs)(n.p,{children:["This allow to create named ",(0,i.jsx)(n.a,{href:"/docs/lips/intro#gensyms",children:"gensyms"})," that are unique:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:"(eq? #:foo #:foo)\n;; ==> #f\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can use them with lisp macros instead of ",(0,i.jsx)(n.code,{children:"gensym"})," expressions. The autogensyms are actually part\nof the standard library."]}),"\n",(0,i.jsx)(n.h3,{id:"string-interpolation",children:"String interpolation"}),"\n",(0,i.jsx)(n.p,{children:"With syntax extensions you can create string interpolation that expand into a Scheme code:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "$" \'interpolate)\n\n(define (interpolate str)\n (typecheck "interpolate" str "string")\n (let* ((re #/(\\$\\{[^\\}]+\\})/)\n (parts (--\x3e str (split re) (filter Boolean))))\n `(string-append ,@(map (lambda (part)\n (if (not (null? (part.match re)))\n (let* ((expr (part.replace #/(^\\$\\{)|(\\}$)/g ""))\n (port (open-input-string expr))\n (value (with-input-from-port port read)))\n `(repr ,value))\n part))\n (vector->list parts)))))\n\n(pprint (macroexpand-1 (let ((x 10)) $"x = ${(+ x 2)}")))\n;; ==> (let ((x 10))\n;; ==> (string-append "x = " (repr (+ x 2))))\n\n(let ((x 10))\n $"x = ${(+ x 2)}")\n;; ==> "x = 12"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The limitation of this solution is that you can't use strings inside ",(0,i.jsx)(n.code,{children:"${ ... }"}),". It will break the\nLexer. In order to have full string interpolation you need to read the parser stream (See ",(0,i.jsx)(n.a,{href:"#standard-input",children:"Standard\ninput"})," inside syntax extensions)."]}),"\n",(0,i.jsx)(n.h3,{id:"accessing-parser",children:"Accessing parser"}),"\n",(0,i.jsx)(n.p,{children:"In LIPS syntax extensions you can access the parser instance, so you can implement syntax\nextension that return line number:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "#:num" \'line-num lips.specials.SYMBOL)\n\n(define (line-num)\n (let* ((lexer lips.__parser__.__lexer__)\n (token lexer.__token__))\n (write token)\n (newline)\n ;; line number start from 0\n (+ token.line 1)))\n\n(print (list\n #:num\n #:num))\n;; ==> &(:token "#:num" :col 8 :offset 260 :line 11)\n;; ==> &(:token "#:num" :col 10 :offset 274 :line 12)\n;; ==> (12 13)\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"NOTE"}),": The provided output will be exactly the same, when the code will be put into a single file\nand executed."]}),"\n",(0,i.jsx)(n.h3,{id:"standard-input",children:"Standard input"}),"\n",(0,i.jsxs)(n.p,{children:["In syntax extensions ",(0,i.jsx)(n.code,{children:"current-input-port"})," points into the parser stream. So you can implement\nyour own parser logic. The best way to implement custom syntax extension (that works similar to\ncommon lips reader macros)."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "$" \'raw-string lips.specials.SYMBOL)\n\n(define (raw-string)\n (if (char=? (peek-char) #\\")\n (begin\n (read-char)\n (let loop ((result (vector)) (char (peek-char)))\n (read-char)\n (if (char=? char #\\")\n (apply string (vector->list result))\n (loop (vector-append result (vector char)) (peek-char)))))))\n\n(print $"foo \\ bar")\n;; ==> "foo \\\\ bar"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["This extension implements raw string, like in Python, where you don't need to escape the characters that are thread literally.\nSimilarly, you can implement strings that use backticks, you only need to replace ",(0,i.jsx)(n.code,{children:'#\\"'})," with ",(0,i.jsx)(n.code,{children:"#\\`"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! "$" \'raw-string lips.specials.SYMBOL)\n\n(define (raw-string)\n (if (char=? (peek-char) #\\`)\n (begin\n (read-char)\n (let loop ((result (vector)) (char (peek-char)))\n (read-char)\n (if (char=? char #\\`)\n (apply string (vector->list result))\n (loop (vector-append result (vector char)) (peek-char)))))))\n\n(print $`foo \\ bar`)\n;; ==> "foo \\\\ bar"\n'})}),"\n",(0,i.jsx)(n.p,{children:"With this feature in hand you can implement full string interpolation (that will probably be part of\nLIPS Scheme in the future)."}),"\n",(0,i.jsx)(n.h3,{id:"limitations",children:"Limitations"}),"\n",(0,i.jsx)(n.p,{children:"The limitation of syntax extensions is that you can't define a variable that starts with the\nsame characters as syntax extension. This may be a benefit and not a limitation:"}),"\n",(0,i.jsx)(n.h2,{id:"new-homoiconic-data-types",children:"New Homoiconic data types"}),"\n",(0,i.jsx)(n.p,{children:"With LIPS, you can define representation of custom data types that are the same when printed and read."}),"\n",(0,i.jsxs)(n.p,{children:["To create custom representation of new data type you can use ",(0,i.jsx)(n.code,{children:"set-repr!"})," expression. It only works\nwith JavaScript classes. But Scheme records in LIPS define new JavaScript class. So you can create\nnew records and create different representation for them."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(define-record-type :Person\n (make-person name age)\n person?\n (name person-name set-name!)\n (age person-age set-age!))\n\n(set-repr! :Person (lambda (obj quot)\n (string-append "(make-person "\n (repr (person-name obj) quot)\n " "\n (repr (person-age obj) quot)\n ")")))\n\n(write (make-person "Mick Jagger" 80))\n;; ==> (make-person "Mick Jagger" 80)\n(display (make-person "Mick Jagger" 80))\n;; ==> (make-person Mick Jagger 80)\n'})}),"\n",(0,i.jsxs)(n.p,{children:["As you can see the ",(0,i.jsx)(n.code,{children:"display"})," don't quote the strings because of ",(0,i.jsx)(n.code,{children:"repr"})," expression that use ",(0,i.jsx)(n.code,{children:"quot"}),"\nargument to the ",(0,i.jsx)(n.code,{children:"set-repr!"})," handler."]}),"\n",(0,i.jsx)(n.h3,{id:"combining-with-syntax-extensions",children:"Combining with syntax extensions"}),"\n",(0,i.jsx)(n.p,{children:"You can combine syntax extensions with custom representation:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-scheme",children:'(set-special! ":P" \'make-person lips.specials.SPLICE)\n\n(set-repr! :Person (lambda (obj quot)\n (string-append ":P("\n (repr (person-name obj) quot)\n " "\n (repr (person-age obj) quot)\n ")")))\n\n(write :P("Mick Jagger" 80))\n;; ==> :P("Mick Jagger" 80)\n'})})]})}function h(e={}){const{wrapper:n}={...(0,t.a)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},1151:(e,n,s)=>{s.d(n,{Z:()=>o,a:()=>r});var i=s(7294);const t={},a=i.createContext(t);function r(e){const n=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),i.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.59481896.js b/assets/js/runtime~main.f121cbbb.js similarity index 98% rename from assets/js/runtime~main.59481896.js rename to assets/js/runtime~main.f121cbbb.js index caa69acb..ecc08cae 100644 --- a/assets/js/runtime~main.59481896.js +++ b/assets/js/runtime~main.f121cbbb.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,f,c,t,r={},d={};function b(e){var a=d[e];if(void 0!==a)return a.exports;var f=d[e]={exports:{}};return r[e].call(f.exports,f,f.exports,b),f.exports}b.m=r,e=[],b.O=(a,f,c,t)=>{if(!f){var r=1/0;for(i=0;i=t)&&Object.keys(b.O).every((e=>b.O[e](f[o])))?f.splice(o--,1):(d=!1,t0&&e[i-1][2]>t;i--)e[i]=e[i-1];e[i]=[f,c,t]},b.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return b.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,b.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var t=Object.create(null);b.r(t);var r={};a=a||[null,f({}),f([]),f(f)];for(var d=2&c&&e;"object"==typeof d&&!~a.indexOf(d);d=f(d))Object.getOwnPropertyNames(d).forEach((a=>r[a]=()=>e[a]));return r.default=()=>e,b.d(t,r),t},b.d=(e,a)=>{for(var f in a)b.o(a,f)&&!b.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce(((a,f)=>(b.f[f](e,a),a)),[])),b.u=e=>"assets/js/"+({53:"935f2afb",81:"2abfd7a8",533:"b2b675dd",743:"7e736cac",1070:"f196f4a4",1113:"e511e452",1208:"3828c8cd",1317:"75505f57",1477:"b2f554cd",1652:"ea13dd3a",1677:"0a637f08",1713:"a7023ddc",2535:"814f3328",2553:"6878938b",2813:"4b1723f1",2822:"35ee46b0",3085:"1f391b9e",3089:"a6aa9e1f",3237:"1df93b7f",3604:"9ede617c",3608:"9e4087bc",4013:"01a85c17",4015:"69aa9e93",4368:"a94703ab",4699:"ab461798",4985:"14a48451",5460:"9abc7bc7",5564:"468f2e1c",5609:"e1902777",5747:"726afaaf",6103:"ccc49370",6143:"1f27c5d1",6296:"c74dcec5",7018:"413466e5",7184:"68243087",7393:"acecf23e",7414:"393be207",7836:"840797c7",7868:"aa178e2b",7903:"db96436e",7918:"17896441",7953:"f724a01c",8071:"f2e31a35",8097:"4f7497d7",8424:"5ad8d976",8518:"a7bd4aaa",8610:"6875c492",9195:"d969d8ea",9242:"c71491fd",9301:"430f917c",9342:"df5a86e1",9661:"5e95c892",9671:"0e384e19",9817:"14eb3368"}[e]||e)+"."+{53:"cc6240be",81:"eaf1209f",533:"7f33185e",743:"41e22d1d",1070:"4d17d4a0",1113:"6dce99c6",1208:"aa6ba2aa",1317:"519f4209",1477:"92ae2c22",1652:"e9dd3f0f",1677:"61ff06c6",1713:"f57aae3f",1772:"205fa78b",2535:"33be7cdd",2553:"62200649",2813:"7994837a",2822:"5499b20c",3085:"6e4b1596",3089:"ca60c0b4",3237:"7bff3f57",3604:"9d811c06",3608:"eb64b0cc",4013:"0d6af7b9",4015:"00e22eaa",4368:"fac9076a",4699:"e445eee8",4985:"b2bb271d",5005:"facea533",5460:"58965eb1",5564:"1eb5cbc8",5609:"c9bdda11",5747:"5e9ff151",6103:"75bf7d8f",6143:"4d1e30de",6296:"2757b8e1",6705:"8d1cee0d",7018:"5b84004c",7184:"0f23da7a",7393:"3caaec2f",7414:"6bb4bbad",7534:"5a903753",7836:"60904e28",7868:"f31d8ce2",7903:"8da5bafb",7918:"af71c726",7953:"087dbc0b",8071:"eff52960",8097:"07ed6383",8424:"f15ddef4",8518:"b2f1f87c",8610:"54a03b73",9195:"6ce6b63f",9242:"11363cc6",9301:"756863bd",9342:"5a8b1f83",9386:"eb7dae53",9661:"daee7819",9671:"4a2ae178",9817:"131961d5"}[e]+".js",b.miniCssF=e=>{},b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},t="new-docs:",b.l=(e,a,f,r)=>{if(c[e])c[e].push(a);else{var d,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var t=c[e];if(delete c[e],d.parentNode&&d.parentNode.removeChild(d),t&&t.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),o&&document.head.appendChild(d)}},b.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},b.p="/",b.gca=function(e){return e={17896441:"7918",68243087:"7184","935f2afb":"53","2abfd7a8":"81",b2b675dd:"533","7e736cac":"743",f196f4a4:"1070",e511e452:"1113","3828c8cd":"1208","75505f57":"1317",b2f554cd:"1477",ea13dd3a:"1652","0a637f08":"1677",a7023ddc:"1713","814f3328":"2535","6878938b":"2553","4b1723f1":"2813","35ee46b0":"2822","1f391b9e":"3085",a6aa9e1f:"3089","1df93b7f":"3237","9ede617c":"3604","9e4087bc":"3608","01a85c17":"4013","69aa9e93":"4015",a94703ab:"4368",ab461798:"4699","14a48451":"4985","9abc7bc7":"5460","468f2e1c":"5564",e1902777:"5609","726afaaf":"5747",ccc49370:"6103","1f27c5d1":"6143",c74dcec5:"6296","413466e5":"7018",acecf23e:"7393","393be207":"7414","840797c7":"7836",aa178e2b:"7868",db96436e:"7903",f724a01c:"7953",f2e31a35:"8071","4f7497d7":"8097","5ad8d976":"8424",a7bd4aaa:"8518","6875c492":"8610",d969d8ea:"9195",c71491fd:"9242","430f917c":"9301",df5a86e1:"9342","5e95c892":"9661","0e384e19":"9671","14eb3368":"9817"}[e]||e,b.p+b.u(e)},(()=>{var e={1303:0,532:0};b.f.j=(a,f)=>{var c=b.o(e,a)?e[a]:void 0;if(0!==c)if(c)f.push(c[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var t=new Promise(((f,t)=>c=e[a]=[f,t]));f.push(c[2]=t);var r=b.p+b.u(a),d=new Error;b.l(r,(f=>{if(b.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var t=f&&("load"===f.type?"missing":f.type),r=f&&f.target&&f.target.src;d.message="Loading chunk "+a+" failed.\n("+t+": "+r+")",d.name="ChunkLoadError",d.type=t,d.request=r,c[1](d)}}),"chunk-"+a,a)}},b.O.j=a=>0===e[a];var a=(a,f)=>{var c,t,r=f[0],d=f[1],o=f[2],n=0;if(r.some((a=>0!==e[a]))){for(c in d)b.o(d,c)&&(b.m[c]=d[c]);if(o)var i=o(b)}for(a&&a(f);n{"use strict";var e,a,f,c,t,r={},d={};function b(e){var a=d[e];if(void 0!==a)return a.exports;var f=d[e]={exports:{}};return r[e].call(f.exports,f,f.exports,b),f.exports}b.m=r,e=[],b.O=(a,f,c,t)=>{if(!f){var r=1/0;for(i=0;i=t)&&Object.keys(b.O).every((e=>b.O[e](f[o])))?f.splice(o--,1):(d=!1,t0&&e[i-1][2]>t;i--)e[i]=e[i-1];e[i]=[f,c,t]},b.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return b.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,b.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var t=Object.create(null);b.r(t);var r={};a=a||[null,f({}),f([]),f(f)];for(var d=2&c&&e;"object"==typeof d&&!~a.indexOf(d);d=f(d))Object.getOwnPropertyNames(d).forEach((a=>r[a]=()=>e[a]));return r.default=()=>e,b.d(t,r),t},b.d=(e,a)=>{for(var f in a)b.o(a,f)&&!b.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce(((a,f)=>(b.f[f](e,a),a)),[])),b.u=e=>"assets/js/"+({53:"935f2afb",81:"2abfd7a8",533:"b2b675dd",743:"7e736cac",1070:"f196f4a4",1113:"e511e452",1208:"3828c8cd",1317:"75505f57",1477:"b2f554cd",1652:"ea13dd3a",1677:"0a637f08",1713:"a7023ddc",2535:"814f3328",2553:"6878938b",2813:"4b1723f1",2822:"35ee46b0",3085:"1f391b9e",3089:"a6aa9e1f",3237:"1df93b7f",3604:"9ede617c",3608:"9e4087bc",4013:"01a85c17",4015:"69aa9e93",4368:"a94703ab",4699:"ab461798",4985:"14a48451",5460:"9abc7bc7",5564:"468f2e1c",5609:"e1902777",5747:"726afaaf",6103:"ccc49370",6143:"1f27c5d1",6296:"c74dcec5",7018:"413466e5",7184:"68243087",7393:"acecf23e",7414:"393be207",7836:"840797c7",7868:"aa178e2b",7903:"db96436e",7918:"17896441",7953:"f724a01c",8071:"f2e31a35",8097:"4f7497d7",8424:"5ad8d976",8518:"a7bd4aaa",8610:"6875c492",9195:"d969d8ea",9242:"c71491fd",9301:"430f917c",9342:"df5a86e1",9661:"5e95c892",9671:"0e384e19",9817:"14eb3368"}[e]||e)+"."+{53:"cc6240be",81:"eaf1209f",533:"7f33185e",743:"41e22d1d",1070:"4d17d4a0",1113:"6dce99c6",1208:"aa6ba2aa",1317:"519f4209",1477:"92ae2c22",1652:"e9dd3f0f",1677:"61ff06c6",1713:"f57aae3f",1772:"205fa78b",2535:"33be7cdd",2553:"62200649",2813:"7994837a",2822:"5499b20c",3085:"6e4b1596",3089:"ca60c0b4",3237:"7bff3f57",3604:"9d811c06",3608:"eb64b0cc",4013:"0d6af7b9",4015:"00e22eaa",4368:"fac9076a",4699:"e445eee8",4985:"b2bb271d",5005:"facea533",5460:"58965eb1",5564:"1eb5cbc8",5609:"c9bdda11",5747:"5e9ff151",6103:"75bf7d8f",6143:"4d1e30de",6296:"2757b8e1",6705:"8d1cee0d",7018:"5b84004c",7184:"0f23da7a",7393:"3caaec2f",7414:"6bb4bbad",7534:"5a903753",7836:"60904e28",7868:"b3cb7e23",7903:"8da5bafb",7918:"af71c726",7953:"087dbc0b",8071:"eff52960",8097:"07ed6383",8424:"f15ddef4",8518:"b2f1f87c",8610:"54a03b73",9195:"6ce6b63f",9242:"11363cc6",9301:"756863bd",9342:"5a8b1f83",9386:"eb7dae53",9661:"daee7819",9671:"4a2ae178",9817:"131961d5"}[e]+".js",b.miniCssF=e=>{},b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},t="new-docs:",b.l=(e,a,f,r)=>{if(c[e])c[e].push(a);else{var d,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var t=c[e];if(delete c[e],d.parentNode&&d.parentNode.removeChild(d),t&&t.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),o&&document.head.appendChild(d)}},b.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},b.p="/",b.gca=function(e){return e={17896441:"7918",68243087:"7184","935f2afb":"53","2abfd7a8":"81",b2b675dd:"533","7e736cac":"743",f196f4a4:"1070",e511e452:"1113","3828c8cd":"1208","75505f57":"1317",b2f554cd:"1477",ea13dd3a:"1652","0a637f08":"1677",a7023ddc:"1713","814f3328":"2535","6878938b":"2553","4b1723f1":"2813","35ee46b0":"2822","1f391b9e":"3085",a6aa9e1f:"3089","1df93b7f":"3237","9ede617c":"3604","9e4087bc":"3608","01a85c17":"4013","69aa9e93":"4015",a94703ab:"4368",ab461798:"4699","14a48451":"4985","9abc7bc7":"5460","468f2e1c":"5564",e1902777:"5609","726afaaf":"5747",ccc49370:"6103","1f27c5d1":"6143",c74dcec5:"6296","413466e5":"7018",acecf23e:"7393","393be207":"7414","840797c7":"7836",aa178e2b:"7868",db96436e:"7903",f724a01c:"7953",f2e31a35:"8071","4f7497d7":"8097","5ad8d976":"8424",a7bd4aaa:"8518","6875c492":"8610",d969d8ea:"9195",c71491fd:"9242","430f917c":"9301",df5a86e1:"9342","5e95c892":"9661","0e384e19":"9671","14eb3368":"9817"}[e]||e,b.p+b.u(e)},(()=>{var e={1303:0,532:0};b.f.j=(a,f)=>{var c=b.o(e,a)?e[a]:void 0;if(0!==c)if(c)f.push(c[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var t=new Promise(((f,t)=>c=e[a]=[f,t]));f.push(c[2]=t);var r=b.p+b.u(a),d=new Error;b.l(r,(f=>{if(b.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var t=f&&("load"===f.type?"missing":f.type),r=f&&f.target&&f.target.src;d.message="Loading chunk "+a+" failed.\n("+t+": "+r+")",d.name="ChunkLoadError",d.type=t,d.request=r,c[1](d)}}),"chunk-"+a,a)}},b.O.j=a=>0===e[a];var a=(a,f)=>{var c,t,r=f[0],d=f[1],o=f[2],n=0;if(r.some((a=>0!==e[a]))){for(c in d)b.o(d,c)&&(b.m[c]=d[c]);if(o)var i=o(b)}for(a&&a(f);n Archive | LIPS Scheme - + diff --git a/blog/emacs-scheme-regex/index.html b/blog/emacs-scheme-regex/index.html index 22d05634..c6add592 100644 --- a/blog/emacs-scheme-regex/index.html +++ b/blog/emacs-scheme-regex/index.html @@ -4,7 +4,7 @@ Scheme Regex literals in Emacs | LIPS Scheme - + diff --git a/blog/index.html b/blog/index.html index cf96c5d0..dd85d5dc 100644 --- a/blog/index.html +++ b/blog/index.html @@ -4,7 +4,7 @@ Blog | LIPS Scheme - + diff --git a/blog/lips-history/index.html b/blog/lips-history/index.html index e9212f79..095a384a 100644 --- a/blog/lips-history/index.html +++ b/blog/lips-history/index.html @@ -4,7 +4,7 @@ LIPS History | LIPS Scheme - + diff --git a/blog/tags/emacs/index.html b/blog/tags/emacs/index.html index d20c50fc..55bcbdf7 100644 --- a/blog/tags/emacs/index.html +++ b/blog/tags/emacs/index.html @@ -4,7 +4,7 @@ One post tagged with "emacs" | LIPS Scheme - + diff --git a/blog/tags/history/index.html b/blog/tags/history/index.html index ec1788fe..0cd6456e 100644 --- a/blog/tags/history/index.html +++ b/blog/tags/history/index.html @@ -4,7 +4,7 @@ One post tagged with "history" | LIPS Scheme - + diff --git a/blog/tags/index.html b/blog/tags/index.html index fad1ad1f..af6bf799 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -4,7 +4,7 @@ Tags | LIPS Scheme - + diff --git a/blog/tags/lips/index.html b/blog/tags/lips/index.html index 74a96957..f1ba9b27 100644 --- a/blog/tags/lips/index.html +++ b/blog/tags/lips/index.html @@ -4,7 +4,7 @@ One post tagged with "lips" | LIPS Scheme - + diff --git a/blog/tags/scheme/index.html b/blog/tags/scheme/index.html index 91d0b735..95d98ce0 100644 --- a/blog/tags/scheme/index.html +++ b/blog/tags/scheme/index.html @@ -4,7 +4,7 @@ 2 posts tagged with "scheme" | LIPS Scheme - + diff --git a/docs/category/introduction-to-scheme/index.html b/docs/category/introduction-to-scheme/index.html index 4297e15f..715374c5 100644 --- a/docs/category/introduction-to-scheme/index.html +++ b/docs/category/introduction-to-scheme/index.html @@ -4,7 +4,7 @@ Introduction to Scheme | LIPS Scheme - + diff --git a/docs/category/lips-introduction/index.html b/docs/category/lips-introduction/index.html index ed341bbe..ecc3d446 100644 --- a/docs/category/lips-introduction/index.html +++ b/docs/category/lips-introduction/index.html @@ -4,7 +4,7 @@ LIPS introduction | LIPS Scheme - + diff --git a/docs/intro/index.html b/docs/intro/index.html index 802b6c62..50acd196 100644 --- a/docs/intro/index.html +++ b/docs/intro/index.html @@ -4,7 +4,7 @@ Getting Started | LIPS Scheme - + diff --git a/docs/lips/REPL/index.html b/docs/lips/REPL/index.html index 43e277a4..778b351a 100644 --- a/docs/lips/REPL/index.html +++ b/docs/lips/REPL/index.html @@ -4,7 +4,7 @@ REPL | LIPS Scheme - + diff --git a/docs/lips/environments/index.html b/docs/lips/environments/index.html index f8616061..621ed1dd 100644 --- a/docs/lips/environments/index.html +++ b/docs/lips/environments/index.html @@ -4,7 +4,7 @@ Environments | LIPS Scheme - + diff --git a/docs/lips/extension/index.html b/docs/lips/extension/index.html index 82d5bb9d..3c6ac1e1 100644 --- a/docs/lips/extension/index.html +++ b/docs/lips/extension/index.html @@ -4,7 +4,7 @@ Extending LIPS | LIPS Scheme - + @@ -198,8 +198,8 @@

String ;; ==> "x = 12"

The limitation of this solution is that you can't use strings inside ${ ... }. It will break the -Lexer. In the future there may be a way to define such syntax extensions (See Add full string -interpolation as syntax extension).

+Lexer. In order to have full string interpolation you need to read the parser stream (See Standard +input inside syntax extensions).

Accessing parser

In LIPS syntax extensions you can access the parser instance, so you can implement syntax extension that return line number:

diff --git a/docs/lips/functional-helpers/index.html b/docs/lips/functional-helpers/index.html index 9451a44c..06fc4679 100644 --- a/docs/lips/functional-helpers/index.html +++ b/docs/lips/functional-helpers/index.html @@ -4,7 +4,7 @@ Functional and other utils | LIPS Scheme - + diff --git a/docs/lips/intro/index.html b/docs/lips/intro/index.html index 002aff63..c024e4fb 100644 --- a/docs/lips/intro/index.html +++ b/docs/lips/intro/index.html @@ -4,7 +4,7 @@ Core features | LIPS Scheme - + diff --git a/docs/lips/reflection/index.html b/docs/lips/reflection/index.html index d01a4596..be69f067 100644 --- a/docs/lips/reflection/index.html +++ b/docs/lips/reflection/index.html @@ -4,7 +4,7 @@ Reflection | LIPS Scheme - + diff --git a/docs/lips/sxml/index.html b/docs/lips/sxml/index.html index 16512c79..306dc83f 100644 --- a/docs/lips/sxml/index.html +++ b/docs/lips/sxml/index.html @@ -4,7 +4,7 @@ SXML (e.g. for React) | LIPS Scheme - + diff --git a/docs/scheme-intro/continuations/index.html b/docs/scheme-intro/continuations/index.html index f08e5f1e..8be042f1 100644 --- a/docs/scheme-intro/continuations/index.html +++ b/docs/scheme-intro/continuations/index.html @@ -4,7 +4,7 @@ Continuations | LIPS Scheme - + diff --git a/docs/scheme-intro/core/index.html b/docs/scheme-intro/core/index.html index 2f383796..2bcdf61d 100644 --- a/docs/scheme-intro/core/index.html +++ b/docs/scheme-intro/core/index.html @@ -4,7 +4,7 @@ Core of Scheme | LIPS Scheme - + diff --git a/docs/scheme-intro/data-types/index.html b/docs/scheme-intro/data-types/index.html index f30e2b0b..38326dd8 100644 --- a/docs/scheme-intro/data-types/index.html +++ b/docs/scheme-intro/data-types/index.html @@ -4,7 +4,7 @@ Data Types | LIPS Scheme - + diff --git a/docs/scheme-intro/input-output/index.html b/docs/scheme-intro/input-output/index.html index 1afe3493..129fd69e 100644 --- a/docs/scheme-intro/input-output/index.html +++ b/docs/scheme-intro/input-output/index.html @@ -4,7 +4,7 @@ Input and Output | LIPS Scheme - + diff --git a/docs/scheme-intro/macros/index.html b/docs/scheme-intro/macros/index.html index ac5d0c20..c4600a81 100644 --- a/docs/scheme-intro/macros/index.html +++ b/docs/scheme-intro/macros/index.html @@ -4,7 +4,7 @@ Macros | LIPS Scheme - + diff --git a/docs/scheme-intro/next-step/index.html b/docs/scheme-intro/next-step/index.html index 32ee44c8..620af8fa 100644 --- a/docs/scheme-intro/next-step/index.html +++ b/docs/scheme-intro/next-step/index.html @@ -4,7 +4,7 @@ What Next | LIPS Scheme - + diff --git a/docs/scheme-intro/streams/index.html b/docs/scheme-intro/streams/index.html index ffe89790..4730d67d 100644 --- a/docs/scheme-intro/streams/index.html +++ b/docs/scheme-intro/streams/index.html @@ -4,7 +4,7 @@ Streams | LIPS Scheme - + diff --git a/docs/scheme-intro/what-is-lisp/index.html b/docs/scheme-intro/what-is-lisp/index.html index 6089353e..a856fe91 100644 --- a/docs/scheme-intro/what-is-lisp/index.html +++ b/docs/scheme-intro/what-is-lisp/index.html @@ -4,7 +4,7 @@ What is Lisp and Scheme | LIPS Scheme - + diff --git a/index.html b/index.html index 602bcada..4c6f5f53 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Powerful Scheme interpreter in JavaScript | LIPS Scheme - + diff --git a/markdown-page/index.html b/markdown-page/index.html index 2487cd62..7d9e9f90 100644 --- a/markdown-page/index.html +++ b/markdown-page/index.html @@ -4,7 +4,7 @@ Markdown page example | LIPS Scheme - + diff --git a/reference/index.html b/reference/index.html index 06b80248..81dfcc8f 100644 --- a/reference/index.html +++ b/reference/index.html @@ -4,7 +4,7 @@ LIPS Scheme | LIPS Scheme - + diff --git a/screenshooter/index.html b/screenshooter/index.html index f83a01c6..7ff48126 100644 --- a/screenshooter/index.html +++ b/screenshooter/index.html @@ -4,7 +4,7 @@ LIPS Scheme | LIPS Scheme - +