Add landpage components
This commit is contained in:
parent
5b260e2f6f
commit
1714d7ee76
@ -17,6 +17,9 @@
|
|||||||
src: url('/Iosevka/Bold.woff2') format('woff2');
|
src: url('/Iosevka/Bold.woff2') format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--c-thp: #f472b6;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--c-bg: #121212;
|
--c-bg: #121212;
|
||||||
|
11
src/components/CodeEditor.astro
Normal file
11
src/components/CodeEditor.astro
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
import { trimAndDedent } from "./utils";
|
||||||
|
|
||||||
|
const {thpcode} = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="flex h-full py-8 items-center">
|
||||||
|
<div class="p-4 w-full">
|
||||||
|
<pre class="language-thp" data-language="thp"><code class="language-thp">{trimAndDedent(thpcode).join("\n")}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -2,77 +2,9 @@
|
|||||||
import { lex } from "../lexer/lexer";
|
import { lex } from "../lexer/lexer";
|
||||||
import type { Instruction } from "../thp_machine/machine_parser";
|
import type { Instruction } from "../thp_machine/machine_parser";
|
||||||
import { parse_str } from "../thp_machine/machine_parser";
|
import { parse_str } from "../thp_machine/machine_parser";
|
||||||
|
import { trimAndDedent } from "./utils";
|
||||||
const {code, steps} = Astro.props;
|
const {code, steps} = Astro.props;
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the following:
|
|
||||||
* - Removes the first & last line, if they are empty
|
|
||||||
* - Picks the indentation level from the first non-white line
|
|
||||||
* - Dedents the following lines
|
|
||||||
*/
|
|
||||||
function trimAndDedent(input: string): Array<string> {
|
|
||||||
let lines = input.split("\n");
|
|
||||||
|
|
||||||
// Remove empty lines at the start
|
|
||||||
while (lines[0] === "") {
|
|
||||||
lines = lines.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get indentation level
|
|
||||||
let indentationLevel = 0;
|
|
||||||
for (const char of lines[0]!) {
|
|
||||||
if (char === " ") {
|
|
||||||
indentationLevel += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce indentation, or trim
|
|
||||||
for (let i = 0; i < lines.length; i += 1) {
|
|
||||||
// If the line is empty, continue
|
|
||||||
const characters = lines[i]!.split("");
|
|
||||||
if (characters.length === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all characters are whitespace, append just a newline
|
|
||||||
const nonWhitespace = characters.find((x) => x !== " ");
|
|
||||||
if (nonWhitespace === undefined) {
|
|
||||||
lines[i] = "";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce indentation
|
|
||||||
if (characters.length < indentationLevel) {
|
|
||||||
throw new Error("Invalid indentation while parsing THP code: " + lines[i]);
|
|
||||||
}
|
|
||||||
let currentIndentation = 0;
|
|
||||||
for (const c of characters) {
|
|
||||||
if (c === " ") { currentIndentation += 1 }
|
|
||||||
else {break;}
|
|
||||||
}
|
|
||||||
if (currentIndentation < indentationLevel) {
|
|
||||||
throw new Error("Invalid indentation while parsing THP code: " + lines[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines[i] = characters.slice(4).join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Remove empty lines at the end
|
|
||||||
let endPosition = lines.length - 1;
|
|
||||||
while (true) {
|
|
||||||
if (lines[endPosition] === "") {
|
|
||||||
lines = lines.slice(0, -1);
|
|
||||||
endPosition -= 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
function highlightCode(lines: Array<string>): string {
|
function highlightCode(lines: Array<string>): string {
|
||||||
let outLines: Array<string> = [];
|
let outLines: Array<string> = [];
|
||||||
|
70
src/components/utils.ts
Normal file
70
src/components/utils.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the following:
|
||||||
|
* - Removes the first & last line, if they are empty
|
||||||
|
* - Picks the indentation level from the first non-white line
|
||||||
|
* - Dedents the following lines
|
||||||
|
*/
|
||||||
|
export function trimAndDedent(input: string): Array<string> {
|
||||||
|
let lines = input.split("\n");
|
||||||
|
|
||||||
|
// Remove empty lines at the start
|
||||||
|
while (lines[0] === "") {
|
||||||
|
lines = lines.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get indentation level
|
||||||
|
let indentationLevel = 0;
|
||||||
|
for (const char of lines[0]!) {
|
||||||
|
if (char === " ") {
|
||||||
|
indentationLevel += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce indentation, or trim
|
||||||
|
for (let i = 0; i < lines.length; i += 1) {
|
||||||
|
// If the line is empty, continue
|
||||||
|
const characters = lines[i]!.split("");
|
||||||
|
if (characters.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all characters are whitespace, append just a newline
|
||||||
|
const nonWhitespace = characters.find((x) => x !== " ");
|
||||||
|
if (nonWhitespace === undefined) {
|
||||||
|
lines[i] = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce indentation
|
||||||
|
if (characters.length < indentationLevel) {
|
||||||
|
throw new Error("Invalid indentation while parsing THP code: " + lines[i]);
|
||||||
|
}
|
||||||
|
let currentIndentation = 0;
|
||||||
|
for (const c of characters) {
|
||||||
|
if (c === " ") { currentIndentation += 1 }
|
||||||
|
else {break;}
|
||||||
|
}
|
||||||
|
if (currentIndentation < indentationLevel) {
|
||||||
|
throw new Error("Invalid indentation while parsing THP code: " + lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines[i] = characters.slice(4).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Remove empty lines at the end
|
||||||
|
let endPosition = lines.length - 1;
|
||||||
|
while (true) {
|
||||||
|
if (lines[endPosition] === "") {
|
||||||
|
lines = lines.slice(0, -1);
|
||||||
|
endPosition -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
@ -34,7 +34,7 @@ export function scan_identifier(input: string, starting_position: number, is_dat
|
|||||||
}
|
}
|
||||||
|
|
||||||
function check_keyword(value: string): string {
|
function check_keyword(value: string): string {
|
||||||
const keywords = ["throws", "extends", "constructor", "case", "static", "const", "enum", "union", "loop", "use", "break", "catch", "continue", "do", "else", "finally", "for", "fun", "if", "in", "fn", "nil", "return", "throw", "try", "while", "type", "match", "with", "of", "abstract", "class", "interface", "private", "pub", "override", "open", "init", "val", "var", "mut", "clone"];
|
const keywords = ["throws", "extends", "constructor", "case", "static", "const", "enum", "union", "loop", "use", "break", "catch", "continue", "as", "do", "else", "finally", "for", "fun", "if", "in", "fn", "nil", "return", "throw", "try", "while", "type", "match", "with", "of", "abstract", "class", "interface", "private", "pub", "override", "open", "init", "val", "var", "mut", "clone"];
|
||||||
|
|
||||||
if (keywords.includes(value)) {
|
if (keywords.includes(value)) {
|
||||||
return "keyword";
|
return "keyword";
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
import Navbar from "../components/Navbar.astro";
|
import Navbar from "../components/Navbar.astro";
|
||||||
|
import CodeEditor from "../components/CodeEditor.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<Navbar showSidebarButton={false} />
|
<Navbar showSidebarButton={false} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="container mx-auto lg:py-16 pb-2 pt-20 lg:grid 2xl:grid-cols-[auto_32rem] lg:grid-cols-[auto_36rem] gap-4 lg:px-10 px-2"
|
class="container mx-auto lg:py-16 pb-2 pt-20 lg:grid 2xl:grid-cols-[auto_32rem] lg:grid-cols-[auto_36rem] gap-4 lg:px-10 px-2 min-h-[85vh]"
|
||||||
>
|
>
|
||||||
<div class="lg:pl-10 table">
|
<div class="lg:pl-10 table">
|
||||||
<div class="table-cell align-middle">
|
<div class="table-cell align-middle">
|
||||||
@ -21,7 +22,7 @@ import Navbar from "../components/Navbar.astro";
|
|||||||
compiled to PHP
|
compiled to PHP
|
||||||
</h1>
|
</h1>
|
||||||
<p class="font-display text-c-text text-xl pt-6 lg:pr-12">
|
<p class="font-display text-c-text text-xl pt-6 lg:pr-12">
|
||||||
Inspired by Rust, Zig and Swift, THP has a modern syntax,
|
Inspired by Rust, Zig and Kotlin, THP has a modern syntax,
|
||||||
semantics, type system and stdlib.
|
semantics, type system and stdlib.
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
@ -46,8 +47,10 @@ import Navbar from "../components/Navbar.astro";
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="table">
|
||||||
|
<div class="table-cell align-middle">
|
||||||
<div
|
<div
|
||||||
class="bg-[var(--code-theme-bg-color)] lg:p-6 p-2 rounded-lg relative"
|
class="bg-[var(--code-theme-bg-color)] lg:p-6 p-2 rounded-lg relative w-full"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="absolute lg:inline-block hidden h-[35rem] w-[35rem] -z-10 top-1/2 left-1/2 rounded-full
|
class="absolute lg:inline-block hidden h-[35rem] w-[35rem] -z-10 top-1/2 left-1/2 rounded-full
|
||||||
@ -91,6 +94,54 @@ import Navbar from "../components/Navbar.astro";
|
|||||||
<div id="editor" class="font-mono language-thp"></div>
|
<div id="editor" class="font-mono language-thp"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="bg-c-thp text-c-bg">
|
||||||
|
<h1 class="container mx-auto font-medium py-8 text-3xl">
|
||||||
|
THP is <span class="font-black">actually typed</span>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container mx-auto lg:grid lg:grid-cols-2 gap-4">
|
||||||
|
<div class="px-8 py-12 flex h-full items-center">
|
||||||
|
<div>
|
||||||
|
THP enforces type safety at compile time and, at the programmer's
|
||||||
|
request, at runtime.
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Everything has a type.
|
||||||
|
There are generics.
|
||||||
|
No implicit type conversions. No <code>mixed</code>.
|
||||||
|
No client <code>Any</code> .Type inference included.
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
All possible checks are made at compile time. Runtime checks
|
||||||
|
have a small performance penalty.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<CodeEditor thpcode={`
|
||||||
|
Array[Int] numbers = [0, 1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
numbers.push("7") // Compile error: expected an Int, found a String
|
||||||
|
|
||||||
|
// Runtime type checking
|
||||||
|
val object = try JSON::decode(data)
|
||||||
|
val name = try object.get[String]("name")
|
||||||
|
|
||||||
|
// Any is available, but it's not usable without an explicit cast
|
||||||
|
fun mock() -> Any { ... }
|
||||||
|
|
||||||
|
// Compile error: Cannot use \`Any\` without an explicit cast
|
||||||
|
val result = mock()
|
||||||
|
// Ok
|
||||||
|
val result = mock() as String
|
||||||
|
`} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { thp_highlighter, CodeJar } from "../lexer/highlighter";
|
import { thp_highlighter, CodeJar } from "../lexer/highlighter";
|
||||||
@ -100,29 +151,28 @@ import Navbar from "../components/Navbar.astro";
|
|||||||
});
|
});
|
||||||
|
|
||||||
jar.updateCode(
|
jar.updateCode(
|
||||||
`// Sum types
|
`union Animal {
|
||||||
enum Animal {
|
|
||||||
Dog(String),
|
Dog(String),
|
||||||
Cat(String, Int),
|
Cat(String, Int),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Null safety
|
val cat_name = Post::get("cat_name") ?? "Michifu"
|
||||||
val cat_name = Post::get("cat_name") ?? "Michi"
|
val my_cat = Animal::Cat(cat_name, 9)
|
||||||
val cat1 = Animal::Cat(cat_name, 9)
|
|
||||||
|
|
||||||
// Errors as values, easy handling
|
try change_fur_color(cat) else die("Not a cat")
|
||||||
val cat2 = try clone_cat(cat) else die("Not a cat")
|
|
||||||
|
|
||||||
// Pattern matching
|
|
||||||
match random_animal()
|
match random_animal()
|
||||||
case Dog(name) { print("{name} has 1 live ") }
|
case Dog(name)
|
||||||
case Cat(name, lives) { print("{name} has {lives}") }
|
{
|
||||||
|
print("{name} has 1 live ")
|
||||||
// First class HTML-like templates & components
|
}
|
||||||
<a href="/person/reports/{person.id}">
|
case Cat(name, lives) {
|
||||||
welcome, {person.name}
|
print("{name} has {lives}")
|
||||||
</a>
|
}`,
|
||||||
// And more!`,
|
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
import {highlightOnDom} from "../layouts/thpHighlighter";
|
||||||
|
document.addEventListener("DOMContentLoaded", highlightOnDom);
|
||||||
|
</script>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
@ -4,6 +4,7 @@ export default {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
"c-thp": "var(--c-thp)",
|
||||||
"c-bg": "var(--c-bg)",
|
"c-bg": "var(--c-bg)",
|
||||||
"c-text": "var(--c-text)",
|
"c-text": "var(--c-text)",
|
||||||
"c-text-2": "var(--c-text-2)",
|
"c-text-2": "var(--c-text-2)",
|
||||||
|
Loading…
Reference in New Issue
Block a user