raiguard/odin-wayland

git clone https://git.raiguard.me/raiguard/odin-wayland

Caleb HeuerAdd arena boilerplate, remove unused allocator argumentb1d3c88

trunk
4.5 KiB181 linesraw
1package codegen
2
3import rt "base:runtime"
4import "core:encoding/xml"
5import "core:fmt"
6import "core:strconv"
7import "core:strings"
8
9// =====================================================================================================================
10// TYPES
11
12Parser :: struct {
13	doc: ^xml.Document,
14	i: int,
15	parents: [dynamic; 16]int
16}
17
18Parser_Error :: Maybe(Parser_Error_Value)
19
20Parser_Error_Value :: struct {
21	type: Parser_Error_Type,
22	expected, got: string,
23}
24
25Parser_Error_Type :: enum {
26	Unknown_Elem_Type,
27	Ident_Mismatch,
28	Too_Many_Values,
29	Body_Type_Mismatch,
30	Missing_Attribute,
31	Invalid_Attribute_Format,
32	Invalid_Attribute_Value,
33}
34
35// =====================================================================================================================
36// LIFECYCLE
37
38parser_make :: proc(doc: ^xml.Document) -> (p: Parser) {
39	p.doc = doc
40	return
41}
42
43// =====================================================================================================================
44// USAGE
45
46parser_iterate :: proc(p: ^Parser) -> (ident: string, cond: bool) {
47	p.i += 1
48
49	elems := &p.doc.elements
50	if p.i >= len(elems) {
51		return
52	}
53
54	cond = elems[p.i].parent == _parser_get_parent(p)
55	if !cond {
56		p.i -= 1
57	}
58	return elems[p.i].ident, cond
59}
60
61parser_enter :: proc(p: ^Parser) {
62	append(&p.parents, p.i)
63}
64
65parser_exit :: proc(p: ^Parser) {
66	unordered_remove(&p.parents, len(p.parents) - 1)
67}
68
69parser_ensure_ident :: proc(p: ^Parser, ident: string) -> Parser_Error {
70	elem := _parser_get_elem(p)
71	// TODO: "Got" ident is owned by the stack frame! We need to clone it.
72	return elem.ident == ident ? nil : parser_make_error(p, .Ident_Mismatch, ident, elem.ident)
73}
74
75parser_get_ident :: proc(p: ^Parser) -> string {
76	return _parser_get_elem(p).ident
77}
78
79parser_get_attr :: proc(p: ^Parser, key: string, $T: typeid) -> (val: T, err: Parser_Error) {
80	ok: bool
81	val, ok = parser_maybe_get_attr(p, key, T) or_return
82	if !ok {
83		err = parser_make_error(p, .Missing_Attribute, key, "")
84	}
85	return
86}
87
88parser_maybe_get_attr :: proc(p: ^Parser, key: string, $T: typeid) -> (val: T, found: bool, err: Parser_Error) {
89	attr := _parser_find_attr(p, key)
90	if attr == nil {
91		return
92	}
93	found = true
94
95	val = _parser_strconv_attr(p, attr, T) or_return
96	return
97}
98
99parser_get_body :: proc(p: ^Parser, $T: typeid) -> (ret: T, err: Parser_Error) {
100	elem := _parser_get_elem(p)
101	if len(elem.value) == 0 {
102		// Description with summary but without body.
103		return
104	} else if len(elem.value) > 1 {
105		fmt.println(elem)
106		err = parser_make_error(p, .Too_Many_Values, 1, len(elem.value))
107		return
108	}
109
110	val, ok := elem.value[0].(T)
111	if !ok {
112		err = parser_make_error(p, .Body_Type_Mismatch, typeid_of(T), typeid_of(type_of(elem.value[0])))
113		return
114	}
115
116	return val, nil
117}
118
119parser_make_error :: proc(p: ^Parser, type: Parser_Error_Type, expected: $E, got: $G) -> Parser_Error_Value {
120	return {
121		type = type,
122		expected = fmt.aprint(expected),
123		got = fmt.aprint(got),
124	}
125}
126
127// =====================================================================================================================
128// INTERNAL
129
130_parser_get_elem :: proc(p: ^Parser) -> ^xml.Element {
131	return &p.doc.elements[p.i]
132}
133
134_parser_get_parent :: proc(p: ^Parser) -> u32 {
135	if len(p.parents) == 0 {
136		return 0
137	}
138	return u32(p.parents[len(p.parents) - 1])
139}
140
141_parser_find_attr :: proc(p: ^Parser, key: string) -> ^xml.Attribute {
142	for &attr in _parser_get_elem(p).attribs {
143		if attr.key == key {
144			return &attr
145		}
146	}
147	return nil
148}
149
150_parser_strconv_attr :: proc(p: ^Parser, attr: ^xml.Attribute, $T: typeid) -> (val: T, err: Parser_Error) {
151	ok_to_err :: proc(p: ^Parser, retval: $R, ok: bool) -> (R, Parser_Error) {
152		return retval, ok ? nil : parser_make_error(p, .Invalid_Attribute_Format, typeid_of(T), "")
153	}
154
155	when T == string {
156		// TODO: Normalize whitespace and remove newlines.
157		return attr.val, nil
158	} else when T == bool {
159		return ok_to_err(p, strconv.parse_bool(attr.val))
160	} else when T == int {
161		return ok_to_err(p, strconv.parse_int(attr.val))
162	} else when T == Arg_Type {
163		switch attr.val {
164		case "int":    return .Int,    nil
165		case "uint":   return .Uint,   nil
166		case "fixed":  return .Fixed,  nil
167		case "string": return .String, nil
168		case "array":  return .Array,  nil
169		case "fd":     return .FD,     nil
170		case "new_id": return .New_ID, nil
171		case "object": return .Object, nil
172		}
173		return T{}, parser_make_error(p, .Invalid_Attribute_Value, attr.val, "")
174	} else when T == Arg_Enum {
175		if !strings.contains(attr.val, ".") {
176			return attr.val, nil
177		}
178		head, _, tail := strings.partition(attr.val, ".")
179		return Arg_Ext_Enum{head, tail}, nil
180	}
181}