package main 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, } push_token :: proc(parent: ^Token, type: TokenType, value: TokenValue = nil) -> ^Token { token := new(Token) token^ = Token{type, value, nil, parent} append(&parent.children, token) return token } 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 { current_token = push_token(&root, TokenType.Paragraph, nil) continue } else { continue } } header_level, text := parse_header(line) if header_level > 0 { current_token = push_token(&root, TokenType.Header, header_level) push_token(current_token, TokenType.Text, text) current_token = &root continue } if strings.has_prefix(text, "> ") { if current_token.type != TokenType.Blockquote { current_token = push_token(&root, TokenType.Blockquote, nil) } push_token(current_token, TokenType.Text, text[2:]) continue } // LAST-CASE SCENARIO: NORMAL TEXT if current_token.parent == nil { current_token = push_token(&root, TokenType.Paragraph, nil) } #partial switch current_token.parent.type { case TokenType.Blockquote: current_token = current_token.parent } push_token(current_token, TokenType.Text, line) } return root }