Rm ltx mem leaks (sort of)
This commit is contained in:
+94
-53
@@ -1,4 +1,6 @@
|
|||||||
package ltx
|
package ltx
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:path/filepath"
|
import "core:path/filepath"
|
||||||
@@ -8,14 +10,18 @@ import "core:unicode"
|
|||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
if len(os.args) < 2 {
|
if len(os.args) < 2 {
|
||||||
fmt.eprintf("A naive implementation of TeX-based arbitrary markup. Prints syntax tree and space-preserving XML substitution\nUsage:\n\t%s <filepath>\n", os.args[0])
|
fmt.eprintf(
|
||||||
|
"A naive implementation of TeX-based arbitrary markup. Prints syntax tree and space-preserving XML substitution\nUsage:\n\t%s <filepath>\n",
|
||||||
|
os.args[0],
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ltx: Ltx
|
ltx: Ltx
|
||||||
|
|
||||||
parse_err := ltx_parse_file(<x, os.args[1])
|
parse_err := ltx_parse_file(<x, os.args[1])
|
||||||
|
defer ltx_free_file(<x)
|
||||||
if parse_err != .None {
|
if parse_err != .None {
|
||||||
fmt.eprintln(ltx_get_error(<x, parse_err))
|
fmt.eprintln(ltx_get_error(<x, parse_err, context.temp_allocator))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for node in ltx.nodes {
|
for node in ltx.nodes {
|
||||||
@@ -24,10 +30,18 @@ main :: proc() {
|
|||||||
|
|
||||||
sb := strings.builder_make()
|
sb := strings.builder_make()
|
||||||
defer strings.builder_destroy(&sb)
|
defer strings.builder_destroy(&sb)
|
||||||
strip_ltx(&sb, ltx.nodes)
|
|
||||||
|
fmt.println("--- BEGIN XML ---")
|
||||||
strings.builder_reset(&sb)
|
strings.builder_reset(&sb)
|
||||||
ltx_to_xml(&sb, ltx.nodes)
|
ltx_to_xml(&sb, ltx.nodes)
|
||||||
fmt.println(strings.to_string(sb))
|
fmt.println(strings.to_string(sb))
|
||||||
|
fmt.println("--- END XML ---")
|
||||||
|
|
||||||
|
fmt.println("--- BEGIN PLAIN TEXT ---")
|
||||||
|
strings.builder_reset(&sb)
|
||||||
|
ltx_to_text_with_smart_indent(&sb, ltx.nodes)
|
||||||
|
fmt.println(strings.to_string(sb))
|
||||||
|
fmt.println("--- END PLAIN TEXT ---")
|
||||||
}
|
}
|
||||||
|
|
||||||
Tokens :: enum {
|
Tokens :: enum {
|
||||||
@@ -122,19 +136,11 @@ ltx_consume_whitespace :: proc(ltx: ^Ltx) -> Ltx_Error {
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
is_numeric :: proc(c: rune) -> b32 {
|
|
||||||
return c >= '0' && c <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
is_alpha :: proc(c: rune) -> b32 {
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
validate_key :: proc(key: string) -> Ltx_Error {
|
validate_key :: proc(key: string) -> Ltx_Error {
|
||||||
assert(len(key) > 0, "expected non-empty key")
|
assert(len(key) > 0, "expected non-empty key")
|
||||||
if !is_alpha(rune(key[0])) do return .InvalidKey
|
if !unicode.is_alpha(rune(key[0])) do return .InvalidKey
|
||||||
for c in key {
|
for c in key {
|
||||||
if !is_alpha(c) && !is_numeric(c) && c != '_' do return .InvalidKey
|
if !unicode.is_alpha(c) && !unicode.is_number(c) && c != '_' do return .InvalidKey
|
||||||
}
|
}
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
@@ -262,31 +268,41 @@ ltx_parse :: proc(ltx: ^Ltx) -> (err: Ltx_Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ltx_parse_file :: proc(ltx: ^Ltx, file_path: string) -> Ltx_Error {
|
ltx_parse_file :: proc(ltx: ^Ltx, file_path: string) -> Ltx_Error {
|
||||||
source, ok := os.read_entire_file(file_path)
|
source, err := os.read_entire_file_from_path(file_path, context.allocator)
|
||||||
if !ok do return .CannotReadFile
|
if err != nil do return .CannotReadFile
|
||||||
abs_path, abs_ok := filepath.abs(file_path)
|
abs_path, abs_err := filepath.abs(file_path)
|
||||||
if !abs_ok do return .CannotReadFile
|
if abs_err != nil do return .CannotReadFile
|
||||||
ltx.source_path = abs_path
|
ltx.source_path = abs_path
|
||||||
ltx.source = string(source)
|
ltx.source = string(source)
|
||||||
return ltx_parse(ltx)
|
return ltx_parse(ltx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ltx_free_file :: proc(ltx: ^Ltx) -> runtime.Allocator_Error {
|
||||||
|
delete(ltx.source) or_return
|
||||||
|
delete(ltx.source_path) or_return
|
||||||
|
return .None
|
||||||
|
}
|
||||||
|
|
||||||
print_indent :: proc(level: int) {
|
print_indent :: proc(level: int) {
|
||||||
for i in 0 ..< level {
|
for i in 0 ..< level {
|
||||||
fmt.print(" ")
|
fmt.print(" ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print_node :: proc(node: Node, indent_level := 0) {
|
print_node :: proc(node: Node, indent_level := 0) {
|
||||||
print_indent(indent_level)
|
print_indent(indent_level)
|
||||||
switch node.kind {
|
switch node.kind {
|
||||||
case .Text:
|
case .Text:
|
||||||
fmt.printf("TEXT: \"%s\"\n", escape_white_space(node.text))
|
fmt.printf("TEXT: \"%s\"\n", escape_white_space(node.text, context.temp_allocator))
|
||||||
case .Tag:
|
case .Tag:
|
||||||
fmt.print("TAG:", node.name)
|
fmt.print("TAG:", node.name)
|
||||||
if len(node.attributes) > 0 {
|
if len(node.attributes) > 0 {
|
||||||
for k, v in node.attributes {
|
for k, v in node.attributes {
|
||||||
if v.type == .Flag do fmt.printf(" (%s)", k)
|
if v.type == .Flag {
|
||||||
else do fmt.printf(" {{%s: %s}}", k, v.value)
|
fmt.printf(" (%s)", k)
|
||||||
|
} else {
|
||||||
|
fmt.printf(" {{%s: %s}}", k, v.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.println()
|
fmt.println()
|
||||||
@@ -298,18 +314,43 @@ print_node :: proc(node: Node, indent_level := 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strip_ltx :: proc(sb: ^strings.Builder, nodes: [dynamic]Node) -> b32 {
|
ltx_to_text_with_smart_indent :: proc (sb: ^strings.Builder, nodes: [dynamic]Node, indent_level := 0) {
|
||||||
|
should_inc_indent_level := true
|
||||||
|
for node in nodes {
|
||||||
|
if node.kind == .Text {
|
||||||
|
should_inc_indent_level = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
switch node.kind {
|
switch node.kind {
|
||||||
case .Text:
|
case .Text:
|
||||||
fmt.sbprint(sb, (node.text))
|
for i in 0..<indent_level-1 do strings.write_rune(sb, ' ')
|
||||||
|
text := strings.trim(process_white_space(node.text, context.temp_allocator), " \t\n\r\v")
|
||||||
|
// TODO(Paul): do something about newlines; have them be limited to 2 max if they appear consecutively perhaps
|
||||||
|
if text != "" {
|
||||||
|
fmt.sbprintln(sb, text)
|
||||||
|
}
|
||||||
case .Tag:
|
case .Tag:
|
||||||
strip_ltx(sb, node.children)
|
ltx_to_text_with_smart_indent(sb, node.children, should_inc_indent_level ? indent_level + 1 : indent_level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ltx to plain text; strip ltx tags
|
||||||
|
ltx_to_plain_text :: proc(sb: ^strings.Builder, nodes: [dynamic]Node) {
|
||||||
|
for node in nodes {
|
||||||
|
switch node.kind {
|
||||||
|
case .Text:
|
||||||
|
fmt.sbprint(sb, node.text)
|
||||||
|
case .Tag:
|
||||||
|
ltx_to_plain_text(sb, node.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ltx to XML
|
||||||
ltx_to_xml :: proc(sb: ^strings.Builder, nodes: [dynamic]Node, depth := 0) {
|
ltx_to_xml :: proc(sb: ^strings.Builder, nodes: [dynamic]Node, depth := 0) {
|
||||||
if len(nodes) <= 0 do return
|
if len(nodes) <= 0 do return
|
||||||
if depth == 0 do fmt.sbprintln(sb, "<ltx>")
|
if depth == 0 do fmt.sbprintln(sb, "<ltx>")
|
||||||
@@ -332,6 +373,7 @@ ltx_to_xml :: proc(sb: ^strings.Builder, nodes: [dynamic]Node, depth := 0) {
|
|||||||
if depth == 0 do fmt.sbprintln(sb, "</ltx>")
|
if depth == 0 do fmt.sbprintln(sb, "</ltx>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ltx_Error to english string
|
||||||
ltx_error_to_string :: proc(error: Ltx_Error) -> string {
|
ltx_error_to_string :: proc(error: Ltx_Error) -> string {
|
||||||
switch error {
|
switch error {
|
||||||
case .None:
|
case .None:
|
||||||
@@ -358,55 +400,54 @@ ltx_error_to_string :: proc(error: Ltx_Error) -> string {
|
|||||||
return "unknown error"
|
return "unknown error"
|
||||||
}
|
}
|
||||||
|
|
||||||
ltx_get_error :: proc(ltx: ^Ltx, error: Ltx_Error) -> string {
|
// Pretty format LTX error
|
||||||
|
ltx_get_error :: proc(ltx: ^Ltx, error: Ltx_Error, allocator := context.allocator) -> string {
|
||||||
if error == .None do return ""
|
if error == .None do return ""
|
||||||
file_path := len(ltx.source_path) > 0 ? ltx.source_path : "[source]"
|
file_path := len(ltx.source_path) > 0 ? ltx.source_path : "[source]"
|
||||||
line := ltx.pos.line + 1
|
line := ltx.pos.line + 1
|
||||||
col := ltx.pos.col + 1
|
col := ltx.pos.col + 1
|
||||||
error_msg := ltx_error_to_string(error)
|
error_msg := ltx_error_to_string(error)
|
||||||
return fmt.tprintf("%s(%d,%d): error: %s", file_path, line, col, error_msg)
|
return fmt.aprintf("%s(%d,%d): error: %s", file_path, line, col, error_msg, allocator = allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
escape_white_space :: proc(s: string) -> string {
|
// Escapes white space characters with in their c-string slash form
|
||||||
sb: strings.Builder = strings.builder_make()
|
escape_white_space :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
// defer strings.builder_destroy(&sb)
|
sb := strings.builder_make(allocator)
|
||||||
for c in s {
|
|
||||||
switch c {
|
for c in s {
|
||||||
case '\t':
|
switch c {
|
||||||
fmt.sbprint(&sb, "\\t")
|
case '\t': strings.write_string(&sb, "\\t")
|
||||||
case '\n':
|
case '\n': strings.write_string(&sb, "\\n")
|
||||||
fmt.sbprint(&sb, "\\n")
|
case '\r': strings.write_string(&sb, "\\r")
|
||||||
case:
|
case '\\': strings.write_string(&sb, "\\\\")
|
||||||
fmt.sbprint(&sb, c)
|
case: strings.write_rune(&sb, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strings.to_string(sb)
|
return strings.to_string(sb)
|
||||||
}
|
}
|
||||||
|
|
||||||
process_white_space :: proc(s: string) -> string {
|
// Processes and normalizes whitespace in a multi-line string.
|
||||||
sb: strings.Builder = strings.builder_make()
|
process_white_space :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
defer strings.builder_destroy(&sb)
|
sb: strings.Builder = strings.builder_make(allocator)
|
||||||
lines := strings.split(s, "\n")
|
lines := strings.split(s, "\n")
|
||||||
for _line in lines {
|
for _line in lines {
|
||||||
line := strings.trim_right(_line, "\t ")
|
line := strings.trim_right(_line, "\t ")
|
||||||
if len(line) <= 0 do continue
|
if len(line) <= 0 do continue
|
||||||
last_idx := 0
|
last_idx := 0
|
||||||
last_char: u8 = 0
|
last_char: rune = 0
|
||||||
for i := 0; i < len(line); i += 1 {
|
for r, i in line {
|
||||||
if line[i] == '\r' do continue
|
if r == '\r' do continue
|
||||||
if unicode.is_space(rune(line[i])) {
|
if unicode.is_space(r) {
|
||||||
if last_char == line[i] {
|
if last_char == r {
|
||||||
last_idx = i
|
last_idx = i
|
||||||
} else {
|
} else {
|
||||||
fmt.sbprint(&sb, line[last_idx:i])
|
fmt.sbprint(&sb, line[last_idx:i])
|
||||||
last_idx = i
|
last_idx = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_char = line[i]
|
last_char = r
|
||||||
}
|
}
|
||||||
fmt.sbprintln(&sb, line[last_idx:len(line)])
|
fmt.sbprintln(&sb, line[last_idx:])
|
||||||
}
|
}
|
||||||
// return ""
|
|
||||||
return strings.to_string(sb)
|
return strings.to_string(sb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-16
@@ -4,26 +4,26 @@
|
|||||||
|
|
||||||
\section{
|
\section{
|
||||||
\title{Resources}
|
\title{Resources}
|
||||||
\list{
|
\list{
|
||||||
\item{\a[link_flag]{sdf}}
|
\item{\a[link_flag]{sdf}}
|
||||||
\item{\a[link_flag2]{}}
|
\item{\a[link_flag2]{}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
\lol
|
\lol
|
||||||
|
|
||||||
\section{
|
\section{
|
||||||
\title{Arbitrary tags!}
|
\title{Arbitrary tags!}
|
||||||
\list{
|
\list{
|
||||||
\item{
|
\item{
|
||||||
\name{yo!}
|
\name{yo!}
|
||||||
\desc{yoyo!}
|
\desc{yoyo!}
|
||||||
}
|
}
|
||||||
\item{
|
\item{
|
||||||
\name{oy!}
|
\name{oy!}
|
||||||
\desc{oyoy!}
|
\desc{oyoy!}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
\link[link_flag]{https://example.com}
|
\link[link_flag]{https://example.com}
|
||||||
@@ -33,5 +33,3 @@
|
|||||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||||
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user