Compare commits
No commits in common. "a36fd874ecf9d0bfb0a90de06efed49a4cd460c9" and "1b5bb837eabb3de022e8dbc1fc37fe5c03f1adc4" have entirely different histories.
a36fd874ec
...
1b5bb837ea
8 changed files with 9 additions and 832 deletions
|
|
@ -4,7 +4,6 @@ import { renderToStaticMarkup } from "react-dom/server";
|
||||||
import * as runtime from "react/jsx-runtime";
|
import * as runtime from "react/jsx-runtime";
|
||||||
import { feedPlugin } from "@11ty/eleventy-plugin-rss";
|
import { feedPlugin } from "@11ty/eleventy-plugin-rss";
|
||||||
import { IdAttributePlugin } from "@11ty/eleventy";
|
import { IdAttributePlugin } from "@11ty/eleventy";
|
||||||
import rehypePrism from "rehype-prism-plus";
|
|
||||||
|
|
||||||
export default function (eleventyConfig) {
|
export default function (eleventyConfig) {
|
||||||
eleventyConfig.setInputDirectory("site-src");
|
eleventyConfig.setInputDirectory("site-src");
|
||||||
|
|
@ -16,7 +15,6 @@ export default function (eleventyConfig) {
|
||||||
const { default: mdxContent } = await evaluate(str, {
|
const { default: mdxContent } = await evaluate(str, {
|
||||||
...runtime,
|
...runtime,
|
||||||
baseUrl: pathToFileURL(inputPath),
|
baseUrl: pathToFileURL(inputPath),
|
||||||
rehypePlugins: [rehypePrism],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return async function (data) {
|
return async function (data) {
|
||||||
|
|
|
||||||
188
package-lock.json
generated
188
package-lock.json
generated
|
|
@ -13,8 +13,7 @@
|
||||||
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
||||||
"@mdx-js/mdx": "^3.1.1",
|
"@mdx-js/mdx": "^3.1.1",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3"
|
||||||
"rehype-prism-plus": "^2.0.2"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@11ty/dependency-tree": {
|
"node_modules/@11ty/dependency-tree": {
|
||||||
|
|
@ -331,12 +330,6 @@
|
||||||
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/prismjs": {
|
|
||||||
"version": "1.26.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz",
|
|
||||||
"integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/unist": {
|
"node_modules/@types/unist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
|
|
@ -1186,57 +1179,6 @@
|
||||||
"js-yaml": "bin/js-yaml.js"
|
"js-yaml": "bin/js-yaml.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hast-util-from-html": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
|
|
||||||
"integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/hast": "^3.0.0",
|
|
||||||
"devlop": "^1.1.0",
|
|
||||||
"hast-util-from-parse5": "^8.0.0",
|
|
||||||
"parse5": "^7.0.0",
|
|
||||||
"vfile": "^6.0.0",
|
|
||||||
"vfile-message": "^4.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/unified"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hast-util-from-parse5": {
|
|
||||||
"version": "8.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
|
|
||||||
"integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/hast": "^3.0.0",
|
|
||||||
"@types/unist": "^3.0.0",
|
|
||||||
"devlop": "^1.0.0",
|
|
||||||
"hastscript": "^9.0.0",
|
|
||||||
"property-information": "^7.0.0",
|
|
||||||
"vfile": "^6.0.0",
|
|
||||||
"vfile-location": "^5.0.0",
|
|
||||||
"web-namespaces": "^2.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/unified"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hast-util-parse-selector": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/hast": "^3.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/unified"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hast-util-to-estree": {
|
"node_modules/hast-util-to-estree": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz",
|
||||||
|
|
@ -1292,19 +1234,6 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hast-util-to-string": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/hast": "^3.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/unified"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hast-util-whitespace": {
|
"node_modules/hast-util-whitespace": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
|
||||||
|
|
@ -1318,23 +1247,6 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hastscript": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/hast": "^3.0.0",
|
|
||||||
"comma-separated-tokens": "^2.0.0",
|
|
||||||
"hast-util-parse-selector": "^4.0.0",
|
|
||||||
"property-information": "^7.0.0",
|
|
||||||
"space-separated-tokens": "^2.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/unified"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/htmlparser2": {
|
"node_modules/htmlparser2": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
|
||||||
|
|
@ -2626,30 +2538,12 @@
|
||||||
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/parse-numeric-range": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/parse-srcset": {
|
"node_modules/parse-srcset": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
||||||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/parse5": {
|
|
||||||
"version": "7.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
|
||||||
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"entities": "^6.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parseurl": {
|
"node_modules/parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
|
@ -2875,51 +2769,6 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/refractor": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/hast": "^3.0.0",
|
|
||||||
"@types/prismjs": "^1.0.0",
|
|
||||||
"hastscript": "^9.0.0",
|
|
||||||
"parse-entities": "^4.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/wooorm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rehype-parse": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/hast": "^3.0.0",
|
|
||||||
"hast-util-from-html": "^2.0.0",
|
|
||||||
"unified": "^11.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/unified"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rehype-prism-plus": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-jTHb8ZtQHd2VWAAKeCINgv/8zNEF0+LesmwJak69GemoPVN9/8fGEARTvqOpKqmN57HwaM9z8UKBVNVJe8zggw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"hast-util-to-string": "^3.0.1",
|
|
||||||
"parse-numeric-range": "^1.3.0",
|
|
||||||
"refractor": "^5.0.0",
|
|
||||||
"rehype-parse": "^9.0.1",
|
|
||||||
"unist-util-filter": "^5.0.1",
|
|
||||||
"unist-util-visit": "^5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rehype-recma": {
|
"node_modules/rehype-recma": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz",
|
||||||
|
|
@ -3238,17 +3087,6 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unist-util-filter": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/unist": "^3.0.0",
|
|
||||||
"unist-util-is": "^6.0.0",
|
|
||||||
"unist-util-visit-parents": "^6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unist-util-is": {
|
"node_modules/unist-util-is": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
|
||||||
|
|
@ -3359,20 +3197,6 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vfile-location": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/unist": "^3.0.0",
|
|
||||||
"vfile": "^6.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/unified"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vfile-message": {
|
"node_modules/vfile-message": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
|
||||||
|
|
@ -3387,16 +3211,6 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/web-namespaces": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/wooorm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.19.0",
|
"version": "8.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
||||||
"@mdx-js/mdx": "^3.1.1",
|
"@mdx-js/mdx": "^3.1.1",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3"
|
||||||
"rehype-prism-plus": "^2.0.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,340 +0,0 @@
|
||||||
---
|
|
||||||
title: Emulating GOTO in Scheme with continuations
|
|
||||||
description: GOTO sucks and is evil and I hate it, but what if there were parentheses? `call/cc` is kinda like goto, so let’s use it to make goto.
|
|
||||||
tags: post,short
|
|
||||||
date: 2026-02-18 14:28:46 -5
|
|
||||||
---
|
|
||||||
|
|
||||||
export function Basic() {
|
|
||||||
return <abbr title="Beginners’ All-purpose Symbolic Instruction Code">BASIC</abbr>;
|
|
||||||
}
|
|
||||||
|
|
||||||
In his 1968 letter, [<i>A case against the GO TO statement</i>](https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD215.html)
|
|
||||||
(known only by that name), Dijkstra said “[t]he go to statement as it stands is just too primitive, it is too much an invitation to make a mess of one’s program.”
|
|
||||||
Unfortunately, scheme programmers aren’t given that invitation.
|
|
||||||
That’s no fair!
|
|
||||||
Fortunately, scheme has a procedure, `call/cc`, that we can use to emulate the control flow that `GOTO` provides.
|
|
||||||
We can use syntactic abstraction to invite scheme programmers to make a mess of their programs in a limited context.
|
|
||||||
|
|
||||||
## How `GOTO` works
|
|
||||||
Odds are, you know how `GOTO` works, but let’s briefly review.
|
|
||||||
Perhaps you’ve seen a <Basic/> program that looks something like this:
|
|
||||||
```basic
|
|
||||||
10 PRINT "Hello, world!"
|
|
||||||
20 GOTO 10
|
|
||||||
```
|
|
||||||
|
|
||||||
This, as you may have guessed, outputs:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Hello, world!
|
|
||||||
Hello, world!
|
|
||||||
Hello, world!
|
|
||||||
Hello, world!
|
|
||||||
...
|
|
||||||
```
|
|
||||||
…forever.
|
|
||||||
|
|
||||||
Normally, control proceeds from the lowest line number to the highest line number, but the `GOTO` statement “jumps” to the given line, no matter where it is.
|
|
||||||
(Forgive my imprecision, this is a basic tutorial, not a <Basic/> tutorial.)
|
|
||||||
|
|
||||||
You’re more likely to see `goto` in `C`:
|
|
||||||
|
|
||||||
```c
|
|
||||||
void do_something() {
|
|
||||||
char *important_stuff = (char*)malloc(/* ... */);
|
|
||||||
FILE *important_file = fopen(/* ... */);
|
|
||||||
|
|
||||||
// do stuff...
|
|
||||||
if (errno != 0) goto cleanup;
|
|
||||||
|
|
||||||
// do more stuff...
|
|
||||||
if (errno != 0) goto cleanup;
|
|
||||||
|
|
||||||
printf("Success!\n");
|
|
||||||
|
|
||||||
// control falls through even if everything goes well
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
free(important_stuff);
|
|
||||||
fclose(important_file);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Using `goto` here lets us avoid repeating the `cleanup` logic.
|
|
||||||
Not my thing, but this is what most `goto` fans like.
|
|
||||||
In `C`, `goto` uses `labels:` instead of line numbers, and it can’t leave the function, but otherwise it is substantially similar to <Basic/>’s `GOTO`.
|
|
||||||
|
|
||||||
Hopefully you understand `goto` now. It lets you jump around.
|
|
||||||
The second thing you need to understand before we can implement `goto` with `call/cc` is how `call/cc` works.
|
|
||||||
|
|
||||||
## How `call/cc` works
|
|
||||||
`call/cc` is short for `call-with-current-continuation`.
|
|
||||||
`call/cc` takes one argument, a procedure, and returns the result of applying that procedure with the current continuation as an argument.
|
|
||||||
|
|
||||||
What is “the current continuation?”
|
|
||||||
Let’s start with an example.
|
|
||||||
```scheme
|
|
||||||
(define cont #f)
|
|
||||||
(begin
|
|
||||||
(+ 1 (call/cc
|
|
||||||
(lambda (k)
|
|
||||||
(set! cont k)
|
|
||||||
0)))
|
|
||||||
(display "The number is: ")
|
|
||||||
(write (cont 41))
|
|
||||||
(newline))
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
You might call this example contrived.
|
|
||||||
That is because I contrived it to be an example.
|
|
||||||
Let’s run it anyway. It outputs:
|
|
||||||
```
|
|
||||||
The number is: The number is: The number is: ...
|
|
||||||
```
|
|
||||||
…forever‽
|
|
||||||
|
|
||||||
`cont` is actually something like
|
|
||||||
```scheme
|
|
||||||
(define cont
|
|
||||||
(lambda (x)
|
|
||||||
(+ 1 x)
|
|
||||||
(display "The number is: ")
|
|
||||||
(write (cont 41))
|
|
||||||
(newline)))
|
|
||||||
```
|
|
||||||
In this form, the unconditional recursion is obvious.
|
|
||||||
|
|
||||||
Continuations are a lot like procedures, but they don’t necessarily come back to where you called them.
|
|
||||||
This example demonstrates that difference in behavior:
|
|
||||||
```scheme
|
|
||||||
(define (displayln obj)
|
|
||||||
(display obj)
|
|
||||||
(newline))
|
|
||||||
|
|
||||||
(define cont #f)
|
|
||||||
|
|
||||||
(displayln
|
|
||||||
(call/cc
|
|
||||||
(lambda (k)
|
|
||||||
(set! cont k)
|
|
||||||
"cont set")))
|
|
||||||
|
|
||||||
(begin
|
|
||||||
(displayln "procedure called")
|
|
||||||
(displayln "after procedure call")
|
|
||||||
(cont "continuation called")
|
|
||||||
(displayln "after continuation call"))
|
|
||||||
```
|
|
||||||
|
|
||||||
This outputs
|
|
||||||
|
|
||||||
```text
|
|
||||||
cont set
|
|
||||||
procedure called
|
|
||||||
after procedure call
|
|
||||||
continuation called
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice how after calling a procedure, in this case `displayln`, the output continues but not after calling `cont`.
|
|
||||||
When we call `cont` with a new value, it’s like we ran the same code but chose another value—this
|
|
||||||
is the principle that underlies the
|
|
||||||
<a id="Sitaram-link" href="https://ds26gte.github.io/tyscheme/index-Z-H-16.html#TAG:__tex2page_sec_14.1"><code>amb</code>iguous choice</a>
|
|
||||||
operator.
|
|
||||||
|
|
||||||
The `k` that `call/cc` calls its argument with represents, roughly, the rest of the computation.
|
|
||||||
The “current continuation” is what will be executed next at the point that `call/cc` is called.
|
|
||||||
|
|
||||||
Incidentally, this helps me understand scheme’s multiple return values; `(values v1 v2 ...)` is just `(call/cc (lambda (k) (k v1 v2 ...)))`.
|
|
||||||
|
|
||||||
I recommend reading about continuations in Dybvig’s [<i>The Scheme Programming Language</i>](https://www.scheme.com/tspl4/further.html#g63)
|
|
||||||
if you’re (justly) dissatisfied with my explanation or just want to learn more precisely how they work and their applications.
|
|
||||||
|
|
||||||
We now have a decent understand of how `call/cc` works, so let’s finally use it to implement `goto` in scheme!
|
|
||||||
|
|
||||||
## `goto` in scheme
|
|
||||||
Here you go:
|
|
||||||
```scheme
|
|
||||||
(define-syntax with-goto
|
|
||||||
(syntax-rules ()
|
|
||||||
[(_ goto rest ...)
|
|
||||||
(let ()
|
|
||||||
(define goto #f)
|
|
||||||
(%labels rest ...)
|
|
||||||
(call/cc
|
|
||||||
(lambda (k)
|
|
||||||
(set! goto
|
|
||||||
(lambda (label) (k (label))))
|
|
||||||
rest ...)))]))
|
|
||||||
|
|
||||||
(define-syntax %labels
|
|
||||||
(syntax-rules ()
|
|
||||||
[(_) (begin)]
|
|
||||||
[(_ (_ ...) rest ...) (%labels rest ...)]
|
|
||||||
[(_ label rest ...)
|
|
||||||
(begin
|
|
||||||
(define (label) rest ...)
|
|
||||||
(%labels rest ...))]))
|
|
||||||
```
|
|
||||||
Let’s run that with our favorite [<abbr title="Revised Revised Revised Revised Revised Revised Report on the algorithmic language Scheme">R⁶RS</abbr>](https://www.r6rs.org/) implementation (mine is [Chez Scheme](https://cisco.github.io/ChezScheme/)):
|
|
||||||
```scheme
|
|
||||||
(with-goto goto
|
|
||||||
loop (display "Hello, world!\n")
|
|
||||||
(goto loop))
|
|
||||||
```
|
|
||||||
|
|
||||||
```text
|
|
||||||
Hello, world!
|
|
||||||
Hello, world!
|
|
||||||
Hello, world!
|
|
||||||
Hello, world!
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Here’s an example that doesn't loop forever:
|
|
||||||
```scheme
|
|
||||||
(let ([x 1])
|
|
||||||
(with-goto go
|
|
||||||
(go loop)
|
|
||||||
double
|
|
||||||
(set! x (* 2 x))
|
|
||||||
loop
|
|
||||||
(display x) (newline)
|
|
||||||
(when (< x 1000)
|
|
||||||
(go double))
|
|
||||||
(display "done\n")))
|
|
||||||
```
|
|
||||||
It outputs:
|
|
||||||
```scheme
|
|
||||||
1
|
|
||||||
2
|
|
||||||
4
|
|
||||||
8
|
|
||||||
16
|
|
||||||
32
|
|
||||||
64
|
|
||||||
128
|
|
||||||
256
|
|
||||||
512
|
|
||||||
1024
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
I’ll explain this macro one part at a time.
|
|
||||||
|
|
||||||
First, `(define-syntax goto (syntax-rules () [...]))` defines `goto` as a syntax transformer (more precise name for a macro) using the `syntax-rules` pattern-matching language.
|
|
||||||
The `()` after `syntax-rules` is the empty list of literals; we don't have any special words here, so it doesn't apply.
|
|
||||||
You can read more about how `syntax-rules` works in [TSPL](https://scheme.com/tspl4/syntax.html#./syntax:s14), but we'll only be using the most basic features here.
|
|
||||||
The important thing is to know that matched names are replaced in the output and that `x ...` matches/splices zero or more expressions.
|
|
||||||
Also, `syntax-rules` is hygienic, so don’t stress about name collisions.
|
|
||||||
|
|
||||||
Then, we match `(_ goto rest ...)`.
|
|
||||||
Anything else is a syntax error.
|
|
||||||
The `_` is for `with-goto` (we do not want to repeat ourselves).
|
|
||||||
|
|
||||||
We output a big `let` expression.
|
|
||||||
Notice how the second example uses `go` instead of `goto`?
|
|
||||||
That's because the first element in `with-goto` is the name of the `goto` procedure.
|
|
||||||
We `define` it as false because we will set it later.
|
|
||||||
|
|
||||||
Next, we pass the body (`rest ...`) to `%labels`, which deserves its own heading.
|
|
||||||
|
|
||||||
## Extracting labels
|
|
||||||
`%labels` is a syntax transformer with three cases:
|
|
||||||
1. `(_)` Nothing is passed: `(begin)` (do nothing)
|
|
||||||
2. `(_ (_ ...) rest ...)` A list is passed: Ignore it and process `rest ...`. We treat expressions of the form `(x ...)` as statements, not labels.
|
|
||||||
3. `(_ label rest ...)` Finally, a label!
|
|
||||||
|
|
||||||
When we encounter a label, we define a thunk (procedure that takes no arguments) with the rest of the arguments as its body, like so:
|
|
||||||
```scheme
|
|
||||||
(define (label) rest ...)
|
|
||||||
```
|
|
||||||
|
|
||||||
Putting it all together,
|
|
||||||
```scheme
|
|
||||||
(%labels
|
|
||||||
a
|
|
||||||
(display 1)
|
|
||||||
b
|
|
||||||
(display 2)
|
|
||||||
c
|
|
||||||
(display 3))
|
|
||||||
```
|
|
||||||
(morally) expands to
|
|
||||||
```scheme
|
|
||||||
(begin
|
|
||||||
(define (a)
|
|
||||||
(display 1)
|
|
||||||
b
|
|
||||||
(display 2)
|
|
||||||
c
|
|
||||||
(display 3))
|
|
||||||
|
|
||||||
(define (b)
|
|
||||||
(display 2)
|
|
||||||
c
|
|
||||||
(display 3))
|
|
||||||
|
|
||||||
(define (c) (display 3)))
|
|
||||||
```
|
|
||||||
(The leftover labels have no effect)
|
|
||||||
|
|
||||||
This helper on its own is a really crappy way to define functions with shared tails, so let’s bring it all together.
|
|
||||||
|
|
||||||
## Going to
|
|
||||||
We have our labels as functions, but what for?
|
|
||||||
If we call these procedures, they will return control to us, so they aren’t like `C` labels at all.
|
|
||||||
Well, remember how I said that continuations don’t necessarily come back to where you called them?”
|
|
||||||
We’re going to exploit that property to implement `goto`.
|
|
||||||
We wrap the body of `with-goto` in `(call/cc (lambda (k) ...))`.
|
|
||||||
|
|
||||||
Inside the body, if we call `k` like `(k (label))` we effectively replace the body with the result of calling `label`.
|
|
||||||
We accomplished a jump!
|
|
||||||
|
|
||||||
`(set! goto (lambda (label) (k (label))))` makes `goto` do exactly this (note that function arguments have to be evaluated before the procedure call takes place).
|
|
||||||
We use `(define goto #f)` combined with a `set!` because the labels we defined earlier need to be able to see the `goto` function.
|
|
||||||
|
|
||||||
This is what our first `with-goto` looks like when we expand it:
|
|
||||||
```scheme
|
|
||||||
(let ()
|
|
||||||
(define goto #f)
|
|
||||||
(define (loop) (display "Hello, world!\n") (goto loop))
|
|
||||||
(call/cc
|
|
||||||
(lambda (k)
|
|
||||||
(set! goto (lambda (label) (k (label))))
|
|
||||||
loop
|
|
||||||
(display "Hello, world!\n")
|
|
||||||
(goto loop))))
|
|
||||||
```
|
|
||||||
It is in fact expanded slightly differently and more efficiently, it does not use unbounded stack space <abbr title="As Far As I Know">AFAIK</abbr>, which makes sense because we aren’t actually increasing the depth of the callstack when we `goto`.
|
|
||||||
|
|
||||||
To demonstrate that fact and that labels are values, here is one last program.
|
|
||||||
Make an educated guess about what it does before running it, and see if you can make any general statements about its output (other than “the output never ends”).
|
|
||||||
|
|
||||||
```scheme
|
|
||||||
(with-goto go
|
|
||||||
a
|
|
||||||
(display "A")
|
|
||||||
b
|
|
||||||
(display "B")
|
|
||||||
(go (if (zero? (random 2)) a b)))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
This is useless.
|
|
||||||
There are a lot of cool things that you can implement with `call/cc`, but this is dumb!
|
|
||||||
There is a *lot* of nonsense that you can do with this implementation (try messing with nested `with-goto` or storing `goto` elsewhere).
|
|
||||||
Still, I hope you learned a bit about `call/cc` and what building abstractions with it can look like.
|
|
||||||
|
|
||||||
## Further reading
|
|
||||||
`call/cc` is awesome, but unfortunately [it sucks](https://okmij.org/ftp/continuations/against-callcc.html)!
|
|
||||||
This has been known for decades!
|
|
||||||
[Delimited continuations](https://en.wikipedia.org/wiki/Delimited_continuation) are way better!
|
|
||||||
Consider the [`⁻Ƒ⁻` operator](https://web.archive.org/web/20250112082613/https://legacy.cs.indiana.edu/~dyb/pubs/monadicDC.pdf)!
|
|
||||||
Thanks for the soapbox.
|
|
||||||
|
|
||||||
Here’s a satisfying full-circle:
|
|
||||||
[Earlier](#Sitaram-link), I linked to an implementation of `amb` by Dorai Sitaram.
|
|
||||||
I recognized the name because Racket implements [the two operators](https://docs.racket-lang.org/reference/cont.html#%28form._%28%28lib._racket%2Fcontrol..rkt%29._~25%29%29)
|
|
||||||
he describes in [<i>Handling Control</i>](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.22.7256), a 1993 article in <i>Programming Language Design and Implementation</i>.
|
|
||||||
|
|
@ -16,7 +16,7 @@ Importantly, literally nothing changes for (cool) users who (righteously) block
|
||||||
It is perfect [progressive enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement).
|
It is perfect [progressive enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement).
|
||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
1. Add htmx to your `<head>`: <code class="long">{`<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>`}</code>
|
1. Add htmx to your `<head>`: `<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>`.
|
||||||
2. Add `hx-boost="true"` to your `<body>`.
|
2. Add `hx-boost="true"` to your `<body>`.
|
||||||
3. Add `hx-preserve="true"` to stuff that is static between pages (probably a sidebar/footer). Add a unique `id` if it doesn't already have one.
|
3. Add `hx-preserve="true"` to stuff that is static between pages (probably a sidebar/footer). Add a unique `id` if it doesn't already have one.
|
||||||
4. Be happy that it works with and without JavaScript.
|
4. Be happy that it works with and without JavaScript.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ title: terezi.pyrope.net
|
||||||
|
|
||||||
<link rel="stylesheet" href="/res/main.css">
|
<link rel="stylesheet" href="/res/main.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/res/charis.css"/>
|
<link rel="stylesheet" type="text/css" href="/res/charis.css"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/res/syntax-highlighting.css"/>
|
|
||||||
|
|
||||||
<link rel="icon" href="/res/pyralspite.webp">
|
<link rel="icon" href="/res/pyralspite.webp">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,11 +123,9 @@ pre {
|
||||||
/* yeah what the heck let's do 100 (for rust's sake) */
|
/* yeah what the heck let's do 100 (for rust's sake) */
|
||||||
/* reducing the font-size a touch makes this more than reasonable methinks */
|
/* reducing the font-size a touch makes this more than reasonable methinks */
|
||||||
/* nvm */
|
/* nvm */
|
||||||
width: min(80.5ch, 95%);
|
width: min(80ch, 95vw);
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
padding: 0.5rem;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.anchor {
|
.anchor {
|
||||||
|
|
@ -163,8 +161,7 @@ h2 > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
p,
|
p,
|
||||||
blockquote,
|
blockquote {
|
||||||
pre {
|
|
||||||
margin-block: 0 8pt;
|
margin-block: 0 8pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,8 +173,11 @@ pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mmmm */
|
/* mmmm */
|
||||||
code.long {
|
code {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
/*
|
||||||
|
color: #008282;
|
||||||
|
filter: grayscale(0.7); */
|
||||||
}
|
}
|
||||||
|
|
||||||
pre > code {
|
pre > code {
|
||||||
|
|
@ -367,16 +367,3 @@ footer > * {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
code,
|
|
||||||
pre {
|
|
||||||
background-color: #282828;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
code,
|
|
||||||
pre {
|
|
||||||
background-color: #fbf0c9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,280 +0,0 @@
|
||||||
/*
|
|
||||||
* For both of these, I, mehbark, made some silly tweaks to fit my own needs. Don't use this!
|
|
||||||
*/
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
/**
|
|
||||||
* Gruvbox dark theme
|
|
||||||
*
|
|
||||||
* Adapted from a theme based on:
|
|
||||||
* Vim Gruvbox dark Theme (https://github.com/morhetz/gruvbox)
|
|
||||||
*
|
|
||||||
* @author Azat S. <to@azat.io>
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
code,
|
|
||||||
pre[class*="language-"] {
|
|
||||||
color: #ebdbb2; /* fg1 / fg */
|
|
||||||
font-family: Consolas, Monaco, "Andale Mono", monospace;
|
|
||||||
direction: ltr;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre;
|
|
||||||
word-spacing: normal;
|
|
||||||
word-break: normal;
|
|
||||||
line-height: 1.5;
|
|
||||||
|
|
||||||
-moz-tab-size: 4;
|
|
||||||
-o-tab-size: 4;
|
|
||||||
tab-size: 4;
|
|
||||||
|
|
||||||
-webkit-hyphens: none;
|
|
||||||
-moz-hyphens: none;
|
|
||||||
-ms-hyphens: none;
|
|
||||||
hyphens: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre[class*="language-"]::-moz-selection,
|
|
||||||
pre[class*="language-"] ::-moz-selection,
|
|
||||||
code::-moz-selection,
|
|
||||||
code ::-moz-selection {
|
|
||||||
color: #fbf1c7; /* fg0 */
|
|
||||||
background: #7c6f64; /* bg4 */
|
|
||||||
}
|
|
||||||
|
|
||||||
pre[class*="language-"]::selection,
|
|
||||||
pre[class*="language-"] ::selection,
|
|
||||||
code::selection,
|
|
||||||
code ::selection {
|
|
||||||
color: #fbf1c7; /* fg0 */
|
|
||||||
background: #7c6f64; /* bg4 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* :not(pre) > code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
background: #1d2021; /* bg0_h
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* Inline code */
|
|
||||||
:not(pre) > code[class*="language-"] {
|
|
||||||
padding: 0.1em;
|
|
||||||
border-radius: 0.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.comment,
|
|
||||||
.token.prolog,
|
|
||||||
.token.cdata {
|
|
||||||
color: #a89984; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.delimiter,
|
|
||||||
.token.boolean,
|
|
||||||
.token.keyword,
|
|
||||||
.token.selector,
|
|
||||||
.token.important,
|
|
||||||
.token.atrule {
|
|
||||||
color: #fb4934; /* red2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.operator,
|
|
||||||
.token.punctuation,
|
|
||||||
.token.attr-name {
|
|
||||||
color: #a89984; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.tag,
|
|
||||||
.token.tag .punctuation,
|
|
||||||
.token.doctype,
|
|
||||||
.token.builtin {
|
|
||||||
color: #fabd2f; /* yellow2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.entity,
|
|
||||||
.token.number,
|
|
||||||
.token.symbol {
|
|
||||||
color: #d3869b; /* purple2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.property,
|
|
||||||
.token.constant,
|
|
||||||
.token.variable {
|
|
||||||
color: #fb4934; /* red2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.string,
|
|
||||||
.token.char {
|
|
||||||
color: #b8bb26; /* green2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.attr-value,
|
|
||||||
.token.attr-value .punctuation {
|
|
||||||
color: #a89984; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.url {
|
|
||||||
color: #b8bb26; /* green2 */
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.function {
|
|
||||||
color: #fabd2f; /* yellow2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.regex {
|
|
||||||
background: #b8bb26; /* green2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.italic {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.inserted {
|
|
||||||
background: #a89984; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.deleted {
|
|
||||||
background: #fb4934; /* red2 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
/**
|
|
||||||
* Gruvbox light theme
|
|
||||||
*
|
|
||||||
* Based on Gruvbox: https://github.com/morhetz/gruvbox
|
|
||||||
* Adapted from PrismJS gruvbox-dark theme: https://github.com/schnerring/prism-themes/blob/master/themes/prism-gruvbox-dark.css
|
|
||||||
*
|
|
||||||
* @author Michael Schnerring (https://schnerring.net)
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
code,
|
|
||||||
pre[class*="language-"] {
|
|
||||||
color: #3c3836; /* fg1 / fg */
|
|
||||||
font-family: Consolas, Monaco, "Andale Mono", monospace;
|
|
||||||
direction: ltr;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre;
|
|
||||||
word-spacing: normal;
|
|
||||||
word-break: normal;
|
|
||||||
line-height: 1.5;
|
|
||||||
|
|
||||||
-moz-tab-size: 4;
|
|
||||||
-o-tab-size: 4;
|
|
||||||
tab-size: 4;
|
|
||||||
|
|
||||||
-webkit-hyphens: none;
|
|
||||||
-moz-hyphens: none;
|
|
||||||
-ms-hyphens: none;
|
|
||||||
hyphens: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre[class*="language-"]::-moz-selection,
|
|
||||||
pre[class*="language-"] ::-moz-selection,
|
|
||||||
code::-moz-selection,
|
|
||||||
code ::-moz-selection {
|
|
||||||
color: #282828; /* fg0 */
|
|
||||||
background: #a89984; /* bg4 */
|
|
||||||
}
|
|
||||||
|
|
||||||
pre[class*="language-"]::selection,
|
|
||||||
pre[class*="language-"] ::selection,
|
|
||||||
code::selection,
|
|
||||||
code::selection {
|
|
||||||
color: #282828; /* fg0 */
|
|
||||||
background: #a89984; /* bg4 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* :not(pre) > code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
background: #f9f5d7; /* bg0_h
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* Inline code */
|
|
||||||
:not(pre) > code[class*="language-"] {
|
|
||||||
padding: 0.1em;
|
|
||||||
border-radius: 0.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.comment,
|
|
||||||
.token.prolog,
|
|
||||||
.token.cdata {
|
|
||||||
color: #7c6f64; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.delimiter,
|
|
||||||
.token.boolean,
|
|
||||||
.token.keyword,
|
|
||||||
.token.selector,
|
|
||||||
.token.important,
|
|
||||||
.token.atrule {
|
|
||||||
color: #9d0006; /* red2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.operator,
|
|
||||||
.token.punctuation,
|
|
||||||
.token.attr-name {
|
|
||||||
color: #7c6f64; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.tag,
|
|
||||||
.token.tag .punctuation,
|
|
||||||
.token.doctype,
|
|
||||||
.token.builtin {
|
|
||||||
color: #b57614; /* yellow2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.entity,
|
|
||||||
.token.number,
|
|
||||||
.token.symbol {
|
|
||||||
color: #8f3f71; /* purple2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.property,
|
|
||||||
.token.constant,
|
|
||||||
.token.variable {
|
|
||||||
color: #9d0006; /* red2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.string,
|
|
||||||
.token.char {
|
|
||||||
color: #797403; /* green2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.attr-value,
|
|
||||||
.token.attr-value .punctuation {
|
|
||||||
color: #7c6f64; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.url {
|
|
||||||
color: #797403; /* green2 */
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.function {
|
|
||||||
color: #b57614; /* yellow2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.regex {
|
|
||||||
background: #797403; /* green2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.italic {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.inserted {
|
|
||||||
background: #7c6f64; /* fg4 / gray1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.deleted {
|
|
||||||
background: #9d0006; /* red2 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue