Compare commits
No commits in common. "a6c32df1ff509bbea097f957bd653a98bb0cba01" and "b5e22cca62b45191255d14018818415540374b5a" have entirely different histories.
a6c32df1ff
...
b5e22cca62
@ -1,13 +0,0 @@
|
|||||||
/** @type {import("prettier").Config} */
|
|
||||||
export default {
|
|
||||||
plugins: ["prettier-plugin-astro"],
|
|
||||||
useTabs: true,
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: "*.astro",
|
|
||||||
options: {
|
|
||||||
parser: "astro",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
@ -1,4 +1,4 @@
|
|||||||
import { defineConfig } from "astro/config";
|
import { defineConfig } from 'astro/config';
|
||||||
import tailwind from "@astrojs/tailwind";
|
import tailwind from "@astrojs/tailwind";
|
||||||
|
|
||||||
import mdx from "@astrojs/mdx";
|
import mdx from "@astrojs/mdx";
|
||||||
|
@ -20,3 +20,4 @@ networks:
|
|||||||
proxy:
|
proxy:
|
||||||
name: proxy
|
name: proxy
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"prettier": "prettier \"./src/**/*.astro\" --write",
|
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -20,9 +19,5 @@
|
|||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"vitest": "^1.5.0"
|
"vitest": "^1.5.0"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"prettier": "^3.3.3",
|
|
||||||
"prettier-plugin-astro": "^0.14.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
5158
pnpm-lock.yaml
5158
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -9,19 +9,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Iosevka Fixed Web";
|
font-family: 'Iosevka Fixed Web';
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-stretch: normal;
|
font-stretch: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
src: url("/Iosevka/Bold.woff2") format("woff2");
|
src: url('/Iosevka/Bold.woff2') format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--c-thp: #f472b6;
|
--c-thp: #f472b6;
|
||||||
|
|
||||||
--font-display: "Atkinson Hyperlegible", sans-serif;
|
--font-display: 'Atkinson Hyperlegible', sans-serif;
|
||||||
--font-body: "Atkinson Hyperlegible", sans-serif;
|
--font-body: 'Atkinson Hyperlegible', sans-serif;
|
||||||
--font-code: "Iosevka Fixed Web", "Iosevka Nerd Font", Iosevka, monospace;
|
--font-code: "Iosevka Fixed Web", "Iosevka Nerd Font", Iosevka, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,10 +30,10 @@
|
|||||||
--c-text: rgb(200, 200, 200);
|
--c-text: rgb(200, 200, 200);
|
||||||
--c-text-2: white;
|
--c-text-2: white;
|
||||||
--c-primary: #884b6a;
|
--c-primary: #884b6a;
|
||||||
--c-purple: #7f669d;
|
--c-purple: #7F669D;
|
||||||
--c-purple-light: #ba94d1;
|
--c-purple-light: #BA94D1;
|
||||||
--c-box-shadow: #fbfacd;
|
--c-box-shadow: #FBFACD;
|
||||||
--c-pink: #ae508d;
|
--c-pink: #AE508D;
|
||||||
--c-link: #38bdf8;
|
--c-link: #38bdf8;
|
||||||
|
|
||||||
--c-nav-bg: rgb(18, 18, 18, 0.5);
|
--c-nav-bg: rgb(18, 18, 18, 0.5);
|
||||||
@ -46,7 +46,7 @@
|
|||||||
--c-text: #121212;
|
--c-text: #121212;
|
||||||
--c-text-2: black;
|
--c-text-2: black;
|
||||||
--c-purple: #374259;
|
--c-purple: #374259;
|
||||||
--c-purple-light: #ba94d1;
|
--c-purple-light: #BA94D1;
|
||||||
--c-box-shadow: #374259;
|
--c-box-shadow: #374259;
|
||||||
--c-primary: rgb(255, 180, 180);
|
--c-primary: rgb(255, 180, 180);
|
||||||
--c-pink: #374259;
|
--c-pink: #374259;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
.markdown > h1 {
|
.markdown > h1 {
|
||||||
font-size: 2.25rem;
|
font-size: 2.25rem;
|
||||||
line-height: 2.5rem;
|
line-height: 2.5rem;
|
||||||
@ -101,3 +102,4 @@
|
|||||||
.two-column h3 {
|
.two-column h3 {
|
||||||
margin: 0.75rem 0;
|
margin: 0.75rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
117
public/css/prism.min.css
vendored
117
public/css/prism.min.css
vendored
@ -1,118 +1,3 @@
|
|||||||
/* PrismJS 1.29.0
|
/* PrismJS 1.29.0
|
||||||
https://prismjs.com/download.html#themes=prism */
|
https://prismjs.com/download.html#themes=prism */
|
||||||
code[class*="language-"],
|
code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap: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}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
|
||||||
pre[class*="language-"] {
|
|
||||||
color: #000;
|
|
||||||
background: 0 0;
|
|
||||||
text-shadow: 0 1px #fff;
|
|
||||||
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
|
||||||
font-size: 1em;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre;
|
|
||||||
word-spacing: normal;
|
|
||||||
word-break: normal;
|
|
||||||
word-wrap: 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;
|
|
||||||
}
|
|
||||||
code[class*="language-"] ::-moz-selection,
|
|
||||||
code[class*="language-"]::-moz-selection,
|
|
||||||
pre[class*="language-"] ::-moz-selection,
|
|
||||||
pre[class*="language-"]::-moz-selection {
|
|
||||||
text-shadow: none;
|
|
||||||
background: #b3d4fc;
|
|
||||||
}
|
|
||||||
code[class*="language-"] ::selection,
|
|
||||||
code[class*="language-"]::selection,
|
|
||||||
pre[class*="language-"] ::selection,
|
|
||||||
pre[class*="language-"]::selection {
|
|
||||||
text-shadow: none;
|
|
||||||
background: #b3d4fc;
|
|
||||||
}
|
|
||||||
@media print {
|
|
||||||
code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pre[class*="language-"] {
|
|
||||||
padding: 1em;
|
|
||||||
margin: 0.5em 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
:not(pre) > code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
background: #f5f2f0;
|
|
||||||
}
|
|
||||||
:not(pre) > code[class*="language-"] {
|
|
||||||
padding: 0.1em;
|
|
||||||
border-radius: 0.3em;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
.token.cdata,
|
|
||||||
.token.comment,
|
|
||||||
.token.doctype,
|
|
||||||
.token.prolog {
|
|
||||||
color: #708090;
|
|
||||||
}
|
|
||||||
.token.punctuation {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
.token.namespace {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
.token.boolean,
|
|
||||||
.token.constant,
|
|
||||||
.token.deleted,
|
|
||||||
.token.number,
|
|
||||||
.token.property,
|
|
||||||
.token.symbol,
|
|
||||||
.token.tag {
|
|
||||||
color: #905;
|
|
||||||
}
|
|
||||||
.token.attr-name,
|
|
||||||
.token.builtin,
|
|
||||||
.token.char,
|
|
||||||
.token.inserted,
|
|
||||||
.token.selector,
|
|
||||||
.token.string {
|
|
||||||
color: #690;
|
|
||||||
}
|
|
||||||
.language-css .token.string,
|
|
||||||
.style .token.string,
|
|
||||||
.token.entity,
|
|
||||||
.token.operator,
|
|
||||||
.token.url {
|
|
||||||
color: #9a6e3a;
|
|
||||||
background: hsla(0, 0%, 100%, 0.5);
|
|
||||||
}
|
|
||||||
.token.atrule,
|
|
||||||
.token.attr-value,
|
|
||||||
.token.keyword {
|
|
||||||
color: #07a;
|
|
||||||
}
|
|
||||||
.token.class-name,
|
|
||||||
.token.function {
|
|
||||||
color: #dd4a68;
|
|
||||||
}
|
|
||||||
.token.important,
|
|
||||||
.token.regex,
|
|
||||||
.token.variable {
|
|
||||||
color: #e90;
|
|
||||||
}
|
|
||||||
.token.bold,
|
|
||||||
.token.important {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.token.italic {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.token.entity {
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
2855
public/js/alpine-3.14.0.min.js
vendored
2855
public/js/alpine-3.14.0.min.js
vendored
File diff suppressed because one or more lines are too long
@ -5,10 +5,7 @@ import CodeError from "./docs/CodeError.astro";
|
|||||||
|
|
||||||
const { thpcode, no_warnings, level } = Astro.props;
|
const { thpcode, no_warnings, level } = Astro.props;
|
||||||
|
|
||||||
const [native_html, error_message] = await native_highlighter(
|
const [native_html, error_message] = await native_highlighter(thpcode, level as HighlightLevel);
|
||||||
thpcode,
|
|
||||||
level as HighlightLevel,
|
|
||||||
);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<pre
|
<pre
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import Code from "./Code.astro";
|
import Code from "./Code.astro"
|
||||||
|
|
||||||
const { thpcode, no_warnings, level } = Astro.props;
|
const { thpcode, no_warnings, level } = Astro.props;
|
||||||
---
|
---
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
const { showSidebarButton = true, version = "latest" } = Astro.props;
|
const { showSidebarButton = true } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<nav
|
<nav
|
||||||
@ -14,11 +14,18 @@ const { showSidebarButton = true, version = "latest" } = Astro.props;
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
<a href="/" class="px-4 flex gap-2 items-center">
|
<a
|
||||||
<img class="inline-block h-10" src="/img/thp_logo_exp.svg" alt="thp" />
|
href="/"
|
||||||
|
class="px-4 flex gap-2 items-center"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="inline-block h-10"
|
||||||
|
src="/img/thp_logo_exp.svg"
|
||||||
|
alt="thp"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href={`/en/${version}/learn/`}
|
href="/en/latest/learn/"
|
||||||
class="hidden sm:inline-block px-4 font-display font-bold-text-xl hover:underline"
|
class="hidden sm:inline-block px-4 font-display font-bold-text-xl hover:underline"
|
||||||
>
|
>
|
||||||
Learn
|
Learn
|
||||||
@ -44,9 +51,7 @@ const { showSidebarButton = true, version = "latest" } = Astro.props;
|
|||||||
const sidebarToggle = document.getElementById("sidebar-toggle");
|
const sidebarToggle = document.getElementById("sidebar-toggle");
|
||||||
|
|
||||||
if (!sidebar || !sidebarToggle) {
|
if (!sidebar || !sidebarToggle) {
|
||||||
console.log(
|
console.log("Sidebar or Sidebar toggle not found. Not enabling sidebar on mobile");
|
||||||
"Sidebar or Sidebar toggle not found. Not enabling sidebar on mobile",
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,10 @@ const post_url = entry.url + (entry.url.endsWith("/") ? "" : "/");
|
|||||||
// this may deal with folders.
|
// this may deal with folders.
|
||||||
// if so, it will turn any `-` into whitespace,
|
// if so, it will turn any `-` into whitespace,
|
||||||
// and remove any leading number
|
// and remove any leading number
|
||||||
const entry_title = entry.title.replaceAll("-", " ").replaceAll(/\d+_/g, "");
|
const entry_title = entry.title
|
||||||
|
.replaceAll("-", " ")
|
||||||
|
.replaceAll(/\d+_/g, "");
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -31,7 +34,9 @@ const entry_title = entry.title.replaceAll("-", " ").replaceAll(/\d+_/g, "");
|
|||||||
|
|
||||||
<ul class="my-1">
|
<ul class="my-1">
|
||||||
{entry.children!.map((nextEntry) => (
|
{entry.children!.map((nextEntry) => (
|
||||||
<Astro.self entry={nextEntry} />
|
<Astro.self
|
||||||
|
entry={nextEntry}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
|
@ -20,7 +20,8 @@ function buildHierarchy(headings: any) {
|
|||||||
toc.push(heading);
|
toc.push(heading);
|
||||||
} else if (heading.depth === 1) {
|
} else if (heading.depth === 1) {
|
||||||
/** empty */
|
/** empty */
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
parentHeadings.get(heading.depth - 1).subheadings.push(heading);
|
parentHeadings.get(heading.depth - 1).subheadings.push(heading);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,10 +9,7 @@ const monoClass = parentMono ? " font-mono" : "";
|
|||||||
---
|
---
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a class={"inline-block py-1 hover:underline" + monoClass} href={"#" + heading.slug}>
|
||||||
class={"inline-block py-1 hover:underline" + monoClass}
|
|
||||||
href={"#" + heading.slug}
|
|
||||||
>
|
|
||||||
{heading.text}
|
{heading.text}
|
||||||
</a>
|
</a>
|
||||||
{
|
{
|
||||||
|
@ -6,8 +6,4 @@ const { thpcode, href } = Astro.props;
|
|||||||
const [native_html] = await native_highlighter(thpcode);
|
const [native_html] = await native_highlighter(thpcode);
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a href={href} class="inline-block w-full py-2 font-mono whitespace-pre" set:html={native_html} />
|
||||||
href={href}
|
|
||||||
class="inline-block w-full py-2 font-mono whitespace-pre"
|
|
||||||
set:html={native_html}
|
|
||||||
/>
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
<div
|
<div class="my-6 px-4 py-2 rounded bg-[#f8e287] text-[#221b00] dark:bg-[#dbc66e] dark:text-[#3a3000]">
|
||||||
class="my-6 px-4 py-2 rounded bg-[#f8e287] text-[#221b00] dark:bg-[#dbc66e] dark:text-[#3a3000]"
|
|
||||||
>
|
|
||||||
<div class="font-bold pt-2">Warning</div>
|
<div class="font-bold pt-2">Warning</div>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,82 +1,111 @@
|
|||||||
import { expect, test } from "vitest";
|
import { expect, test } from 'vitest'
|
||||||
import { leftTrimDedent } from "./utils";
|
import { leftTrimDedent } from "./utils"
|
||||||
|
|
||||||
test("should trim empty string", () => {
|
test("should trim empty string", () => {
|
||||||
const input = ``;
|
const input = ``;
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual([""]);
|
expect(leftTrimDedent(input)).toEqual([""]);
|
||||||
});
|
})
|
||||||
|
|
||||||
test("should work on a single line", () => {
|
test("should work on a single line", () => {
|
||||||
const input = `hello`;
|
const input = `hello`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello"
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should trim a single line", () => {
|
test("should trim a single line", () => {
|
||||||
const input = ` hello`;
|
const input = ` hello`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello"
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should trim multiple lines", () => {
|
test("should trim multiple lines", () => {
|
||||||
const input = ` hello\n world`;
|
const input = ` hello\n world`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
"world"
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should trim multiple lines without indentation", () => {
|
test("should trim multiple lines without indentation", () => {
|
||||||
const input = `hello\nworld`;
|
const input = `hello\nworld`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
"world"
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should consume only whitespace", () => {
|
test("should consume only whitespace", () => {
|
||||||
const input = ` hello\nworld`;
|
const input = ` hello\nworld`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = leftTrimDedent(input);
|
const res = leftTrimDedent(input);
|
||||||
expect(res).not.toEqual(["hello", "rld"]);
|
expect(res).not.toEqual([
|
||||||
|
"hello",
|
||||||
|
"rld",
|
||||||
|
]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e).toBeInstanceOf(Error);
|
expect(e).toBeInstanceOf(Error);
|
||||||
expect(e).toHaveProperty(
|
expect(e).toHaveProperty("message", "Invalid indentation while trimming: Expected 2 spaces, got 0");
|
||||||
"message",
|
|
||||||
"Invalid indentation while trimming: Expected 2 spaces, got 0",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
test("should preserve deeper indentation", () => {
|
test("should preserve deeper indentation", () => {
|
||||||
const input = ` hello\n world`;
|
const input = ` hello\n world`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", " world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
" world",
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should ignore empty lines", () => {
|
test("should ignore empty lines", () => {
|
||||||
const input = ` hello\n\n world`;
|
const input = ` hello\n\n world`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "", "world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
"",
|
||||||
|
"world",
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should ignore lines with only whitespace", () => {
|
test("should ignore lines with only whitespace", () => {
|
||||||
const input = ` hello\n \n \n world`;
|
const input = ` hello\n \n \n world`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "", " ", "world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
"",
|
||||||
|
" ",
|
||||||
|
"world",
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should trim multiple without indentation", () => {
|
test("should trim multiple without indentation", () => {
|
||||||
const input = `hello\nworld\n!`;
|
const input = `hello\nworld\n!`
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "world", "!"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
"world",
|
||||||
|
"!",
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should ignore empty first line", () => {
|
test("should ignore empty first line", () => {
|
||||||
const input = `
|
const input = `
|
||||||
hello
|
hello
|
||||||
world`;
|
world`;
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
"world",
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should ignore empty first line 2", () => {
|
test("should ignore empty first line 2", () => {
|
||||||
const input = `
|
const input = `
|
||||||
@ -84,8 +113,12 @@ test("should ignore empty first line 2", () => {
|
|||||||
|
|
||||||
world`;
|
world`;
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "", "world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
});
|
"hello",
|
||||||
|
"",
|
||||||
|
"world",
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
|
||||||
test("should ignore empty last line", () => {
|
test("should ignore empty last line", () => {
|
||||||
const input = `
|
const input = `
|
||||||
@ -93,5 +126,8 @@ test("should ignore empty last line", () => {
|
|||||||
world
|
world
|
||||||
`;
|
`;
|
||||||
|
|
||||||
expect(leftTrimDedent(input)).toEqual(["hello", "world"]);
|
expect(leftTrimDedent(input)).toEqual([
|
||||||
|
"hello",
|
||||||
|
"world",
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the following:
|
* Performs the following:
|
||||||
* - Removes the first & last line, if they are empty
|
* - Removes the first & last line, if they are empty
|
||||||
@ -53,9 +54,7 @@ function trimWhitespace(input: string, count: number): string {
|
|||||||
if (indentCount >= count || indentCount == input.length) {
|
if (indentCount >= count || indentCount == input.length) {
|
||||||
return input.slice(count);
|
return input.slice(count);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(`Invalid indentation while trimming: Expected ${count} spaces, got ${indentCount}`);
|
||||||
`Invalid indentation while trimming: Expected ${count} spaces, got ${indentCount}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,6 @@ const { title } = Astro.props;
|
|||||||
href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<script src="//unpkg.com/alpinejs" defer></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-c-bg text-c-text">
|
<body class="bg-c-bg text-c-text">
|
||||||
|
@ -11,40 +11,38 @@ export type PageEntry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface AstroFile {
|
export interface AstroFile {
|
||||||
frontmatter: Frontmatter;
|
frontmatter: Frontmatter
|
||||||
__usesAstroImage: boolean;
|
__usesAstroImage: boolean
|
||||||
url: string;
|
url: string
|
||||||
file: string;
|
file: string
|
||||||
relative_file: string;
|
relative_file: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Frontmatter {
|
export interface Frontmatter {
|
||||||
layout: string;
|
layout: string
|
||||||
title: string;
|
title: string
|
||||||
order: number;
|
order: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/** Base url. It is used to later build a tree file system */
|
/** Base url. It is used to later build a tree file system */
|
||||||
base_url: string;
|
base_url: string,
|
||||||
frontmatter: Frontmatter;
|
frontmatter: Frontmatter;
|
||||||
headings: any;
|
headings: any;
|
||||||
posts: Array<AstroFile>;
|
posts: Array<AstroFile>;
|
||||||
version: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
base_url,
|
base_url,
|
||||||
frontmatter,
|
frontmatter,
|
||||||
headings,
|
headings,
|
||||||
posts,
|
posts
|
||||||
version = "latest",
|
|
||||||
}: Props = Astro.props;
|
}: Props = Astro.props;
|
||||||
|
|
||||||
const base_len = base_url.length;
|
const base_len = base_url.length;
|
||||||
|
|
||||||
const posts_2 = posts
|
const posts_2 = posts
|
||||||
.map((post) => ({
|
.map(post => ({
|
||||||
...post,
|
...post,
|
||||||
title: post.frontmatter.title,
|
title: post.frontmatter.title,
|
||||||
// this should be a path relative to the base url.
|
// this should be a path relative to the base url.
|
||||||
@ -52,11 +50,11 @@ const posts_2 = posts
|
|||||||
// being `/spec/ast/tokens` would be `/ast/tokens`
|
// being `/spec/ast/tokens` would be `/ast/tokens`
|
||||||
path: post.url.substring(base_len),
|
path: post.url.substring(base_len),
|
||||||
}))
|
}))
|
||||||
.sort((p1, p2) => (p1.frontmatter.order > p2.frontmatter.order ? 1 : -1));
|
.sort((p1, p2) => p1.frontmatter.order > p2.frontmatter.order? 1 : -1);
|
||||||
|
|
||||||
// build a hierarchy of the files
|
// build a hierarchy of the files
|
||||||
const second_level: Record<string, Array<AstroFile>> = {
|
const second_level: Record<string, Array<AstroFile>> = {
|
||||||
_: [],
|
"_": [],
|
||||||
};
|
};
|
||||||
for (const post of posts_2) {
|
for (const post of posts_2) {
|
||||||
const fragments = post.path.split("/");
|
const fragments = post.path.split("/");
|
||||||
@ -68,7 +66,8 @@ for (const post of posts_2) {
|
|||||||
second_level[folder_name] = [];
|
second_level[folder_name] = [];
|
||||||
}
|
}
|
||||||
second_level[folder_name].push(post);
|
second_level[folder_name].push(post);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// add to root folder
|
// add to root folder
|
||||||
second_level["_"]!.push(post);
|
second_level["_"]!.push(post);
|
||||||
}
|
}
|
||||||
@ -89,7 +88,8 @@ for (const levels_key of levels_keys) {
|
|||||||
if (levels_key === "_") {
|
if (levels_key === "_") {
|
||||||
// top level, already inserted
|
// top level, already inserted
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
const posts = second_level[levels_key]!;
|
const posts = second_level[levels_key]!;
|
||||||
const sorted_posts = posts.toSorted(sort_posts);
|
const sorted_posts = posts.toSorted(sort_posts);
|
||||||
const parentEntry = {
|
const parentEntry = {
|
||||||
@ -109,11 +109,6 @@ function sort_posts(p1, p2) {
|
|||||||
return p1.title > p2.title? 1 : -1;
|
return p1.title > p2.title? 1 : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const versions = ["latest", "v0.0.1"].filter((x) => x !== version);
|
|
||||||
const url_components = base_url.split(version);
|
|
||||||
const lc = url_components[0] ?? "";
|
|
||||||
const rc = url_components[1] ?? "";
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={frontmatter.title}>
|
<BaseLayout title={frontmatter.title}>
|
||||||
@ -128,53 +123,23 @@ const rc = url_components[1] ?? "";
|
|||||||
border-c-border-1 -translate-x-64 lg:translate-x-0 transition-transform"
|
border-c-border-1 -translate-x-64 lg:translate-x-0 transition-transform"
|
||||||
>
|
>
|
||||||
<nav class="py-4 pr-2 overflow-x-scroll h-[calc(100vh-3rem)]">
|
<nav class="py-4 pr-2 overflow-x-scroll h-[calc(100vh-3rem)]">
|
||||||
<div class="mb-2 relative" x-data="{open: false}">
|
|
||||||
<div>
|
<form class="pb-8 px-1">
|
||||||
<span> Language version: </span>
|
<label for="version-select">THP version:</label>
|
||||||
</div>
|
<select
|
||||||
<button
|
id="version-select"
|
||||||
class="bg-c-bg text-c-text border border-c-primary rounded px-3 w-full font-mono mt-2 flex items-center hover:dark:bg-zinc-800 transition-colors cursor-pointer text-left"
|
class="bg-c-bg text-c-on-bg border border-pink-700 rounded px-3 p-1 w-full font-mono"
|
||||||
@click="open = !open"
|
|
||||||
>
|
|
||||||
<span class="inline-block w-full">
|
|
||||||
{version}
|
|
||||||
</span>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
fill="var(--c-primary)"
|
|
||||||
viewBox="0 0 256 256"
|
|
||||||
style="padding-bottom: 4px"
|
|
||||||
><path
|
|
||||||
d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
x-bind:class="open? '': 'hidden'"
|
|
||||||
class="bottom-0 w-full absolute"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="absolute top-0 w-full bg-c-bg border border-c-primary rounded"
|
|
||||||
>
|
>
|
||||||
|
<option selected>latest</option>
|
||||||
|
<option>v0.0.1</option>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
|
||||||
{
|
{
|
||||||
versions.map((x) => (
|
entries.map((entry) => (
|
||||||
<a
|
<Sidebar entry={entry} />
|
||||||
class="px-3 py-1 hover:dark:bg-zinc-800 transition-colors font-mono inline-block w-full text-left"
|
|
||||||
href={`${lc}${x}${rc}`}
|
|
||||||
>
|
|
||||||
{x}
|
|
||||||
</a>
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="my-6" />
|
|
||||||
|
|
||||||
{entries.map((entry) => <Sidebar entry={entry} />)}
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
|
|
||||||
export function highlightOnDom() {
|
export function highlightOnDom() {
|
||||||
const pre_elements = document.querySelectorAll("pre");
|
const pre_elements = document.querySelectorAll("pre");
|
||||||
for (const pre_el of pre_elements) {
|
for (const pre_el of pre_elements) {
|
||||||
const language = pre_el.getAttribute("data-language");
|
const language = pre_el.getAttribute("data-language");
|
||||||
if (language === null) {
|
if (language === null) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a visual indicador
|
// Create a visual indicador
|
||||||
const indicator = document.createElement("span");
|
const indicator = document.createElement("span");
|
||||||
|
|
||||||
let indicator_bg_class = "";
|
let indicator_bg_class = "";
|
||||||
if (language === "php") {
|
if (language === "php") { indicator_bg_class = "bg-[#4f5b93]"; }
|
||||||
indicator_bg_class = "bg-[#4f5b93]";
|
else if (language === "html") { indicator_bg_class = "bg-[#dc4a20]"; }
|
||||||
} else if (language === "html") {
|
|
||||||
indicator_bg_class = "bg-[#dc4a20]";
|
|
||||||
}
|
|
||||||
|
|
||||||
indicator.className = `absolute top-1 right-0 inline-block text-sm select-none opacity-85 ${indicator_bg_class} px-2 rounded-full`;
|
indicator.className = `absolute top-1 right-0 inline-block text-sm select-none opacity-85 ${indicator_bg_class} px-2 rounded-full`;
|
||||||
indicator.innerText = language;
|
indicator.innerText = language;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
export function sidebarHighlight() {
|
export function sidebarHighlight() {
|
||||||
let current_uri = window.location.pathname;
|
let current_uri = window.location.pathname;
|
||||||
|
|
||||||
@ -6,7 +7,8 @@ export function sidebarHighlight() {
|
|||||||
const links = sidebar.querySelectorAll("a");
|
const links = sidebar.querySelectorAll("a");
|
||||||
for (const link of [...links]) {
|
for (const link of [...links]) {
|
||||||
if (link.getAttribute("href") === current_uri) {
|
if (link.getAttribute("href") === current_uri) {
|
||||||
sidebar.scrollTop = link.offsetTop - sidebar.offsetTop - 250;
|
sidebar.scrollTop =
|
||||||
|
link.offsetTop - sidebar.offsetTop - 250;
|
||||||
|
|
||||||
link.classList.add("bg-pink-200", "dark:bg-pink-950");
|
link.classList.add("bg-pink-200", "dark:bg-pink-950");
|
||||||
break;
|
break;
|
||||||
|
@ -1,16 +1,9 @@
|
|||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
import { leftTrimDedent } from "../components/utils";
|
import { leftTrimDedent } from "../components/utils";
|
||||||
import { HighlightLevel } from "./types";
|
import { HighlightLevel } from "./types";
|
||||||
import type {
|
import type { ErrorLabel, MistiErr, Token, TokenizeResult, TokenType } from "./types";
|
||||||
ErrorLabel,
|
|
||||||
MistiErr,
|
|
||||||
Token,
|
|
||||||
TokenizeResult,
|
|
||||||
TokenType,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
const error_classes =
|
const error_classes = "underline underline-offset-4 decoration-wavy decoration-red-500";
|
||||||
"underline underline-offset-4 decoration-wavy decoration-red-500";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlights code using the compiler
|
* Highlights code using the compiler
|
||||||
@ -19,10 +12,7 @@ const error_classes =
|
|||||||
* - The tokens as a list of <span /> elements
|
* - The tokens as a list of <span /> elements
|
||||||
* - An error message, if any
|
* - An error message, if any
|
||||||
*/
|
*/
|
||||||
export async function native_highlighter(
|
export async function native_highlighter(code: string, level = HighlightLevel.Lexic): Promise<[string, string | null]> {
|
||||||
code: string,
|
|
||||||
level = HighlightLevel.Lexic,
|
|
||||||
): Promise<[string, string | null]> {
|
|
||||||
let formatted_code = leftTrimDedent(code).join("\n");
|
let formatted_code = leftTrimDedent(code).join("\n");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -40,10 +30,7 @@ export async function native_highlighter(
|
|||||||
* - The tokens as a list of <span /> elements
|
* - The tokens as a list of <span /> elements
|
||||||
* - An error message, if any
|
* - An error message, if any
|
||||||
*/
|
*/
|
||||||
function highlight_syntax(
|
function highlight_syntax(code: string, result: TokenizeResult): [string, string | null] {
|
||||||
code: string,
|
|
||||||
result: TokenizeResult,
|
|
||||||
): [string, string | null] {
|
|
||||||
if (result.Ok) {
|
if (result.Ok) {
|
||||||
const tokens_html = render_tokens(code, result.Ok);
|
const tokens_html = render_tokens(code, result.Ok);
|
||||||
|
|
||||||
@ -57,21 +44,21 @@ function highlight_syntax(
|
|||||||
} else if (result.Err) {
|
} else if (result.Err) {
|
||||||
// TODO: Implement error rendering, based on the new error schema
|
// TODO: Implement error rendering, based on the new error schema
|
||||||
|
|
||||||
return [code, `lexical error ${result.Err.error_code}`];
|
return [code, `lexical error ${result.Err.error_code}`]
|
||||||
} else {
|
} else {
|
||||||
console.error(result);
|
console.error(result);
|
||||||
throw new Error(
|
throw new Error("Web page error: The compiler returned a case that wasn't handled.");
|
||||||
"Web page error: The compiler returned a case that wasn't handled.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** A fatal error with the THP compiler */
|
/** A fatal error with the THP compiler */
|
||||||
function compiler_error(code: string, error: MistiErr): [string, string] {
|
function compiler_error(code: string, error: MistiErr): [string, string] {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return [code, "Fatal compiler error"];
|
return [code, "Fatal compiler error"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a list of tokens into colored HTML, and underlines present errors
|
* Transforms a list of tokens into colored HTML, and underlines present errors
|
||||||
*
|
*
|
||||||
@ -81,19 +68,12 @@ function compiler_error(code: string, error: MistiErr): [string, string] {
|
|||||||
* @param error_end Absolute position to where the error ends.
|
* @param error_end Absolute position to where the error ends.
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function render_tokens(
|
function render_tokens(input: string, tokens: Array<Token>, error_labels: Array<ErrorLabel> = []): string {
|
||||||
input: string,
|
|
||||||
tokens: Array<Token>,
|
|
||||||
error_labels: Array<ErrorLabel> = [],
|
|
||||||
): string {
|
|
||||||
const input_chars = input.split("");
|
const input_chars = input.split("");
|
||||||
let output = "";
|
let output = "";
|
||||||
|
|
||||||
// Collects all the token ranges in all error labels
|
// Collects all the token ranges in all error labels
|
||||||
const error_ranges: Array<[number, number]> = error_labels.map((l) => [
|
const error_ranges: Array<[number, number]> = error_labels.map(l => [l.start, l.end]);
|
||||||
l.start,
|
|
||||||
l.end,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let current_pos = 0;
|
let current_pos = 0;
|
||||||
for (let i = 0; i < tokens.length; i += 1) {
|
for (let i = 0; i < tokens.length; i += 1) {
|
||||||
@ -123,11 +103,7 @@ function render_tokens(
|
|||||||
output += input_chars.slice(current_pos, token_start).join("");
|
output += input_chars.slice(current_pos, token_start).join("");
|
||||||
|
|
||||||
// Append the token
|
// Append the token
|
||||||
const [token_value, new_token_end] = process_token_value_and_end(
|
const [token_value, new_token_end] = process_token_value_and_end(t.value, t.token_type, token_end);
|
||||||
t.value,
|
|
||||||
t.token_type,
|
|
||||||
token_end,
|
|
||||||
);
|
|
||||||
const token_type = translate_token_type(t.token_type, token_value);
|
const token_type = translate_token_type(t.token_type, token_value);
|
||||||
output += `<span class="token ${token_type} ${is_errored ? error_classes : ""}">${token_value}</span>`;
|
output += `<span class="token ${token_type} ${is_errored ? error_classes : ""}">${token_value}</span>`;
|
||||||
|
|
||||||
@ -143,21 +119,12 @@ function render_tokens(
|
|||||||
let offset = 0;
|
let offset = 0;
|
||||||
for (const label of error_labels) {
|
for (const label of error_labels) {
|
||||||
// get the line number of the label
|
// get the line number of the label
|
||||||
const [line_number, col_number] = absolute_to_line_column(
|
const [line_number, col_number] = absolute_to_line_column(input, label.start);
|
||||||
input,
|
|
||||||
label.start,
|
|
||||||
);
|
|
||||||
let spaces_len = col_number - 1;
|
let spaces_len = col_number - 1;
|
||||||
if (spaces_len < 0) {
|
if (spaces_len < 0) { spaces_len = 0 }
|
||||||
spaces_len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const spaces = new Array(spaces_len).fill(" ").join("");
|
const spaces = new Array(spaces_len).fill(" ").join("");
|
||||||
lines.splice(
|
lines.splice(line_number + offset, 0, create_inline_error_message(spaces, label.message));
|
||||||
line_number + offset,
|
|
||||||
0,
|
|
||||||
create_inline_error_message(spaces, label.message),
|
|
||||||
);
|
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,10 +143,7 @@ function create_inline_error_message(spaces: string, message: string): string {
|
|||||||
* @param input the source code
|
* @param input the source code
|
||||||
* @param absolute the absolute position
|
* @param absolute the absolute position
|
||||||
*/
|
*/
|
||||||
function absolute_to_line_column(
|
function absolute_to_line_column(input: string, absolute: number): [number, number] {
|
||||||
input: string,
|
|
||||||
absolute: number,
|
|
||||||
): [number, number] {
|
|
||||||
let line_count = 1;
|
let line_count = 1;
|
||||||
let last_newline_pos = 0;
|
let last_newline_pos = 0;
|
||||||
|
|
||||||
@ -208,11 +172,7 @@ function absolute_to_line_column(
|
|||||||
* @param first_end The position where the token ends according to the token value
|
* @param first_end The position where the token ends according to the token value
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function process_token_value_and_end(
|
function process_token_value_and_end(value: string, token_type: TokenType, first_end: number): [string, number] {
|
||||||
value: string,
|
|
||||||
token_type: TokenType,
|
|
||||||
first_end: number,
|
|
||||||
): [string, number] {
|
|
||||||
let token_value = value;
|
let token_value = value;
|
||||||
let new_end = first_end;
|
let new_end = first_end;
|
||||||
if (token_type === "MultilineComment") {
|
if (token_type === "MultilineComment") {
|
||||||
@ -226,49 +186,16 @@ function process_token_value_and_end(
|
|||||||
// Escape html and return
|
// Escape html and return
|
||||||
return [
|
return [
|
||||||
token_value.replaceAll(/</g, "<").replaceAll(/>/g, ">"),
|
token_value.replaceAll(/</g, "<").replaceAll(/>/g, ">"),
|
||||||
new_end,
|
new_end
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function translate_token_type(tt: TokenType, value: string): string {
|
function translate_token_type(tt: TokenType, value: string): string {
|
||||||
const keywords = [
|
const keywords = ["throws", "extends", "constructor", "static", "const",
|
||||||
"throws",
|
"enum", "union", "use", "break", "catch", "continue", "as", "do",
|
||||||
"extends",
|
"finally", "fun", "fn", "nil", "return", "throw",
|
||||||
"constructor",
|
"try", "type", "with", "of", "abstract", "class", "interface",
|
||||||
"static",
|
"private", "protected", "pub", "override", "open", "init", "val", "var", "mut", "clone"];
|
||||||
"const",
|
|
||||||
"enum",
|
|
||||||
"union",
|
|
||||||
"use",
|
|
||||||
"break",
|
|
||||||
"catch",
|
|
||||||
"continue",
|
|
||||||
"as",
|
|
||||||
"do",
|
|
||||||
"finally",
|
|
||||||
"fun",
|
|
||||||
"fn",
|
|
||||||
"nil",
|
|
||||||
"return",
|
|
||||||
"throw",
|
|
||||||
"try",
|
|
||||||
"type",
|
|
||||||
"with",
|
|
||||||
"of",
|
|
||||||
"abstract",
|
|
||||||
"class",
|
|
||||||
"interface",
|
|
||||||
"private",
|
|
||||||
"protected",
|
|
||||||
"pub",
|
|
||||||
"override",
|
|
||||||
"open",
|
|
||||||
"init",
|
|
||||||
"val",
|
|
||||||
"var",
|
|
||||||
"mut",
|
|
||||||
"clone",
|
|
||||||
];
|
|
||||||
|
|
||||||
switch (tt) {
|
switch (tt) {
|
||||||
case "Datatype":
|
case "Datatype":
|
||||||
@ -306,8 +233,7 @@ function translate_token_type(tt: TokenType, value: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const native_lex = (code: string, level: HighlightLevel) =>
|
const native_lex = (code: string, level: HighlightLevel) => new Promise<TokenizeResult>((resolve, reject) => {
|
||||||
new Promise<TokenizeResult>((resolve, reject) => {
|
|
||||||
// Get binary path from .env
|
// Get binary path from .env
|
||||||
const binary = import.meta.env.THP_BINARY;
|
const binary = import.meta.env.THP_BINARY;
|
||||||
if (!binary) {
|
if (!binary) {
|
||||||
@ -336,4 +262,4 @@ const native_lex = (code: string, level: HighlightLevel) =>
|
|||||||
reject(new Error(error));
|
reject(new Error(error));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
export type ReferenceItem = {
|
export type ReferenceItem = {
|
||||||
symbol_start: number;
|
symbol_start: number
|
||||||
symbol_end: number;
|
symbol_end: number
|
||||||
reference: string;
|
reference: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface Token {
|
export interface Token {
|
||||||
token_type: TokenType;
|
token_type: TokenType
|
||||||
value: string;
|
value: string
|
||||||
position: number;
|
position: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TokenType =
|
export type TokenType =
|
||||||
@ -40,29 +40,30 @@ export type TokenType =
|
|||||||
| "IN"
|
| "IN"
|
||||||
| "WHILE"
|
| "WHILE"
|
||||||
| "MATCH"
|
| "MATCH"
|
||||||
| "CASE";
|
| "CASE"
|
||||||
|
;
|
||||||
|
|
||||||
export interface MistiErr {
|
export interface MistiErr {
|
||||||
error_code: number;
|
error_code: number
|
||||||
error_offset: number;
|
error_offset: number
|
||||||
labels: Array<ErrorLabel>;
|
labels: Array<ErrorLabel>
|
||||||
note: string | null;
|
note: string | null,
|
||||||
help: string | null;
|
help: string | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ErrorLabel {
|
export interface ErrorLabel {
|
||||||
message: string;
|
message: string
|
||||||
start: number;
|
start: number
|
||||||
end: number;
|
end: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenizeResult {
|
export interface TokenizeResult {
|
||||||
/** All checks passed */
|
/** All checks passed */
|
||||||
Ok?: Array<Token>;
|
Ok?: Array<Token>,
|
||||||
/** A non lexic error was found */
|
/** A non lexic error was found */
|
||||||
MixedErr?: [Array<Token>, MistiErr];
|
MixedErr?: [Array<Token>, MistiErr],
|
||||||
/** A lexic error was found */
|
/** A lexic error was found */
|
||||||
Err?: MistiErr;
|
Err?: MistiErr,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum HighlightLevel {
|
export enum HighlightLevel {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
export function is_digit(c: string): boolean {
|
export function is_digit(c: string): boolean {
|
||||||
return c >= "0" && c <= "9";
|
return c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function is_lowercase(c: string): boolean {
|
export function is_lowercase(c: string): boolean {
|
||||||
return c >= "a" && c <= "z";
|
return c >= 'a' && c <= 'z';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function is_uppercase(c: string): boolean {
|
export function is_uppercase(c: string): boolean {
|
||||||
return c >= "A" && c <= "Z";
|
return c >= 'A' && c <= 'Z';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function is_identifier_char(c: string): boolean {
|
export function is_identifier_char(c: string): boolean {
|
||||||
return is_lowercase(c) || is_uppercase(c) || is_digit(c) || c === "_";
|
return is_lowercase(c) || is_uppercase(c) || is_digit(c) || c === '_';
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
---
|
---
|
||||||
layout: ../../../layouts/ApiLayout.astro
|
layout: ../../../layouts/ApiLayout.astro
|
||||||
---
|
---
|
||||||
|
import TwoColumn from "../../../components/TwoColumn.astro"
|
||||||
import TwoColumn from "../../../components/TwoColumn.astro";
|
import Code from "../../../components/Code.astro"
|
||||||
import Code from "../../../components/Code.astro";
|
import CodeMin from "../../../components/docs/CodeMin.astro"
|
||||||
import CodeMin from "../../../components/docs/CodeMin.astro";
|
import Warning from "../../../components/docs/Warning.astro"
|
||||||
import Warning from "../../../components/docs/Warning.astro";
|
|
||||||
|
|
||||||
# Array
|
# Array
|
||||||
|
|
||||||
@ -20,32 +19,28 @@ THP arrays are 0-indexed.
|
|||||||
|
|
||||||
## Signature
|
## Signature
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
type Array[T] = Map[Int, T]
|
type Array[T] = Map[Int, T]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Where `T` is the datatype that the Array stores. For example:
|
Where `T` is the datatype that the Array stores. For example:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
Array[Int] // An array of integers
|
Array[Int] // An array of integers
|
||||||
Array[Float] // An array of floats
|
Array[Float] // An array of floats
|
||||||
Array[Array[String]] // A 2-dimensional array of strings
|
Array[Array[String]] // A 2-dimensional array of strings
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## PHP array internals
|
## PHP array internals
|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
TL;DR: **Never** assign to an array using an invalid index. If you do the
|
TL;DR: **Never** assign to an array using an invalid index. If you do
|
||||||
program will not crash, instead it will not behaved as expected [(this is a
|
the program will not crash, instead it will not behaved as expected
|
||||||
common problem in
|
[(this is a common problem in PHP)](https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/).
|
||||||
PHP)](https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/). THP tries
|
THP tries its best to solve such behavior.
|
||||||
its best to solve such behavior.
|
|
||||||
</Warning>
|
</Warning>
|
||||||
|
|
||||||
|
|
||||||
Since THP compiles down to PHP, it's important to understand how PHP
|
Since THP compiles down to PHP, it's important to understand how PHP
|
||||||
represents arrays internally.
|
represents arrays internally.
|
||||||
PHP doesn't have arrays. Instead, PHP has ordered maps and syntax sugar
|
PHP doesn't have arrays. Instead, PHP has ordered maps and syntax sugar
|
||||||
@ -53,11 +48,9 @@ to make them look like arrays.
|
|||||||
|
|
||||||
When declaring an array like:
|
When declaring an array like:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
var arr = ["a", "b", "c"]
|
var arr = ["a", "b", "c"]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
in reality what goes into memory is a map with numbers as keys:
|
in reality what goes into memory is a map with numbers as keys:
|
||||||
|
|
||||||
@ -109,7 +102,7 @@ numbers[7] = "!" // Out of bounds assignment
|
|||||||
|
|
||||||
// In this loop, values will be printed following when
|
// In this loop, values will be printed following when
|
||||||
// they were inserted, not by order of the index.
|
// they were inserted, not by order of the index.
|
||||||
for #(\_, value) in numbers
|
for #(_, value) in numbers
|
||||||
{
|
{
|
||||||
print("{value} ") // Will print: a b c ? !
|
print("{value} ") // Will print: a b c ? !
|
||||||
}
|
}
|
||||||
@ -142,9 +135,12 @@ This is one of many fundamental flaws with PHP. The only way to solve it
|
|||||||
would be to check every insertion at runtime, and this would have a
|
would be to check every insertion at runtime, and this would have a
|
||||||
performance penalty.
|
performance penalty.
|
||||||
|
|
||||||
|
|
||||||
From now on, the documentation will continue to work with the Array
|
From now on, the documentation will continue to work with the Array
|
||||||
abstraction, as if all indexes were valid.
|
abstraction, as if all indexes were valid.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Empty array
|
### Empty array
|
||||||
@ -152,36 +148,31 @@ abstraction, as if all indexes were valid.
|
|||||||
To create an empty array use square brackets.
|
To create an empty array use square brackets.
|
||||||
If you create an empty array, you need to specify the datatype.
|
If you create an empty array, you need to specify the datatype.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
Array[Int] empty = []
|
Array[Int] empty = []
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
### Creation
|
### Creation
|
||||||
|
|
||||||
To create an array use square brackets notation:
|
To create an array use square brackets notation:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val numbers = [0, 1, 2, 3, 4, 5]
|
val numbers = [0, 1, 2, 3, 4, 5]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
When the array is not empty, you don't need to specify a datatype.
|
When the array is not empty, you don't need to specify a datatype.
|
||||||
|
|
||||||
When the Array is declared over many lines, the last
|
When the Array is declared over many lines, the last
|
||||||
item should have a trailing comma:
|
item should have a trailing comma:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val colors = [
|
val colors = [
|
||||||
"red",
|
"red",
|
||||||
"blue",
|
"blue",
|
||||||
"green", // trailing comma
|
"green", // trailing comma
|
||||||
]
|
]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
If it doesn't, the code formatter will automatically
|
If it doesn't, the code formatter will automatically
|
||||||
insert one for you.
|
insert one for you.
|
||||||
@ -207,14 +198,13 @@ mutable[0] = 322
|
|||||||
|
|
||||||
To append an element to an array, use the method `push()`:
|
To append an element to an array, use the method `push()`:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
mutable.push(4)
|
mutable.push(4)
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Do not insert into an invalid index. See [PHP array internals](#php-array-internals) to learn why.
|
Do not insert into an invalid index. See [PHP array internals](#php-array-internals) to learn why.
|
||||||
|
|
||||||
|
|
||||||
### Iteration
|
### Iteration
|
||||||
|
|
||||||
Use a `for` loop to iterate over the elements of an array:
|
Use a `for` loop to iterate over the elements of an array:
|
||||||
@ -245,6 +235,7 @@ print("{c} ")
|
|||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
You can also declare an index along with the value:
|
You can also declare an index along with the value:
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
@ -262,37 +253,41 @@ item 1: green
|
|||||||
item 2: blue
|
item 2: blue
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Access
|
### Access
|
||||||
|
|
||||||
To access a value of the array use square brackets notation:
|
To access a value of the array use square brackets notation:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
print(colors[0])
|
print(colors[0])
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Since the index might not exist, this will return a
|
Since the index might not exist, this will return a
|
||||||
[nullable type](/learn/error-handling/null/) that you have to handle.
|
[nullable type](/learn/error-handling/null/) that you have to handle.
|
||||||
|
|
||||||
|
|
||||||
### Destructuring
|
### Destructuring
|
||||||
|
|
||||||
THP arrays don't have destructuring, since the values can all be `null`.
|
THP arrays don't have destructuring, since the values can all be `null`.
|
||||||
If you know that the number of elements is fixed and valid, use Tuples instead.
|
If you know that the number of elements is fixed and valid, use Tuples instead.
|
||||||
|
|
||||||
|
|
||||||
### Operators
|
### Operators
|
||||||
|
|
||||||
While PHP allows using certain operators with arrays, THP disallows that.
|
While PHP allows using certain operators with arrays, THP disallows that.
|
||||||
Methods that perform comparisons should be used instead.
|
Methods that perform comparisons should be used instead.
|
||||||
|
|
||||||
|
|
||||||
### Assignment
|
### Assignment
|
||||||
|
|
||||||
// TODO: Detail that assignment of arrays is copy on write
|
// TODO: Detail that assignment of arrays is copy on write
|
||||||
|
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
In the parameters, <code class="token keyword">self</code> is the array to operate on.
|
In the parameters, <code class="token keyword">self</code> is the array to operate on.
|
||||||
|
|
||||||
|
|
||||||
<table class="table-fixed w-full border-collapse border-2 dark:border-zinc-900 border-stone-400">
|
<table class="table-fixed w-full border-collapse border-2 dark:border-zinc-900 border-stone-400">
|
||||||
<thead>
|
<thead>
|
||||||
<td class="p-2">Method</td>
|
<td class="p-2">Method</td>
|
||||||
@ -301,34 +296,30 @@ In the parameters, <code class="token keyword">self</code> is the array to opera
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
||||||
<td class="px-2">
|
<td class="px-2">
|
||||||
<CodeMin
|
<CodeMin href="./concat" thpcode={`
|
||||||
href="./concat"
|
|
||||||
thpcode={`
|
|
||||||
fun concat[T](
|
fun concat[T](
|
||||||
self,
|
self,
|
||||||
Array[T]... arrays,
|
Array[T]... arrays,
|
||||||
) -> Array[T]
|
) -> Array[T]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Concatenate with other arrays, and return the result as a new array.
|
Concatenate with other arrays, and return the result
|
||||||
|
as a new array.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
||||||
<td class="px-2">
|
<td class="px-2">
|
||||||
<CodeMin
|
<CodeMin thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun filter[T](
|
fun filter[T](
|
||||||
self,
|
self,
|
||||||
(T) -> (Bool) callback,
|
(T) -> (Bool) callback,
|
||||||
) -> Array[T]
|
) -> Array[T]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Filters elements using a callback function, and returns them in a new
|
Filters elements using a callback function, and returns
|
||||||
array.
|
them in a new array.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
||||||
@ -336,15 +327,19 @@ In the parameters, <code class="token keyword">self</code> is the array to opera
|
|||||||
<CodeMin thpcode="fun push[T](self, T... elements) -> Int" />
|
<CodeMin thpcode="fun push[T](self, T... elements) -> Int" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Appends one or more elements to the end of the array. Returns the new
|
Appends one or more elements to the end of the array.
|
||||||
length of the array.
|
Returns the new length of the array.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
<tr class="dark:odd:bg-zinc-900 odd:bg-stone-200">
|
||||||
<td class="px-2">
|
<td class="px-2">
|
||||||
<CodeMin thpcode="fun pop[T](self) -> T" />
|
<CodeMin thpcode="fun pop[T](self) -> T" />
|
||||||
</td>
|
</td>
|
||||||
<td>Removes the last value of the array, and returns it.</td>
|
<td>
|
||||||
|
Removes the last value of the array, and returns it.
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: ../../../../layouts/ApiLayout.astro
|
layout: ../../../../layouts/ApiLayout.astro
|
||||||
---
|
---
|
||||||
|
import Code from "../../../../components/Code.astro"
|
||||||
import Code from "../../../../components/Code.astro";
|
|
||||||
|
|
||||||
# `Array.concat`
|
# `Array.concat`
|
||||||
|
|
||||||
@ -10,14 +9,12 @@ Concatenate with other arrays, and return the result as a new array.
|
|||||||
|
|
||||||
## Signature
|
## Signature
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun concat[T](
|
fun concat[T](
|
||||||
self,
|
self,
|
||||||
Array[T]... arrays,
|
Array[T]... arrays,
|
||||||
) -> Array[T]
|
) -> Array[T]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
@ -28,6 +25,7 @@ Concatenate with other arrays, and return the result as a new array.
|
|||||||
|
|
||||||
`Array[T]`: A new array that contains the elements from all arrays.
|
`Array[T]`: A new array that contains the elements from all arrays.
|
||||||
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
Concatenates the elements of the callee and the elements of each array in the
|
Concatenates the elements of the callee and the elements of each array in the
|
||||||
@ -51,7 +49,6 @@ Example concatenating 2 arrays:
|
|||||||
|
|
||||||
val result = first.concat(second)
|
val result = first.concat(second)
|
||||||
assert_eq(result, [1, 2, 3, 4, 5, 6])
|
assert_eq(result, [1, 2, 3, 4, 5, 6])
|
||||||
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
Example concatenating 3 arrays:
|
Example concatenating 3 arrays:
|
||||||
@ -63,7 +60,6 @@ Example concatenating 3 arrays:
|
|||||||
|
|
||||||
val result = first.concat(second, third)
|
val result = first.concat(second, third)
|
||||||
assert_eq(result, ["a", "b", "c", "d", "e", "f"])
|
assert_eq(result, ["a", "b", "c", "d", "e", "f"])
|
||||||
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
Example concatenating without any other array. In this case
|
Example concatenating without any other array. In this case
|
||||||
@ -74,7 +70,6 @@ Example concatenating without any other array. In this case
|
|||||||
|
|
||||||
val result = first.concat()
|
val result = first.concat()
|
||||||
assert_eq(result, [1, 2, 3])
|
assert_eq(result, [1, 2, 3])
|
||||||
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
Example concatenating an empty array with a filled array:
|
Example concatenating an empty array with a filled array:
|
||||||
@ -85,9 +80,9 @@ Example concatenating an empty array with a filled array:
|
|||||||
|
|
||||||
val result = first.concat(second)
|
val result = first.concat(second)
|
||||||
assert_eq(result, [10, 20, 30])
|
assert_eq(result, [10, 20, 30])
|
||||||
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## PHP interop
|
## PHP interop
|
||||||
|
|
||||||
This method is directly compiled to `array_merge`. Since THP guarantees that
|
This method is directly compiled to `array_merge`. Since THP guarantees that
|
||||||
@ -112,3 +107,6 @@ array(3) {
|
|||||||
[2]=> string(1) "c"
|
[2]=> string(1) "c"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: ../../../../layouts/ApiLayout.astro
|
layout: ../../../../layouts/ApiLayout.astro
|
||||||
---
|
---
|
||||||
|
import Code from "../../../../components/Code.astro"
|
||||||
import Code from "../../../../components/Code.astro";
|
|
||||||
|
|
||||||
# `Array.fold`
|
# `Array.fold`
|
||||||
|
|
||||||
@ -12,15 +11,13 @@ the left.
|
|||||||
|
|
||||||
## Signature
|
## Signature
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun fold[A, B](
|
fun fold[A, B](
|
||||||
self[A],
|
self[A],
|
||||||
B init,
|
B init,
|
||||||
(B acc, A next) -> (B) transform_function,
|
(B acc, A next) -> (B) transform_function,
|
||||||
) -> B
|
) -> B
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
@ -34,6 +31,7 @@ the left.
|
|||||||
- Otherwise, returns the result of applying `transform_function` to the accumulator `acc`,
|
- Otherwise, returns the result of applying `transform_function` to the accumulator `acc`,
|
||||||
and the next element `next`.
|
and the next element `next`.
|
||||||
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
Fold allows you to transform an array of `A` into a single `B`, following
|
Fold allows you to transform an array of `A` into a single `B`, following
|
||||||
@ -41,15 +39,14 @@ an arbitraty function.
|
|||||||
|
|
||||||
For example, let's say that you have an array of numbers:
|
For example, let's say that you have an array of numbers:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val digits = [1, 9, 9, 5]
|
val digits = [1, 9, 9, 5]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
And you want to join all digits into a String like `"1985"`. You can
|
And you want to join all digits into a String like `"1985"`. You can
|
||||||
achieve this with a fold.
|
achieve this with a fold.
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
val digits = [1, 9, 8, 5]
|
val digits = [1, 9, 8, 5]
|
||||||
val init = ""
|
val init = ""
|
||||||
@ -96,12 +93,10 @@ The `transform_function` can be any function, and can operate over any type.
|
|||||||
For example, you could sum all numbers instead of concatenating
|
For example, you could sum all numbers instead of concatenating
|
||||||
like this:
|
like this:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val digits = [1, 9, 8, 5]
|
val digits = [1, 9, 8, 5]
|
||||||
digits.fold(0, fun(acc, next) = acc + next) // 23
|
digits.fold(0, fun(acc, next) = acc + next) // 23
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
f( f( f( f(0, 1), 9), 8), 5)
|
f( f( f( f(0, 1), 9), 8), 5)
|
||||||
@ -123,3 +118,8 @@ val result = builder.build()
|
|||||||
`} />
|
`} />
|
||||||
|
|
||||||
Or anything.
|
Or anything.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: ../../../../layouts/ApiLayout.astro
|
layout: ../../../../layouts/ApiLayout.astro
|
||||||
---
|
---
|
||||||
|
import Code from "../../../../components/Code.astro"
|
||||||
import Code from "../../../../components/Code.astro";
|
|
||||||
|
|
||||||
# `Array.map`
|
# `Array.map`
|
||||||
|
|
||||||
@ -20,9 +19,10 @@ and returns their result in a new array.
|
|||||||
self[],
|
self[],
|
||||||
(A) -> (B) map_function
|
(A) -> (B) map_function
|
||||||
) -> Array[B]
|
) -> Array[B]
|
||||||
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
|
|
||||||
fun multi_def
|
fun multi_def
|
||||||
@ -40,3 +40,4 @@ fmt.Println("prosor prosor %d", 322)
|
|||||||
}
|
}
|
||||||
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
---
|
---
|
||||||
layout: ../../../layouts/ApiLayout.astro
|
layout: ../../../layouts/ApiLayout.astro
|
||||||
---
|
---
|
||||||
|
import TwoColumn from "../../../components/TwoColumn.astro"
|
||||||
import TwoColumn from "../../../components/TwoColumn.astro";
|
import Code from "../../../components/Code.astro"
|
||||||
import Code from "../../../components/Code.astro";
|
|
||||||
|
|
||||||
# module `std`
|
# module `std`
|
||||||
|
|
||||||
@ -29,14 +28,12 @@ if (str_contains("abc", "a")) {
|
|||||||
In THP there is no `str_contains` function. Instead, you'd call the
|
In THP there is no `str_contains` function. Instead, you'd call the
|
||||||
`contains` method on the string:
|
`contains` method on the string:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
if "abc".contains("a")
|
if "abc".contains("a")
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## On naming
|
## On naming
|
||||||
|
|
||||||
@ -59,11 +56,10 @@ If you need to use a PHP class with a lowercase name you can use the following s
|
|||||||
class animal {}
|
class animal {}
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val my_animal = 'animal()
|
val my_animal = 'animal()
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## API: Datatypes
|
## API: Datatypes
|
||||||
|
|
||||||
@ -100,3 +96,5 @@ A [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) double precision floating p
|
|||||||
Prints text into stdout.
|
Prints text into stdout.
|
||||||
|
|
||||||
</TwoColumn>
|
</TwoColumn>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
---
|
---
|
||||||
layout: ../../../layouts/ApiLayout.astro
|
layout: ../../../layouts/ApiLayout.astro
|
||||||
---
|
---
|
||||||
|
import TwoColumn from "../../../components/TwoColumn.astro"
|
||||||
import TwoColumn from "../../../components/TwoColumn.astro";
|
import Code from "../../../components/Code.astro"
|
||||||
import Code from "../../../components/Code.astro";
|
|
||||||
|
|
||||||
# `print`
|
# `print`
|
||||||
|
|
||||||
@ -11,11 +10,9 @@ Prints to stdout.
|
|||||||
|
|
||||||
## Signature
|
## Signature
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun print(String value) {}
|
fun print(String value) {}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
@ -23,8 +20,10 @@ Prints a single `String` into stdout. Doesn't return anything.
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
print("Hello world!")
|
print("Hello world!")
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@ layout: "../_wrapper.astro"
|
|||||||
title: Comments
|
title: Comments
|
||||||
order: 2
|
order: 2
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Comments
|
# Comments
|
||||||
|
|
||||||
THP supports single and multi line comments:
|
THP supports single and multi line comments:
|
||||||
|
|
||||||
|
|
||||||
## Single line
|
## Single line
|
||||||
|
|
||||||
Begin with double slash `//` and continue until the end of the line.
|
Begin with double slash `//` and continue until the end of the line.
|
||||||
@ -21,37 +21,33 @@ print("hello!")
|
|||||||
print("the result is {5 + 5}") // This will print 10
|
print("the result is {5 + 5}") // This will print 10
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Multi line
|
## Multi line
|
||||||
|
|
||||||
These begin with `/*` and end with `*/`. Everything in between is ignored.
|
These begin with `/*` and end with `*/`. Everything in between is ignored.
|
||||||
|
|
||||||
Multi line comments can be nested in THP.
|
Multi line comments can be nested in THP.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
/*
|
/*
|
||||||
This is a
|
This is a
|
||||||
multiline comment
|
multiline comment
|
||||||
*/
|
*/
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
/*
|
/*
|
||||||
Multiline comments
|
Multiline comments
|
||||||
can be /* nested */
|
can be /* nested */
|
||||||
*/
|
*/
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Documentation comments
|
## Documentation comments
|
||||||
|
|
||||||
Documentation comments use triple slashes `///`.
|
Documentation comments use triple slashes `///`.
|
||||||
These use [CommonMark Markdown](https://commonmark.org/) syntax.
|
These use [CommonMark Markdown](https://commonmark.org/) syntax.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
/// Transforms the format from A to B...
|
/// Transforms the format from A to B...
|
||||||
///
|
///
|
||||||
/// ## Errors
|
/// ## Errors
|
||||||
@ -61,5 +57,6 @@ These use [CommonMark Markdown](https://commonmark.org/) syntax.
|
|||||||
fun transform() {
|
fun transform() {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
@ -3,8 +3,7 @@ layout: "../_wrapper.astro"
|
|||||||
title: Datatypes
|
title: Datatypes
|
||||||
order: 4
|
order: 4
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Datatypes
|
# Datatypes
|
||||||
|
|
||||||
@ -35,48 +34,44 @@ Int not_octal = 032 // This is 32, not 26
|
|||||||
// TODO: Make it a compile error to have leading zeroes,
|
// TODO: Make it a compile error to have leading zeroes,
|
||||||
and force users to use `0o` for octal
|
and force users to use `0o` for octal
|
||||||
|
|
||||||
|
|
||||||
## Float
|
## Float
|
||||||
|
|
||||||
Same as php float
|
Same as php float
|
||||||
|
|
||||||
<Code
|
|
||||||
thpcode={`
|
<Code thpcode={`
|
||||||
Float pi = 3.141592
|
Float pi = 3.141592
|
||||||
Float light = 2.99e+8
|
Float light = 2.99e+8
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## String
|
## String
|
||||||
|
|
||||||
THP strings use **only** double quotes. Single quotes are
|
THP strings use **only** double quotes. Single quotes are
|
||||||
used elsewhere.
|
used elsewhere.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
String name = "Rose"
|
String name = "Rose"
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Strings have interpolation with `{}`.
|
Strings have interpolation with `{}`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
print("Hello, {name}") // Hello, Rose
|
print("Hello, {name}") // Hello, Rose
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Unlike PHP, THP strings are concatenated with `++`, not with `.`.
|
Unlike PHP, THP strings are concatenated with `++`, not with `.`.
|
||||||
This new operator implicitly converts any operator into a string.
|
This new operator implicitly converts any operator into a string.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val name = "John" ++ " " ++ "Doe"
|
val name = "John" ++ " " ++ "Doe"
|
||||||
val greeting = "My name is " ++ name ++ " and I'm " ++ 32 ++ " years old"
|
val greeting = "My name is " ++ name ++ " and I'm " ++ 32 ++ " years old"
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
The plus operator `+` is reserved for numbers.
|
The plus operator `+` is reserved for numbers.
|
||||||
|
|
||||||
|
|
||||||
## Bool
|
## Bool
|
||||||
|
|
||||||
THP booleans are `true` and `false`. They are case sensitive,
|
THP booleans are `true` and `false`. They are case sensitive,
|
||||||
@ -89,3 +84,4 @@ Bool is_false = false
|
|||||||
// This is a compile error
|
// This is a compile error
|
||||||
val invalid = TRUE
|
val invalid = TRUE
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
@ -3,9 +3,8 @@ layout: "../_wrapper.astro"
|
|||||||
title: Hello world
|
title: Hello world
|
||||||
order: 1
|
order: 1
|
||||||
---
|
---
|
||||||
|
|
||||||
import InteractiveCode from "@/components/InteractiveCode.astro";
|
import InteractiveCode from "@/components/InteractiveCode.astro";
|
||||||
import Code from "@/components/Code.astro";
|
import Code from "@/components/Code.astro"
|
||||||
|
|
||||||
# Hello, world!
|
# Hello, world!
|
||||||
|
|
||||||
@ -19,14 +18,13 @@ detailed later on.
|
|||||||
|
|
||||||
To write a hello world program write the following code in a file:
|
To write a hello world program write the following code in a file:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
print("Hello, world!")
|
print("Hello, world!")
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Then run `thp hello.thp` from your terminal.
|
Then run `thp hello.thp` from your terminal.
|
||||||
|
|
||||||
|
|
||||||
## Instruction separation
|
## Instruction separation
|
||||||
|
|
||||||
THP uses whitespace to determine when a statement is over. In short,
|
THP uses whitespace to determine when a statement is over. In short,
|
||||||
@ -38,12 +36,11 @@ echo("B");
|
|||||||
echo("C");
|
echo("C");
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
print("A")
|
print("A")
|
||||||
print("B")
|
print("B")
|
||||||
print("C")
|
print("C")
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
As a consequence of this, there can only be 1 statement per line.
|
As a consequence of this, there can only be 1 statement per line.
|
||||||
|
|
||||||
|
@ -3,11 +3,11 @@ layout: "../_wrapper.astro"
|
|||||||
title: Operators
|
title: Operators
|
||||||
order: 5
|
order: 5
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Operators
|
# Operators
|
||||||
|
|
||||||
|
|
||||||
Most of the PHP operators are present in THP.
|
Most of the PHP operators are present in THP.
|
||||||
|
|
||||||
## Numbers
|
## Numbers
|
||||||
@ -17,13 +17,13 @@ var number = 322
|
|||||||
|
|
||||||
number + 1
|
number + 1
|
||||||
number - 1
|
number - 1
|
||||||
number \* 1
|
number * 1
|
||||||
number / 1
|
number / 1
|
||||||
number % 2
|
number % 2
|
||||||
|
|
||||||
number += 1
|
number += 1
|
||||||
number -= 1
|
number -= 1
|
||||||
number \*= 1
|
number *= 1
|
||||||
number /= 1
|
number /= 1
|
||||||
number %= 2
|
number %= 2
|
||||||
`} />
|
`} />
|
||||||
@ -44,71 +44,62 @@ number++ // This is a compile error
|
|||||||
These operators will not do implicit type conversion. They can
|
These operators will not do implicit type conversion. They can
|
||||||
only be used with same datatypes.
|
only be used with same datatypes.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
v1 < v2
|
v1 < v2
|
||||||
v1 <= v2
|
v1 <= v2
|
||||||
v1 > v2
|
v1 > v2
|
||||||
v1 >= v2
|
v1 >= v2
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
There is only `==` and `!=`. They are equivalent to `===` and `!==`.
|
There is only `==` and `!=`. They are equivalent to `===` and `!==`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
v1 == v2
|
v1 == v2
|
||||||
v1 != v2
|
v1 != v2
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
### Bitwise
|
### Bitwise
|
||||||
|
|
||||||
TBD
|
TBD
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
number and 1
|
number and 1
|
||||||
number or 2
|
number or 2
|
||||||
number xor 1
|
number xor 1
|
||||||
number xand 1
|
number xand 1
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Strings
|
## Strings
|
||||||
|
|
||||||
Strings **do not use `.`** for concatenation. They use `++`.
|
Strings **do not use `.`** for concatenation. They use `++`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
"Hello " ++ "world."
|
"Hello " ++ "world."
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
This new operator **implicitly converts** types to string
|
This new operator **implicitly converts** types to string
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
"Hello " ++ 322 // 322 will be converted to "322"
|
"Hello " ++ 322 // 322 will be converted to "322"
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Boolean
|
## Boolean
|
||||||
|
|
||||||
These operators work **only with booleans**, they do not perform
|
These operators work **only with booleans**, they do not perform
|
||||||
type coercion.
|
type coercion.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
c1 && c2
|
c1 && c2
|
||||||
c1 || c2
|
c1 || c2
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Ternary
|
## Ternary
|
||||||
|
|
||||||
There is no ternary operator. See [Conditionals](/learn/flow-control/conditionals) for alternatives.
|
There is no ternary operator. See [Conditionals](/learn/flow-control/conditionals) for alternatives.
|
||||||
|
|
||||||
|
|
||||||
## Null
|
## Null
|
||||||
|
|
||||||
These are detailed in their section: [Nullable types](/learn/error-handling/null)
|
These are detailed in their section: [Nullable types](/learn/error-handling/null)
|
||||||
@ -124,3 +115,6 @@ if person? {
|
|||||||
person.name
|
person.name
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,8 +3,7 @@ layout: "../_wrapper.astro"
|
|||||||
title: Variables
|
title: Variables
|
||||||
order: 3
|
order: 3
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
|
|
||||||
@ -25,59 +24,47 @@ As a regex: `[a-z_][a-zA-Z0-9_]*`
|
|||||||
|
|
||||||
Defined with `val`, followed by a variable name and a value.
|
Defined with `val`, followed by a variable name and a value.
|
||||||
|
|
||||||
<Code
|
<Code level={2} thpcode={`
|
||||||
level={2}
|
|
||||||
thpcode={`
|
|
||||||
val surname = "Doe"
|
val surname = "Doe"
|
||||||
val year_of_birth = 1984
|
val year_of_birth = 1984
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
It's a compile error to attempt to modify it
|
It's a compile error to attempt to modify it
|
||||||
|
|
||||||
<Code
|
<Code level={2} thpcode={`
|
||||||
level={2}
|
|
||||||
thpcode={`
|
|
||||||
val surname = "Doe"
|
val surname = "Doe"
|
||||||
surname = "Dane" // Error
|
surname = "Dane" // Error
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
### Datatype annotation
|
### Datatype annotation
|
||||||
|
|
||||||
Written after the `val` keyword but before the variable name.
|
Written after the `val` keyword but before the variable name.
|
||||||
|
|
||||||
<Code
|
<Code level={2} thpcode={`
|
||||||
level={2}
|
|
||||||
thpcode={`
|
|
||||||
val String surname = "Doe"
|
val String surname = "Doe"
|
||||||
val Int year_of_birth = 1984
|
val Int year_of_birth = 1984
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
When annotating an immutable variable the `val` keyword is optional
|
When annotating an immutable variable the `val` keyword is optional
|
||||||
|
|
||||||
<Code
|
<Code level={2} thpcode={`
|
||||||
level={2}
|
|
||||||
thpcode={`
|
|
||||||
// Equivalent to the previous code
|
// Equivalent to the previous code
|
||||||
String surname = "Doe"
|
String surname = "Doe"
|
||||||
Int year_of_birth = 1984
|
Int year_of_birth = 1984
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
This means that if a variable only has a datatype, it is immutable.
|
This means that if a variable only has a datatype, it is immutable.
|
||||||
|
|
||||||
It is a compile error to declare a variable of a datatype,
|
It is a compile error to declare a variable of a datatype,
|
||||||
but use another.
|
but use another.
|
||||||
|
|
||||||
<Code
|
<Code level={2} thpcode={`
|
||||||
level={2}
|
|
||||||
thpcode={`
|
|
||||||
// Declare the variable as a String, but use a Float as its value
|
// Declare the variable as a String, but use a Float as its value
|
||||||
String capital = 123.456
|
String capital = 123.456
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Mutable variables
|
## Mutable variables
|
||||||
|
|
||||||
@ -94,21 +81,18 @@ age = 33
|
|||||||
|
|
||||||
Written after the `var` keywords but before the variable name.
|
Written after the `var` keywords but before the variable name.
|
||||||
|
|
||||||
<Code
|
<Code level={2} thpcode={`
|
||||||
level={2}
|
|
||||||
thpcode={`
|
|
||||||
var String name = "John"
|
var String name = "John"
|
||||||
var Int age = 32
|
var Int age = 32
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
When annotating a mutable variable the keyword `var` is still **required**.
|
When annotating a mutable variable the keyword `var` is still **required**.
|
||||||
|
|
||||||
<Code
|
<Code level={2} thpcode={`
|
||||||
level={2}
|
|
||||||
thpcode={`
|
|
||||||
// Equivalent to the previous code
|
// Equivalent to the previous code
|
||||||
var String name = "John"
|
var String name = "John"
|
||||||
var Int age = 32
|
var Int age = 32
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Arrays
|
title: Arrays
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Arrays
|
# Arrays
|
||||||
|
|
||||||
@ -28,15 +27,19 @@ numbers[3] = 5
|
|||||||
print(numbers[3]) // 5
|
print(numbers[3]) // 5
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Type signature
|
## Type signature
|
||||||
|
|
||||||
<Code
|
|
||||||
thpcode={`
|
<Code thpcode={`
|
||||||
Array[String]
|
Array[String]
|
||||||
Array[Int]
|
Array[Int]
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
The Array signature **requires** the word `Array`.
|
The Array signature __requires__ the word `Array`.
|
||||||
There is no `Int[]` or `[Int]` signature, since that would cause
|
There is no `Int[]` or `[Int]` signature, since that would cause
|
||||||
problems with the language's grammar.
|
problems with the language's grammar.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Enums
|
title: Enums
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Enums
|
# Enums
|
||||||
|
|
||||||
@ -34,8 +33,7 @@ val suit = Suit::Hearts
|
|||||||
Backed enums can have a scalar for each case. The scalar values can only be
|
Backed enums can have a scalar for each case. The scalar values can only be
|
||||||
`String` or `Int`.
|
`String` or `Int`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
enum Suit(String)
|
enum Suit(String)
|
||||||
{
|
{
|
||||||
Hearts = "H",
|
Hearts = "H",
|
||||||
@ -43,7 +41,8 @@ enum Suit(String)
|
|||||||
Clubs = "C",
|
Clubs = "C",
|
||||||
Spades = "S",
|
Spades = "S",
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
All cases must explicitly define their value, there is no automatic generation.
|
All cases must explicitly define their value, there is no automatic generation.
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Maps
|
title: Maps
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Maps
|
# Maps
|
||||||
|
|
||||||
@ -42,48 +41,41 @@ age: 27,
|
|||||||
|
|
||||||
To access the fields of a map we use square braces `[]`.
|
To access the fields of a map we use square braces `[]`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
mary_jane["age"] += 1
|
mary_jane["age"] += 1
|
||||||
print(mary_jane["name"]) // Mary
|
print(mary_jane["name"]) // Mary
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Or dot access `.` if the field's name is a valid identifier.
|
Or dot access `.` if the field's name is a valid identifier.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
mary_jane.age += 1
|
mary_jane.age += 1
|
||||||
print(mary_jane.name)
|
print(mary_jane.name)
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Anonymous maps
|
## Anonymous maps
|
||||||
|
|
||||||
An anonymous map allows us to store and retrieve any key of any datatype.
|
An anonymous map allows us to store and retrieve any key of any datatype.
|
||||||
They are declared as `Map`.
|
They are declared as `Map`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val car = Map {
|
val car = Map {
|
||||||
brand: "Toyota",
|
brand: "Toyota",
|
||||||
model: "Corolla",
|
model: "Corolla",
|
||||||
year: 2012,
|
year: 2012,
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Anonymous maps can also can have their type omitted.
|
Anonymous maps can also can have their type omitted.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
var car = .{
|
var car = .{
|
||||||
brand: "Toyota",
|
brand: "Toyota",
|
||||||
model: "Corolla",
|
model: "Corolla",
|
||||||
year: 2012,
|
year: 2012,
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
If the compiler encounters a map without a type (that is, `.{}`)
|
If the compiler encounters a map without a type (that is, `.{}`)
|
||||||
and doesn't expect a specific type, it will assume it is an
|
and doesn't expect a specific type, it will assume it is an
|
||||||
@ -91,14 +83,12 @@ anonymous map.
|
|||||||
|
|
||||||
We can freely assign fields to an anonymous map:
|
We can freely assign fields to an anonymous map:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// Modify an existing field
|
// Modify an existing field
|
||||||
car["year"] = 2015
|
car["year"] = 2015
|
||||||
// Create a new field
|
// Create a new field
|
||||||
car["status"] = "used"
|
car["status"] = "used"
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
However, if we try to access a field of an anonymous map we'll get
|
However, if we try to access a field of an anonymous map we'll get
|
||||||
a nullable type, and we must annotate it.
|
a nullable type, and we must annotate it.
|
||||||
@ -114,11 +104,9 @@ var car_status = car["status"]
|
|||||||
Instead, we can use the `get` function of the map, which expects a
|
Instead, we can use the `get` function of the map, which expects a
|
||||||
datatype and returns that type as nullable
|
datatype and returns that type as nullable
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val car_status = car.get[String]("status")
|
val car_status = car.get[String]("status")
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Both ways to get a value will check that the key exists in the map,
|
Both ways to get a value will check that the key exists in the map,
|
||||||
and that it has the correct datatype. If either the key doesn't exist
|
and that it has the correct datatype. If either the key doesn't exist
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Tuples
|
title: Tuples
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Tuples
|
# Tuples
|
||||||
|
|
||||||
@ -17,10 +16,13 @@ val person = #("John", "Doe", 32)
|
|||||||
val #(name, surname, age) = person
|
val #(name, surname, age) = person
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Signature
|
## Signature
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
#(String, String, Int)
|
#(String, String, Int)
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Tagged unions
|
title: Tagged unions
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Tagged unions
|
# Tagged unions
|
||||||
|
|
||||||
@ -24,8 +23,7 @@ val rectangle1 = Shape::Rectangle(5, 15)
|
|||||||
|
|
||||||
## Pattern matching
|
## Pattern matching
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
match shape_1
|
match shape_1
|
||||||
case ::Square(side)
|
case ::Square(side)
|
||||||
{
|
{
|
||||||
@ -35,8 +33,7 @@ case ::Rectangle(length, height)
|
|||||||
{
|
{
|
||||||
print("Area of the rectangle: {length * height}")
|
print("Area of the rectangle: {length * height}")
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Internal representation
|
## Internal representation
|
||||||
|
|
||||||
@ -45,6 +42,7 @@ When compiled down to PHP, tagged unions are a combination of an enum and an arr
|
|||||||
THP creates an enum of the same name and with the same cases, and the values
|
THP creates an enum of the same name and with the same cases, and the values
|
||||||
are contained as part of an array.
|
are contained as part of an array.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// The first snippet is compiled to:
|
// The first snippet is compiled to:
|
||||||
enum Shape
|
enum Shape
|
||||||
@ -58,3 +56,4 @@ $dot = [Shape::Dot];
|
|||||||
$square1 = [Shape::Square, 10];
|
$square1 = [Shape::Square, 10];
|
||||||
$rectangle1 = [Shape::Rectangle, 5, 15]
|
$rectangle1 = [Shape::Rectangle, 5, 15]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Blocks
|
title: Blocks
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Blocks
|
# Blocks
|
||||||
|
|
||||||
@ -18,8 +17,8 @@ val result = {
|
|||||||
val temp = 161
|
val temp = 161
|
||||||
|
|
||||||
temp * 2 // This will be assigned to \`result\`
|
temp * 2 // This will be assigned to \`result\`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print(result) // 322
|
print(result) // 322
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Conditionals
|
title: Conditionals
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Conditionals
|
# Conditionals
|
||||||
|
|
||||||
@ -13,6 +12,7 @@ import Code from "@/components/Code.astro";
|
|||||||
- Paretheses for the condition are not required.
|
- Paretheses for the condition are not required.
|
||||||
- There's no ternary operator
|
- There's no ternary operator
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
if condition {
|
if condition {
|
||||||
// code
|
// code
|
||||||
@ -24,20 +24,22 @@ else {
|
|||||||
// even more code
|
// even more code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val result = if condition { value1 } else { value2 }
|
val result = if condition { value1 } else { value2 }
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Check for datatypes
|
## Check for datatypes
|
||||||
|
|
||||||
TBD
|
TBD
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
if variable is Datatype {
|
if variable is Datatype {
|
||||||
// code using variable
|
// code using variable
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## If variable is of enum
|
## If variable is of enum
|
||||||
|
|
||||||
@ -54,3 +56,6 @@ if Some(user_id) = user_id && user_id > 0 {
|
|||||||
print("user_id is greater than 0: {user_id}")
|
print("user_id is greater than 0: {user_id}")
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Loops
|
title: Loops
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Loops
|
# Loops
|
||||||
|
|
||||||
@ -35,6 +34,7 @@ print("{value}")
|
|||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
### Loop over keys and values
|
### Loop over keys and values
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
@ -57,6 +57,7 @@ print("{key} => {value}")
|
|||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## While loop
|
## While loop
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
@ -69,19 +70,20 @@ index += 1
|
|||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Labelled loops
|
## Labelled loops
|
||||||
|
|
||||||
TBD
|
TBD
|
||||||
|
|
||||||
You can give labels to loops, allowing you to `break` and `continue` in nested loops.
|
You can give labels to loops, allowing you to `break` and `continue` in nested loops.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
:top for i in values_1 {
|
:top for i in values_1 {
|
||||||
for j in values_2 {
|
for j in values_2 {
|
||||||
// ...
|
// ...
|
||||||
break :top
|
break :top
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Match
|
title: Match
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Match
|
# Match
|
||||||
|
|
||||||
@ -14,6 +13,7 @@ Braces are **required**.
|
|||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
val user_id = POST::get("user_id")
|
val user_id = POST::get("user_id")
|
||||||
|
|
||||||
|
|
||||||
match user_id
|
match user_id
|
||||||
case Some(id) { print("user_id exists: {id}") }
|
case Some(id) { print("user_id exists: {id}") }
|
||||||
case None { print("user_id doesn't exist") }
|
case None { print("user_id doesn't exist") }
|
||||||
@ -26,6 +26,7 @@ case None {
|
|||||||
print("user_id doesn't exist")
|
print("user_id doesn't exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
match user_id
|
match user_id
|
||||||
case Some(id) if id > 0 {
|
case Some(id) if id > 0 {
|
||||||
print("user_id exists: {id}")
|
print("user_id exists: {id}")
|
||||||
@ -42,14 +43,15 @@ else {
|
|||||||
print("hello dear")
|
print("hello dear")
|
||||||
}
|
}
|
||||||
|
|
||||||
match customer*id
|
match customer_id
|
||||||
| 1 | 2 | 3 {
|
| 1 | 2 | 3 {
|
||||||
print("ehhhh")
|
print("ehhhh")
|
||||||
}
|
}
|
||||||
| 4 | 5 {
|
| 4 | 5 {
|
||||||
print("ohhh")
|
print("ohhh")
|
||||||
}
|
}
|
||||||
| * {
|
| _ {
|
||||||
print("???")
|
print("???")
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Pipes
|
title: Pipes
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Pipes
|
# Pipes
|
||||||
|
|
||||||
@ -12,30 +11,24 @@ expression as input to another.
|
|||||||
|
|
||||||
For example, instead of writing:
|
For example, instead of writing:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val result = third_function(second_function(first_function(my_value)))
|
val result = third_function(second_function(first_function(my_value)))
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
You can use pipes:
|
You can use pipes:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val result = my_value
|
val result = my_value
|
||||||
|> first_function
|
|> first_function
|
||||||
|> second_function
|
|> second_function
|
||||||
|> third_function
|
|> third_function
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Or use it to group expressions:
|
Or use it to group expressions:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
print <| (2 + 3 * 4) / 7
|
print <| (2 + 3 * 4) / 7
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
TBD: How to handle piping to functions with more than 1 param
|
TBD: How to handle piping to functions with more than 1 param
|
||||||
|
|
||||||
@ -47,7 +40,7 @@ fun add_one(x: Int) -> Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun times_two(x: Int) -> Int {
|
fun times_two(x: Int) -> Int {
|
||||||
x \* 2
|
x * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Int) -> (Int)
|
// (Int) -> (Int)
|
||||||
@ -55,3 +48,5 @@ val plus_one_times_2 = add_one >> times_two
|
|||||||
|
|
||||||
print(plus_one_times_2(5)) // 12
|
print(plus_one_times_2(5)) // 12
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Declaration
|
title: Declaration
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Declaration
|
# Declaration
|
||||||
|
|
||||||
@ -11,6 +10,7 @@ Functions in THP have a different syntax than PHP.
|
|||||||
|
|
||||||
Function names **must** begin with a lowercase letter.
|
Function names **must** begin with a lowercase letter.
|
||||||
|
|
||||||
|
|
||||||
## Minimal function
|
## Minimal function
|
||||||
|
|
||||||
The following code shows a function without parameters
|
The following code shows a function without parameters
|
||||||
@ -27,12 +27,14 @@ say_hello()
|
|||||||
|
|
||||||
Functions are called the same way as in PHP.
|
Functions are called the same way as in PHP.
|
||||||
|
|
||||||
|
|
||||||
## With return type
|
## With return type
|
||||||
|
|
||||||
If your function return any value, annotating the return
|
If your function return any value, annotating the return
|
||||||
type is **mandatory**, and it's done by placing an
|
type is __mandatory__, and it's done by placing an
|
||||||
arrow `->` followed by the return datatype:
|
arrow `->` followed by the return datatype:
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
fun get_random_number() -> Int
|
fun get_random_number() -> Int
|
||||||
{
|
{
|
||||||
@ -42,38 +44,38 @@ fun get_random_number() -> Int
|
|||||||
val number = get_random_number()
|
val number = get_random_number()
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
It's an error to return from a function that doesn't declare
|
It's an error to return from a function that doesn't declare
|
||||||
a return type:
|
a return type:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun get_random_number()
|
fun get_random_number()
|
||||||
{
|
{
|
||||||
// Error: the function does not define a return type
|
// Error: the function does not define a return type
|
||||||
return Random::get(0, 35_222)
|
return Random::get(0, 35_222)
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
You can omit the `return` keyword if it's placed on the last
|
You can omit the `return` keyword if it's placed on the last
|
||||||
expression on the function:
|
expression on the function:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun get_random_number() -> Int
|
fun get_random_number() -> Int
|
||||||
{
|
{
|
||||||
// The last expression of a function is
|
// The last expression of a function is
|
||||||
// automatically returned
|
// automatically returned
|
||||||
Random::get(0, 35_222)
|
Random::get(0, 35_222)
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Function parameters
|
## Function parameters
|
||||||
|
|
||||||
Parameters are declared like C-style languages:
|
Parameters are declared like C-style languages:
|
||||||
`Type name`, separated by commas.
|
`Type name`, separated by commas.
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
fun add(Int a, Int b) -> Int
|
fun add(Int a, Int b) -> Int
|
||||||
{
|
{
|
||||||
@ -87,6 +89,8 @@ THP has different semantics on parameters concerning
|
|||||||
pass by value, pass by reference and copy on write.
|
pass by value, pass by reference and copy on write.
|
||||||
This is detailed in another chapter.
|
This is detailed in another chapter.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Generic types
|
## Generic types
|
||||||
|
|
||||||
Functions can declare generic types by using the syntax
|
Functions can declare generic types by using the syntax
|
||||||
@ -95,6 +99,7 @@ Functions can declare generic types by using the syntax
|
|||||||
The following example declares a generic `T` and uses it
|
The following example declares a generic `T` and uses it
|
||||||
in the parameters and return type:
|
in the parameters and return type:
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
fun get_first_item[T](Array[T] array) -> T
|
fun get_first_item[T](Array[T] array) -> T
|
||||||
{
|
{
|
||||||
@ -107,24 +112,27 @@ val first = get_first_item[Int](numbers)
|
|||||||
val first = get_first_item(numbers)
|
val first = get_first_item(numbers)
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Default arguments
|
## Default arguments
|
||||||
|
|
||||||
TBD
|
TBD
|
||||||
|
|
||||||
|
|
||||||
## Variadic arguments
|
## Variadic arguments
|
||||||
|
|
||||||
TBD
|
TBD
|
||||||
|
|
||||||
|
|
||||||
## Signature
|
## Signature
|
||||||
|
|
||||||
<Code
|
|
||||||
thpcode={`
|
<Code thpcode={`
|
||||||
() -> ()
|
() -> ()
|
||||||
() -> (Int)
|
() -> (Int)
|
||||||
(Int, Int) -> (Int)
|
(Int, Int) -> (Int)
|
||||||
[T](Array[T]) -> (T)
|
[T](Array[T]) -> (T)
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Named arguments
|
## Named arguments
|
||||||
|
|
||||||
@ -157,3 +165,8 @@ fun greet(
|
|||||||
|
|
||||||
greet("John", from: "LA")
|
greet("John", from: "LA")
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Higher Order Functions
|
title: Higher Order Functions
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Higher Order functions
|
# Higher Order functions
|
||||||
|
|
||||||
|
|
||||||
## Function as parameters
|
## Function as parameters
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
@ -28,6 +28,10 @@ fun generate_generator() -> () -> Int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val generator = generate_generator() // A function
|
val generator = generate_generator() // A function
|
||||||
val value = generate_generator()() // An Int
|
val value = generate_generator()() // An Int
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,45 +2,40 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Lambdas
|
title: Lambdas
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Lambdas / Anonymous functions
|
# Lambdas / Anonymous functions
|
||||||
|
|
||||||
|
|
||||||
## Anonymous function
|
## Anonymous function
|
||||||
|
|
||||||
An anonymous function is declared with `fn`.
|
An anonymous function is declared with `fn`.
|
||||||
Other than not having a name, it can declare parameter
|
Other than not having a name, it can declare parameter
|
||||||
and return types.
|
and return types.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fn(Int x, Int y) -> Int {
|
fn(Int x, Int y) -> Int {
|
||||||
x + y
|
x + y
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Anonymous function can omit declaring those types as well:
|
Anonymous function can omit declaring those types as well:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
numbers.map(fun(x) {
|
numbers.map(fun(x) {
|
||||||
x * 2
|
x * 2
|
||||||
})
|
})
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Anonymous function short form
|
## Anonymous function short form
|
||||||
|
|
||||||
If you need an anonymous function that returns a single
|
If you need an anonymous function that returns a single
|
||||||
expression you can write it like this:
|
expression you can write it like this:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun(x, y) = x + y
|
fun(x, y) = x + y
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
It uses an equal `=` instead of an arrow. It can contain
|
It uses an equal `=` instead of an arrow. It can contain
|
||||||
only a single expression.
|
only a single expression.
|
||||||
@ -48,18 +43,20 @@ only a single expression.
|
|||||||
This is common when declaring functions that immediately
|
This is common when declaring functions that immediately
|
||||||
return a map.
|
return a map.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fn(value) = .{
|
fn(value) = .{
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Closure types
|
## Closure types
|
||||||
|
|
||||||
By default closures **always** capture variables as **references**.
|
By default closures **always** capture variables as **references**.
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
var x = 20
|
var x = 20
|
||||||
|
|
||||||
@ -73,15 +70,14 @@ x = 30
|
|||||||
f() // 30
|
f() // 30
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
You can force a closure to capture variables by value.
|
You can force a closure to capture variables by value.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun(parameters) clone(variables) {
|
fun(parameters) clone(variables) {
|
||||||
// code
|
// code
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
var x = 20
|
var x = 20
|
||||||
@ -96,6 +92,7 @@ x = 30
|
|||||||
f() // 20
|
f() // 20
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Lambdas
|
## Lambdas
|
||||||
|
|
||||||
Lambdas are a short form of anonymous functions. They are declared with `#{}`.
|
Lambdas are a short form of anonymous functions. They are declared with `#{}`.
|
||||||
@ -112,6 +109,9 @@ numbers.map() #{
|
|||||||
// the above lambda is equivalent to:
|
// the above lambda is equivalent to:
|
||||||
|
|
||||||
numbers.map(fun(param1) {
|
numbers.map(fun(param1) {
|
||||||
$1 \* 2
|
$1 * 2
|
||||||
})
|
})
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,20 +2,18 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Function parameters
|
title: Function parameters
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Function parameters
|
# Function parameters
|
||||||
|
|
||||||
|
|
||||||
## Immutable reference
|
## Immutable reference
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun add_25(Array[Int] numbers) {
|
fun add_25(Array[Int] numbers) {
|
||||||
numbers.push(25) // Error: \`numbers\` is immutable
|
numbers.push(25) // Error: \`numbers\` is immutable
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
When using a regular type as a parameter, only it's immutable
|
When using a regular type as a parameter, only it's immutable
|
||||||
properties can be used
|
properties can be used
|
||||||
@ -25,21 +23,19 @@ fun count(Array[Int] numbers) -> Int {
|
|||||||
val items_count = numbers.size() // Ok, \`size\` is pure
|
val items_count = numbers.size() // Ok, \`size\` is pure
|
||||||
|
|
||||||
items_count
|
items_count
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
To use immutable properties you must use a mutable reference.
|
To use immutable properties you must use a mutable reference.
|
||||||
|
|
||||||
|
|
||||||
## Mutable reference
|
## Mutable reference
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun push_25(mut Array[Int] numbers) {
|
fun push_25(mut Array[Int] numbers) {
|
||||||
numbers.push(25) // Ok, will also mutate the original array
|
numbers.push(25) // Ok, will also mutate the original array
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Placing a `mut` before the type makes the parameter a mutable
|
Placing a `mut` before the type makes the parameter a mutable
|
||||||
reference. Mutable methods can be used, and the original
|
reference. Mutable methods can be used, and the original
|
||||||
@ -56,21 +52,22 @@ push_25(mut numbers) // Pass \`numbers\` as reference.
|
|||||||
print(numbers(4)) // \`Some(25)\`
|
print(numbers(4)) // \`Some(25)\`
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Clone
|
## Clone
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun add_25(clone Array[Int] numbers) {
|
fun add_25(clone Array[Int] numbers) {
|
||||||
numbers.push(25) // Ok, the original array is unaffected
|
numbers.push(25) // Ok, the original array is unaffected
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Using the `clone` keyword before the type creates a mutable copy
|
Using the `clone` keyword before the type creates a mutable copy
|
||||||
of the parameter (CoW). The original data will **not** be mutated.
|
of the parameter (CoW). The original data will **not** be mutated.
|
||||||
|
|
||||||
The caller must also use `clone` when calling the function.
|
The caller must also use `clone` when calling the function.
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
val numbers = Array(1, 2, 3, 4)
|
val numbers = Array(1, 2, 3, 4)
|
||||||
|
|
||||||
@ -78,3 +75,5 @@ add_25(clone numbers) // Pass \`numbers\` as clone.
|
|||||||
|
|
||||||
print(numbers(4)) // None
|
print(numbers(4)) // None
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Nullable types
|
title: Nullable types
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Nullable types
|
# Nullable types
|
||||||
|
|
||||||
@ -14,11 +13,9 @@ by the question mark `?` character.
|
|||||||
For instance, a POST request may have a `username` parameter,
|
For instance, a POST request may have a `username` parameter,
|
||||||
or it may not. This can be represented with an `?String`.
|
or it may not. This can be represented with an `?String`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
?String new_username = POST::get("username")
|
?String new_username = POST::get("username")
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
When we have a `?Type` we cannot use it directly. We must first
|
When we have a `?Type` we cannot use it directly. We must first
|
||||||
check if the value is null, and then use it.
|
check if the value is null, and then use it.
|
||||||
@ -74,6 +71,7 @@ val name = person?.name
|
|||||||
- If `person` is null, `person?.name` will return `null`
|
- If `person` is null, `person?.name` will return `null`
|
||||||
- If `person` is not null, `person?.name` will return `name`
|
- If `person` is not null, `person?.name` will return `name`
|
||||||
|
|
||||||
|
|
||||||
## Null unboxing
|
## Null unboxing
|
||||||
|
|
||||||
The `!!` operator transforms a `?Type` into `Type`.
|
The `!!` operator transforms a `?Type` into `Type`.
|
||||||
@ -92,17 +90,16 @@ String s = lastname!!
|
|||||||
|
|
||||||
You can use it to chain access:
|
You can use it to chain access:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val children_lastname = person!!.child!!.lastname
|
val children_lastname = person!!.child!!.lastname
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
However, if at runtime you use `!!` on a null value,
|
However, if at runtime you use `!!` on a null value,
|
||||||
the null value will be returned and your program will
|
the null value will be returned and your program will
|
||||||
blow up later. So make sure to use this operator
|
blow up later. So make sure to use this operator
|
||||||
only when you are sure a value cannot be null.
|
only when you are sure a value cannot be null.
|
||||||
|
|
||||||
|
|
||||||
## Elvis operator
|
## Elvis operator
|
||||||
|
|
||||||
The Elvis operator `??` is used to give a default value in case a `null` is found.
|
The Elvis operator `??` is used to give a default value in case a `null` is found.
|
||||||
@ -117,12 +114,13 @@ val test_score = get_score() ?? 0
|
|||||||
For the above code:
|
For the above code:
|
||||||
|
|
||||||
- If `get_score()` is not null, `??` will return `get_score()`
|
- If `get_score()` is not null, `??` will return `get_score()`
|
||||||
- If `get_score()` _is_ null, `??` will return `0`
|
- If `get_score()` *is* null, `??` will return `0`
|
||||||
|
|
||||||
You can use the Elvis operator to return early
|
You can use the Elvis operator to return early
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val username = get_username() ?? return
|
val username = get_username() ?? return
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Try/Exceptions
|
title: Try/Exceptions
|
||||||
---
|
---
|
||||||
|
|
||||||
import InteractiveCode from "@/components/InteractiveCode.astro";
|
import InteractiveCode from "@/components/InteractiveCode.astro";
|
||||||
import Code from "@/components/Code.astro";
|
import Code from "@/components/Code.astro"
|
||||||
|
|
||||||
# Try/exceptions
|
# Try/exceptions
|
||||||
|
|
||||||
@ -28,7 +27,6 @@ fun invert(Int number) -> DivisionByZero!Int
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 1 / number
|
return 1 / number
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
@ -38,6 +36,7 @@ or an `Int`.
|
|||||||
|
|
||||||
There is no `throw` keyword, errors are just returned.
|
There is no `throw` keyword, errors are just returned.
|
||||||
|
|
||||||
|
|
||||||
### Multiple error returns
|
### Multiple error returns
|
||||||
|
|
||||||
TODO: properly define syntax, how this interacts with type unions.
|
TODO: properly define syntax, how this interacts with type unions.
|
||||||
@ -45,12 +44,12 @@ TODO: properly define syntax, how this interacts with type unions.
|
|||||||
Multiple errors are chained with `!`. The last one is always
|
Multiple errors are chained with `!`. The last one is always
|
||||||
the success value.
|
the success value.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun sample() -> Error1!Error2!Error3!Int
|
fun sample() -> Error1!Error2!Error3!Int
|
||||||
{ /* ... */}
|
{ /* ... */}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
@ -111,11 +110,10 @@ Use a naked `try` when you want to rethrow an error, if there is any.
|
|||||||
unset " = = ="
|
unset " = = ="
|
||||||
set " dangerous()" "Int 50"
|
set " dangerous()" "Int 50"
|
||||||
}
|
}
|
||||||
|
|
||||||
`}
|
`}
|
||||||
|
|
||||||
></InteractiveCode>
|
></InteractiveCode>
|
||||||
|
|
||||||
|
|
||||||
In the previous example:
|
In the previous example:
|
||||||
|
|
||||||
- If `dangerous()` returns an `Exception`, this exception
|
- If `dangerous()` returns an `Exception`, this exception
|
||||||
@ -123,6 +121,7 @@ In the previous example:
|
|||||||
- If `dangerous()` succeedes, its value is assigned
|
- If `dangerous()` succeedes, its value is assigned
|
||||||
to `result`, and the function continues executing.
|
to `result`, and the function continues executing.
|
||||||
|
|
||||||
|
|
||||||
### Try/return
|
### Try/return
|
||||||
|
|
||||||
Try/return will return a new value if an expression fails,
|
Try/return will return a new value if an expression fails,
|
||||||
@ -137,7 +136,6 @@ fun run() -> Int
|
|||||||
val result = try dangerous() return 0
|
val result = try dangerous() return 0
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
@ -148,6 +146,7 @@ In the previous example:
|
|||||||
- If `dangerous()` succeedes, its value will be assigned to `result`,
|
- If `dangerous()` succeedes, its value will be assigned to `result`,
|
||||||
and the function continues executing.
|
and the function continues executing.
|
||||||
|
|
||||||
|
|
||||||
### Try/else
|
### Try/else
|
||||||
|
|
||||||
Try/else will assign a new value if an expression fails.
|
Try/else will assign a new value if an expression fails.
|
||||||
@ -211,9 +210,7 @@ Try/else will assign a new value if an expression fails.
|
|||||||
}
|
}
|
||||||
step{line 0}
|
step{line 0}
|
||||||
`}
|
`}
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
</InteractiveCode>
|
</InteractiveCode>
|
||||||
|
|
||||||
- If `possible_value` is an error, the value `666` is used.
|
- If `possible_value` is an error, the value `666` is used.
|
||||||
@ -221,10 +218,12 @@ Try/else will assign a new value if an expression fails.
|
|||||||
|
|
||||||
Either way, the function will continue executing.
|
Either way, the function will continue executing.
|
||||||
|
|
||||||
|
|
||||||
### Try/catch
|
### Try/catch
|
||||||
|
|
||||||
Try/catch allows the error to be manually used & handled.
|
Try/catch allows the error to be manually used & handled.
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
fun run()
|
fun run()
|
||||||
{
|
{
|
||||||
@ -240,14 +239,12 @@ fun run()
|
|||||||
// Return a new value to be assigned to \`result\`
|
// Return a new value to be assigned to \`result\`
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
A try/catch may have many `catch` clauses:
|
A try/catch may have many `catch` clauses:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
try dangerous()
|
try dangerous()
|
||||||
catch Exception1 e
|
catch Exception1 e
|
||||||
{...}
|
{...}
|
||||||
@ -255,5 +252,6 @@ catch Exception2 e
|
|||||||
{...}
|
{...}
|
||||||
catch Exception3 e
|
catch Exception3 e
|
||||||
{...}
|
{...}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Abstract
|
title: Abstract
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Abstract
|
# Abstract
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Anonymous classes
|
title: Anonymous classes
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Anonymous classes
|
# Anonymous classes
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
class Logger {
|
class Logger {
|
||||||
pub fun log(String msg) {
|
pub fun log(String msg) {
|
||||||
@ -25,12 +25,10 @@ print(msg)
|
|||||||
})
|
})
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
setLogger(class(Int param1) -> SomeClass(param1), SomeInterface {
|
setLogger(class(Int param1) -> SomeClass(param1), SomeInterface {
|
||||||
pub fun method() {
|
pub fun method() {
|
||||||
// code
|
// code
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Constructor/Destructor
|
title: Constructor/Destructor
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Constructor/Destructor
|
# Constructor/Destructor
|
||||||
|
|
||||||
|
|
||||||
## Constructor
|
## Constructor
|
||||||
|
|
||||||
The constructor syntax in THP is inspired by Kotlin.
|
The constructor syntax in THP is inspired by Kotlin.
|
||||||
@ -37,6 +37,7 @@ If you want to declare a constructor as protected
|
|||||||
or private you need to add the `constructor`
|
or private you need to add the `constructor`
|
||||||
keyword, after the visibility modifier:
|
keyword, after the visibility modifier:
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
// Cow has a protected constructor
|
// Cow has a protected constructor
|
||||||
class Cow
|
class Cow
|
||||||
@ -47,6 +48,7 @@ class Bat
|
|||||||
private constructor(Int height)
|
private constructor(Int height)
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
### Init block
|
### Init block
|
||||||
|
|
||||||
The `init` block allow us to run code during the
|
The `init` block allow us to run code during the
|
||||||
@ -60,7 +62,6 @@ class Dog(String name) {
|
|||||||
init {
|
init {
|
||||||
print("Dog created: {name}")
|
print("Dog created: {name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
@ -79,13 +80,15 @@ class Dog {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Constructor promotion
|
### Constructor promotion
|
||||||
|
|
||||||
Constructor parameters can serve as class properties.
|
Constructor parameters can serve as class properties.
|
||||||
This is done by adding a modifier and `var`/`val`.
|
This is done by adding a modifier and `var`/`val`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class Parrot(
|
class Parrot(
|
||||||
// A public property
|
// A public property
|
||||||
pub val String name,
|
pub val String name,
|
||||||
@ -94,14 +97,15 @@ class Parrot(
|
|||||||
// A private property
|
// A private property
|
||||||
var String last_name,
|
var String last_name,
|
||||||
)
|
)
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
By using this syntax you are declaring properties and assigning them
|
By using this syntax you are declaring properties and assigning them
|
||||||
at the same time.
|
at the same time.
|
||||||
|
|
||||||
The contructor parameters can also have default values.
|
The contructor parameters can also have default values.
|
||||||
|
|
||||||
|
|
||||||
### Derived properties
|
### Derived properties
|
||||||
|
|
||||||
You can declare properties whose values depend on values
|
You can declare properties whose values depend on values
|
||||||
@ -121,37 +125,40 @@ val a2 = Animal("Doa")
|
|||||||
print(a2.name_length) //: 3
|
print(a2.name_length) //: 3
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
### Constructor that may fail
|
### Constructor that may fail
|
||||||
|
|
||||||
A constructor may only fail if there is code
|
A constructor may only fail if there is code
|
||||||
that can fail on the `init` block.
|
that can fail on the `init` block.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TBD
|
TBD
|
||||||
|
|
||||||
Proposal 1:
|
Proposal 1:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class PrayingMantis(String name)
|
class PrayingMantis(String name)
|
||||||
throws Error {
|
throws Error {
|
||||||
init -> Error! {
|
init -> Error! {
|
||||||
// Initialization code that may fail
|
// Initialization code that may fail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Destructor
|
## Destructor
|
||||||
|
|
||||||
The destructor in THP is the same as PHP.
|
The destructor in THP is the same as PHP.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class Owl() {
|
class Owl() {
|
||||||
pub fun __destruct() {
|
pub fun __destruct() {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
print("Destroying Owl...")
|
print("Destroying Owl...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Basics
|
title: Basics
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Classes
|
# Classes
|
||||||
|
|
||||||
@ -15,27 +14,25 @@ Classes in THP are significantly different than PHP.
|
|||||||
|
|
||||||
A class is defined as follows:
|
A class is defined as follows:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class Animal() {}
|
class Animal() {}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
The name of the class **MUST** begin with an uppercase letter.
|
The name of the class **MUST** begin with an uppercase letter.
|
||||||
|
|
||||||
Classes have a parameter list even if they have no parameters
|
Classes have a parameter list even if they have no parameters
|
||||||
for consistency sake.
|
for consistency sake.
|
||||||
|
|
||||||
|
|
||||||
## Instanciation
|
## Instanciation
|
||||||
|
|
||||||
To create an instance of a class call it as if it was a function,
|
To create an instance of a class call it as if it was a function,
|
||||||
without `new`.
|
without `new`.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val animal = Animal()
|
val animal = Animal()
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@ -53,7 +50,6 @@ class Person() {
|
|||||||
|
|
||||||
// This is also okay
|
// This is also okay
|
||||||
String name = "Jane Doe"
|
String name = "Jane Doe"
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
@ -66,7 +62,6 @@ class Person() {
|
|||||||
|
|
||||||
// To make a property public use \`pub\`
|
// To make a property public use \`pub\`
|
||||||
pub var Int age = 30
|
pub var Int age = 30
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val p = Person()
|
val p = Person()
|
||||||
@ -84,19 +79,19 @@ Readonly properties are explained in the Readonly page.
|
|||||||
The interaction between properties and the constructor is explained
|
The interaction between properties and the constructor is explained
|
||||||
in the Constructor page.
|
in the Constructor page.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
Methods are declared with `fun`, as regular functions.
|
Methods are declared with `fun`, as regular functions.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class Person() {
|
class Person() {
|
||||||
fun greet() {
|
fun greet() {
|
||||||
print("Hello")
|
print("Hello")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
**Methods are private by default**, and are made public with `pub`.
|
**Methods are private by default**, and are made public with `pub`.
|
||||||
|
|
||||||
@ -111,7 +106,6 @@ class Person() {
|
|||||||
pub fun greet() {
|
pub fun greet() {
|
||||||
print("Hello from greet")
|
print("Hello from greet")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val p = Person()
|
val p = Person()
|
||||||
@ -131,10 +125,10 @@ class Person() {
|
|||||||
fun name() -> String {
|
fun name() -> String {
|
||||||
"Rose"
|
"Rose"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## This
|
## This
|
||||||
|
|
||||||
THP uses the dollar sign `$` as this inside classes.
|
THP uses the dollar sign `$` as this inside classes.
|
||||||
@ -152,23 +146,21 @@ class Person() {
|
|||||||
val person_name = $get_name()
|
val person_name = $get_name()
|
||||||
print("Hello, I'm {person_name}")
|
print("Hello, I'm {person_name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Mutable methods
|
## Mutable methods
|
||||||
|
|
||||||
By default methods cannot mutate the state of the object.
|
By default methods cannot mutate the state of the object.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class Animal(var String name) {
|
class Animal(var String name) {
|
||||||
pub fun set_name(String new_name) {
|
pub fun set_name(String new_name) {
|
||||||
$name = new_name // Error: Cannot mutate $name
|
$name = new_name // Error: Cannot mutate $name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
To do so the method must be annotated. The caller must also
|
To do so the method must be annotated. The caller must also
|
||||||
declare a mutable variable.
|
declare a mutable variable.
|
||||||
@ -183,3 +175,4 @@ class Animal(var String name) {
|
|||||||
var michi = Animal("Michifu")
|
var michi = Animal("Michifu")
|
||||||
michi.set_name("Garfield")
|
michi.set_name("Garfield")
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Inheritance
|
title: Inheritance
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Inheritance
|
# Inheritance
|
||||||
|
|
||||||
@ -29,14 +28,12 @@ Cat("Michi", 9).say_name()
|
|||||||
The call to the parent constructor is done right there, after
|
The call to the parent constructor is done right there, after
|
||||||
the parent class name.
|
the parent class name.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class Dog(String name)
|
class Dog(String name)
|
||||||
extends Animal(name) {}
|
extends Animal(name) {}
|
||||||
// |----|
|
// |----|
|
||||||
// This is the call to the parent constructor
|
// This is the call to the parent constructor
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
You must always call super, even if the parent doesn't
|
You must always call super, even if the parent doesn't
|
||||||
define any parameters:
|
define any parameters:
|
||||||
@ -47,3 +44,10 @@ class Parent() {}
|
|||||||
class Child()
|
class Child()
|
||||||
extends Parent() {}
|
extends Parent() {}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Interfaces
|
title: Interfaces
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Interfaces
|
# Interfaces
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
interface Serializable
|
interface Serializable
|
||||||
{
|
{
|
||||||
@ -14,6 +15,8 @@ interface Serializable
|
|||||||
fun serialize() -> String
|
fun serialize() -> String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Cat -> Serializable
|
class Cat -> Serializable
|
||||||
{
|
{
|
||||||
pub fun Serializable() -> String
|
pub fun Serializable() -> String
|
||||||
@ -25,3 +28,4 @@ pub fun Serializable() -> String
|
|||||||
`} />
|
`} />
|
||||||
|
|
||||||
No interface inheritance.
|
No interface inheritance.
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Magic methods
|
title: Magic methods
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Magic methods
|
# Magic methods
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ Don't get special treatment.
|
|||||||
|
|
||||||
class Cat
|
class Cat
|
||||||
{
|
{
|
||||||
pub fun \_\_sleep() -> Array[String]
|
pub fun __sleep() -> Array[String]
|
||||||
{
|
{
|
||||||
// logic
|
// logic
|
||||||
}
|
}
|
||||||
@ -21,6 +20,7 @@ pub fun \_\_sleep() -> Array[String]
|
|||||||
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
val option = Some("GAAA")
|
val option = Some("GAAA")
|
||||||
val Some(value) = option
|
val Some(value) = option
|
||||||
|
@ -2,15 +2,14 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Readonly
|
title: Readonly
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Readonly
|
# Readonly
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
class Caño
|
class Caño
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Static
|
title: Static
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Static in classes
|
# Static in classes
|
||||||
|
|
||||||
|
|
||||||
## Class constants
|
## Class constants
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
@ -23,10 +23,12 @@ const CONSTANT = "constant value"
|
|||||||
print(Cat::CONSTANT)
|
print(Cat::CONSTANT)
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Static methods
|
## Static methods
|
||||||
|
|
||||||
aka. plain, old functions
|
aka. plain, old functions
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
static Cat
|
static Cat
|
||||||
{
|
{
|
||||||
@ -39,10 +41,12 @@ static Cat
|
|||||||
Cat::static_method()
|
Cat::static_method()
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Static properties
|
## Static properties
|
||||||
|
|
||||||
aka. global variables
|
aka. global variables
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
static Cat
|
static Cat
|
||||||
{
|
{
|
||||||
@ -53,3 +57,7 @@ print(Cat::access_count) // 0
|
|||||||
Cat::access_count += 1
|
Cat::access_count += 1
|
||||||
print(Cat::access_count) // 1
|
print(Cat::access_count) // 1
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Visibility
|
title: Visibility
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Visibility
|
# Visibility
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Components
|
title: Components
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
import Info from "@/components/docs/Info.astro"
|
||||||
import Info from "@/components/docs/Info.astro";
|
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
|
|
||||||
@ -15,17 +14,14 @@ Like React, a component is any function that returns `HTML`.
|
|||||||
will be uppercase or not.
|
will be uppercase or not.
|
||||||
|
|
||||||
For now they will be uppercase.
|
For now they will be uppercase.
|
||||||
|
|
||||||
</Info>
|
</Info>
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun MyComponent() -> HTML
|
fun MyComponent() -> HTML
|
||||||
{
|
{
|
||||||
<p>Hello templates!</p>
|
<p>Hello templates!</p>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Inside the HTML tags you can (mostly) write the HTML you already
|
Inside the HTML tags you can (mostly) write the HTML you already
|
||||||
know.
|
know.
|
||||||
@ -41,7 +37,6 @@ fun MyComponent() -> HTML
|
|||||||
val name = "John"
|
val name = "John"
|
||||||
|
|
||||||
<p>Hello {name}!</p>
|
<p>Hello {name}!</p>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
@ -62,7 +57,6 @@ fun MyComponent() -> HTML
|
|||||||
{if is_vip {"Welcome"} else {"Hi"}}
|
{if is_vip {"Welcome"} else {"Hi"}}
|
||||||
{name}!
|
{name}!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
@ -80,10 +74,8 @@ fun MyComponent() -> HTML
|
|||||||
val user_input = "<b>BOLD</b>"
|
val user_input = "<b>BOLD</b>"
|
||||||
|
|
||||||
<p>answer: {user_input}</p>
|
<p>answer: {user_input}</p>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<p>answer: <b>BOLD</b></p>
|
<p>answer: <b>BOLD</b></p>
|
||||||
```
|
```
|
||||||
@ -96,30 +88,25 @@ fun MyComponent() -> HTML
|
|||||||
val user_input = "<b>BOLD</b>"
|
val user_input = "<b>BOLD</b>"
|
||||||
|
|
||||||
<p>answer: <span raw-html={user_input}></span></p>
|
<p>answer: <span raw-html={user_input}></span></p>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<p>
|
<p>answer: <span><b>BOLD</b></span></p>
|
||||||
answer: <span><b>BOLD</b></span>
|
|
||||||
</p>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Dynamic attributes
|
## Dynamic attributes
|
||||||
|
|
||||||
TODO: boolean attributes
|
TODO: boolean attributes
|
||||||
|
|
||||||
Normal attributes (plain strings) work as you'd expect:
|
Normal attributes (plain strings) work as you'd expect:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun MyComponent() -> HTML
|
fun MyComponent() -> HTML
|
||||||
{
|
{
|
||||||
<button class="red">hello</button>
|
<button class="red">hello</button>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
```html
|
```html
|
||||||
<button class="red">hello</button>
|
<button class="red">hello</button>
|
||||||
```
|
```
|
||||||
@ -134,30 +121,29 @@ fun MyComponent() -> HTML
|
|||||||
|
|
||||||
// Note the braces
|
// Note the braces
|
||||||
<button class={button_class}>hello</button>
|
<button class={button_class}>hello</button>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<button class="blue">hello</button>
|
<button class="blue">hello</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Fragments
|
## Fragments
|
||||||
|
|
||||||
An HTML expression consist of a single tag that may have children inside.
|
An HTML expression consist of a single tag that may have children inside.
|
||||||
If you need to return multiple tags at once you can use fragments.
|
If you need to return multiple tags at once you can use fragments.
|
||||||
|
|
||||||
|
|
||||||
The following code doesn't work as you would expect:
|
The following code doesn't work as you would expect:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun MyComponent() -> HTML
|
fun MyComponent() -> HTML
|
||||||
{
|
{
|
||||||
<p>hello</p> // This is an error, an ignored expression
|
<p>hello</p> // This is an error, an ignored expression
|
||||||
<p>world</p>
|
<p>world</p>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Each `<p>` is a single expression, they are not grouped.
|
Each `<p>` is a single expression, they are not grouped.
|
||||||
And since we cannot have unused expressions, the code will not compile.
|
And since we cannot have unused expressions, the code will not compile.
|
||||||
@ -165,8 +151,7 @@ And since we cannot have unused expressions, the code will not compile.
|
|||||||
To have these two `<p>` tags as a single expression use a fragment:
|
To have these two `<p>` tags as a single expression use a fragment:
|
||||||
`<></>`
|
`<></>`
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun MyComponent() -> HTML
|
fun MyComponent() -> HTML
|
||||||
{
|
{
|
||||||
// This is the root "element"
|
// This is the root "element"
|
||||||
@ -175,8 +160,7 @@ fun MyComponent() -> HTML
|
|||||||
<p>world</p>
|
<p>world</p>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
```html
|
```html
|
||||||
<p>hello</p>
|
<p>hello</p>
|
||||||
<p>world</p>
|
<p>world</p>
|
||||||
@ -195,14 +179,16 @@ fun User() -> HTML
|
|||||||
|
|
||||||
fun MyComponent() -> HTML
|
fun MyComponent() -> HTML
|
||||||
{
|
{
|
||||||
|
|
||||||
<>
|
<>
|
||||||
<p>status</p>
|
<p>status</p>
|
||||||
<User /> // Here we are using the other component
|
<User /> // Here we are using the other component
|
||||||
</>
|
</>
|
||||||
} `} />
|
}
|
||||||
|
`} />
|
||||||
```html
|
```html
|
||||||
<p>status</p>
|
<p>status</p>
|
||||||
<span>world</span>
|
<span>world</span>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Control flow
|
title: Control flow
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
import Info from "@/components/docs/Info.astro"
|
||||||
import Info from "@/components/docs/Info.astro";
|
|
||||||
|
|
||||||
# Control flow
|
# Control flow
|
||||||
|
|
||||||
@ -13,8 +12,7 @@ import Info from "@/components/docs/Info.astro";
|
|||||||
Use `@if`, `@else if` and `@else` to branch during the template
|
Use `@if`, `@else if` and `@else` to branch during the template
|
||||||
creation.
|
creation.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun User(User user) -> HTML
|
fun User(User user) -> HTML
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
@ -28,8 +26,8 @@ fun User(User user) -> HTML
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Loops
|
## Loops
|
||||||
|
|
||||||
@ -46,16 +44,15 @@ fun Users() -> HTML
|
|||||||
<User user={user} />
|
<User user={user} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Match
|
## Match
|
||||||
|
|
||||||
Use `@match` for pattern matching:
|
Use `@match` for pattern matching:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun UserDetail(User user) -> HTML
|
fun UserDetail(User user) -> HTML
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
@ -70,5 +67,8 @@ fun UserDetail(User user) -> HTML
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Introduction
|
title: Introduction
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
import Info from "@/components/docs/Info.astro"
|
||||||
import Info from "@/components/docs/Info.astro";
|
|
||||||
|
|
||||||
# THP templating
|
# THP templating
|
||||||
|
|
||||||
@ -63,19 +62,18 @@ and compose them.
|
|||||||
|
|
||||||
The following would be the equivalent in THP:
|
The following would be the equivalent in THP:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun Button(String name) -> HTML {
|
fun Button(String name) -> HTML {
|
||||||
<button class="some tailwind classes">
|
<button class="some tailwind classes">
|
||||||
Hello {name}!
|
Hello {name}!
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
It is very similar to React. The HTML is inside the THP code, not the other
|
It is very similar to React. The HTML is inside the THP code, not the other
|
||||||
way around, so you can have arbitrary logic in the component.
|
way around, so you can have arbitrary logic in the component.
|
||||||
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
fun User(String name) -> HTML {
|
fun User(String name) -> HTML {
|
||||||
// Get info from the database
|
// Get info from the database
|
||||||
@ -94,15 +92,15 @@ fun User(String name) -> HTML {
|
|||||||
<TransactionItem t={t} />
|
<TransactionItem t={t} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TransactionItem(Transaction t) -> HTML {
|
fun TransactionItem(Transaction t) -> HTML {
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
{t.date} - {t.name} ({t.price})
|
{t.date} - {t.name} ({t.price})
|
||||||
</li>
|
</li>
|
||||||
} `} />
|
}
|
||||||
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Is this a JavaScript Front-End Framework?
|
## Is this a JavaScript Front-End Framework?
|
||||||
|
|
||||||
@ -116,14 +114,19 @@ If you need reactivity on the front-end and want to use this templating
|
|||||||
take a look at [htmx](https://htmx.org/), [Alpine.js](https://alpinejs.dev/)
|
take a look at [htmx](https://htmx.org/), [Alpine.js](https://alpinejs.dev/)
|
||||||
and [hyperscript](https://hyperscript.org/).
|
and [hyperscript](https://hyperscript.org/).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Styling
|
## Styling
|
||||||
|
|
||||||
We don't provide any syntax or facility for styling.
|
We don't provide any syntax or facility for styling.
|
||||||
However, this component model is good to use with TailwindCSS.
|
However, this component model is good to use with TailwindCSS.
|
||||||
|
|
||||||
|
|
||||||
## Conversion
|
## Conversion
|
||||||
|
|
||||||
<Info>TBD: This is a draft, subject to change.</Info>
|
<Info>
|
||||||
|
TBD: This is a draft, subject to change.
|
||||||
|
</Info>
|
||||||
|
|
||||||
HTML expressions will be compiled to plain strings, and
|
HTML expressions will be compiled to plain strings, and
|
||||||
those will be then composed.
|
those will be then composed.
|
||||||
@ -142,7 +145,6 @@ fun Sample(String name) -> HTML {
|
|||||||
Not logged in
|
Not logged in
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
@ -161,3 +163,6 @@ function Sample(name) {
|
|||||||
return "<button>{$__expr_1}</button>"
|
return "<button>{$__expr_1}</button>"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Props
|
title: Props
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
import Info from "@/components/docs/Info.astro"
|
||||||
import Info from "@/components/docs/Info.astro";
|
|
||||||
|
|
||||||
# Props
|
# Props
|
||||||
|
|
||||||
@ -16,27 +15,23 @@ and they are defined as normal parameters.
|
|||||||
For example, to receive a `String` declare it as a
|
For example, to receive a `String` declare it as a
|
||||||
parameter:
|
parameter:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun Greeter(String name) -> HTML
|
fun Greeter(String name) -> HTML
|
||||||
{
|
{
|
||||||
<p>Hello {name}!</p>
|
<p>Hello {name}!</p>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
And to send its value type the parameter name as an attribute:
|
And to send its value type the parameter name as an attribute:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun Home() -> HTML
|
fun Home() -> HTML
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
<Greeter name="Rose" />
|
<Greeter name="Rose" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div><p>Hello Rose</p></div>
|
<div><p>Hello Rose</p></div>
|
||||||
@ -44,21 +39,18 @@ fun Home() -> HTML
|
|||||||
|
|
||||||
You can have as many props as you'd like, of any datatype:
|
You can have as many props as you'd like, of any datatype:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun Greeter(String name, Int age, Array[String] friends) -> HTML
|
fun Greeter(String name, Int age, Array[String] friends) -> HTML
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Static props
|
## Static props
|
||||||
|
|
||||||
If the prop has a type `String` you can use a normal attribute.
|
If the prop has a type `String` you can use a normal attribute.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun Home() -> HTML
|
fun Home() -> HTML
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
@ -66,8 +58,8 @@ fun Home() -> HTML
|
|||||||
<Greeter name="Rose" />
|
<Greeter name="Rose" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Dynamic props
|
## Dynamic props
|
||||||
|
|
||||||
@ -88,17 +80,16 @@ val my_cat = Cat("Michifu")
|
|||||||
<div>
|
<div>
|
||||||
<Sample cat={my_cat} />
|
<Sample cat={my_cat} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
## Components as props
|
## Components as props
|
||||||
|
|
||||||
If for some reason you want to use a component as a prop
|
If for some reason you want to use a component as a prop
|
||||||
use the `HTML` datatype:
|
use the `HTML` datatype:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// The parameter can have any name, not only \`child\`
|
// The parameter can have any name, not only \`child\`
|
||||||
fun Sample(HTML child) -> HTML
|
fun Sample(HTML child) -> HTML
|
||||||
{
|
{
|
||||||
@ -107,22 +98,19 @@ fun Sample(HTML child) -> HTML
|
|||||||
{child}
|
{child}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
This, however, means that your prop component must be declared
|
This, however, means that your prop component must be declared
|
||||||
as an attribute:
|
as an attribute:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
fun Home() -> HTML
|
fun Home() -> HTML
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
<Sample child={<span>I am the child</span>} />
|
<Sample child={<span>I am the child</span>} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div>
|
<div>
|
||||||
@ -148,16 +136,21 @@ fun MyButton() -> HTML
|
|||||||
|
|
||||||
fun Home() -> HTML
|
fun Home() -> HTML
|
||||||
{
|
{
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<MyButton>
|
<MyButton>
|
||||||
buy <b>now!</b>
|
buy <b>now!</b>
|
||||||
</MyButton>
|
</MyButton>
|
||||||
</div>
|
</div>
|
||||||
} `} />
|
}
|
||||||
|
`} />
|
||||||
```html
|
```html
|
||||||
<div>
|
<div>
|
||||||
<button>buy <b>now!</b></button>
|
<button>
|
||||||
|
buy <b>now!</b>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,13 +4,10 @@ import NewDocsLayout, { type AstroFile } from "@/layouts/NewDocsLayout.astro";
|
|||||||
const { frontmatter, headings } = Astro.props;
|
const { frontmatter, headings } = Astro.props;
|
||||||
// Get all the posts from this dir
|
// Get all the posts from this dir
|
||||||
|
|
||||||
const posts = (await Astro.glob(
|
const posts = await Astro.glob("./**/*.{md,mdx}") as unknown as Array<AstroFile>;
|
||||||
"./**/*.{md,mdx}",
|
|
||||||
)) as unknown as Array<AstroFile>;
|
|
||||||
|
|
||||||
// The base of every URL under this glob
|
// The base of every URL under this glob
|
||||||
const base_url = "/en/latest/learn";
|
const base_url = "/en/latest/learn"
|
||||||
const version = "latest";
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<NewDocsLayout
|
<NewDocsLayout
|
||||||
@ -18,7 +15,6 @@ const version = "latest";
|
|||||||
frontmatter={frontmatter}
|
frontmatter={frontmatter}
|
||||||
headings={headings}
|
headings={headings}
|
||||||
posts={posts}
|
posts={posts}
|
||||||
version={version}
|
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</NewDocsLayout>
|
</NewPagesLayout>
|
||||||
|
@ -3,9 +3,9 @@ layout: "./_wrapper.astro"
|
|||||||
title: Welcome
|
title: Welcome
|
||||||
order: 1
|
order: 1
|
||||||
---
|
---
|
||||||
|
|
||||||
import InteractiveCode from "@/components/InteractiveCode.astro";
|
import InteractiveCode from "@/components/InteractiveCode.astro";
|
||||||
import Code from "@/components/Code.astro";
|
import Code from "@/components/Code.astro"
|
||||||
|
|
||||||
|
|
||||||
# Welcome
|
# Welcome
|
||||||
|
|
||||||
@ -15,9 +15,11 @@ 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)
|
||||||
|
|
||||||
|
|
||||||
This page details the main design desitions of the language,
|
This page details the main design desitions 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)
|
||||||
|
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
|
||||||
PHP is an old language. It has been growing since 1995, adopting a
|
PHP is an old language. It has been growing since 1995, adopting a
|
||||||
@ -69,6 +71,10 @@ essential today: A good, unified LSP, type definitions,
|
|||||||
accesible documentation, an opinionated code formatter
|
accesible documentation, an opinionated code formatter
|
||||||
and plugins for major editors like VSCode and Neovim.
|
and plugins for major editors like VSCode and Neovim.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
- Bring static typing to PHP: Generics, type checks at compile and runtime, etc.
|
- Bring static typing to PHP: Generics, type checks at compile and runtime, etc.
|
||||||
@ -87,6 +93,7 @@ and plugins for major editors like VSCode and Neovim.
|
|||||||
|
|
||||||
![Friendship ended with Rust, now Zig is my best friend.](/img/mudasir.jpg)
|
![Friendship ended with Rust, now Zig is my best friend.](/img/mudasir.jpg)
|
||||||
|
|
||||||
|
|
||||||
## Not goals
|
## Not goals
|
||||||
|
|
||||||
These are **not** things that THP wants to solve or implement
|
These are **not** things that THP wants to solve or implement
|
||||||
@ -97,6 +104,7 @@ These are **not** things that THP wants to solve or implement
|
|||||||
THP **_intentionally_** uses a different syntax from PHP to signal
|
THP **_intentionally_** uses a different syntax from PHP to signal
|
||||||
that it is a different language, and has different semantics.
|
that it is a different language, and has different semantics.
|
||||||
|
|
||||||
|
|
||||||
## Some differences with PHP
|
## Some differences with PHP
|
||||||
|
|
||||||
```php
|
```php
|
||||||
@ -105,13 +113,11 @@ $has_key = str_contains($haystack, 'needle');
|
|||||||
print("has key? " . $has_key);
|
print("has key? " . $has_key);
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// THP
|
// THP
|
||||||
val has_key = haystack.contains("needle")
|
val has_key = haystack.contains("needle")
|
||||||
print("has key? " + has_key)
|
print("has key? " + has_key)
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
- Explicit variable declaration
|
- Explicit variable declaration
|
||||||
- No `$` for variable names (and thus no `$$variable`, use a map instead)
|
- No `$` for variable names (and thus no `$$variable`, use a map instead)
|
||||||
@ -120,6 +126,7 @@ print("has key? " + has_key)
|
|||||||
- Strings use only double quotes
|
- Strings use only double quotes
|
||||||
- String concatenation with `+`
|
- String concatenation with `+`
|
||||||
|
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
@ -132,16 +139,14 @@ $obj = [
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// THP
|
// THP
|
||||||
val obj = .{
|
val obj = .{
|
||||||
names: #("Toni", "Stark"), // Tuple
|
names: #("Toni", "Stark"), // Tuple
|
||||||
age: 33,
|
age: 33,
|
||||||
numbers: [32, 64, 128]
|
numbers: [32, 64, 128]
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
- Tuples, Arrays, Sets, Maps are clearly different
|
- Tuples, Arrays, Sets, Maps are clearly different
|
||||||
- JSON-like object syntax
|
- JSON-like object syntax
|
||||||
@ -155,13 +160,12 @@ $cat = new Cat("Michifu", 7);
|
|||||||
$cat->meow();
|
$cat->meow();
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
|
||||||
thpcode={`
|
<Code thpcode={`
|
||||||
// THP
|
// THP
|
||||||
val cat = Cat("Michifu", 7)
|
val cat = Cat("Michifu", 7)
|
||||||
cat.meow()
|
cat.meow()
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
- Instantiate classes without `new`
|
- Instantiate classes without `new`
|
||||||
- Use dot `.` instead of arrow `->` syntax
|
- Use dot `.` instead of arrow `->` syntax
|
||||||
@ -175,12 +179,11 @@ use \Some\Deeply\Nested\Class
|
|||||||
use \Some\Deeply\Nested\Interface
|
use \Some\Deeply\Nested\Interface
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
|
||||||
thpcode={`
|
<Code thpcode={`
|
||||||
// THP
|
// THP
|
||||||
use Some::Deeply::Nested::{Class, Interface}
|
use Some::Deeply::Nested::{Class, Interface}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
- Different module syntax
|
- Different module syntax
|
||||||
- Explicit module declaration
|
- Explicit module declaration
|
||||||
@ -190,19 +193,20 @@ use Some::Deeply::Nested::{Class, Interface}
|
|||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
Other things:
|
Other things:
|
||||||
|
|
||||||
- Pattern matching
|
- Pattern matching
|
||||||
- ADTs
|
- ADTs
|
||||||
|
|
||||||
|
|
||||||
## Runtime changes
|
## Runtime changes
|
||||||
|
|
||||||
Where possible THP will compile to available PHP functions/classes/methods/etc.
|
Where possible THP will compile to available PHP functions/classes/methods/etc.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// This expression
|
// This expression
|
||||||
val greeting =
|
val greeting =
|
||||||
match get_person()
|
match get_person()
|
||||||
@ -218,8 +222,7 @@ val greeting =
|
|||||||
{
|
{
|
||||||
"Nobody is here"
|
"Nobody is here"
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Would compile to:
|
// Would compile to:
|
||||||
@ -240,3 +243,5 @@ else {
|
|||||||
|
|
||||||
However, more advanced datatypes & helper functions will require a sort of
|
However, more advanced datatypes & helper functions will require a sort of
|
||||||
runtime (new classes/functions/etc) or abuse the language's syntax/semantics.
|
runtime (new classes/functions/etc) or abuse the language's syntax/semantics.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
import NewDocsLayout, { type AstroFile } from "@/layouts/NewDocsLayout.astro";
|
|
||||||
|
|
||||||
const { frontmatter, headings } = Astro.props;
|
|
||||||
// Get all the posts from this dir
|
|
||||||
|
|
||||||
const posts = (await Astro.glob(
|
|
||||||
"./**/*.{md,mdx}",
|
|
||||||
)) as unknown as Array<AstroFile>;
|
|
||||||
|
|
||||||
// The base of every URL under this glob
|
|
||||||
const base_url = "/en/v0.0.1/learn";
|
|
||||||
const version = "v0.0.1";
|
|
||||||
---
|
|
||||||
|
|
||||||
<NewDocsLayout
|
|
||||||
base_url={base_url}
|
|
||||||
frontmatter={frontmatter}
|
|
||||||
headings={headings}
|
|
||||||
posts={posts}
|
|
||||||
version={version}
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</NewDocsLayout>
|
|
@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
layout: "./_wrapper.astro"
|
|
||||||
title: Welcome
|
|
||||||
order: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
import InteractiveCode from "@/components/InteractiveCode.astro";
|
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# THP :D
|
|
||||||
|
|
||||||
Version 0.0.1
|
|
@ -45,8 +45,8 @@ const [thp_html] = await native_highlighter(
|
|||||||
compiled to PHP
|
compiled to PHP
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-c-text text-lg pt-6 lg:pr-12">
|
<p class="text-c-text text-lg pt-6 lg:pr-12">
|
||||||
Inspired by Rust, Zig and Kotlin, THP has a modern syntax, semantics,
|
Inspired by Rust, Zig and Kotlin, THP has a modern syntax,
|
||||||
type system and stdlib.
|
semantics, type system and stdlib.
|
||||||
</p>
|
</p>
|
||||||
<div class="text-center pb-4">
|
<div class="text-center pb-4">
|
||||||
<a
|
<a
|
||||||
@ -85,7 +85,11 @@ const [thp_html] = await native_highlighter(
|
|||||||
height="14"
|
height="14"
|
||||||
viewBox="0 0 54 14"
|
viewBox="0 0 54 14"
|
||||||
>
|
>
|
||||||
<g fill="none" fill-rule="evenodd" transform="translate(1 1)">
|
<g
|
||||||
|
fill="none"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
transform="translate(1 1)"
|
||||||
|
>
|
||||||
<circle
|
<circle
|
||||||
cx="6"
|
cx="6"
|
||||||
cy="6"
|
cy="6"
|
||||||
@ -140,10 +144,10 @@ const [thp_html] = await native_highlighter(
|
|||||||
val result = mock() as String
|
val result = mock() as String
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
Type safety is enforced at compile time. Everything has a specific type.
|
Type safety is enforced at compile time. Everything
|
||||||
There is no `mixed`.
|
has a specific type. There is no `mixed`.
|
||||||
<br />
|
<br>
|
||||||
<br />
|
<br>
|
||||||
You can use generics where neccesary.
|
You can use generics where neccesary.
|
||||||
</HeroSection>
|
</HeroSection>
|
||||||
|
|
||||||
@ -195,9 +199,9 @@ const [thp_html] = await native_highlighter(
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
Nulls are explicit and require handling. Types can be nullable, and they
|
Nulls are explicit and require handling. Types can be nullable,
|
||||||
must be checked before usage.
|
and they must be checked before usage.
|
||||||
<br />
|
<br>
|
||||||
The stdlib makes extensive use of them.
|
The stdlib makes extensive use of them.
|
||||||
</HeroSection>
|
</HeroSection>
|
||||||
|
|
||||||
@ -212,9 +216,11 @@ const [thp_html] = await native_highlighter(
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
Exceptions are values and don't disrupt control flow.
|
Exceptions are values and don't disrupt
|
||||||
<br />
|
control flow.
|
||||||
<br />
|
<br>
|
||||||
Errors cannot be ignored, and we have syntax sugar to ease them.
|
<br>
|
||||||
|
Errors cannot be ignored, and we have
|
||||||
|
syntax sugar to ease them.
|
||||||
</HeroSection>
|
</HeroSection>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
@ -4,12 +4,10 @@ import NewDocsLayout, { type AstroFile } from "@/layouts/NewDocsLayout.astro";
|
|||||||
const { frontmatter, headings } = Astro.props;
|
const { frontmatter, headings } = Astro.props;
|
||||||
// Get all the posts from this dir
|
// Get all the posts from this dir
|
||||||
|
|
||||||
const posts = (await Astro.glob(
|
const posts = await Astro.glob("./**/*.{md,mdx}") as unknown as Array<AstroFile>;
|
||||||
"./**/*.{md,mdx}",
|
|
||||||
)) as unknown as Array<AstroFile>;
|
|
||||||
|
|
||||||
// The base of every URL under this glob
|
// The base of every URL under this glob
|
||||||
const base_url = "/spec";
|
const base_url = "/spec"
|
||||||
---
|
---
|
||||||
|
|
||||||
<NewDocsLayout
|
<NewDocsLayout
|
||||||
@ -19,4 +17,5 @@ const base_url = "/spec";
|
|||||||
posts={posts}
|
posts={posts}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</NewDocsLayout>
|
</NewPagesLayout>
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ BlockMember = Statement
|
|||||||
| Expression
|
| Expression
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Assignment
|
## Assignment
|
||||||
|
|
||||||
The target of an assignment can only be an identifier for now.
|
The target of an assignment can only be an identifier for now.
|
||||||
@ -86,3 +87,5 @@ AssignmentOperator = "="
|
|||||||
| "/="
|
| "/="
|
||||||
| "%="
|
| "%="
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,12 +8,14 @@ title: Expression
|
|||||||
The expression parser effectively implements a precedence table.
|
The expression parser effectively implements a precedence table.
|
||||||
|
|
||||||
| Operator | Precedence |
|
| Operator | Precedence |
|
||||||
| --------- | ---------- |
|
|------------|------------|
|
||||||
| == != | 5 |
|
| == != | 5 |
|
||||||
| > >= < <= | 4 |
|
| > >= < <= | 4 |
|
||||||
| - + ++ | 3 |
|
| - + ++ | 3 |
|
||||||
| . ?. !. | 2 |
|
| . ?. !. | 2 |
|
||||||
| / \* % | 1 |
|
| / * % | 1 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```ebnf
|
```ebnf
|
||||||
Expression = Equality
|
Expression = Equality
|
||||||
@ -27,6 +29,7 @@ Unary = ("!" | "-"), Expression
|
|||||||
| CallExpression
|
| CallExpression
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## CallExpression
|
## CallExpression
|
||||||
|
|
||||||
It's so hard to properly name these constructions.
|
It's so hard to properly name these constructions.
|
||||||
@ -37,3 +40,6 @@ CallExpression = primary, "(", (arguments list)?, ")"
|
|||||||
| primary, "[", (expression, (comma, expression)*, comma?)? "]"
|
| primary, "[", (expression, (comma, expression)*, comma?)? "]"
|
||||||
| primary
|
| primary
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
layout: "./_wrapper.astro"
|
layout: "./_wrapper.astro"
|
||||||
title: Welcome
|
title: Welcome
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
|
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# The THP Language Specification
|
# The THP Language Specification
|
||||||
|
|
||||||
@ -42,11 +42,14 @@ The compiler consists of 5 common phases:
|
|||||||
- **IR**: Transforms the THP AST into a PHP AST
|
- **IR**: Transforms the THP AST into a PHP AST
|
||||||
- **Codegen**: Generates PHP source code from the PHP AST
|
- **Codegen**: Generates PHP source code from the PHP AST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Source Code representation
|
## Source Code representation
|
||||||
|
|
||||||
Source code is encoded in UTF-8, and a single UTF-8 codepoint is
|
Source code is encoded in UTF-8, and a single UTF-8 codepoint is
|
||||||
a single character.
|
a single character.
|
||||||
|
|
||||||
|
|
||||||
## Basic characters
|
## Basic characters
|
||||||
|
|
||||||
Although the source code must be encoded in UTF-8, most of the actual
|
Although the source code must be encoded in UTF-8, most of the actual
|
||||||
@ -65,6 +68,7 @@ lowercase_letter = "a".."z"
|
|||||||
uppercase_letter = "A".."Z"
|
uppercase_letter = "A".."Z"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Whitespace & Automatic semicolon insertion
|
## Whitespace & Automatic semicolon insertion
|
||||||
|
|
||||||
This section is being reworked on the Zig rewrite of the compiler.
|
This section is being reworked on the Zig rewrite of the compiler.
|
||||||
@ -81,12 +85,10 @@ parenthesis, square brackets, etc.
|
|||||||
Other statements require a explicit terminator. For example,
|
Other statements require a explicit terminator. For example,
|
||||||
the assignment statement:
|
the assignment statement:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val computation = 123 + 456 // how to detect if the statement ends here
|
val computation = 123 + 456 // how to detect if the statement ends here
|
||||||
* 789 // or extends up to here?
|
* 789 // or extends up to here?
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
In other languages a semicolon would be used to signal the end of the
|
In other languages a semicolon would be used to signal the end of the
|
||||||
statement:
|
statement:
|
||||||
@ -104,25 +106,21 @@ to the rule:
|
|||||||
No matter the indentation, whitespace or others, every statement ends
|
No matter the indentation, whitespace or others, every statement ends
|
||||||
with a newline.
|
with a newline.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val compute = 1 + 2 * 3 / 4
|
val compute = 1 + 2 * 3 / 4
|
||||||
// statement ends here ↑
|
// statement ends here ↑
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
As mentioned before, this does not affect statements that have clear delimiters.
|
As mentioned before, this does not affect statements that have clear delimiters.
|
||||||
For example, the following code will work as expected:
|
For example, the following code will work as expected:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val compute = my_function(
|
val compute = my_function(
|
||||||
param1,
|
param1,
|
||||||
param2,
|
param2,
|
||||||
) / 64
|
) / 64
|
||||||
// ↑ statement ends here
|
// ↑ statement ends here
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
In a way, the parenthesis will "disable" the rule.
|
In a way, the parenthesis will "disable" the rule.
|
||||||
|
|
||||||
@ -135,13 +133,11 @@ continues.
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val computation = 123 + 456
|
val computation = 123 + 456
|
||||||
* 789
|
* 789
|
||||||
// ↑ statement ends here, and there is a single statement
|
// ↑ statement ends here, and there is a single statement
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
This is so no matter the indentation:
|
This is so no matter the indentation:
|
||||||
|
|
||||||
@ -149,26 +145,25 @@ This is so no matter the indentation:
|
|||||||
// weird indentation:
|
// weird indentation:
|
||||||
|
|
||||||
val computation = 123 + 456
|
val computation = 123 + 456
|
||||||
|
* 789
|
||||||
- 789
|
|
||||||
// ↑ statement still ends here
|
// ↑ statement still ends here
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
What is important is that an operator begins the new line.
|
What is important is that an operator begins the new line.
|
||||||
If the operator is left on the previous line, this will not work:
|
If the operator is left on the previous line, this will not work:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// statement ends here ↓, and now there is a syntax error (dangling operator)
|
// statement ends here ↓, and now there is a syntax error (dangling operator)
|
||||||
val computation = 123 + 456 *
|
val computation = 123 + 456 *
|
||||||
789
|
789
|
||||||
// ↑ this is a different statement
|
// ↑ this is a different statement
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
For this the parser must do look-ahead of 1 token. This is the only place the parser
|
For this the parser must do look-ahead of 1 token. This is the only place the parser
|
||||||
does so.
|
does so.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Old Whitespace rules
|
## Old Whitespace rules
|
||||||
|
|
||||||
THP is partially whitespace sensitive. It uses the following tokens: Indent, Dedent & NewLine
|
THP is partially whitespace sensitive. It uses the following tokens: Indent, Dedent & NewLine
|
||||||
@ -179,33 +174,32 @@ compares the previous indentation to the new one. If the amount of whitespace is
|
|||||||
greater than before, it emits a Indent token. If it's lower, emits a Dedent token, and
|
greater than before, it emits a Indent token. If it's lower, emits a Dedent token, and
|
||||||
if it's the same it does nothing.
|
if it's the same it does nothing.
|
||||||
|
|
||||||
<Code
|
|
||||||
thpcode={`
|
<Code thpcode={`
|
||||||
1 + 2
|
1 + 2
|
||||||
+ 3
|
+ 3
|
||||||
+ 4
|
+ 4
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
The previous code would emit the following tokens: `1` `+` `2` `NewLine` `Indent` `+` `3` `NewLine`
|
The previous code would emit the following tokens: `1` `+` `2` `NewLine` `Indent` `+` `3` `NewLine`
|
||||||
`+` `4` `Dedent`
|
`+` `4` `Dedent`
|
||||||
|
|
||||||
|
|
||||||
Additionaly, it is a lexical error to have wrong indentation. The lexer stores all
|
Additionaly, it is a lexical error to have wrong indentation. The lexer stores all
|
||||||
previous indentation levels in a stack, and reports an error if a decrease in indentation
|
previous indentation levels in a stack, and reports an error if a decrease in indentation
|
||||||
doesn't match a previous level.
|
doesn't match a previous level.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
if true { // 0 indentation
|
if true { // 0 indentation
|
||||||
print() // 4 indentation
|
print() // 4 indentation
|
||||||
print() // 2 indentation. Error. There is no 2-indentation level
|
print() // 2 indentation. Error. There is no 2-indentation level
|
||||||
}
|
}
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
All productions of the grammar ignore whitespace/indentation, except those involved in
|
All productions of the grammar ignore whitespace/indentation, except those involved in
|
||||||
semicolon inference.
|
semicolon inference.
|
||||||
|
|
||||||
|
|
||||||
## Statement termination / Semicolon inference
|
## Statement termination / Semicolon inference
|
||||||
|
|
||||||
**Only inside a block of code** whitespace is used to determine where a statement ends
|
**Only inside a block of code** whitespace is used to determine where a statement ends
|
||||||
@ -213,19 +207,18 @@ and a new one begins. Everywhere else whitespace is ignored.
|
|||||||
|
|
||||||
Statements in THP end when a new line is encountered:
|
Statements in THP end when a new line is encountered:
|
||||||
|
|
||||||
<Code
|
|
||||||
thpcode={`
|
|
||||||
|
<Code thpcode={`
|
||||||
// The statement ends | here, on the newline
|
// The statement ends | here, on the newline
|
||||||
val value = (123 + 456) * 0.75
|
val value = (123 + 456) * 0.75
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
// Each line contains a different statement. They all end on their new lines
|
// Each line contains a different statement. They all end on their new lines
|
||||||
|
|
||||||
var a = 1 + 2 // a = 3
|
var a = 1 + 2 // a = 3
|
||||||
|
+ 3 // this is not part of \`a\`, this is a different statement
|
||||||
- 3 // this is not part of \`a\`, this is a different statement
|
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
This is true even if the line ends with an operator:
|
This is true even if the line ends with an operator:
|
||||||
@ -237,19 +230,18 @@ var a = 1 + 2 + // This is now a compile error, there is a hanging `+`
|
|||||||
3 // This is still a different statement
|
3 // This is still a different statement
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
### Parenthesis
|
### Parenthesis
|
||||||
|
|
||||||
Exception 1: When a parenthesis is open, all following whitespace is ignored
|
Exception 1: When a parenthesis is open, all following whitespace is ignored
|
||||||
until the closing parenthesis.
|
until the closing parenthesis.
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// open parenthesis found, all whitespace is ignored until the closing
|
// open parenthesis found, all whitespace is ignored until the closing
|
||||||
name.contains(
|
name.contains(
|
||||||
"weird"
|
"weird"
|
||||||
)
|
)
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
However, for a parenthesis to begin to act, it needs to be open on the same line.
|
However, for a parenthesis to begin to act, it needs to be open on the same line.
|
||||||
|
|
||||||
@ -272,21 +264,17 @@ Exception 2:
|
|||||||
|
|
||||||
- When a binary operator is followed by indentation:
|
- When a binary operator is followed by indentation:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val sum = 1 + 2 + // The line ends with a binary operator
|
val sum = 1 + 2 + // The line ends with a binary operator
|
||||||
3 // There is indentation
|
3 // There is indentation
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
- Or when indentation is followed by a binary operator:
|
- Or when indentation is followed by a binary operator:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val sum = 1 + 2
|
val sum = 1 + 2
|
||||||
+ 3 // Indentation and a binary operator
|
+ 3 // Indentation and a binary operator
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
In theses cases, all whitespace will be ignored
|
In theses cases, all whitespace will be ignored
|
||||||
until the indentation returns to the initial level.
|
until the indentation returns to the initial level.
|
||||||
@ -303,3 +291,5 @@ val person = PersonBuilder()
|
|||||||
// Here indentation returns, and a new statement begins
|
// Here indentation returns, and a new statement begins
|
||||||
print(person)
|
print(person)
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Comment
|
title: Comment
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Comment
|
# Comment
|
||||||
|
|
||||||
@ -11,10 +10,8 @@ import Code from "@/components/Code.astro";
|
|||||||
Comment = "//", any_except_new_line
|
Comment = "//", any_except_new_line
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
// This is a comment
|
// This is a comment
|
||||||
//
|
//
|
||||||
// Another // comment
|
// Another // comment
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Identifiers & Datatypes
|
title: Identifiers & Datatypes
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Identifiers & Datatypes
|
# Identifiers & Datatypes
|
||||||
|
|
||||||
@ -20,15 +19,14 @@ Identifier = (underscore | lowercase_letter), identifier_letter*
|
|||||||
identifier_letter = underscore | lowercase_letter | uppercase_letter | decimal_digit
|
identifier_letter = underscore | lowercase_letter | uppercase_letter | decimal_digit
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
identifier
|
identifier
|
||||||
_identifier
|
_identifier
|
||||||
_123
|
_123
|
||||||
_many_letters
|
_many_letters
|
||||||
camelCase
|
camelCase
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Datatype
|
## Datatype
|
||||||
|
|
||||||
@ -36,23 +34,22 @@ camelCase
|
|||||||
Datatype = uppercase_letter, indentifier_letter*
|
Datatype = uppercase_letter, indentifier_letter*
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
Datatype
|
Datatype
|
||||||
PDO
|
PDO
|
||||||
WEIRD_DATATYPE
|
WEIRD_DATATYPE
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
## Keywords
|
## Keywords
|
||||||
|
|
||||||
The following are (currently) THP keywords:
|
The following are (currently) THP keywords:
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
val var fun
|
val var fun
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
Keywords are scanned first as identifiers, then transformed
|
Keywords are scanned first as identifiers, then transformed
|
||||||
to their respective tokens.
|
to their respective tokens.
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Numbers
|
title: Numbers
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Numbers
|
# Numbers
|
||||||
|
|
||||||
@ -21,8 +20,7 @@ binary_number = "0", ("b" | "B"), binary_digit+
|
|||||||
decimal_number = "1".."9", decimal_digit*
|
decimal_number = "1".."9", decimal_digit*
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
12345
|
12345
|
||||||
01234 // This is an error, a decimal number cant have
|
01234 // This is an error, a decimal number cant have
|
||||||
// leading zeroes
|
// leading zeroes
|
||||||
@ -30,11 +28,12 @@ decimal_number = "1".."9", decimal_digit*
|
|||||||
0b0110
|
0b0110
|
||||||
0xff25
|
0xff25
|
||||||
0XFfaA
|
0XFfaA
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
`TODO`: Allow underscores `_` between any number: `1_000_000`.
|
`TODO`: Allow underscores `_` between any number: `1_000_000`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Float
|
## Float
|
||||||
|
|
||||||
```ebnf
|
```ebnf
|
||||||
@ -53,7 +52,12 @@ scientific_notation = "e", ("+" | "-"), decimal_digit+
|
|||||||
123e-3
|
123e-3
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
All floating point numbers must start with at least 1 digit.
|
All floating point numbers must start with at least 1 digit.
|
||||||
`.5` is not a valid floating point number.
|
`.5` is not a valid floating point number.
|
||||||
|
|
||||||
|
|
||||||
`TODO`: Allow scientific notation to omit the `+`/`-`: `10e4`.
|
`TODO`: Allow scientific notation to omit the `+`/`-`: `10e4`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: Operator
|
title: Operator
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# Operator
|
# Operator
|
||||||
|
|
||||||
|
|
||||||
```ebnf
|
```ebnf
|
||||||
Operator = operator_char+
|
Operator = operator_char+
|
||||||
|
|
||||||
@ -15,11 +15,9 @@ operator_char = "+" | "-" | "=" | "*" | "!" | "/" | "|"
|
|||||||
| "<" | ">" | "^" | "." | ":"
|
| "<" | ">" | "^" | "." | ":"
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
+ - / * % < > <= >= -> =>
|
+ - / * % < > <= >= -> =>
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
These are all the characters that can make an operator.
|
These are all the characters that can make an operator.
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
layout: "../_wrapper.astro"
|
layout: "../_wrapper.astro"
|
||||||
title: String
|
title: String
|
||||||
---
|
---
|
||||||
|
import Code from "@/components/Code.astro"
|
||||||
import Code from "@/components/Code.astro";
|
|
||||||
|
|
||||||
# String
|
# String
|
||||||
|
|
||||||
@ -21,13 +20,11 @@ escape_seq = "\n"
|
|||||||
string_char = any_unicode_except_newline_and_double_quote
|
string_char = any_unicode_except_newline_and_double_quote
|
||||||
```
|
```
|
||||||
|
|
||||||
<Code
|
<Code thpcode={`
|
||||||
thpcode={`
|
|
||||||
"hello"
|
"hello"
|
||||||
""
|
""
|
||||||
"it's me"
|
"it's me"
|
||||||
"\\"Mario\\""
|
"\\"Mario\\""
|
||||||
`}
|
`} />
|
||||||
/>
|
|
||||||
|
|
||||||
`TODO`: String interpolation
|
`TODO`: String interpolation
|
||||||
|
@ -34,3 +34,5 @@ pub enum TokenType {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Every keyword has its own token.
|
Every keyword has its own token.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,ts,tsx}"],
|
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,ts,tsx}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
@ -17,48 +17,48 @@ export default {
|
|||||||
"c-primary": "var(--c-primary)",
|
"c-primary": "var(--c-primary)",
|
||||||
"c-secondary": "var(--c-secondary)",
|
"c-secondary": "var(--c-secondary)",
|
||||||
"c-nav-bg": "var(--c-nav-bg)",
|
"c-nav-bg": "var(--c-nav-bg)",
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
mono: "var(--font-code)",
|
"mono": "var(--font-code)",
|
||||||
display: "var(--font-display)",
|
"display": "var(--font-display)",
|
||||||
body: "var(--font-body)",
|
"body": "var(--font-body)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
container: false,
|
container: false
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
function ({ addComponents }) {
|
function ({ addComponents }) {
|
||||||
addComponents({
|
addComponents({
|
||||||
".container": {
|
'.container': {
|
||||||
width: "98%",
|
width: '98%',
|
||||||
"@screen sm": {
|
'@screen sm': {
|
||||||
maxWidth: "640px",
|
maxWidth: '640px',
|
||||||
},
|
},
|
||||||
"@screen md": {
|
'@screen md': {
|
||||||
maxWidth: "768px",
|
maxWidth: '768px',
|
||||||
},
|
},
|
||||||
"@screen lg": {
|
'@screen lg': {
|
||||||
maxWidth: "1024px",
|
maxWidth: '1024px',
|
||||||
},
|
},
|
||||||
"@screen xl": {
|
'@screen xl': {
|
||||||
maxWidth: "1400px",
|
maxWidth: '1400px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
".small-container": {
|
'.small-container': {
|
||||||
width: "98%",
|
width: '98%',
|
||||||
"@screen sm": {
|
'@screen sm': {
|
||||||
maxWidth: "640px",
|
maxWidth: '640px',
|
||||||
},
|
},
|
||||||
"@screen md": {
|
'@screen md': {
|
||||||
maxWidth: "768px",
|
maxWidth: '768px',
|
||||||
},
|
},
|
||||||
"@screen lg": {
|
'@screen lg': {
|
||||||
maxWidth: "inherit",
|
maxWidth: 'inherit',
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user