package main import "core:fmt" import "core:strings" import "core:unicode" escape_html :: proc(s: string, allocator := context.temp_allocator) -> string { escaped := decode_escapes(s, allocator) b := strings.builder_make(allocator) for ch in escaped { switch ch { case '&': strings.write_string(&b, "&") case '<': strings.write_string(&b, "<") case '>': strings.write_string(&b, ">") case '"': strings.write_string(&b, """) case: strings.write_rune(&b, ch) } } return strings.to_string(b) } get_text_content :: proc(token: ^Token, b: ^strings.Builder) { if token.type == .Text { if v, ok := token.value.(string); ok { strings.write_string(b, v) } } for child in token.children { get_text_content(child, b) } } 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 { b := strings.builder_make(context.temp_allocator) for ch in text { lower_ch := unicode.to_lower(ch) if unicode.is_letter(lower_ch) || unicode.is_digit(lower_ch) { strings.write_rune(&b, lower_ch) } else if lower_ch == ' ' || lower_ch == '_' || lower_ch == '-' { strings.write_rune(&b, '-') } } res := strings.to_string(b) for { new_res, replaced := strings.replace_all(res, "--", "-", context.temp_allocator) if !replaced { break } res = new_res } res = strings.trim(res, "-") return res } print_html :: proc(token: ^Token) { switch token.type { case TokenType.Root: has_footnotes := false for child in token.children { if child.type == .FootnoteDef && !has_footnotes { fmt.print("
\n") has_footnotes = true } print_html(child) } case TokenType.Header: level := 1 if v, ok := token.value.(int); ok { level = v } b := strings.builder_make(context.temp_allocator) for child in token.children { get_text_content(child, &b) } slug := slugifyifyifyify(strings.to_string(b)) fmt.printf("", level, escape_html(slug)) for child in token.children { print_html(child) } fmt.printf("\n", level) case TokenType.Paragraph: only_var_defs := check_only_var_defs(token) if !only_var_defs { fmt.print("

") for child in token.children { print_html(child) } fmt.print("

\n") } else { for child in token.children { if child.type != .BreakLine { print_html(child) } } } case TokenType.Blockquote: fmt.print("
\n") for child in token.children { print_html(child) } fmt.print("
\n") case TokenType.UnorderedList: fmt.print("\n") case TokenType.OrderedList: fmt.print("
    \n") for child in token.children { print_html(child) } fmt.print("
\n") case TokenType.ListItem: fmt.print("
  • ") for child in token.children { print_html(child) } fmt.print("
  • \n") case TokenType.Bold: fmt.print("") for child in token.children { print_html(child) } fmt.print("") case TokenType.Italics: fmt.print("") for child in token.children { print_html(child) } fmt.print("") case TokenType.Strikethrough: fmt.print("") for child in token.children { print_html(child) } fmt.print("") case TokenType.Table: fmt.print("\n") for child in token.children { print_html(child) } fmt.print("
    \n") case TokenType.TableHeaderRow: fmt.print("") for child in token.children { align := "" if len(child.children) > 0 { first_child := child.children[0] if first_child.type == .Left { align = "left" } else if first_child.type == .Right { align = "right" } else if first_child.type == .Middle { align = "center" } } if align != "" { fmt.printf("", align) for grandchild in child.children[0].children { print_html(grandchild) } } else { fmt.print("") for grandchild in child.children { print_html(grandchild) } } fmt.print("") } fmt.print("\n") case TokenType.TableRow: fmt.print("") for child in token.children { print_html(child) } fmt.print("\n") case TokenType.TableCell: align := "" if len(token.children) > 0 { first_child := token.children[0] if first_child.type == .Left { align = "left" } else if first_child.type == .Right { align = "right" } else if first_child.type == .Middle { align = "center" } } if align != "" { fmt.printf("", align) for child in token.children[0].children { print_html(child) } } else { fmt.print("") for child in token.children { print_html(child) } } fmt.print("") case TokenType.TableSeparator: fmt.print("\n") case TokenType.Left: fmt.print("
    ") for child in token.children { print_html(child) } fmt.print("
    ") case TokenType.Right: fmt.print("
    ") for child in token.children { print_html(child) } fmt.print("
    ") case TokenType.Middle: fmt.print("
    ") for child in token.children { print_html(child) } fmt.print("
    ") case TokenType.FootnoteRef: if id, ok := token.value.(string); ok { fmt.printf( "%s", escape_html(id), escape_html(id), escape_html(id), ) } case TokenType.FootnoteDef: if id, ok := token.value.(string); ok { fmt.printf( "
    ^ ", escape_html(id), escape_html(id), ) for child in token.children { print_html(child) } fmt.print("
    \n") } case TokenType.CodeBlock: lang := "" if v, ok := token.value.(string); ok && v != "" { lang = v } if lang != "" { fmt.printf("
    ", lang)
    		} else {
    			fmt.print("
    ")
    		}
    		for i := 0; i < len(token.children); i += 1 {
    			print_html(token.children[i])
    			if i + 1 < len(token.children) {
    				fmt.print("\n")
    			}
    		}
    		fmt.print("\n
    \n") case TokenType.InlineCode: if code, ok := token.value.(string); ok { fmt.printf("%s", escape_html(code)) } case TokenType.HorizontalRule: fmt.print("
    \n") case TokenType.Underline: fmt.print("") for child in token.children { print_html(child) } fmt.print("") case TokenType.Superscript: fmt.print("") for child in token.children { print_html(child) } fmt.print("") case TokenType.Subscript: fmt.print("") for child in token.children { print_html(child) } fmt.print("") case TokenType.HeaderLink: if target, ok := token.value.(string); ok { slug := slugifyifyifyify(target) fmt.printf("", escape_html(slug)) for child in token.children { print_html(child) } fmt.print("") } case TokenType.Link: if url, ok := token.value.(string); ok { fmt.printf("", escape_html(url)) for child in token.children { print_html(child) } fmt.print("") } case TokenType.Image: if url, ok := token.value.(string); ok { b := strings.builder_make(context.temp_allocator) for child in token.children { get_text_content(child, &b) } alt_text := strings.to_string(b) fmt.printf("\"%s\"", escape_html(url), escape_html(alt_text)) } case TokenType.Audio: if url, ok := token.value.(string); ok { mime := detect_mime_type(url) b := strings.builder_make(context.temp_allocator) for child in token.children { get_text_content(child, &b) } alt_text := strings.to_string(b) if alt_text == "" { alt_text = url } fmt.printf("\n", escape_html(url), escape_html(mime)) fmt.printf("\n", escape_html(url)) fmt.printf("\n") fmt.printf("\n") fmt.printf("%s\n", escape_html(url), escape_html(alt_text)) fmt.print("\n") } case TokenType.Video: if url, ok := token.value.(string); ok { mime := detect_mime_type(url) b := strings.builder_make(context.temp_allocator) for child in token.children { get_text_content(child, &b) } alt_text := strings.to_string(b) if alt_text == "" { alt_text = url } fmt.printf("\n", escape_html(url), escape_html(mime)) fmt.printf("\n", escape_html(url)) fmt.printf("\n") fmt.printf("\n") fmt.printf("%s\n", escape_html(url), escape_html(alt_text)) fmt.print("\n") } case TokenType.Iframe: if url, ok := token.value.(string); ok { fmt.printf("", escape_html(url)) } case TokenType.Script: if url, ok := token.value.(string); ok { fmt.printf("", escape_html(url)) } case TokenType.ScriptBlock: if content, ok := token.value.(string); ok { fmt.printf("", content) } case TokenType.Variable: if name, ok := token.value.(string); ok { fmt.printf("", escape_html(name)) for child in token.children { print_html(child) } fmt.print("") } 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( "", escape_html(name), escape_html(val), ) } case TokenType.BreakLine: fmt.print("
    \n") case TokenType.Text: if v, ok := token.value.(string); ok { fmt.print(escape_html(v)) } for child in token.children { print_html(child) } } }