Compare commits
10 Commits
253e4bbb48
...
ea7889726c
Author | SHA1 | Date | |
---|---|---|---|
ea7889726c | |||
0d4aad83da | |||
0861b053d8 | |||
3fd4b719b9 | |||
525e4f5197 | |||
063a9e1c06 | |||
43cc334417 | |||
6c898b0d01 | |||
d3089905d4 | |||
1a7a0f1e3a |
BIN
public/Iosevka/Bold.woff2
Normal file
BIN
public/Iosevka/Bold.woff2
Normal file
Binary file not shown.
BIN
public/Iosevka/Heavy.woff2
Normal file
BIN
public/Iosevka/Heavy.woff2
Normal file
Binary file not shown.
BIN
public/Iosevka/Regular.woff2
Normal file
BIN
public/Iosevka/Regular.woff2
Normal file
Binary file not shown.
@ -1,3 +1,22 @@
|
|||||||
|
/* Iosevka web */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Iosevka Fixed Web";
|
||||||
|
font-display: swap;
|
||||||
|
font-weight: 400;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-style: normal;
|
||||||
|
src: url("/Iosevka/Regular.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Iosevka Fixed Web';
|
||||||
|
font-display: swap;
|
||||||
|
font-weight: 700;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-style: normal;
|
||||||
|
src: url('/Iosevka/Bold.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--c-bg: #121212;
|
--c-bg: #121212;
|
||||||
@ -49,7 +68,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre, code {
|
pre, code {
|
||||||
font-family: Iosevka, 'Fira Code', monospace;
|
font-family: "Iosevka Fixed Web", "Iosevka Nerd Font", Iosevka, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-effect-receiver {
|
.button-effect-receiver {
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown pre {
|
.markdown > pre {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
padding: 0.75em 0.75em;
|
padding: 0.75em 0.75em;
|
||||||
color: var(--code-theme-color);
|
color: var(--code-theme-color);
|
||||||
|
193
src/components/InteractiveCode.astro
Normal file
193
src/components/InteractiveCode.astro
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
---
|
||||||
|
import { lex } from "../lexer/lexer";
|
||||||
|
const {code, steps} = Astro.props;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the following:
|
||||||
|
* - Removes the first & last line, if they are empty
|
||||||
|
* - Picks the indentation level from the first non-white line
|
||||||
|
* - Dedents the following lines
|
||||||
|
*/
|
||||||
|
function trimAndDedent(input: string): Array<string> {
|
||||||
|
let lines = input.split("\n");
|
||||||
|
|
||||||
|
// Remove empty lines at the start
|
||||||
|
while (lines[0] === "") {
|
||||||
|
lines = lines.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get indentation level
|
||||||
|
let indentationLevel = 0;
|
||||||
|
for (const char of lines[0]!) {
|
||||||
|
if (char === " ") {
|
||||||
|
indentationLevel += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce indentation, or trim
|
||||||
|
for (let i = 0; i < lines.length; i += 1) {
|
||||||
|
// If the line is empty, continue
|
||||||
|
const characters = lines[i]!.split("");
|
||||||
|
if (characters.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all characters are whitespace, append just a newline
|
||||||
|
const nonWhitespace = characters.find((x) => x !== " ");
|
||||||
|
if (nonWhitespace === undefined) {
|
||||||
|
lines[i] = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce indentation
|
||||||
|
if (characters.length < indentationLevel) {
|
||||||
|
throw new Error("Invalid indentation while parsing THP code: " + lines[i]);
|
||||||
|
}
|
||||||
|
let currentIndentation = 0;
|
||||||
|
for (const c of characters) {
|
||||||
|
if (c === " ") { currentIndentation += 1 }
|
||||||
|
else {break;}
|
||||||
|
}
|
||||||
|
if (currentIndentation < indentationLevel) {
|
||||||
|
throw new Error("Invalid indentation while parsing THP code: " + lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines[i] = characters.slice(4).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Remove empty lines at the end
|
||||||
|
let endPosition = lines.length - 1;
|
||||||
|
while (true) {
|
||||||
|
if (lines[endPosition] === "") {
|
||||||
|
lines = lines.slice(0, -1);
|
||||||
|
endPosition -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightCode(lines: Array<string>): string {
|
||||||
|
let outLines: Array<string> = [];
|
||||||
|
|
||||||
|
for (const [idx, line] of lines.entries()) {
|
||||||
|
const tokens = lex(line);
|
||||||
|
const lineArray = [
|
||||||
|
`<div class=\"inline-block w-full\" :class=\"line === ${idx + 1}? 'bg-green-200 dark:bg-green-900': ''\">`
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const token of tokens) {
|
||||||
|
if (token.token_type !== "") {
|
||||||
|
lineArray.push(`<span class="token ${token.token_type}">${token.v}</span>`);
|
||||||
|
} else {
|
||||||
|
lineArray.push(token.v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineArray.push("</div>");
|
||||||
|
|
||||||
|
outLines.push(lineArray.join(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
return outLines.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeHtml = highlightCode(trimAndDedent(code));
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="bg-black text-white rounded px-1"
|
||||||
|
x-data={`{
|
||||||
|
line: 0,
|
||||||
|
stdout: "",
|
||||||
|
ip: 0,
|
||||||
|
inst: ${steps},
|
||||||
|
done: false,
|
||||||
|
state: {},
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span class="inline-block bg-[var(--code-theme-bg-acolor)] px-2 rounded-tl rounded-tr font-mono text-sm">thp code</span>
|
||||||
|
<pre class="language-thp" style="margin: 0;" data-disabled><code set:html={codeHtml}></code></pre>
|
||||||
|
<div class="grid grid-cols-2 font-mono text-sm">
|
||||||
|
<div>
|
||||||
|
<div class="p-1 border-b border-r border-white">stdout</div>
|
||||||
|
<div class="h-24 p-1 border-r border-white">
|
||||||
|
<pre><code class="bg-black" x-text="stdout"></code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="p-1 border-b border-white">state</div>
|
||||||
|
<div class="h-24 p-1">
|
||||||
|
<template x-for="(value, key) in state">
|
||||||
|
<div x-text="key + ' = ' + value"></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-white p-1">
|
||||||
|
<button class="font-mono px-1 rounded bg-pink-950 disabled:opacity-50 disabled:cursor-not-allowed" @click="alpineNext($data)" :disabled="done && 'true'">
|
||||||
|
Step: <span x-text="ip"></span>
|
||||||
|
</button>
|
||||||
|
<button class="font-mono px-1 rounded bg-pink-950" @click="alpineReset($data)">
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
type Instruction = "line" | "out" | "set";
|
||||||
|
type AlpineState = {
|
||||||
|
line: number,
|
||||||
|
stdout: string,
|
||||||
|
ip: number,
|
||||||
|
inst: Array<Array<[Instruction, string, string | undefined]>>
|
||||||
|
done: boolean,
|
||||||
|
state: {[key: string]: string},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the instruction following the state of the machine.
|
||||||
|
function alpineNext(data: AlpineState) {
|
||||||
|
const len = data.inst.length;
|
||||||
|
const ip = data.ip;
|
||||||
|
data.ip += 1;
|
||||||
|
|
||||||
|
const instructions = data.inst[ip]!;
|
||||||
|
for (const instructionSet of instructions) {
|
||||||
|
const instructionArr = instructionSet;
|
||||||
|
switch (instructionArr[0]) {
|
||||||
|
case "line": {
|
||||||
|
data.line = Number(instructionArr[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "out": {
|
||||||
|
data.stdout += String(instructionArr[1])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "set": {
|
||||||
|
data.state[String(instructionArr[1])] = String(instructionArr[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.ip >= len) {
|
||||||
|
data.done = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
window.alpineNext = alpineNext;
|
||||||
|
|
||||||
|
function alpineReset(data: AlpineState) {
|
||||||
|
data.line = 0;
|
||||||
|
data.stdout = "";
|
||||||
|
data.ip = 0;
|
||||||
|
data.done = false;
|
||||||
|
data.state = {};
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
window.alpineReset = alpineReset;
|
||||||
|
</script>
|
@ -35,5 +35,7 @@ const { title } = Astro.props;
|
|||||||
|
|
||||||
<body class="bg-c-bg text-c-text">
|
<body class="bg-c-bg text-c-text">
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
|
<script src="//unpkg.com/alpinejs" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -6,10 +6,10 @@ import Sidebar from "../components/Sidebar.astro";
|
|||||||
|
|
||||||
const { frontmatter, headings } = Astro.props;
|
const { frontmatter, headings } = Astro.props;
|
||||||
|
|
||||||
const posts = await Astro.glob("../pages/learn/**/*.md");
|
const posts = await Astro.glob("../pages/learn/**/*.{md,mdx}");
|
||||||
|
|
||||||
// The index.md page must have a `pagesLayout` frontmatter, which declares the order of all the pages.
|
// The index.md page must have a `pagesLayout` frontmatter, which declares the order of all the pages.
|
||||||
const indexSubpath = `/learn/index.md`;
|
const indexSubpath = `/learn/index.mdx`;
|
||||||
|
|
||||||
const indexPage = posts.find((post) => post.file.endsWith(indexSubpath));
|
const indexPage = posts.find((post) => post.file.endsWith(indexSubpath));
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ function validateEntry(entry: PageEntry, basePath: string) {
|
|||||||
if (!entry.children) {
|
if (!entry.children) {
|
||||||
// Attempt to get the page title from frontmatter
|
// Attempt to get the page title from frontmatter
|
||||||
const pageData = posts.find((post) =>
|
const pageData = posts.find((post) =>
|
||||||
post.file.endsWith(entry.path + ".md"),
|
post.file.endsWith(entry.path + ".md") || post.file.endsWith(entry.path + ".mdx"),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pageData === undefined) {
|
if (pageData === undefined) {
|
||||||
@ -116,6 +116,10 @@ for (const entry of pagesIndex) {
|
|||||||
|
|
||||||
for (const e of [...code_elements]) {
|
for (const e of [...code_elements]) {
|
||||||
const el = e as HTMLElement;
|
const el = e as HTMLElement;
|
||||||
|
// Some elements with .language-thp don't want to be processed
|
||||||
|
if (e.getAttribute("data-disabled") !== null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const pre_parent = el.parentElement!;
|
const pre_parent = el.parentElement!;
|
||||||
const new_div = document.createElement("div");
|
const new_div = document.createElement("div");
|
||||||
const code = el.innerText;
|
const code = el.innerText;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
layout: ../../../layouts/PagesLayout.astro
|
layout: ../../../layouts/PagesLayout.astro
|
||||||
title: Hello world
|
title: Hello world
|
||||||
---
|
---
|
||||||
|
import InteractiveCode from "../../../components/InteractiveCode.astro";
|
||||||
|
|
||||||
# Hello, world!
|
# Hello, world!
|
||||||
|
|
||||||
@ -12,3 +13,4 @@ print("Hello, world!")
|
|||||||
```
|
```
|
||||||
|
|
||||||
Then run `thp hello.thp`
|
Then run `thp hello.thp`
|
||||||
|
|
@ -2,6 +2,7 @@
|
|||||||
layout: ../../../layouts/PagesLayout.astro
|
layout: ../../../layouts/PagesLayout.astro
|
||||||
title: Try/Exceptions
|
title: Try/Exceptions
|
||||||
---
|
---
|
||||||
|
import InteractiveCode from "../../../components/InteractiveCode.astro";
|
||||||
|
|
||||||
# Try/exceptions
|
# Try/exceptions
|
||||||
|
|
||||||
@ -62,21 +63,62 @@ via try expressions:
|
|||||||
|
|
||||||
### Naked try
|
### Naked try
|
||||||
|
|
||||||
Use a naked `try` when you want to rethrow an error, if any.
|
Use a naked `try` when you want to rethrow an error, if there is any.
|
||||||
|
|
||||||
```thp
|
<InteractiveCode
|
||||||
// May return an Int or an Exception
|
code={`
|
||||||
fun dangerous() -> Int!Exception
|
fun dangerous() -> Int!Exception
|
||||||
{...}
|
{ // May throw randomly
|
||||||
|
return if Math.random() < 0.5 { 50 }
|
||||||
|
else { Exception("Unlucky") }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun run() -> !Exception
|
||||||
|
{ // If \`dangerous()\` throws, the function exits with the same error.
|
||||||
|
// Otherwise, continues
|
||||||
|
val result = try dangerous()
|
||||||
|
print("The result is {result}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val res1 = run() // First, without error
|
||||||
|
val res2 = run() // Then, an example with error
|
||||||
|
`}
|
||||||
|
steps={`[
|
||||||
|
[["line", 14]],
|
||||||
|
[["line", 7]],
|
||||||
|
[["line", 10]],
|
||||||
|
[["line", 1]],
|
||||||
|
[["line", 3]],
|
||||||
|
[
|
||||||
|
["line", 10],
|
||||||
|
["set", "result", 50]
|
||||||
|
],
|
||||||
|
[["line", 11]],
|
||||||
|
[
|
||||||
|
["line", 14],
|
||||||
|
["out", "The result is 50\\n"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 15],
|
||||||
|
["set", "res1", "<empty>"],
|
||||||
|
],
|
||||||
|
[["line", 7]],
|
||||||
|
[["line", 10]],
|
||||||
|
[["line", 1]],
|
||||||
|
[["line", 3]],
|
||||||
|
[["line", 4]],
|
||||||
|
[
|
||||||
|
["line", 10],
|
||||||
|
["set", "result", "Exception(\\"Unlucky\\")"]
|
||||||
|
],
|
||||||
|
[["line", 15]],
|
||||||
|
[
|
||||||
|
["line", 0,],
|
||||||
|
["set", "res2", "Exception(\\"Unlucky\\")"],
|
||||||
|
]
|
||||||
|
]`}
|
||||||
|
></InteractiveCode>
|
||||||
|
|
||||||
fun run() -> !Exception
|
|
||||||
{
|
|
||||||
// Use a naked `try` to rethrow if there's an error
|
|
||||||
val result = try dangerous()
|
|
||||||
// Here result is `Int`
|
|
||||||
print(result)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In the previous example:
|
In the previous example:
|
||||||
|
|
@ -46,8 +46,9 @@ pagesLayout:
|
|||||||
- path: interfaces
|
- path: interfaces
|
||||||
- path: anonymous
|
- path: anonymous
|
||||||
- path: magic
|
- path: magic
|
||||||
|
|
||||||
---
|
---
|
||||||
|
import InteractiveCode from "../../components/InteractiveCode.astro";
|
||||||
|
|
||||||
|
|
||||||
# Welcome
|
# Welcome
|
||||||
|
|
||||||
@ -57,12 +58,49 @@ THP is a new programming language that compiles to PHP.
|
|||||||
|
|
||||||
![Accurate visual description of THP](/img/desc_thp.jpg)
|
![Accurate visual description of THP](/img/desc_thp.jpg)
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
This page discusses some of the design decitions of the language,
|
This page discusses some of the design decitions of the language,
|
||||||
if you want to install THP go to the [installation guide](install)
|
if you want to install THP go to the [installation guide](install)
|
||||||
|
|
||||||
If you want to learn the language, go to the learn section.
|
|
||||||
|
## Interactive execution
|
||||||
|
|
||||||
|
The documentation contains snippets of interactive THP code, like this:
|
||||||
|
|
||||||
|
<InteractiveCode
|
||||||
|
code={`
|
||||||
|
val x = "android"
|
||||||
|
var y = 17
|
||||||
|
|
||||||
|
fun f(Int a, Int b) {
|
||||||
|
print("hello, {a} {b}")
|
||||||
|
}
|
||||||
|
|
||||||
|
f(x, y)
|
||||||
|
`}
|
||||||
|
steps={`[
|
||||||
|
[["line", 1]],
|
||||||
|
[
|
||||||
|
["line", 2],
|
||||||
|
["set", "String x", "\\"android\\""]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 8],
|
||||||
|
["set", "Int y", "17"]
|
||||||
|
],
|
||||||
|
[["line", 4]],
|
||||||
|
[["line", 5]],
|
||||||
|
[
|
||||||
|
["out", "hello, android 17"],
|
||||||
|
["line", 6]
|
||||||
|
],
|
||||||
|
[["line", 8]],
|
||||||
|
[["line", 0]]
|
||||||
|
]`}
|
||||||
|
></InteractiveCode>
|
||||||
|
|
||||||
|
Use the `Step` and `Reset` buttons to emulate the execution of a
|
||||||
|
THP program!
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
@ -19,7 +19,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
"mono": ["Iosevka", "'Iosevka Nerd Font'", "'Fira Code'", "monospace"],
|
"mono": ["'Iosevka Fixed Web'", "Iosevka", "'Iosevka Nerd Font'", "monospace"],
|
||||||
"display": ["Inter", "'Josefin Sans'", "'Fugaz One'", "sans-serif"],
|
"display": ["Inter", "'Josefin Sans'", "'Fugaz One'", "sans-serif"],
|
||||||
"body": ["Inter", "sans-serif"],
|
"body": ["Inter", "sans-serif"],
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user