Escaping
This commit is contained in:
+4
-1
@@ -8,7 +8,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam blandit dolor nibh,
|
||||
#[Link to nekoweb.org in the middle](https://n0va.nekoweb.org) ^^
|
||||
#[https://krzak.org]
|
||||
#(https://krzak.org)
|
||||
^ Links with no alt text
|
||||
\^ Links with no alt text
|
||||
|
||||
$[https://krzak.org/krzak-icon.png|img]
|
||||
$[/staircase.mp3]
|
||||
@@ -56,6 +56,8 @@ Here is some __underlined__ text
|
||||
|
||||
[Go to Top](#Heading)
|
||||
|
||||
! Escaped star: \*
|
||||
|
||||
[&&1] Ordered list item 2 not included
|
||||
[&&named] This is the definition for a named footnote
|
||||
|
||||
@@ -65,6 +67,7 @@ $(counter=0)
|
||||
$(link=https://git.krzak.org/N0VA/TypoML)
|
||||
|
||||
`tlm.counter` = $(counter)
|
||||
\^ This is JS!!!
|
||||
|
||||
#($(link))[Repo link]
|
||||
|
||||
|
||||
+3
-1
@@ -5,8 +5,10 @@ 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 s {
|
||||
for ch in escaped {
|
||||
switch ch {
|
||||
case '&':
|
||||
strings.write_string(&b, "&")
|
||||
|
||||
+109
-44
@@ -121,6 +121,15 @@ push_text :: proc(parent: ^Token, text: string) {
|
||||
push_token(target, TokenType.Text, cleaned)
|
||||
}
|
||||
|
||||
push_text_line :: proc(target: ^Token, text: string) {
|
||||
if len(target.children) > 0 {
|
||||
br := new(Token)
|
||||
br^ = Token{TokenType.BreakLine, nil, nil, target}
|
||||
append(&target.children, br)
|
||||
}
|
||||
push_text(target, text)
|
||||
}
|
||||
|
||||
strip_alignment_markers :: proc(text: string) -> string {
|
||||
trimmed := strings.trim_space(text)
|
||||
if strings.has_suffix(trimmed, ">>") {
|
||||
@@ -144,6 +153,92 @@ strip_alignment_markers :: proc(text: string) -> string {
|
||||
return trimmed
|
||||
}
|
||||
|
||||
ESCAPE_PLACEHOLDER_BASE :: 0xE000 // Unicode Private Use Area
|
||||
|
||||
is_escapable_char :: proc(b: byte) -> bool {
|
||||
switch b {
|
||||
case '\\',
|
||||
'*',
|
||||
'_',
|
||||
'~',
|
||||
'^',
|
||||
'`',
|
||||
'[',
|
||||
']',
|
||||
'(',
|
||||
')',
|
||||
'$',
|
||||
'&',
|
||||
'!',
|
||||
'>',
|
||||
'|',
|
||||
'-',
|
||||
'=',
|
||||
'#',
|
||||
'.':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
escape_placeholder_rune :: proc(b: byte) -> rune {
|
||||
return rune(ESCAPE_PLACEHOLDER_BASE + int(b))
|
||||
}
|
||||
|
||||
is_escape_placeholder :: proc(r: rune) -> bool {
|
||||
return r >= ESCAPE_PLACEHOLDER_BASE && r < ESCAPE_PLACEHOLDER_BASE + 256
|
||||
}
|
||||
|
||||
escape_placeholder_to_byte :: proc(r: rune) -> byte {
|
||||
return byte(int(r) - ESCAPE_PLACEHOLDER_BASE)
|
||||
}
|
||||
|
||||
unescape_backslashes :: proc(s: string, allocator := context.temp_allocator) -> string {
|
||||
if !strings.contains(s, "\\") {
|
||||
return s
|
||||
}
|
||||
|
||||
b := strings.builder_make(allocator)
|
||||
i := 0
|
||||
for i < len(s) {
|
||||
if s[i] == '\\' && i + 1 < len(s) && is_escapable_char(s[i + 1]) {
|
||||
strings.write_rune(&b, escape_placeholder_rune(s[i + 1]))
|
||||
i += 2
|
||||
} else {
|
||||
strings.write_byte(&b, s[i])
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
decode_escapes :: proc(s: string, allocator := context.temp_allocator) -> string {
|
||||
has_placeholder := false
|
||||
for r in s {
|
||||
if is_escape_placeholder(r) {
|
||||
has_placeholder = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !has_placeholder {
|
||||
return s
|
||||
}
|
||||
|
||||
b := strings.builder_make(allocator)
|
||||
for r in s {
|
||||
if is_escape_placeholder(r) {
|
||||
strings.write_byte(&b, escape_placeholder_to_byte(r))
|
||||
} else {
|
||||
strings.write_rune(&b, r)
|
||||
}
|
||||
}
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
is_space_byte :: proc(b: byte) -> bool {
|
||||
return b == ' ' || b == '\t'
|
||||
}
|
||||
|
||||
parse_header :: proc(line: string) -> (int, string) {
|
||||
if len(line) == 0 {
|
||||
return 0, line
|
||||
@@ -221,6 +316,14 @@ parse_inline_style :: proc(token: ^Token, symbol: byte, toggles: []StyleToggle)
|
||||
i += 1
|
||||
}
|
||||
|
||||
preceded_by_space := symbol_start > 0 && is_space_byte(str[symbol_start - 1])
|
||||
followed_by_space := i < len(str) && is_space_byte(str[i])
|
||||
if preceded_by_space && followed_by_space {
|
||||
push_token(current_node, TokenType.Text, str[symbol_start:i])
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
|
||||
for count > 0 {
|
||||
matched := false
|
||||
for toggle in toggles {
|
||||
@@ -827,40 +930,6 @@ resolve_variables :: proc(token: ^Token, vars: ^map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
add_breaklines :: proc(token: ^Token) {
|
||||
if token.type == .CodeBlock {
|
||||
return
|
||||
}
|
||||
if token.children == nil {
|
||||
return
|
||||
}
|
||||
|
||||
new_children: [dynamic]^Token
|
||||
for i := 0; i < len(token.children); i += 1 {
|
||||
child := token.children[i]
|
||||
add_breaklines(child)
|
||||
append(&new_children, child)
|
||||
is_child_inline :=
|
||||
child.type == TokenType.Text ||
|
||||
child.type == TokenType.Left ||
|
||||
child.type == TokenType.Right ||
|
||||
child.type == TokenType.Middle
|
||||
is_next_inline :=
|
||||
i + 1 < len(token.children) &&
|
||||
(token.children[i + 1].type == TokenType.Text ||
|
||||
token.children[i + 1].type == TokenType.Left ||
|
||||
token.children[i + 1].type == TokenType.Right ||
|
||||
token.children[i + 1].type == TokenType.Middle)
|
||||
|
||||
if is_child_inline && is_next_inline {
|
||||
br := new(Token)
|
||||
br^ = Token{TokenType.BreakLine, nil, nil, token}
|
||||
append(&new_children, br)
|
||||
}
|
||||
}
|
||||
token.children = new_children
|
||||
}
|
||||
|
||||
parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
||||
root := Token{TokenType.Root, nil, nil, nil}
|
||||
current_token := &root
|
||||
@@ -900,6 +969,8 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
||||
continue
|
||||
}
|
||||
|
||||
line = unescape_backslashes(line)
|
||||
|
||||
if strings.has_prefix(line, "```") {
|
||||
lang := strings.trim_space(line[3:])
|
||||
current_token = push_token(&root, TokenType.CodeBlock, lang)
|
||||
@@ -965,7 +1036,7 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
||||
push_token(current_token, TokenType.Paragraph, nil)
|
||||
}
|
||||
last_p := current_token.children[len(current_token.children) - 1]
|
||||
push_text(last_p, text[2:])
|
||||
push_text_line(last_p, text[2:])
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1025,15 +1096,15 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
||||
|
||||
if current_token.type == .Blockquote {
|
||||
last_p := current_token.children[len(current_token.children) - 1]
|
||||
push_text(last_p, line)
|
||||
push_text_line(last_p, line)
|
||||
} else if current_token.type == .UnorderedList || current_token.type == .OrderedList {
|
||||
last_li := current_token.children[len(current_token.children) - 1]
|
||||
push_text(last_li, line)
|
||||
push_text_line(last_li, line)
|
||||
} else {
|
||||
if current_token.type != .Paragraph {
|
||||
current_token = push_token(&root, TokenType.Paragraph, nil)
|
||||
}
|
||||
push_text(current_token, line)
|
||||
push_text_line(current_token, line)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1048,12 +1119,6 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
|
||||
pass_through_children(token)
|
||||
}
|
||||
|
||||
// THIRD PASS
|
||||
|
||||
for token in root.children {
|
||||
add_breaklines(token)
|
||||
}
|
||||
|
||||
// VARIABLE RESOLUTION PASS
|
||||
|
||||
vars: map[string]string
|
||||
|
||||
Reference in New Issue
Block a user