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