Add Odin templating and Z80Disasm
This commit is contained in:
61
odin-templating/README.md
Normal file
61
odin-templating/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# HTML Templates in Odin
|
||||
|
||||
```odin
|
||||
page := html()
|
||||
{head()
|
||||
{title("HTML Title")}
|
||||
}
|
||||
{body()
|
||||
{div()
|
||||
class("container")
|
||||
{div()
|
||||
class("article")
|
||||
{h1("Hello, world!")}
|
||||
{h6("Goodbye, world!")}
|
||||
{
|
||||
{div()
|
||||
attr("aria-data", "idk!")
|
||||
text("Yo, what's up!")
|
||||
}
|
||||
{div()
|
||||
{a("don't be eeeviiiilll", "http://google.com")}
|
||||
{a("This link has a class called 'button'")
|
||||
class("button")}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{img("background.png", "green texture")
|
||||
class("img")
|
||||
}
|
||||
{div()
|
||||
id("lol")
|
||||
class("hello")
|
||||
}
|
||||
{text("Random text")}
|
||||
{text("")}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>HTML Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="article">
|
||||
<h1>Hello, world!</h1>
|
||||
<h6>Goodbye, world!</h6>
|
||||
<div aria-data="idk!">Yo, what's up!</div>
|
||||
<div>
|
||||
<a href="http://google.com">don't be eeeviiiilll</a>
|
||||
<a class="button" href="">This link has a class called 'button'</a>
|
||||
</div>
|
||||
</div>
|
||||
<img class="img" alt="green texture" src="background.png">
|
||||
<div class="hello" id="lol"></div>
|
||||
Random text
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
237
odin-templating/html_builder.odin
Normal file
237
odin-templating/html_builder.odin
Normal file
@@ -0,0 +1,237 @@
|
||||
package html_builder
|
||||
|
||||
import "core:fmt"
|
||||
import "core:slice"
|
||||
import "core:strings"
|
||||
|
||||
Element :: struct {
|
||||
tag_name: string,
|
||||
id: string,
|
||||
children: [dynamic]Node,
|
||||
attributes: map[string]string,
|
||||
class_list: [dynamic]string,
|
||||
}
|
||||
|
||||
Node :: union {
|
||||
Element,
|
||||
string,
|
||||
}
|
||||
|
||||
empty_tags: map[string]bool = {
|
||||
"img" = true,
|
||||
"br" = true,
|
||||
"hr" = true,
|
||||
}
|
||||
|
||||
indent :: proc(sb: ^strings.Builder, depth: int) {
|
||||
for i in 0 ..< depth {
|
||||
fmt.sbprintf(sb, "\t")
|
||||
}
|
||||
}
|
||||
|
||||
element_to_string :: proc(sb: ^strings.Builder, e: Element, depth: int = 0) -> string {
|
||||
indent(sb, depth)
|
||||
|
||||
fmt.sbprintf(sb, "<%s", e.tag_name)
|
||||
|
||||
for name, attr in e.attributes {
|
||||
fmt.sbprintf(sb, " %s=\"%s\"", name, attr)
|
||||
}
|
||||
|
||||
fmt.sbprintf(sb, ">")
|
||||
|
||||
children_len := len(e.children)
|
||||
|
||||
if children_len == 1 {
|
||||
switch ch in e.children[0] {
|
||||
case string:
|
||||
fmt.sbprintf(sb, "%s", ch)
|
||||
case Element:
|
||||
fmt.sbprintf(sb, "\n")
|
||||
element_to_string(sb, ch, depth + 1)
|
||||
fmt.sbprintf(sb, "\n")
|
||||
indent(sb, depth)
|
||||
}
|
||||
}
|
||||
if children_len > 1 {
|
||||
for child, idx in e.children {
|
||||
fmt.sbprintf(sb, "\n")
|
||||
switch ch in child {
|
||||
case string:
|
||||
indent(sb, depth + 1)
|
||||
fmt.sbprintf(sb, "%s", ch)
|
||||
case Element:
|
||||
element_to_string(sb, ch, depth + 1)
|
||||
}
|
||||
}
|
||||
fmt.sbprintf(sb, "\n")
|
||||
indent(sb, depth)
|
||||
}
|
||||
if !empty_tags[e.tag_name] do fmt.sbprintf(sb, "</%s>", e.tag_name)
|
||||
|
||||
return strings.to_string(sb^)
|
||||
}
|
||||
|
||||
parent_element: ^Element
|
||||
elements_by_id: map[string]^Element
|
||||
elements_by_classes: map[string][dynamic]^Element
|
||||
|
||||
_element_scope_start :: proc(tag_name: string, id: string = "") -> (previous_parent: ^Element) {
|
||||
|
||||
children := &parent_element.children
|
||||
new_idx := len(children^)
|
||||
new_element := Element {
|
||||
tag_name = tag_name,
|
||||
children = make([dynamic]Node),
|
||||
}
|
||||
append(children, new_element)
|
||||
|
||||
previous_parent = parent_element
|
||||
parent_element = &children[new_idx].(Element)
|
||||
|
||||
|
||||
return previous_parent
|
||||
}
|
||||
|
||||
_element_scope_end :: proc(previous_parent: ^Element) {
|
||||
parent_element = previous_parent
|
||||
}
|
||||
|
||||
// @(deferred_out = free_html)
|
||||
html :: proc() -> ^Element {
|
||||
parent_element = new(Element)
|
||||
parent_element.tag_name = "html"
|
||||
parent_element.children = make([dynamic]Node)
|
||||
|
||||
return parent_element
|
||||
}
|
||||
|
||||
text :: proc(txt: string) {
|
||||
append(&parent_element.children, txt)
|
||||
}
|
||||
|
||||
attr :: proc(name: string, value: string) {
|
||||
if name == "class" {
|
||||
class(value)
|
||||
return
|
||||
}
|
||||
map_insert(&parent_element.attributes, name, value)
|
||||
}
|
||||
id :: proc(id: string) {
|
||||
if id != "" {
|
||||
parent_element.id = id
|
||||
parent_element.attributes["id"] = id
|
||||
elements_by_id[id] = parent_element
|
||||
}
|
||||
}
|
||||
|
||||
class :: proc(class_names: string) {
|
||||
classes: [dynamic]string
|
||||
for class in strings.split(class_names, " ") {
|
||||
_, found := slice.linear_search(parent_element.class_list[:], class)
|
||||
|
||||
if elements_by_classes[class] == nil do elements_by_classes[class] = make([dynamic]^Element)
|
||||
if !found {
|
||||
append(&classes, class)
|
||||
append(&elements_by_classes[class], parent_element)
|
||||
}
|
||||
}
|
||||
append(&parent_element.class_list, ..classes[:])
|
||||
map_insert(
|
||||
&parent_element.attributes,
|
||||
"class",
|
||||
strings.join(parent_element.class_list[:], " "),
|
||||
)
|
||||
}
|
||||
|
||||
free_html :: proc(el: ^Element) {
|
||||
defer parent_element = nil
|
||||
handle_children :: proc(children: [dynamic]Node) {
|
||||
for e in children {
|
||||
_e, ok := e.(Element)
|
||||
if ok do handle_children(_e.children)
|
||||
}
|
||||
delete(children)
|
||||
}
|
||||
|
||||
handle_children(el.children)
|
||||
free(el)
|
||||
}
|
||||
|
||||
@(deferred_out = _element_scope_end)
|
||||
body :: proc(id: string = "") -> (previous_parent: ^Element) {
|
||||
return _element_scope_start("body", id)
|
||||
}
|
||||
|
||||
@(deferred_out = _element_scope_end)
|
||||
head :: proc(id: string = "") -> (previous_parent: ^Element) {
|
||||
return _element_scope_start("head", id)
|
||||
}
|
||||
|
||||
@(deferred_out = _element_scope_end)
|
||||
title :: proc(txt: string = "") -> (previous_parent: ^Element) {
|
||||
el := _element_scope_start("title")
|
||||
text(txt)
|
||||
return el
|
||||
}
|
||||
|
||||
@(deferred_out = _element_scope_end)
|
||||
h1 :: proc(txt: string = "") -> (previous_parent: ^Element) {
|
||||
el := _element_scope_start("h1")
|
||||
text(txt)
|
||||
return el
|
||||
}
|
||||
@(deferred_out = _element_scope_end)
|
||||
h2 :: proc(txt: string = "") -> (previous_parent: ^Element) {
|
||||
el := _element_scope_start("h2")
|
||||
text(txt)
|
||||
return el
|
||||
}
|
||||
@(deferred_out = _element_scope_end)
|
||||
h3 :: proc(txt: string = "") -> (previous_parent: ^Element) {
|
||||
el := _element_scope_start("h3")
|
||||
text(txt)
|
||||
return el
|
||||
}
|
||||
@(deferred_out = _element_scope_end)
|
||||
h4 :: proc(txt: string = "") -> (previous_parent: ^Element) {
|
||||
el := _element_scope_start("h4")
|
||||
text(txt)
|
||||
return el
|
||||
}
|
||||
@(deferred_out = _element_scope_end)
|
||||
h5 :: proc(txt: string = "") -> (previous_parent: ^Element) {
|
||||
el := _element_scope_start("h5")
|
||||
text(txt)
|
||||
return el
|
||||
}
|
||||
@(deferred_out = _element_scope_end)
|
||||
h6 :: proc(txt: string = "") -> (previous_parent: ^Element) {
|
||||
el := _element_scope_start("h6")
|
||||
text(txt)
|
||||
return el
|
||||
}
|
||||
|
||||
@(deferred_out = _element_scope_end)
|
||||
div :: proc(id: string = "") -> (previous_parent: ^Element) {
|
||||
previous_parent = _element_scope_start("div", id)
|
||||
return previous_parent
|
||||
}
|
||||
|
||||
@(deferred_out = _element_scope_end)
|
||||
img :: proc(src: string = "", alt: string = "") -> (previous_parent: ^Element) {
|
||||
previous_parent = _element_scope_start("img")
|
||||
parent_element.children = nil
|
||||
parent_element.attributes["src"] = src
|
||||
if alt != "" do parent_element.attributes["alt"] = alt
|
||||
return previous_parent
|
||||
}
|
||||
|
||||
@(deferred_out = _element_scope_end)
|
||||
a :: proc(txt: string, href: string = "") -> (previous_parent: ^Element) {
|
||||
previous_parent = _element_scope_start("a")
|
||||
append(&parent_element.children, txt)
|
||||
parent_element.attributes["href"] = href
|
||||
return previous_parent
|
||||
}
|
||||
|
||||
46
odin-templating/main.odin
Normal file
46
odin-templating/main.odin
Normal file
@@ -0,0 +1,46 @@
|
||||
package html_builder
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
|
||||
main :: proc() {
|
||||
page := html()
|
||||
{head()
|
||||
{title("HTML Title")}
|
||||
}
|
||||
{body()
|
||||
{div()
|
||||
class("container")
|
||||
{div()
|
||||
class("article")
|
||||
{h1("Hello, world!")}
|
||||
{h6("Goodbye, world!")}
|
||||
{
|
||||
{div()
|
||||
attr("aria-data", "idk!")
|
||||
text("Yo, what's up!")
|
||||
}
|
||||
{div()
|
||||
{a("don't be eeeviiiilll", "http://google.com")}
|
||||
{a("This link has a class called 'button'")
|
||||
class("button")}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{img("background.png", "green texture")
|
||||
class("img")
|
||||
}
|
||||
{div()
|
||||
id("lol")
|
||||
class("hello")
|
||||
}
|
||||
{text("Random text")}
|
||||
}
|
||||
}
|
||||
|
||||
defer free_html(page)
|
||||
sb: strings.Builder
|
||||
fmt.println(element_to_string(&sb, page^))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user