removing hardcoded html files and replacing them with markdown

the markdown gets transpiled on build and built files are then included
in the server executable

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2024-12-14 13:17:58 +01:00
parent 71ba2c2486
commit fae75072f4
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
18 changed files with 195 additions and 82 deletions

1
.gitignore vendored
View file

@ -9,5 +9,6 @@ db.sqlite-journal
# assets # assets
/depell/src/*.gz /depell/src/*.gz
/depell/src/*.wasm /depell/src/*.wasm
/depell/src/static-pages/*.html
#**/*-sv.rs #**/*-sv.rs
/bytecode/src/instrs.rs /bytecode/src/instrs.rs

16
Cargo.lock generated
View file

@ -844,6 +844,15 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "markdown"
version = "1.0.0-alpha.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6491e6c702bf7e3b24e769d800746d5f2c06a6c6a2db7992612e0f429029e81"
dependencies = [
"unicode-id",
]
[[package]] [[package]]
name = "matchit" name = "matchit"
version = "0.7.3" version = "0.7.3"
@ -1446,6 +1455,12 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-id"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.13"
@ -1648,6 +1663,7 @@ name = "xtask"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"markdown",
"walrus", "walrus",
] ]

View file

@ -17,9 +17,7 @@ members = [
[workspace.dependencies] [workspace.dependencies]
hbbytecode = { path = "bytecode", default-features = false } hbbytecode = { path = "bytecode", default-features = false }
hbvm = { path = "vm", default-features = false } hbvm = { path = "vm", default-features = false }
hbxrt = { path = "xrt" }
hblang = { path = "lang", default-features = false } hblang = { path = "lang", default-features = false }
hbjit = { path = "jit" }
[profile.release] [profile.release]
lto = true lto = true

View file

@ -1,5 +1,6 @@
* { * {
font-family: var(--font); font-family: var(--font);
line-height: 1.3;
} }
body { body {
@ -135,6 +136,11 @@ button:hover:not(:active) {
background: var(--primary); background: var(--primary);
} }
code {
font-family: var(--monospace);
line-height: 1;
}
div#code-editor { div#code-editor {
display: flex; display: flex;
position: relative; position: relative;

View file

@ -433,9 +433,12 @@ function cacheInputs(target) {
/** @param {string} [path] */ /** @param {string} [path] */
function updateTab(path) { function updateTab(path) {
console.log(path);
for (const elem of document.querySelectorAll("button[hx-push-url]")) { for (const elem of document.querySelectorAll("button[hx-push-url]")) {
if (elem instanceof HTMLButtonElement) if (elem instanceof HTMLButtonElement)
elem.disabled = elem.getAttribute("hx-push-url") === (path ?? window.location.pathname); elem.disabled =
elem.getAttribute("hx-push-url") === path
|| elem.getAttribute("hx-push-url") === window.location.pathname;
} }
} }

View file

@ -1,4 +1,4 @@
#![feature(iter_collect_into)] #![feature(iter_collect_into, macro_metavar_expr)]
use { use {
argon2::{password_hash::SaltString, PasswordVerifier}, argon2::{password_hash::SaltString, PasswordVerifier},
axum::{ axum::{
@ -52,12 +52,15 @@ async fn amain() {
db::init(); db::init();
let router = axum::Router::new() let router = axum::Router::new()
.route("/", get(Index::page))
.route("/index.css", static_asset!("text/css", "index.css")) .route("/index.css", static_asset!("text/css", "index.css"))
.route("/index.js", static_asset!("text/javascript", "index.js")) .route("/index.js", static_asset!("text/javascript", "index.js"))
.route("/hbfmt.wasm", static_asset!("application/wasm", "hbfmt.wasm")) .route("/hbfmt.wasm", static_asset!("application/wasm", "hbfmt.wasm"))
.route("/hbc.wasm", static_asset!("application/wasm", "hbc.wasm")) .route("/hbc.wasm", static_asset!("application/wasm", "hbc.wasm"))
.route("/index-view", get(Index::get)) .route("/", get(Index::page))
.route("/index-view", get(Index::get_with_blog))
.route("/blogs/index-view", get(Index::get))
.route("/blogs/developing-hblang", get(DevelopingHblang::page))
.route("/blogs/developing-hblang-view", get(DevelopingHblang::get))
.route("/feed", get(Feed::page)) .route("/feed", get(Feed::page))
.route("/feed-view", get(Feed::get)) .route("/feed-view", get(Feed::get))
.route("/feed-more", post(Feed::more)) .route("/feed-more", post(Feed::more))
@ -199,12 +202,45 @@ impl Page for Feed {
} }
} }
#[derive(Default)] macro_rules! decl_static_pages {
struct Index; ($(
#[derive(PublicPage)]
#[page(static = $file:literal)]
struct $name:ident;
)*) => {
const ALL_STATIC_PAGES: [&str; ${count($file)}] = [$($file),*];
impl PublicPage for Index { $(
#[derive(Default)]
struct $name;
impl PublicPage for $name {
fn render_to_buf(self, buf: &mut String) { fn render_to_buf(self, buf: &mut String) {
buf.push_str(include_str!("welcome-page.html")); buf.push_str(include_str!(concat!("static-pages/", $file, ".html")));
}
async fn page(session: Option<Session>) -> Html<String> {
base(|s| blog_base(s, |s| Self::default().render_to_buf(s)), session.as_ref())
}
}
)*
};
}
decl_static_pages! {
#[derive(PublicPage)]
#[page(static = "welcome")]
struct Index;
#[derive(PublicPage)]
#[page(static = "developing-hblang")]
struct DevelopingHblang;
}
impl Index {
async fn get_with_blog() -> Html<String> {
let mut buf = String::new();
blog_base(&mut buf, |s| Index.render_to_buf(s));
Html(buf)
} }
} }
@ -242,7 +278,17 @@ impl Page for Post {
<input type="submit" value="submit"> <input type="submit" value="submit">
<pre id="compiler-output"></pre> <pre id="compiler-output"></pre>
</form> </form>
!{include_str!("post-page.html")}
<div id="dep-list">
<input placeholder="search impoted deps.." oninput="filterCodeDeps(this, event)">
<section id="deps">
"results show here..."
</section>
</div>
<div>
!{include_str!("static-pages/post.html")}
</div>
} }
} }
} }
@ -468,7 +514,6 @@ impl Page for Profile {
} else { } else {
"no posts" "no posts"
} }
!{include_str!("profile-page.html")}
} }
}) })
} }
@ -631,6 +676,29 @@ impl Signup {
} }
} }
fn blog_base(s: &mut String, body: impl FnOnce(&mut String)) {
let nav_button = |f: &mut String, name: &str| {
write_html! {(f)
<button "hx-push-url"={format_args!("/blogs/{name}")}
"hx-get"={format_args!("/blogs/{name}-view")}
"hx-target"="main#blog"
"hx-swap"="innerHTML">name</button>
}
};
write_html! {(*s)
<nav><section>
<button "hx-push-url"="/" "hx-get"="/blogs/index-view"
"hx-target"="main#blog" "hx-swap"="innerHTML">"welcome"</button>
for name in &ALL_STATIC_PAGES[1..] {
|f|{nav_button(f, name)}
}
</section></nav>
<section id="post-form"></section>
<main id="blog">|f|{body(f)}</main>
}
}
fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html<String> { fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html<String> {
let username = session.map(|s| &s.name); let username = session.map(|s| &s.name);

View file

@ -1,21 +0,0 @@
<div id="dep-list">
<input placeholder="search impoted deps.." oninput="filterCodeDeps(this, event)">
<section id="deps">
results show here...
</section>
</div>
<div>
<h3>About posting code</h3>
<p>
If you are unfammiliar with <a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a>, refer to the
<strong>hblang/README.md</strong> or
vizit <a href="/profile/mlokis">mlokis'es posts</a>. Preferably don't edit the code here.
</p>
<h3>Extra textarea features</h3>
<ul>
<li>proper tab behaviour</li>
<li>snap to previous tab boundary on "empty" lines</li>
</ul>

View file

@ -0,0 +1,31 @@
# The journey to optimizing compiler
It's been years since I was continuously trying to make a compiler to implement language of my dreams. Problem was tho that I wanted something similar to Rust, which if you did not know, `rustc` far exceeded the one million lines of code mark some time ago, so implementing such language would take me years if not decades, but I still tired it.
Besides being extremely ambitions, the problem with my earliest attempts at making a compiler, is that literally nobody, not even me, was using the language, and so retroactively I am confident, what I implemented was a complex test-case implementation, and not a compiler. I often fall into a trap of implementing edge cases instead of an algorithm that would handle not only the very few thing the tests do but also all the other stuff that users of the language would try.
Another part of why I was failing for all that time, is that I did the hardest thing first without understanding the core concepts involved in translating written language to IR, god forbid assembly. I wasted a lot of time like this, but at least I learned Rust well. At some point I found a job where I started developing a decentralized network and that fully drawn me away from language development.
## Completely new approach
At some point the company I was working for started having financial issues and they were unable to pay me. During that period, I discovered that my love for networking was majorly fueled by the monetary gains associated with it. I burned out, and started to look for things to do with the free time.
One could say timing was perfect because [`ableos`](https://git.ablecorp.us/AbleOS/ableos) was desperately in need of a sane programming language that compiles to the home made VM ISA used for all software ran in `ableos`, but there was nobody crazy enough to do this. I got terribly nerd sniped, tho I don't regret it. Process of making a language for `ableos` was completely different. Firstly, it needed to be done asap, the lack of a good language blocked everyone form writing drivers for `ableos`, secondly, the moment the language is at least a little bit usable, people other then me will start using it, and lastly, the ISA the language compiles to very simple to emit, understand, and run.
### Urgency is a bliss
I actually managed to make the language somewhat work in one week, mainly because my mind set changed. I no longer spent a lot of time designing syntax for elegance, I designed it so that it incredibly easy to parse, meaning I can spent minimal effort implementing the parser, and fully focus on the hard problem of translating AST to instructions. Surprisingly, making everything an expression and not enforcing any arbitrary rules, makes the code you can write incredibly flexible and (most) people love it. One of the decisions I made to save time (or maybe it was an accident) was to make `,;` not enforced, meaning, you are allowed to write delimiters in lists but, as long as it does not change the intent of the code, you can leave them out. In practice, you actually don't need semicolons, unless the next line starts with something sticky like `*x`, int that case you put a semicolon on the previous line to tell the parser where the current expression ends.
### Only the problem I care about
Its good to note that writing a parser is no longer interesting for me. I wrote many parsers before and writing one no longer feel rewarding, but more like a chore. The real problem I was excited about was translating AST to instructions, I always ended up overcomplicating this step wit edge cases for every possible scenario that can happen in code, for which there are infinite. But why did I succeed this time? Well all the friction related to getting something that I can execute was so low, I could iterate quickly and realize what I am doing wrong before I burn out. In a week I managed to understand what I was failing to do for years, partly because of all the previous suffering, but mainly because it was so easy to pivot and try new things. And so I managed to make my first single pass compiler, and people immediately started using it.
### Don't implement features nobody asked for
Immediately after someone else then me wrote something in `hb` stuff started breaking, over the course of a month I kept fixing bugs and adding new features just fine, and more people started to use the language. All was good and well until I looked into the code. It was incredibly cursed, full of tricks to work around the compiler not doing any optimizations. At that moment I realized the whole compiler after parser needs to be rewritten, I had to implement optimizations, otherwise people wont be able to write readable code that runs fast. All of the features I have added up until now, were a technical dept now. Unless they are all working with optimizations, can't compile the existing code. Yes, if feature exists, be sure as hell it will be used.
It took around 4 months to reimplement everything make make the optimal code look like what you are used to in other languages. I am really thankful for [sea of nodes](https://github.com/SeaOfNodes), and all the amazing work Cliff Click and others do to make demystify optimizers, It would have taken much longer to for me to figure all the principles out without the exhaustive [tutorial](https://github.com/SeaOfNodes/Simple?tab=readme-ov-file).
## How my understanding of optimizations changed
I need to admit, before writing a single pass compiler and later upgrading it to optimizing one, I took optimizations as some magic that makes code faster and honestly believed they are optional and most of the hard work is done in the process of translating readable text to the machine code. That is almost true with minus the readable part. If you want the code you write to perform well, with a compiler that translates your code from text to instructions as its written, you will be forced to do everything modern optimizers do, by hand in your code. TODO...

View file

@ -0,0 +1,8 @@
### About posting code
If you are unfammiliar with [hblang](https://git.ablecorp.us/AbleOS/holey-bytes), refer to the **hblang/README.md** or vizit [mlokis'es posts](/profile/mlokis). Preferably don't edit the code here.
### Extra textarea features
- proper tab behaviour
- snap to previous tab boundary on "empty" lines

View file

@ -0,0 +1,11 @@
## Welcome to depell
Depell (dependency hell) is a simple "social" media site, except that all you can post is [hblang](https://git.ablecorp.us/AbleOS/holey-bytes) code. Instead of likes you run the program, and instead of mentions you import the program as dependency. Run counts even when ran indirectly.
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
## Security?
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
arbitrary JS code, WASM can act as a container inside the JS.

View file

@ -1,17 +0,0 @@
<h1>Welcome to depell</h1>
<p>
Depell (dependency hell) is a simple "social" media site best compared to twitter, except that all you can post is
<a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a> code with no comments allowed. Instead of likes you
run the program, and instead of retweets you import the program as dependency. Run counts even when ran indirectly.
</p>
<p>
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
</p>
<h2>Security?</h2>
<p>
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
arbitrary JS code, WASM can act as a container inside the JS.
</p>

15
foo.hb
View file

@ -1,15 +0,0 @@
Res := fn($O: type, $E: type): type return union(enum) {
ok: O,
err: E,
}
main := fn(): uint {
r := do_something()
if r == .err return v.err
return v.ok
}
do_something := fn(): Res(uint, uint) {
return .{ok: 0}
}

View file

@ -31,7 +31,6 @@ use {
}, },
hashbrown::hash_map, hashbrown::hash_map,
hbbytecode::DisasmError, hbbytecode::DisasmError,
std::panic,
}; };
pub const VOID: Nid = 0; pub const VOID: Nid = 0;
@ -2978,7 +2977,7 @@ impl<'a> Codegen<'a> {
Expr::Comment { .. } => Some(Value::VOID), Expr::Comment { .. } => Some(Value::VOID),
Expr::String { pos, literal } => { Expr::String { pos, literal } => {
let literal = &literal[1..literal.len() - 1]; let literal = &literal[1..literal.len() - 1];
let mut data = std::mem::take(&mut self.pool.lit_buf); let mut data = core::mem::take(&mut self.pool.lit_buf);
debug_assert!(data.is_empty()); debug_assert!(data.is_empty());
let report = |bytes: &core::str::Bytes, message: &str| { let report = |bytes: &core::str::Bytes, message: &str| {

View file

10
smh.hb
View file

@ -1,10 +0,0 @@
main := fn(): int {
a := 10
b := 20
if &a == &a {
return 10
} else {
return 20
}
}

View file

@ -5,5 +5,6 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.89" anyhow = "1.0.89"
markdown = "1.0.0-alpha.21"
walrus = "0.22.0" walrus = "0.22.0"

View file

@ -4,13 +4,42 @@ use {
crate::utils::IterExt, crate::utils::IterExt,
anyhow::Context, anyhow::Context,
std::{ std::{
fs::File, fs::{self, File},
io::{self, BufRead, BufReader, BufWriter, Seek, Write}, io::{self, BufRead, BufReader, BufWriter, Seek, Write},
path::Path, path::{Path, PathBuf},
}, },
walrus::{ir::Value, ConstExpr, GlobalKind, ValType}, walrus::{ir::Value, ConstExpr, GlobalKind, ValType},
}; };
pub fn convert_md_to_html(dir: &str) -> anyhow::Result<()> {
let dir_path = Path::new(dir);
if !dir_path.is_dir() {
anyhow::bail!("{} is not a valid directory", dir);
}
for entry in fs::read_dir(dir_path)? {
let entry = entry?;
let path = entry.path();
if path.extension().is_none_or(|s| s != "md") {
continue;
}
let markdown_content = fs::read_to_string(&path)?;
let html_content =
markdown::to_html_with_options(&markdown_content, &markdown::Options::gfm())
.map_err(anyhow::Error::msg)?;
let mut output_path = PathBuf::from(&path);
output_path.set_extension("html");
fs::write(output_path, html_content)?;
}
Ok(())
}
fn root() -> &'static Path { fn root() -> &'static Path {
Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap() Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap()
} }
@ -73,6 +102,7 @@ fn main() -> anyhow::Result<()> {
build_wasm_blob("hbc", true)?; build_wasm_blob("hbc", true)?;
exec(build_cmd("gzip -k -f depell/src/index.js"))?; exec(build_cmd("gzip -k -f depell/src/index.js"))?;
exec(build_cmd("gzip -k -f depell/src/index.css"))?; exec(build_cmd("gzip -k -f depell/src/index.css"))?;
convert_md_to_html("depell/src/static-pages")?;
Ok(()) Ok(())
} }
"build-depell" => { "build-depell" => {
@ -80,6 +110,7 @@ fn main() -> anyhow::Result<()> {
build_wasm_blob("hbc", false)?; build_wasm_blob("hbc", false)?;
exec(build_cmd("gzip -k -f depell/src/index.js"))?; exec(build_cmd("gzip -k -f depell/src/index.js"))?;
exec(build_cmd("gzip -k -f depell/src/index.css"))?; exec(build_cmd("gzip -k -f depell/src/index.css"))?;
convert_md_to_html("depell/src/static-pages")?;
Ok(()) Ok(())
} }
"watch-depell-debug" => { "watch-depell-debug" => {
@ -102,7 +133,10 @@ fn main() -> anyhow::Result<()> {
))?; ))?;
Ok(()) Ok(())
} }
_ => Ok(()), unknown => {
anyhow::bail!("command {unknown:?} not found, availale commands:\
fmt, build-depell-debug, build-depell, watch-depell-debug, watch-depell, release-depell");
}
} }
} }