├── .github └── workflows │ └── ci.yml ├── kind.v ├── LICENSE ├── type_test.v ├── type.v ├── README.md ├── value_test.v └── value.v /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | 8 | jobs: 9 | run: 10 | name: Run 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | path: reflect 17 | - name: Set up V version latest 18 | uses: nocturlab/setup-vlang-action@v1 19 | with: 20 | v-version: latest 21 | id: v 22 | - name: Run V tests 23 | run: v test reflect 24 | -------------------------------------------------------------------------------- /kind.v: -------------------------------------------------------------------------------- 1 | module reflect 2 | 3 | pub enum Kind { 4 | // is_none is a special placeholder for cases where a type does not apply. 5 | is_none 6 | is_bool 7 | is_string 8 | is_i8 9 | is_i16 10 | is_int 11 | is_i64 12 | is_byte 13 | is_u16 14 | is_u32 15 | is_u64 16 | is_rune 17 | is_f32 18 | is_f64 19 | is_array 20 | is_map 21 | is_struct 22 | } 23 | 24 | pub fn (k Kind) str() string { 25 | return match k { 26 | .is_none { '' } 27 | .is_bool { 'bool' } 28 | .is_string { 'string' } 29 | .is_i8 { 'i8' } 30 | .is_i16 { 'i16' } 31 | .is_int { 'int' } 32 | .is_i64 { 'i64' } 33 | .is_byte { 'byte' } 34 | .is_u16 { 'u16' } 35 | .is_u32 { 'u32' } 36 | .is_u64 { 'u64' } 37 | .is_rune { 'rune' } 38 | .is_f32 { 'f32' } 39 | .is_f64 { 'f64' } 40 | .is_array { 'array' } 41 | .is_map { 'map' } 42 | .is_struct { 'struct' } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Elliot Chance 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /type_test.v: -------------------------------------------------------------------------------- 1 | module reflect 2 | 3 | struct ParseTypeTest { 4 | typ string 5 | expected_kind Kind 6 | expected_elem Type 7 | expected_key Type 8 | } 9 | 10 | fn test_parse_type() ? { 11 | tests := [ 12 | ParseTypeTest{'bool', Kind.is_bool, none_type(), none_type()}, 13 | ParseTypeTest{'string', Kind.is_string, none_type(), none_type()}, 14 | ParseTypeTest{'i8', Kind.is_i8, none_type(), none_type()}, 15 | ParseTypeTest{'i16', Kind.is_i16, none_type(), none_type()}, 16 | ParseTypeTest{'int', Kind.is_int, none_type(), none_type()}, 17 | ParseTypeTest{'i64', Kind.is_i64, none_type(), none_type()}, 18 | ParseTypeTest{'byte', Kind.is_byte, none_type(), none_type()}, 19 | ParseTypeTest{'u16', Kind.is_u16, none_type(), none_type()}, 20 | ParseTypeTest{'u32', Kind.is_u32, none_type(), none_type()}, 21 | ParseTypeTest{'u64', Kind.is_u64, none_type(), none_type()}, 22 | ParseTypeTest{'rune', Kind.is_rune, none_type(), none_type()}, 23 | ParseTypeTest{'f32', Kind.is_f32, none_type(), none_type()}, 24 | ParseTypeTest{'f64', Kind.is_f64, none_type(), none_type()}, 25 | ParseTypeTest{'[]int', Kind.is_array, &Type{Kind.is_int, none_type(), none_type(), ''}, none_type()}, 26 | ParseTypeTest{'[]f64', Kind.is_array, &Type{Kind.is_f64, none_type(), none_type(), ''}, none_type()}, 27 | ParseTypeTest{'map[string]int', Kind.is_map, &Type{Kind.is_int, none_type(), none_type(), ''}, &Type{Kind.is_string, none_type(), none_type(), ''}}, 28 | ParseTypeTest{'map[f64]string', Kind.is_map, &Type{Kind.is_string, none_type(), none_type(), ''}, &Type{Kind.is_f64, none_type(), none_type(), ''}}, 29 | ParseTypeTest{'main.Foo', Kind.is_struct, none_type(), none_type()}, 30 | ] 31 | for test in tests { 32 | println(test.typ) 33 | typ := parse_type(test.typ) ? 34 | assert typ.kind == test.expected_kind 35 | assert (*typ.elem).str() == test.expected_elem.str() 36 | assert (*typ.key).str() == test.expected_key.str() 37 | assert typ.str() == test.typ 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /type.v: -------------------------------------------------------------------------------- 1 | module reflect 2 | 3 | pub struct Type { 4 | pub: 5 | kind Kind 6 | // elem is the element type for arrays and the value type for maps. 7 | elem &Type 8 | // key is only used for describing the map key type. 9 | key &Type 10 | // name is used for structs 11 | name string 12 | } 13 | 14 | pub fn (t Type) str() string { 15 | match t.kind { 16 | .is_array { 17 | return '[]${*t.elem}' 18 | } 19 | .is_map { 20 | return 'map[${*t.key}]${*t.elem}' 21 | } 22 | .is_struct { 23 | return t.name 24 | } 25 | else { 26 | return t.kind.str() 27 | } 28 | } 29 | } 30 | 31 | // none_type is a special constructor to create a none type used in situations 32 | // where the type is not applicable. 33 | pub fn none_type() &Type { 34 | return &Type{ 35 | kind: Kind.is_none 36 | elem: 0 37 | key: 0 38 | } 39 | } 40 | 41 | // parse_type returns the Type definition from the string representation. 42 | pub fn parse_type(t string) ?Type { 43 | if t.starts_with('[]') { 44 | elem := parse_type(t[2..]) ? 45 | return Type{ 46 | kind: Kind.is_array 47 | elem: &elem 48 | key: none_type() 49 | } 50 | } 51 | 52 | if t.starts_with('map[') { 53 | parts := t[4..].split(']') 54 | key := parse_type(parts[0]) ? 55 | elem := parse_type(parts[1]) ? 56 | return Type{ 57 | kind: Kind.is_map 58 | elem: &elem 59 | key: &key 60 | } 61 | } 62 | 63 | return Type{ 64 | kind: match t { 65 | 'bool' { Kind.is_bool } 66 | 'string' { Kind.is_string } 67 | 'i8' { Kind.is_i8 } 68 | 'i16' { Kind.is_i16 } 69 | 'int' { Kind.is_int } 70 | 'i64' { Kind.is_i64 } 71 | 'byte' { Kind.is_byte } 72 | 'u16' { Kind.is_u16 } 73 | 'u32' { Kind.is_u32 } 74 | 'u64' { Kind.is_u64 } 75 | 'rune' { Kind.is_rune } 76 | 'f32' { Kind.is_f32 } 77 | 'f64' { Kind.is_f64 } 78 | // Another other name must be a struct name in the form of 79 | // "mypkg.MyType". 80 | else { Kind.is_struct } 81 | } 82 | elem: none_type() 83 | key: none_type() 84 | name: t 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | elliotchance.reflect 2 | ==================== 3 | 4 | Runtime reflection for [V](https://vlang.io). 5 | 6 | V does not carry runtime information about types. Although compile-time 7 | reflection is more performant it can be limiting in some cases that can't be 8 | avoided. 9 | 10 | *IMPORTANT: This project is more to demonstrate that a lot of the runtime 11 | reflection functionality one might need can be done without technically using 12 | runtime types. This implementation relies on static code being generated from 13 | compile-time reflection for known types going into the appropriate 14 | constructors.* 15 | 16 | - [Installation](#installation) 17 | - [Values](#values) 18 | - [Types](#types) 19 | - [Arrays](#arrays) 20 | - [Maps](#maps) 21 | - [Structs](#structs) 22 | 23 | Installation 24 | ------------ 25 | 26 | ```bash 27 | v install elliotchance.reflect 28 | ``` 29 | 30 | Values 31 | ------ 32 | 33 | A `Value` can be created from any literal or simple value, for example: 34 | 35 | ```v 36 | import elliotchance.reflect 37 | 38 | fn main() { 39 | v := reflect.value_of(1.23) 40 | 41 | println(v.typ) // "f64" 42 | println(v.get_f64()) // 1.23 43 | println(v.get_int()) // V panic: value must be int but is f64 44 | } 45 | ``` 46 | 47 | This becomes especially useful when dealing with arrays of mixed types: 48 | 49 | ```v 50 | // Only sum numbers. 51 | fn sum(items []reflect.Value) f64 { 52 | mut total := 0.0 53 | 54 | for item in items { 55 | match item.typ.kind { 56 | .is_f64 { total += item.get_f64() } 57 | .is_int { total += item.get_int() } 58 | else { /* ignore */ } 59 | } 60 | } 61 | 62 | return total 63 | } 64 | 65 | fn main() { 66 | v := [ 67 | reflect.value_of(1.23), 68 | reflect.value_of("hello"), 69 | reflect.value_of(7), 70 | ] 71 | println(sum(v)) // 8.23 72 | } 73 | ``` 74 | 75 | Types 76 | ----- 77 | 78 | All `Values` have a `Type` which can be accessed on the `.typ` field. 79 | 80 | - `.kind`: one of the `Kind` values: `is_bool`, `is_string`, `is_i8`, `is_i16`, 81 | `is_int`, `is_i64`, `is_byte`, `is_u16`, `is_u32`, `is_u64`, `is_rune`, 82 | `is_f32`, `is_f64`. 83 | - `.elem`: Only applies for arrays, describes the element type. 84 | - `.str()`: The string representation that matches the compile-time type in V. 85 | 86 | Arrays 87 | ------ 88 | 89 | ```v 90 | import elliotchance.reflect 91 | 92 | fn main() { 93 | v := reflect.array_of([5, 6, 7]) 94 | 95 | println(v.typ) // "[]int" 96 | println(v.typ.kind) // "array" 97 | println(v.typ.elem.kind) // "int" 98 | println(v.len()) // 3 99 | println(v.cap()) // 3 100 | println(v.get_index(1).get_int()) // 6 101 | println(v.get_index(5)) // V panic: array index 5 is out of bounds (len = 3) 102 | } 103 | ``` 104 | 105 | Maps 106 | ---- 107 | 108 | ```v 109 | import elliotchance.reflect 110 | 111 | fn main() { 112 | mut m := map[string]int{} 113 | m['a'] = 5 114 | m['b'] = 7 115 | m['c'] = 9 116 | 117 | v := reflect.map_of(m) 118 | 119 | println(v.typ) // "map[string]int" 120 | println(v.typ.kind) // "map" 121 | println(v.typ.key.kind) // "string" 122 | println(v.typ.elem.kind) // "int" 123 | println(v.len()) // 3 124 | println(v.keys()) // ['a', 'b', 'c'] 125 | println(v.get_key(reflect.value_of('b')).get_int()) // 7 126 | println(v.get_key(reflect.value_of('d'))) // V panic: key not found: d 127 | println(v.get_key(reflect.value_of(123))) // V panic: value must be string but is int 128 | } 129 | ``` 130 | 131 | Structs 132 | ------- 133 | 134 | ```v 135 | import elliotchance.reflect 136 | 137 | struct Foo { 138 | a int 139 | b f64 140 | c string 141 | } 142 | 143 | fn main() { 144 | s := Foo{123, 4.56, 'hello'} 145 | v := reflect.struct_of(&s) 146 | 147 | println(v.typ) // "main.Foo" 148 | println(v.typ.kind) // "struct" 149 | println(v.fields()) // ['a', 'b', 'c'] 150 | println(v.field('b').typ) // "f64" 151 | println(v.field('b').get_f64()) // 4.56 152 | println(v.field('d')) // V panic: field not found: d 153 | 154 | v.field('b').set_string('hi') // V panic: value must be f64 but is string 155 | 156 | v.field('c').set_string('hi') 157 | println(v.field('c').get_string()) // "hi" 158 | println(s.c) // "hi" 159 | } 160 | ``` 161 | -------------------------------------------------------------------------------- /value_test.v: -------------------------------------------------------------------------------- 1 | module reflect 2 | 3 | struct Foo { 4 | a int 5 | b f32 6 | c string 7 | } 8 | 9 | fn test_value_of_bool() { 10 | v := value_of(true) 11 | assert v.typ.kind == Kind.is_bool 12 | assert v.typ.str() == 'bool' 13 | assert v.get_bool() == true 14 | } 15 | 16 | fn test_value_of_string() { 17 | v := value_of('hello') 18 | assert v.typ.kind == Kind.is_string 19 | assert v.typ.str() == 'string' 20 | assert v.get_string() == 'hello' 21 | } 22 | 23 | fn test_value_of_i8() { 24 | v := value_of(i8(57)) 25 | assert v.typ.kind == Kind.is_i8 26 | assert v.typ.str() == 'i8' 27 | assert v.get_i8() == 57 28 | } 29 | 30 | fn test_value_of_i16() { 31 | v := value_of(i16(43)) 32 | assert v.typ.kind == Kind.is_i16 33 | assert v.typ.str() == 'i16' 34 | assert v.get_i16() == 43 35 | } 36 | 37 | fn test_value_of_int() { 38 | v := value_of(123) 39 | assert v.typ.kind == Kind.is_int 40 | assert v.typ.str() == 'int' 41 | assert v.get_int() == 123 42 | } 43 | 44 | fn test_value_of_i64() { 45 | v := value_of(i64(123456)) 46 | assert v.typ.kind == Kind.is_i64 47 | assert v.typ.str() == 'i64' 48 | assert v.get_i64() == 123456 49 | } 50 | 51 | fn test_value_of_byte() { 52 | v := value_of(byte(45)) 53 | assert v.typ.kind == Kind.is_byte 54 | assert v.typ.str() == 'byte' 55 | assert v.get_byte() == 45 56 | } 57 | 58 | fn test_value_of_u16() { 59 | v := value_of(u16(34)) 60 | assert v.typ.kind == Kind.is_u16 61 | assert v.typ.str() == 'u16' 62 | assert v.get_u16() == 34 63 | } 64 | 65 | fn test_value_of_u32() { 66 | v := value_of(u32(4567)) 67 | assert v.typ.kind == Kind.is_u32 68 | assert v.typ.str() == 'u32' 69 | assert v.get_u32() == 4567 70 | } 71 | 72 | fn test_value_of_u64() { 73 | v := value_of(u64(56789)) 74 | assert v.typ.kind == Kind.is_u64 75 | assert v.typ.str() == 'u64' 76 | assert v.get_u64() == 56789 77 | } 78 | 79 | fn test_value_of_rune() { 80 | v := value_of(`😃`) 81 | assert v.typ.kind == Kind.is_rune 82 | assert v.typ.str() == 'rune' 83 | assert v.get_rune() == `😃` 84 | } 85 | 86 | fn test_value_of_f32() { 87 | v := value_of(f32(1.23)) 88 | assert v.typ.kind == Kind.is_f32 89 | assert v.typ.str() == 'f32' 90 | assert v.get_f32() == f32(1.23) 91 | } 92 | 93 | fn test_value_of_f64() { 94 | v := value_of(4.56) 95 | assert v.typ.kind == Kind.is_f64 96 | assert v.typ.str() == 'f64' 97 | assert v.get_f64() == 4.56 98 | } 99 | 100 | fn test_array_of_int() { 101 | v := array_of([5, 7, 9]) 102 | assert v.typ.kind == Kind.is_array 103 | assert v.typ.elem.kind == Kind.is_int 104 | assert v.typ.str() == '[]int' 105 | } 106 | 107 | fn test_array_of_f64() { 108 | v := array_of([1.23, 4.56, 7.89]) 109 | assert v.typ.kind == Kind.is_array 110 | assert v.typ.elem.kind == Kind.is_f64 111 | assert v.typ.str() == '[]f64' 112 | } 113 | 114 | fn test_array_of_len() { 115 | v := array_of([5, 7, 9]) 116 | assert v.len() == 3 117 | } 118 | 119 | fn test_array_of_cap() { 120 | v := array_of([]f32{len: 1, cap: 5}) 121 | assert v.len() == 1 122 | assert v.cap() == 5 123 | } 124 | 125 | fn test_get_index_int() { 126 | v := array_of([5, 7, 9]) 127 | e := v.get_index(1) 128 | assert e.typ.str() == 'int' 129 | assert e.get_int() == 7 130 | } 131 | 132 | fn test_get_index_f64() { 133 | v := array_of([1.23, 4.56, 7.89]) 134 | e := v.get_index(2) 135 | assert e.typ.str() == 'f64' 136 | assert e.get_f64() == 7.89 137 | } 138 | 139 | fn test_struct_of_fields() { 140 | // TODO(elliotchance): The linux build has issues with typeof(T).name in 141 | // generics. No idea why, but let's do the worst thing possible and just 142 | // ignore the test for now. 143 | $if !linux { 144 | foo := Foo{ 145 | a: 123 146 | b: 4.56 147 | c: 'hello' 148 | } 149 | v := struct_of(&foo) 150 | assert v.typ.kind == .is_struct 151 | assert v.fields() == ['a', 'b', 'c'] 152 | assert v.field('a').typ.str() == 'int' 153 | assert v.field('b').typ.str() == 'f32' 154 | assert v.field('c').typ.str() == 'string' 155 | } 156 | } 157 | 158 | fn test_struct_of_field_set() { 159 | // TODO(elliotchance): The linux build has issues with typeof(T).name in 160 | // generics. No idea why, but let's do the worst thing possible and just 161 | // ignore the test for now. 162 | $if !linux { 163 | foo := Foo{ 164 | a: 123 165 | b: 4.56 166 | c: 'hello' 167 | } 168 | v := struct_of(&foo) 169 | v.field('c').set_string('hi') 170 | assert v.field('c').get_string() == 'hi' 171 | assert foo.c == 'hi' 172 | } 173 | } 174 | 175 | fn test_struct_of_field_int() { 176 | foo := Foo{ 177 | a: 123 178 | b: 4.56 179 | c: 'hello' 180 | } 181 | v := struct_of(&foo) 182 | assert v.field('a').typ.str() == 'int' 183 | } 184 | 185 | // TODO(elliotchance): Not sure how to test for panics? 186 | // fn test_get_index_bounds() { 187 | // v := array_of([5, 7, 9]) 188 | // v.get_index(3) 189 | // } 190 | 191 | fn test_map_of_string_int() { 192 | mut m := map[string]int{} 193 | m['a'] = 5 194 | m['b'] = 7 195 | m['c'] = 9 196 | v := map_of(m) 197 | assert v.typ.kind == Kind.is_map 198 | assert v.typ.key.kind == Kind.is_string 199 | assert v.typ.elem.kind == Kind.is_int 200 | assert v.typ.str() == 'map[string]int' 201 | assert v.len() == 3 202 | } 203 | 204 | fn test_get_key_int() { 205 | mut m := map[string]int{} 206 | m['a'] = 5 207 | m['b'] = 7 208 | m['c'] = 9 209 | v := map_of(m) 210 | 211 | e := v.get_key(value_of('b')) 212 | assert e.typ.str() == 'int' 213 | assert e.get_int() == 7 214 | } 215 | 216 | // TODO(elliotchance): Not sure how to test for panics? 217 | // fn test_get_key_bounds() { 218 | // mut m := map[string]int{} 219 | // m['a'] = 5 220 | // m['b'] = 7 221 | // m['c'] = 9 222 | // v := map_of(m) 223 | 224 | // v.get_key(value_of('d')) 225 | // } 226 | 227 | // TODO(elliotchance): Not sure how to test for panics? 228 | // fn test_get_key_type() { 229 | // mut m := map[string]int{} 230 | // m['a'] = 5 231 | // m['b'] = 7 232 | // m['c'] = 9 233 | // v := map_of(m) 234 | 235 | // v.get_key(value_of(123)) 236 | // } 237 | 238 | fn test_map_keys() { 239 | mut m := map[string]int{} 240 | m['a'] = 5 241 | m['b'] = 7 242 | m['c'] = 9 243 | v := map_of(m) 244 | 245 | keys := v.keys() 246 | assert keys.len == 3 247 | 248 | expected := [keys[0].get_string(), keys[1].get_string(), keys[2].get_string()] 249 | assert expected == ['a', 'b', 'c'] 250 | } 251 | -------------------------------------------------------------------------------- /value.v: -------------------------------------------------------------------------------- 1 | module reflect 2 | 3 | union Val { 4 | bool bool 5 | string string 6 | i8 i8 7 | i16 i16 8 | int int 9 | i64 i64 10 | byte byte 11 | u16 u16 12 | u32 u32 13 | u64 u64 14 | rune rune 15 | f32 f32 16 | f64 f64 17 | } 18 | 19 | pub struct Value { 20 | mut: 21 | value Val 22 | 23 | array_or_map_len int 24 | 25 | array_cap int 26 | array_elem_size int 27 | 28 | map_keys []Value 29 | map_values []Value 30 | // structs 31 | f map[string]voidptr 32 | ft map[string]Type 33 | // pointer to the array, map or struct 34 | obj voidptr 35 | pub: 36 | typ Type 37 | } 38 | 39 | // value_of is used to create a Value 40 | pub fn value_of(x T) Value { 41 | $if T is bool { 42 | return Value{ 43 | typ: Type{Kind.is_bool, none_type(), none_type(), ''} 44 | value: Val{ 45 | bool: x 46 | } 47 | } 48 | } 49 | 50 | $if T is string { 51 | return Value{ 52 | typ: Type{Kind.is_string, none_type(), none_type(), ''} 53 | value: Val{ 54 | string: x 55 | } 56 | } 57 | } 58 | 59 | $if T is i8 { 60 | return Value{ 61 | typ: Type{Kind.is_i8, none_type(), none_type(), ''} 62 | value: Val{ 63 | i8: x 64 | } 65 | } 66 | } 67 | 68 | $if T is i16 { 69 | return Value{ 70 | typ: Type{Kind.is_i16, none_type(), none_type(), ''} 71 | value: Val{ 72 | i16: x 73 | } 74 | } 75 | } 76 | 77 | $if T is int { 78 | return Value{ 79 | typ: Type{Kind.is_int, none_type(), none_type(), ''} 80 | value: Val{ 81 | int: x 82 | } 83 | } 84 | } 85 | 86 | $if T is i64 { 87 | return Value{ 88 | typ: Type{Kind.is_i64, none_type(), none_type(), ''} 89 | value: Val{ 90 | i64: x 91 | } 92 | } 93 | } 94 | 95 | $if T is byte { 96 | return Value{ 97 | typ: Type{Kind.is_byte, none_type(), none_type(), ''} 98 | value: Val{ 99 | byte: x 100 | } 101 | } 102 | } 103 | 104 | $if T is u16 { 105 | return Value{ 106 | typ: Type{Kind.is_u16, none_type(), none_type(), ''} 107 | value: Val{ 108 | u16: x 109 | } 110 | } 111 | } 112 | 113 | $if T is u32 { 114 | return Value{ 115 | typ: Type{Kind.is_u32, none_type(), none_type(), ''} 116 | value: Val{ 117 | u32: x 118 | } 119 | } 120 | } 121 | 122 | $if T is u64 { 123 | return Value{ 124 | typ: Type{Kind.is_u64, none_type(), none_type(), ''} 125 | value: Val{ 126 | u64: x 127 | } 128 | } 129 | } 130 | 131 | $if T is rune { 132 | return Value{ 133 | typ: Type{Kind.is_rune, none_type(), none_type(), ''} 134 | value: Val{ 135 | rune: x 136 | } 137 | } 138 | } 139 | 140 | $if T is f32 { 141 | return Value{ 142 | typ: Type{Kind.is_f32, none_type(), none_type(), ''} 143 | value: Val{ 144 | f32: x 145 | } 146 | } 147 | } 148 | 149 | $if T is f64 { 150 | return Value{ 151 | typ: Type{Kind.is_f64, none_type(), none_type(), ''} 152 | value: Val{ 153 | f64: x 154 | } 155 | } 156 | } 157 | 158 | panic('unsupported value $x') 159 | } 160 | 161 | // array_of creates a Value from an array. 162 | pub fn array_of(x []T) Value { 163 | return Value{ 164 | typ: parse_type(typeof(x).name) or { panic(err) } 165 | array_or_map_len: x.len 166 | array_cap: x.cap 167 | array_elem_size: x.element_size 168 | obj: x.data 169 | } 170 | } 171 | 172 | // map_of creates a Value from a map. 173 | pub fn map_of(x map[K]V) Value { 174 | mut keys := []Value{cap: x.len} 175 | mut values := []Value{cap: x.len} 176 | 177 | for k, v in x { 178 | keys << value_of(k) 179 | values << value_of(v) 180 | } 181 | 182 | return Value{ 183 | typ: parse_type(typeof(x).name) or { panic(err) } 184 | array_or_map_len: x.len 185 | map_keys: keys 186 | map_values: values 187 | } 188 | } 189 | 190 | pub fn struct_of(x &T) Value { 191 | mut f := map[string]voidptr{} 192 | mut ft := map[string]Type{} 193 | $for field in T.fields { 194 | f[field.name] = &x.$(field.name) 195 | ft[field.name] = parse_type(typeof(field).name) or { panic(err) } 196 | } 197 | return Value{ 198 | obj: voidptr(x) 199 | f: f 200 | ft: ft 201 | typ: Type{Kind.is_struct, none_type(), none_type(), ''} 202 | } 203 | } 204 | 205 | pub fn (v Value) len() int { 206 | v.must_be2(Kind.is_array, Kind.is_map) 207 | return v.array_or_map_len 208 | } 209 | 210 | pub fn (v Value) cap() int { 211 | v.must_be(Kind.is_array) 212 | return v.array_cap 213 | } 214 | 215 | pub fn (v Value) get_index(index int) Value { 216 | v.must_be(Kind.is_array) 217 | 218 | if v.array_or_map_len < 0 || v.array_or_map_len <= index { 219 | panic('array index $index is out of bounds (len = $v.array_or_map_len)') 220 | } 221 | 222 | v2 := Value{ 223 | typ: *v.typ.elem 224 | } 225 | unsafe { 226 | dest := match v2.typ.kind { 227 | .is_bool, .is_string, .is_i8, .is_i16, .is_int, .is_i64, .is_byte, .is_u16, .is_u32, 228 | .is_u64, .is_rune, .is_f32, .is_f64 { 229 | voidptr(&v2.value) 230 | } 231 | // TODO(elliotchance): Doesn't support multidimensional arrays. 232 | else { 233 | voidptr(0) 234 | } 235 | } 236 | 237 | C.memcpy(dest, voidptr(u64(v.obj) + u64(index * v.array_elem_size)), v.array_elem_size) 238 | } 239 | return v2 240 | } 241 | 242 | fn (v Value) must_be(k Kind) { 243 | if v.typ.kind != k { 244 | panic('value must be $k but is $v.typ.kind') 245 | } 246 | } 247 | 248 | fn (v Value) must_be2(k1 Kind, k2 Kind) { 249 | if v.typ.kind != k1 && v.typ.kind != k2 { 250 | panic('value must be $k1 or $k2 but is $v.typ.kind') 251 | } 252 | } 253 | 254 | pub fn (v Value) get_bool() bool { 255 | v.must_be(Kind.is_bool) 256 | unsafe { 257 | return v.value.bool 258 | } 259 | } 260 | 261 | pub fn (v Value) get_string() string { 262 | v.must_be(Kind.is_string) 263 | unsafe { 264 | return v.value.string 265 | } 266 | } 267 | 268 | pub fn (v Value) get_i8() i8 { 269 | v.must_be(Kind.is_i8) 270 | unsafe { 271 | return v.value.i8 272 | } 273 | } 274 | 275 | pub fn (v Value) get_i16() i16 { 276 | v.must_be(Kind.is_i16) 277 | unsafe { 278 | return v.value.i16 279 | } 280 | } 281 | 282 | pub fn (v Value) get_int() int { 283 | v.must_be(Kind.is_int) 284 | unsafe { 285 | return v.value.int 286 | } 287 | } 288 | 289 | pub fn (v Value) get_i64() i64 { 290 | v.must_be(Kind.is_i64) 291 | unsafe { 292 | return v.value.i64 293 | } 294 | } 295 | 296 | pub fn (v Value) get_byte() byte { 297 | v.must_be(Kind.is_byte) 298 | unsafe { 299 | return v.value.byte 300 | } 301 | } 302 | 303 | pub fn (v Value) get_u16() u16 { 304 | v.must_be(Kind.is_u16) 305 | unsafe { 306 | return v.value.u16 307 | } 308 | } 309 | 310 | pub fn (v Value) get_u32() u32 { 311 | v.must_be(Kind.is_u32) 312 | unsafe { 313 | return v.value.u32 314 | } 315 | } 316 | 317 | pub fn (v Value) get_u64() u64 { 318 | v.must_be(Kind.is_u64) 319 | unsafe { 320 | return v.value.u64 321 | } 322 | } 323 | 324 | pub fn (v Value) get_rune() rune { 325 | v.must_be(Kind.is_rune) 326 | unsafe { 327 | return v.value.rune 328 | } 329 | } 330 | 331 | pub fn (v Value) get_f32() f32 { 332 | v.must_be(Kind.is_f32) 333 | unsafe { 334 | return v.value.f32 335 | } 336 | } 337 | 338 | pub fn (v Value) get_f64() f64 { 339 | v.must_be(Kind.is_f64) 340 | unsafe { 341 | return v.value.f64 342 | } 343 | } 344 | 345 | pub fn (v Value) get_key(key Value) Value { 346 | v.must_be(Kind.is_map) 347 | key.must_be(v.typ.key.kind) 348 | 349 | for i, k in v.map_keys { 350 | if k.eq(key) { 351 | return Value{ 352 | typ: *v.typ.elem 353 | value: v.map_values[i].value 354 | } 355 | } 356 | } 357 | 358 | panic('key not found: $key') 359 | } 360 | 361 | fn (v Value) eq(v2 Value) bool { 362 | if v.typ.kind != v2.typ.kind { 363 | return false 364 | } 365 | 366 | unsafe { 367 | return match v.typ.kind { 368 | .is_bool, .is_string, .is_i8, .is_i16, .is_int, .is_i64, .is_byte, .is_u16, .is_u32, 369 | .is_u64, .is_rune, .is_f32, .is_f64 { 370 | // Biggest by memory, 16 bytes 371 | v.value.string == v2.value.string 372 | } 373 | else { 374 | panic('cannot compare $v.str() and $v2.str()') 375 | false 376 | } 377 | } 378 | } 379 | } 380 | 381 | fn (v Value) str() string { 382 | unsafe { 383 | return match v.typ.kind { 384 | .is_bool { '$v.value.bool' } 385 | .is_string { '$v.value.string' } 386 | .is_i8 { '$v.value.i8' } 387 | .is_i16 { '$v.value.i16' } 388 | .is_int { '$v.value.int' } 389 | .is_i64 { '$v.value.i64' } 390 | .is_byte { '$v.value.byte' } 391 | .is_u16 { '$v.value.u16' } 392 | .is_u32 { '$v.value.u32' } 393 | .is_u64 { '$v.value.u64' } 394 | .is_rune { '$v.value.rune' } 395 | .is_f32 { '$v.value.f32' } 396 | .is_f64 { '$v.value.f64' } 397 | // TODO(elliotchance): We should print arrays and maps 398 | else { '' } 399 | } 400 | } 401 | } 402 | 403 | pub fn (v Value) keys() []Value { 404 | v.must_be(Kind.is_map) 405 | return v.map_keys 406 | } 407 | 408 | pub fn (v Value) fields() []string { 409 | v.must_be(Kind.is_struct) 410 | mut fields := []string{cap: v.f.len} 411 | for field, _ in v.f { 412 | fields << field 413 | } 414 | 415 | return fields 416 | } 417 | 418 | pub fn (v Value) field(name string) Value { 419 | v2 := Value{ 420 | typ: v.ft[name] 421 | obj: v.f[name] 422 | } 423 | 424 | unsafe { 425 | match v2.typ.kind { 426 | .is_none {} 427 | .is_bool { C.memcpy(voidptr(&v2.value.bool), v.f[name], sizeof(bool)) } 428 | .is_string { C.memcpy(voidptr(&v2.value.string), v.f[name], sizeof(string)) } 429 | .is_i8 { C.memcpy(voidptr(&v2.value.i8), v.f[name], sizeof(i8)) } 430 | .is_i16 { C.memcpy(voidptr(&v2.value.i16), v.f[name], sizeof(i16)) } 431 | .is_int { C.memcpy(voidptr(&v2.value.int), v.f[name], sizeof(int)) } 432 | .is_i64 { C.memcpy(voidptr(&v2.value.i64), v.f[name], sizeof(i64)) } 433 | .is_byte { C.memcpy(voidptr(&v2.value.byte), v.f[name], sizeof(byte)) } 434 | .is_u16 { C.memcpy(voidptr(&v2.value.u16), v.f[name], sizeof(u16)) } 435 | .is_u32 { C.memcpy(voidptr(&v2.value.u32), v.f[name], sizeof(u32)) } 436 | .is_u64 { C.memcpy(voidptr(&v2.value.u64), v.f[name], sizeof(u64)) } 437 | .is_rune { C.memcpy(voidptr(&v2.value.rune), v.f[name], sizeof(rune)) } 438 | .is_f32 { C.memcpy(voidptr(&v2.value.f32), v.f[name], sizeof(f32)) } 439 | .is_f64 { C.memcpy(voidptr(&v2.value.f64), v.f[name], sizeof(f64)) } 440 | else { panic('bad type $v2.typ for field $name') } 441 | } 442 | } 443 | return v2 444 | } 445 | 446 | pub fn (v Value) set_bool(x bool) { 447 | v.must_be(Kind.is_bool) 448 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 449 | } 450 | 451 | pub fn (v Value) set_string(x string) { 452 | v.must_be(Kind.is_string) 453 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 454 | } 455 | 456 | pub fn (v Value) set_i8(x i8) { 457 | v.must_be(Kind.is_i8) 458 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 459 | } 460 | 461 | pub fn (v Value) set_i16(x i16) { 462 | v.must_be(Kind.is_i16) 463 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 464 | } 465 | 466 | pub fn (v Value) set_int(x int) { 467 | v.must_be(Kind.is_int) 468 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 469 | } 470 | 471 | pub fn (v Value) set_i64(x i64) { 472 | v.must_be(Kind.is_i64) 473 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 474 | } 475 | 476 | pub fn (v Value) set_byte(x byte) { 477 | v.must_be(Kind.is_byte) 478 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 479 | } 480 | 481 | pub fn (v Value) set_u16(x u16) { 482 | v.must_be(Kind.is_u16) 483 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 484 | } 485 | 486 | pub fn (v Value) set_u32(x u32) { 487 | v.must_be(Kind.is_u32) 488 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 489 | } 490 | 491 | pub fn (v Value) set_u64(x u64) { 492 | v.must_be(Kind.is_u64) 493 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 494 | } 495 | 496 | pub fn (v Value) set_rune(x rune) { 497 | v.must_be(Kind.is_rune) 498 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 499 | } 500 | 501 | pub fn (v Value) set_f32(x f32) { 502 | v.must_be(Kind.is_f32) 503 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 504 | } 505 | 506 | pub fn (v Value) set_f64(x f64) { 507 | v.must_be(Kind.is_f64) 508 | unsafe { C.memcpy(voidptr(v.obj), &x, sizeof(x)) } 509 | } 510 | --------------------------------------------------------------------------------