Skip to content

Commit

Permalink
Create simple values for runtime checking
Browse files Browse the repository at this point in the history
  • Loading branch information
elliotchance committed Jul 15, 2021
0 parents commit 6e45d74
Show file tree
Hide file tree
Showing 4 changed files with 351 additions and 0 deletions.
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
elliotchance/reflect
====================

Runtime reflection for [V](https://vlang.io).

V does not carry runtime information about types. Although compile-time
reflection is more performant it can be limiting in some cases that can't be
avoided.

Values
------

A `Value` can be created from any literal or simple value, for example:

```v
v := reflect.value_of(1.23)
println(v.kind) // "f64"
println(v.get_f64()) // 1.23
println(v.get_int()) // V panic: value must be int but is f64
```

This becomes especially useful when dealing with arrays of mixed types:

```v
// Only sum numbers.
fn sum(items []reflect.Value) f64 {
mut total := 0.0
for item in items {
match item.kind {
.is_f64 { total += item.get_f64() }
.is_int { total += item.get_int() }
else { /* ignore */ }
}
}
return total
}
fn main() {
v := [
reflect.value_of(1.23),
reflect.value_of("hello"),
reflect.value_of(7),
]
println(sum(v)) // 8.23
}
```
35 changes: 35 additions & 0 deletions kind.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module reflect

pub enum Kind {
is_bool
is_string
is_i8
is_i16
is_int
is_i64
is_byte
is_u16
is_u32
is_u64
is_rune
is_f32
is_f64
}

pub fn (k Kind) str() string {
return match k {
.is_bool { 'bool' }
.is_string { 'string' }
.is_i8 { 'i8' }
.is_i16 { 'i16' }
.is_int { 'int' }
.is_i64 { 'i64' }
.is_byte { 'byte' }
.is_u16 { 'u16' }
.is_u32 { 'u32' }
.is_u64 { 'u64' }
.is_rune { 'rune' }
.is_f32 { 'f32' }
.is_f64 { 'f64' }
}
}
188 changes: 188 additions & 0 deletions value.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
module reflect

pub struct Value {
// TODO(elliot): I know this is extremely wasteful. We'll come up with
// something more sane later.
value_bool bool
value_string string
value_i8 i8
value_i16 i16
value_int int
value_i64 i64
value_byte byte
value_u16 u16
value_u32 u32
value_u64 u64
value_rune rune
value_f32 f32
value_f64 f64
pub:
kind Kind
}

// value_of is used to create a Value
pub fn value_of<T>(x T) Value {
$if T is bool {
return {
kind: Kind.is_bool
value_bool: x
}
}

$if T is string {
return {
kind: Kind.is_string
value_string: x
}
}

$if T is i8 {
return {
kind: Kind.is_i8
value_i8: x
}
}

$if T is i16 {
return {
kind: Kind.is_i16
value_i16: x
}
}

$if T is int {
return {
kind: Kind.is_int
value_int: x
}
}

$if T is i64 {
return {
kind: Kind.is_i64
value_i64: x
}
}

$if T is byte {
return {
kind: Kind.is_byte
value_byte: x
}
}

$if T is u16 {
return {
kind: Kind.is_u16
value_u16: x
}
}

$if T is u32 {
return {
kind: Kind.is_u32
value_u32: x
}
}

$if T is u64 {
return {
kind: Kind.is_u64
value_u64: x
}
}

$if T is rune {
return {
kind: Kind.is_rune
value_rune: x
}
}

$if T is f32 {
return {
kind: Kind.is_f32
value_f32: x
}
}

$if T is f64 {
return {
kind: Kind.is_f64
value_f64: x
}
}

panic('unsupported value $x')
}

fn (v Value) must_be(k Kind) {
if v.kind != k {
panic('value must be $k but is $v.kind')
}
}

pub fn (v Value) get_bool() bool {
v.must_be(Kind.is_bool)
return v.value_bool
}

pub fn (v Value) get_string() string {
v.must_be(Kind.is_string)
return v.value_string
}

pub fn (v Value) get_i8() i8 {
v.must_be(Kind.is_i8)
return v.value_i8
}

pub fn (v Value) get_i16() i16 {
v.must_be(Kind.is_i16)
return v.value_i16
}

pub fn (v Value) get_int() int {
v.must_be(Kind.is_int)
return v.value_int
}

pub fn (v Value) get_i64() i64 {
v.must_be(Kind.is_i64)
return v.value_i64
}

pub fn (v Value) get_byte() byte {
v.must_be(Kind.is_byte)
return v.value_byte
}

pub fn (v Value) get_u16() u16 {
v.must_be(Kind.is_u16)
return v.value_u16
}

pub fn (v Value) get_u32() u32 {
v.must_be(Kind.is_u32)
return v.value_u32
}

pub fn (v Value) get_u64() u64 {
v.must_be(Kind.is_u64)
return v.value_u64
}

pub fn (v Value) get_rune() rune {
v.must_be(Kind.is_rune)
return v.value_rune
}

pub fn (v Value) get_f32() f32 {
v.must_be(Kind.is_f32)
return v.value_f32
}

pub fn (v Value) get_f64() f64 {
v.must_be(Kind.is_f64)
return v.value_f64
}
79 changes: 79 additions & 0 deletions value_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
module reflect

fn test_value_of_bool() {
v := value_of(true)
assert v.kind == Kind.is_bool
assert v.get_bool() == true
}

fn test_value_of_string() {
v := value_of('hello')
assert v.kind == Kind.is_string
assert v.get_string() == 'hello'
}

fn test_value_of_i8() {
v := value_of(i8(57))
assert v.kind == Kind.is_i8
assert v.get_i8() == 57
}

fn test_value_of_i16() {
v := value_of(i16(43))
assert v.kind == Kind.is_i16
assert v.get_i16() == 43
}

fn test_value_of_int() {
v := value_of(123)
assert v.kind == Kind.is_int
assert v.get_int() == 123
}

fn test_value_of_i64() {
v := value_of(i64(123456))
assert v.kind == Kind.is_i64
assert v.get_i64() == 123456
}

fn test_value_of_byte() {
v := value_of(byte(45))
assert v.kind == Kind.is_byte
assert v.get_byte() == 45
}

fn test_value_of_u16() {
v := value_of(u16(34))
assert v.kind == Kind.is_u16
assert v.get_u16() == 34
}

fn test_value_of_u32() {
v := value_of(u32(4567))
assert v.kind == Kind.is_u32
assert v.get_u32() == 4567
}

fn test_value_of_u64() {
v := value_of(u64(56789))
assert v.kind == Kind.is_u64
assert v.get_u64() == 56789
}

fn test_value_of_rune() {
v := value_of(`😃`)
assert v.kind == Kind.is_rune
assert v.get_rune() == `😃`
}

fn test_value_of_f32() {
v := value_of(f32(1.23))
assert v.kind == Kind.is_f32
assert v.get_f32() == f32(1.23)
}

fn test_value_of_f64() {
v := value_of(4.56)
assert v.kind == Kind.is_f64
assert v.get_f64() == 4.56
}

0 comments on commit 6e45d74

Please sign in to comment.