Scripts
This commit is contained in:
@@ -5,7 +5,35 @@ style="<style>$(cat styles.css)</style>"
|
|||||||
doctype='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
doctype='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
||||||
meta='<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
|
meta='<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
|
||||||
start="$doctype<html><head><title>TypoML Preview</title>$meta$style</head><body>"
|
start="$doctype<html><head><title>TypoML Preview</title>$meta$style</head><body>"
|
||||||
end="</body></html>"
|
script='
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var defs = {};
|
||||||
|
var spans = document.querySelectorAll("[data-tlm-def]");
|
||||||
|
for (var i = 0; i < spans.length; i++) {
|
||||||
|
var el = spans[i];
|
||||||
|
defs[el.dataset.tlmDef] = el.dataset.tlmValue;
|
||||||
|
}
|
||||||
|
var tlm = new Proxy({}, {
|
||||||
|
get: function (_, name) {
|
||||||
|
return defs[name];
|
||||||
|
},
|
||||||
|
set: function (_, name, value) {
|
||||||
|
defs[name] = value;
|
||||||
|
var refs = document.querySelectorAll("[data-tlm-var=\"" + name + "\"]");
|
||||||
|
for (var i = 0; i < refs.length; i++) {
|
||||||
|
refs[i].textContent = value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ownKeys: function () {
|
||||||
|
return Object.keys(defs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.tlm = tlm;
|
||||||
|
})();
|
||||||
|
</script>'
|
||||||
|
end="$script</body></html>"
|
||||||
output="$start$input$end"
|
output="$start$input$end"
|
||||||
echo "$output" > /tmp/preview.html
|
echo "$output" > /tmp/preview.html
|
||||||
nohup xdg-open /tmp/preview.html > /dev/null 2>&1 &
|
nohup xdg-open /tmp/preview.html > /dev/null 2>&1 &
|
||||||
|
|||||||
+69
-4
@@ -14,6 +14,34 @@ get_text_content :: proc(token: ^Token, b: ^strings.Builder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_only_var_defs :: proc(token: ^Token) -> bool {
|
||||||
|
for child in token.children {
|
||||||
|
if child.type == .VariableDef || child.type == .BreakLine {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if child.type == .Text {
|
||||||
|
if child.value != nil {
|
||||||
|
if v, ok := child.value.(string); ok && v != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
has_non_var_def := false
|
||||||
|
for gc in child.children {
|
||||||
|
if gc.type != .VariableDef {
|
||||||
|
has_non_var_def = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has_non_var_def {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
slugifyifyifyify :: proc(text: string) -> string {
|
slugifyifyifyify :: proc(text: string) -> string {
|
||||||
lower := strings.to_lower(text, context.temp_allocator)
|
lower := strings.to_lower(text, context.temp_allocator)
|
||||||
res, _ := strings.replace_all(lower, " ", "-", context.temp_allocator)
|
res, _ := strings.replace_all(lower, " ", "-", context.temp_allocator)
|
||||||
@@ -51,11 +79,20 @@ print_html :: proc(token: ^Token) {
|
|||||||
fmt.printf("</h%d>\n", level)
|
fmt.printf("</h%d>\n", level)
|
||||||
|
|
||||||
case TokenType.Paragraph:
|
case TokenType.Paragraph:
|
||||||
fmt.print("<p>")
|
only_var_defs := check_only_var_defs(token)
|
||||||
for child in token.children {
|
if !only_var_defs {
|
||||||
print_html(child)
|
fmt.print("<p>")
|
||||||
|
for child in token.children {
|
||||||
|
print_html(child)
|
||||||
|
}
|
||||||
|
fmt.print("</p>\n")
|
||||||
|
} else {
|
||||||
|
for child in token.children {
|
||||||
|
if child.type != .BreakLine {
|
||||||
|
print_html(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.print("</p>\n")
|
|
||||||
|
|
||||||
case TokenType.Blockquote:
|
case TokenType.Blockquote:
|
||||||
fmt.print("<blockquote>\n")
|
fmt.print("<blockquote>\n")
|
||||||
@@ -316,6 +353,34 @@ print_html :: proc(token: ^Token) {
|
|||||||
fmt.printf("<script src=\"%s\"></script>", url)
|
fmt.printf("<script src=\"%s\"></script>", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TokenType.ScriptBlock:
|
||||||
|
if content, ok := token.value.(string); ok {
|
||||||
|
fmt.printf("<script>%s</script>", content)
|
||||||
|
}
|
||||||
|
|
||||||
|
case TokenType.Variable:
|
||||||
|
if name, ok := token.value.(string); ok {
|
||||||
|
fmt.printf("<span data-tlm-var=\"%s\">", name)
|
||||||
|
for child in token.children {
|
||||||
|
print_html(child)
|
||||||
|
}
|
||||||
|
fmt.print("</span>")
|
||||||
|
}
|
||||||
|
|
||||||
|
case TokenType.VariableDef:
|
||||||
|
if name, ok := token.value.(string); ok {
|
||||||
|
b := strings.builder_make(context.temp_allocator)
|
||||||
|
for child in token.children {
|
||||||
|
get_text_content(child, &b)
|
||||||
|
}
|
||||||
|
val := strings.to_string(b)
|
||||||
|
fmt.printf(
|
||||||
|
"<template data-tlm-def=\"%s\" data-tlm-value=\"%s\"></template>",
|
||||||
|
name,
|
||||||
|
val,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
case TokenType.BreakLine:
|
case TokenType.BreakLine:
|
||||||
fmt.print("<br>\n")
|
fmt.print("<br>\n")
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ print_token :: proc(token: ^Token, depth: int = 0) {
|
|||||||
type_str = "Iframe"
|
type_str = "Iframe"
|
||||||
case TokenType.Script:
|
case TokenType.Script:
|
||||||
type_str = "Script"
|
type_str = "Script"
|
||||||
|
case TokenType.ScriptBlock:
|
||||||
|
type_str = "ScriptBlock"
|
||||||
|
case TokenType.Variable:
|
||||||
|
type_str = "Variable"
|
||||||
|
case TokenType.VariableDef:
|
||||||
|
type_str = "VariableDef"
|
||||||
}
|
}
|
||||||
|
|
||||||
value_str := ""
|
value_str := ""
|
||||||
|
|||||||
+247
-9
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
@@ -39,6 +40,9 @@ TokenType :: enum {
|
|||||||
Video,
|
Video,
|
||||||
Iframe,
|
Iframe,
|
||||||
Script,
|
Script,
|
||||||
|
ScriptBlock,
|
||||||
|
Variable,
|
||||||
|
VariableDef,
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenValue :: union {
|
TokenValue :: union {
|
||||||
@@ -362,15 +366,39 @@ match_pair :: proc(s: string, open1, close1, open2, close2: byte) -> (int, strin
|
|||||||
if len(s) == 0 || s[0] != open1 {
|
if len(s) == 0 || s[0] != open1 {
|
||||||
return 0, "", "", false
|
return 0, "", "", false
|
||||||
}
|
}
|
||||||
close1_idx := strings.index_byte(s, close1)
|
|
||||||
|
depth := 1
|
||||||
|
close1_idx := -1
|
||||||
|
for i := 1; i < len(s); i += 1 {
|
||||||
|
if s[i] == open1 {
|
||||||
|
depth += 1
|
||||||
|
} else if s[i] == close1 {
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0 {
|
||||||
|
close1_idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if close1_idx == -1 {
|
if close1_idx == -1 {
|
||||||
return 0, "", "", false
|
return 0, "", "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
if close1_idx + 1 < len(s) && s[close1_idx + 1] == open2 {
|
if close1_idx + 1 < len(s) && s[close1_idx + 1] == open2 {
|
||||||
close2_idx := strings.index_byte(s[close1_idx + 2:], close2)
|
depth = 1
|
||||||
|
close2_idx := -1
|
||||||
|
for i := close1_idx + 2; i < len(s); i += 1 {
|
||||||
|
if s[i] == open2 {
|
||||||
|
depth += 1
|
||||||
|
} else if s[i] == close2 {
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0 {
|
||||||
|
close2_idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if close2_idx != -1 {
|
if close2_idx != -1 {
|
||||||
close2_idx += close1_idx + 2
|
|
||||||
return close2_idx + 1, s[1:close1_idx], s[close1_idx + 2:close2_idx], true
|
return close2_idx + 1, s[1:close1_idx], s[close1_idx + 2:close2_idx], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,6 +509,42 @@ parse_links :: proc(token: ^Token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find_script_block :: proc(
|
||||||
|
str: string,
|
||||||
|
) -> (
|
||||||
|
start_idx, end_idx: int,
|
||||||
|
is_block: bool,
|
||||||
|
block_type: string,
|
||||||
|
content: string,
|
||||||
|
) {
|
||||||
|
for i := 0; i < len(str); i += 1 {
|
||||||
|
if str[i] == '$' && i + 1 < len(str) && str[i + 1] == '[' {
|
||||||
|
if end, inner, ok := match_single(str[i + 1:], '[', ']'); ok {
|
||||||
|
inner = strings.trim_space(inner)
|
||||||
|
if strings.has_prefix(inner, "/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if inner == "script" || inner == "js" || inner == "javascript" {
|
||||||
|
b := strings.builder_make(context.temp_allocator)
|
||||||
|
strings.write_string(&b, "$[/")
|
||||||
|
strings.write_string(&b, inner)
|
||||||
|
strings.write_string(&b, "]")
|
||||||
|
close_tag := strings.to_string(b)
|
||||||
|
close_idx := strings.index(str[i + 1 + end:], close_tag)
|
||||||
|
if close_idx != -1 {
|
||||||
|
close_idx += i + 1 + end
|
||||||
|
content := str[i + 1 + end:close_idx]
|
||||||
|
return i, close_idx + len(
|
||||||
|
close_tag,
|
||||||
|
), true, inner, strings.trim_space(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, -1, false, "", ""
|
||||||
|
}
|
||||||
|
|
||||||
find_embed :: proc(
|
find_embed :: proc(
|
||||||
str: string,
|
str: string,
|
||||||
) -> (
|
) -> (
|
||||||
@@ -593,6 +657,23 @@ parse_embeds :: proc(token: ^Token) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s_start, s_end, is_script, s_type, s_content := find_script_block(str)
|
||||||
|
if is_script {
|
||||||
|
token.value = nil
|
||||||
|
|
||||||
|
if s_start > 0 {
|
||||||
|
push_token(token, .Text, str[:s_start])
|
||||||
|
}
|
||||||
|
|
||||||
|
script_token := push_token(token, .ScriptBlock, s_content)
|
||||||
|
|
||||||
|
if s_end < len(str) {
|
||||||
|
rem := push_token(token, .Text, str[s_end:])
|
||||||
|
parse_embeds(rem)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
start_idx, end_idx, is_embed, url, alt, e_type := find_embed(str)
|
start_idx, end_idx, is_embed, url, alt, e_type := find_embed(str)
|
||||||
if !is_embed {
|
if !is_embed {
|
||||||
return
|
return
|
||||||
@@ -627,6 +708,126 @@ parse_embeds :: proc(token: ^Token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find_variable :: proc(
|
||||||
|
str: string,
|
||||||
|
) -> (
|
||||||
|
start_idx, end_idx: int,
|
||||||
|
is_var: bool,
|
||||||
|
name: string,
|
||||||
|
val: string,
|
||||||
|
is_def: bool,
|
||||||
|
) {
|
||||||
|
for i := 0; i < len(str); i += 1 {
|
||||||
|
if str[i] == '$' && i + 1 < len(str) && str[i + 1] == '(' {
|
||||||
|
if end, inner, ok := match_single(str[i + 1:], '(', ')'); ok {
|
||||||
|
eq := strings.index_byte(inner, '=')
|
||||||
|
if eq != -1 {
|
||||||
|
return i,
|
||||||
|
i + 1 + end,
|
||||||
|
true,
|
||||||
|
strings.trim_space(inner[:eq]),
|
||||||
|
strings.trim_space(inner[eq + 1:]),
|
||||||
|
true
|
||||||
|
} else if is_url(inner) {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return i, i + 1 + end, true, strings.trim_space(inner), "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1, -1, false, "", "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_variables :: proc(token: ^Token) {
|
||||||
|
if token.type == .CodeBlock || token.type == .InlineCode {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if token.children != nil {
|
||||||
|
for i := 0; i < len(token.children); i += 1 {
|
||||||
|
parse_variables(token.children[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if token.type == .Text {
|
||||||
|
str, ok := token.value.(string)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
start_idx, end_idx, is_var, name, val, is_def := find_variable(str)
|
||||||
|
if !is_var {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token.value = nil
|
||||||
|
|
||||||
|
if start_idx > 0 {
|
||||||
|
push_token(token, .Text, str[:start_idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_def {
|
||||||
|
def_token := push_token(token, .VariableDef, name)
|
||||||
|
push_token(def_token, .Text, val)
|
||||||
|
} else {
|
||||||
|
push_token(token, .Variable, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if end_idx < len(str) {
|
||||||
|
rem := push_token(token, .Text, str[end_idx:])
|
||||||
|
parse_variables(rem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_variables :: proc(token: ^Token, vars: ^map[string]string) {
|
||||||
|
if token.type == .VariableDef {
|
||||||
|
if name, ok := token.value.(string); ok {
|
||||||
|
if len(token.children) > 0 {
|
||||||
|
if val, vok := token.children[0].value.(string); vok {
|
||||||
|
vars[name] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if token.type == .Variable {
|
||||||
|
if name, ok := token.value.(string); ok {
|
||||||
|
if val, found := vars[name]; found {
|
||||||
|
if token.children == nil {
|
||||||
|
push_token(token, .Text, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if token.type == .Link || token.type == .HeaderLink {
|
||||||
|
if url, ok := token.value.(string); ok {
|
||||||
|
for i := 0; i < len(url); i += 1 {
|
||||||
|
if url[i] == '$' && i + 1 < len(url) && url[i + 1] == '(' {
|
||||||
|
if end, inner, ok := match_single(url[i + 1:], '(', ')'); ok {
|
||||||
|
var_name := strings.trim_space(inner)
|
||||||
|
if val, found := vars[var_name]; found {
|
||||||
|
b := strings.builder_make(context.temp_allocator)
|
||||||
|
strings.write_string(&b, url[:i])
|
||||||
|
strings.write_string(&b, val)
|
||||||
|
strings.write_string(&b, url[i + 1 + end:])
|
||||||
|
token.value = strings.to_string(b)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if token.children != nil {
|
||||||
|
for child in token.children {
|
||||||
|
resolve_variables(child, vars)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add_breaklines :: proc(token: ^Token) {
|
add_breaklines :: proc(token: ^Token) {
|
||||||
if token.type == .CodeBlock {
|
if token.type == .CodeBlock {
|
||||||
return
|
return
|
||||||
@@ -668,6 +869,9 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
|||||||
// FIRST PASS
|
// FIRST PASS
|
||||||
|
|
||||||
in_code_block := false
|
in_code_block := false
|
||||||
|
in_script_block := false
|
||||||
|
script_block_content: [dynamic]string
|
||||||
|
script_block_type := ""
|
||||||
|
|
||||||
for untrimmed_line, line_index in lines {
|
for untrimmed_line, line_index in lines {
|
||||||
line := strings.trim_space(untrimmed_line)
|
line := strings.trim_space(untrimmed_line)
|
||||||
@@ -682,6 +886,21 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if in_script_block {
|
||||||
|
if strings.has_prefix(line, "$[/") {
|
||||||
|
full_content := strings.join(script_block_content[:], "\n")
|
||||||
|
script_token := push_token(&root, TokenType.ScriptBlock, full_content)
|
||||||
|
push_token(script_token, TokenType.Text, script_block_type)
|
||||||
|
in_script_block = false
|
||||||
|
script_block_content = nil
|
||||||
|
script_block_type = ""
|
||||||
|
current_token = &root
|
||||||
|
} else {
|
||||||
|
append(&script_block_content, untrimmed_line)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if strings.has_prefix(line, "```") {
|
if strings.has_prefix(line, "```") {
|
||||||
lang := strings.trim_space(line[3:])
|
lang := strings.trim_space(line[3:])
|
||||||
current_token = push_token(&root, TokenType.CodeBlock, lang)
|
current_token = push_token(&root, TokenType.CodeBlock, lang)
|
||||||
@@ -689,6 +908,17 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.has_prefix(line, "$[") && strings.has_suffix(line, "]") {
|
||||||
|
inner := strings.trim_space(line[2:len(line) - 1])
|
||||||
|
if inner == "script" || inner == "js" || inner == "javascript" {
|
||||||
|
in_script_block = true
|
||||||
|
script_block_type = inner
|
||||||
|
script_block_content = nil
|
||||||
|
current_token = &root
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(line) >= 3 {
|
if len(line) >= 3 {
|
||||||
is_hr := true
|
is_hr := true
|
||||||
ch := line[0]
|
ch := line[0]
|
||||||
@@ -811,17 +1041,25 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
|||||||
// SECOND PASS
|
// SECOND PASS
|
||||||
|
|
||||||
for token in root.children {
|
for token in root.children {
|
||||||
add_breaklines(token)
|
parse_inline_code(token)
|
||||||
|
parse_links(token)
|
||||||
|
parse_variables(token)
|
||||||
|
parse_embeds(token)
|
||||||
|
parse_inline_footnotes(token)
|
||||||
|
pass_through_children(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// THIRD PASS
|
// THIRD PASS
|
||||||
|
|
||||||
for token in root.children {
|
for token in root.children {
|
||||||
parse_inline_code(token)
|
add_breaklines(token)
|
||||||
parse_embeds(token)
|
}
|
||||||
parse_inline_footnotes(token)
|
|
||||||
parse_links(token)
|
// VARIABLE RESOLUTION PASS
|
||||||
pass_through_children(token)
|
|
||||||
|
vars: map[string]string
|
||||||
|
for child in root.children {
|
||||||
|
resolve_variables(child, &vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FOURTH PASS
|
// FOURTH PASS
|
||||||
|
|||||||
Reference in New Issue
Block a user