mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 09:22:22 -07:00
287 lines
7.1 KiB
Odin
287 lines
7.1 KiB
Odin
package xml_example
|
|
|
|
import "core:encoding/xml"
|
|
import "core:os"
|
|
import "core:path"
|
|
import "core:mem"
|
|
import "core:strings"
|
|
import "core:strconv"
|
|
import "core:slice"
|
|
import "core:fmt"
|
|
|
|
/*
|
|
Silent error handler for the parser.
|
|
*/
|
|
Error_Handler :: proc(pos: xml.Pos, fmt: string, args: ..any) {}
|
|
|
|
OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, }, expected_doctype = "unicode", }
|
|
|
|
Entity :: struct {
|
|
name: string,
|
|
codepoint: rune,
|
|
description: string,
|
|
}
|
|
|
|
generate_encoding_entity_table :: proc() {
|
|
using fmt
|
|
|
|
filename := path.join(ODIN_ROOT, "tests", "core", "assets", "XML", "unicode.xml")
|
|
defer delete(filename)
|
|
|
|
generated_filename := path.join(ODIN_ROOT, "core", "encoding", "entity", "generated.odin")
|
|
defer delete(generated_filename)
|
|
|
|
doc, err := xml.parse(filename, OPTIONS, Error_Handler)
|
|
defer xml.destroy(doc)
|
|
|
|
if err != .None {
|
|
printf("Load/Parse error: %v\n", err)
|
|
if err == .File_Error {
|
|
printf("\"%v\" not found. Did you run \"tests\\download_assets.py\"?", filename)
|
|
}
|
|
os.exit(1)
|
|
}
|
|
|
|
printf("\"%v\" loaded and parsed.\n", filename)
|
|
|
|
generated_buf: strings.Builder
|
|
defer strings.builder_destroy(&generated_buf)
|
|
w := strings.to_writer(&generated_buf)
|
|
|
|
charlist, charlist_ok := xml.find_child_by_ident(doc.root, "charlist")
|
|
if !charlist_ok {
|
|
eprintln("Could not locate top-level `<charlist>` tag.")
|
|
os.exit(1)
|
|
}
|
|
|
|
printf("Found `<charlist>` with %v children.\n", len(charlist.children))
|
|
|
|
entity_map: map[string]Entity
|
|
names: [dynamic]string
|
|
|
|
min_name_length := max(int)
|
|
max_name_length := min(int)
|
|
shortest_name: string
|
|
longest_name: string
|
|
|
|
count := 0
|
|
for char in charlist.children {
|
|
if char.ident != "character" {
|
|
eprintf("Expected `<character>`, got `<%v>`\n", char.ident)
|
|
os.exit(1)
|
|
}
|
|
|
|
if codepoint_string, ok := xml.find_attribute_val_by_key(char, "dec"); !ok {
|
|
eprintln("`<character id=\"...\">` attribute not found.")
|
|
os.exit(1)
|
|
} else {
|
|
codepoint := strconv.atoi(codepoint_string)
|
|
|
|
desc, desc_ok := xml.find_child_by_ident(char, "description")
|
|
description := desc.value if desc_ok else ""
|
|
|
|
/*
|
|
For us to be interested in this codepoint, it has to have at least one entity.
|
|
*/
|
|
|
|
nth := 0
|
|
for {
|
|
character_entity, entity_ok := xml.find_child_by_ident(char, "entity", nth)
|
|
if !entity_ok { break }
|
|
|
|
nth += 1
|
|
if name, name_ok := xml.find_attribute_val_by_key(character_entity, "id"); name_ok {
|
|
|
|
if len(name) == 0 {
|
|
/*
|
|
Invalid name. Skip.
|
|
*/
|
|
continue
|
|
}
|
|
|
|
if name == "\"\"" {
|
|
printf("%#v\n", char)
|
|
printf("%#v\n", character_entity)
|
|
}
|
|
|
|
if len(name) > max_name_length { longest_name = name }
|
|
if len(name) < min_name_length { shortest_name = name }
|
|
|
|
min_name_length = min(min_name_length, len(name))
|
|
max_name_length = max(max_name_length, len(name))
|
|
|
|
e := Entity{
|
|
name = name,
|
|
codepoint = rune(codepoint),
|
|
description = description,
|
|
}
|
|
|
|
if _, seen := entity_map[name]; seen {
|
|
continue
|
|
}
|
|
|
|
entity_map[name] = e
|
|
append(&names, name)
|
|
count += 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Sort by name.
|
|
*/
|
|
slice.sort(names[:])
|
|
|
|
printf("Found %v unique `&name;` -> rune mappings.\n", count)
|
|
printf("Shortest name: %v (%v)\n", shortest_name, min_name_length)
|
|
printf("Longest name: %v (%v)\n", longest_name, max_name_length)
|
|
|
|
// println(rune_to_string(1234))
|
|
|
|
/*
|
|
Generate table.
|
|
*/
|
|
wprintln(w, "package unicode_entity")
|
|
wprintln(w, "")
|
|
wprintln(w, GENERATED)
|
|
wprintln(w, "")
|
|
wprintf (w, TABLE_FILE_PROLOG)
|
|
wprintln(w, "")
|
|
|
|
wprintf (w, "// `&%v;`\n", shortest_name)
|
|
wprintf (w, "XML_NAME_TO_RUNE_MIN_LENGTH :: %v\n", min_name_length)
|
|
wprintf (w, "// `&%v;`\n", longest_name)
|
|
wprintf (w, "XML_NAME_TO_RUNE_MAX_LENGTH :: %v\n", max_name_length)
|
|
wprintln(w, "")
|
|
|
|
wprintln(w,
|
|
`
|
|
/*
|
|
Input:
|
|
entity_name - a string, like "copy" that describes a user-encoded Unicode entity as used in XML.
|
|
|
|
Output:
|
|
"decoded" - The decoded rune if found by name, or -1 otherwise.
|
|
"ok" - true if found, false if not.
|
|
|
|
IMPORTANT: XML processors (including browsers) treat these names as case-sensitive. So do we.
|
|
*/
|
|
named_xml_entity_to_rune :: proc(name: string) -> (decoded: rune, ok: bool) {
|
|
/*
|
|
Early out if the name is too short or too long.
|
|
min as a precaution in case the generated table has a bogus value.
|
|
*/
|
|
if len(name) < min(1, XML_NAME_TO_RUNE_MIN_LENGTH) || len(name) > XML_NAME_TO_RUNE_MAX_LENGTH {
|
|
return -1, false
|
|
}
|
|
|
|
switch rune(name[0]) {
|
|
`)
|
|
|
|
prefix := '?'
|
|
should_close := false
|
|
|
|
for v in names {
|
|
if rune(v[0]) != prefix {
|
|
if should_close {
|
|
wprintln(w, "\t\t}\n")
|
|
}
|
|
|
|
prefix = rune(v[0])
|
|
wprintf (w, "\tcase '%v':\n", prefix)
|
|
wprintln(w, "\t\tswitch name {")
|
|
}
|
|
|
|
e := entity_map[v]
|
|
|
|
wprintf(w, "\t\t\tcase \"%v\": \n", e.name)
|
|
wprintf(w, "\t\t\t\t// %v\n", e.description)
|
|
wprintf(w, "\t\t\t\treturn %v, true\n", rune_to_string(e.codepoint))
|
|
|
|
should_close = true
|
|
}
|
|
wprintln(w, "\t\t}")
|
|
wprintln(w, "\t}")
|
|
wprintln(w, "\treturn -1, false")
|
|
wprintln(w, "}\n")
|
|
wprintln(w, GENERATED)
|
|
|
|
println()
|
|
println(strings.to_string(generated_buf))
|
|
println()
|
|
|
|
written := os.write_entire_file(generated_filename, transmute([]byte)strings.to_string(generated_buf))
|
|
|
|
if written {
|
|
fmt.printf("Successfully written generated \"%v\".", generated_filename)
|
|
} else {
|
|
fmt.printf("Failed to write generated \"%v\".", generated_filename)
|
|
}
|
|
|
|
delete(entity_map)
|
|
delete(names)
|
|
for &name in names {
|
|
free(&name)
|
|
}
|
|
}
|
|
|
|
GENERATED :: `/*
|
|
------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
|
|
*/`
|
|
|
|
TABLE_FILE_PROLOG :: `/*
|
|
This file is generated from "https://www.w3.org/2003/entities/2007xml/unicode.xml".
|
|
|
|
UPDATE:
|
|
- Ensure the XML file was downloaded using "tests\core\download_assets.py".
|
|
- Run "core/unicode/tools/generate_entity_table.odin"
|
|
|
|
Odin unicode generated tables: https://github.com/odin-lang/Odin/tree/master/core/encoding/entity
|
|
|
|
Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology,
|
|
European Research Consortium for Informatics and Mathematics, Keio University, Beihang).
|
|
|
|
All Rights Reserved.
|
|
|
|
This work is distributed under the W3C® Software License [1] in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
[1] http://www.w3.org/Consortium/Legal/copyright-software
|
|
|
|
See also: LICENSE_table.md
|
|
*/
|
|
`
|
|
|
|
rune_to_string :: proc(r: rune) -> (res: string) {
|
|
res = fmt.tprintf("%08x", int(r))
|
|
for len(res) > 2 && res[:2] == "00" {
|
|
res = res[2:]
|
|
}
|
|
return fmt.tprintf("rune(0x%v)", res)
|
|
}
|
|
|
|
is_dotted_name :: proc(name: string) -> (dotted: bool) {
|
|
for r in name {
|
|
if r == '.' { return true}
|
|
}
|
|
return false
|
|
}
|
|
|
|
main :: proc() {
|
|
using fmt
|
|
|
|
track: mem.Tracking_Allocator
|
|
mem.tracking_allocator_init(&track, context.allocator)
|
|
context.allocator = mem.tracking_allocator(&track)
|
|
|
|
generate_encoding_entity_table()
|
|
|
|
if len(track.allocation_map) > 0 {
|
|
println()
|
|
for _, v in track.allocation_map {
|
|
printf("%v Leaked %v bytes.\n", v.location, v.size)
|
|
}
|
|
}
|
|
println("Done and cleaned up!")
|
|
} |