package main import "core:fmt" import "core:strings" TokenType :: enum { Text, Italics, Bold, BreakLine, Header, Paragraph, Root, Blockquote, } TokenValue :: union { string, int, } Token :: struct { type: TokenType, value: TokenValue, children: [dynamic]Token, parent: ^Token, } current_token: ^Token replace_text :: proc(text: string, start: int, len_: int, repl: string) -> string { builder := strings.builder_make(context.temp_allocator) strings.write_string(&builder, text[:start]) strings.write_string(&builder, repl) strings.write_string(&builder, text[start + len_:]) return strings.to_string(builder) } parse_header :: proc(line: string) -> (int, string) { if len(line) == 0 { return 0, line } lvl := 0 for lvl < len(line) && line[lvl] == '!' { lvl += 1 } if lvl == 0 || lvl >= len(line) || line[lvl] != ' ' { return 0, line } return lvl, line[lvl + 1:] } parse :: proc(lines: []string, allocator := context.allocator) -> Token { root := Token{TokenType.Root, nil, nil, nil} current_token := &root for untrimmed_line, line_index in lines { line := strings.trim_space(untrimmed_line) if len(line) == 0 { if line_index != len(lines) - 1 { for current_token.parent != nil { current_token = current_token.parent } append( ¤t_token.children, Token{TokenType.Paragraph, nil, nil, current_token}, ) current_token = ¤t_token.children[len(current_token.children) - 1] continue } else { continue } } header_level, text := parse_header(line) if header_level > 0 { if current_token.parent != nil { current_token = current_token.parent } append( ¤t_token.children, Token{TokenType.Header, header_level, nil, current_token}, ) current_token = ¤t_token.children[len(current_token.children) - 1] append(¤t_token.children, Token{TokenType.Text, text, nil, current_token}) current_token = current_token.parent continue } if text[0:2] == "> " { if current_token.type != TokenType.Blockquote { append( ¤t_token.children, Token{TokenType.Blockquote, nil, nil, current_token}, ) current_token = ¤t_token.children[len(current_token.children) - 1] } append(¤t_token.children, Token{TokenType.Text, text[2:], nil, current_token}) continue } // LAST-CASE SCENARIO: NORMAL TEXT if current_token.parent == nil { append(¤t_token.children, Token{TokenType.Paragraph, nil, nil, current_token}) current_token = ¤t_token.children[len(current_token.children) - 1] } #partial switch current_token.parent.type { case TokenType.Blockquote: current_token = current_token.parent } append(¤t_token.children, Token{TokenType.Text, line, nil, current_token}) } return root }