mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 16:47:01 +00:00
216 lines
4.8 KiB
V
216 lines
4.8 KiB
V
// ops.v — arithmetic, comparison, and string operations for the VuurRaaf VM.
|
|
module vm
|
|
|
|
import math
|
|
|
|
fn (mut v Vm) add(a i64, b i64) !i64 {
|
|
// String + any: coerce the other operand to its string form so that
|
|
// `"caught: " + e`, `"n=" + count`, and record/array debugging "just
|
|
// work" the way users expect when building messages.
|
|
if v.is_str(a) && !v.is_str(b) {
|
|
return v.alloc_str(v.strings[v.hand(a)] + v.val_str(b, 0))
|
|
}
|
|
if v.is_str(b) && !v.is_str(a) {
|
|
return v.alloc_str(v.val_str(a, 0) + v.strings[v.hand(b)])
|
|
}
|
|
if v.is_str(a) && v.is_str(b) {
|
|
return v.alloc_str(v.strings[v.hand(a)] + v.strings[v.hand(b)])
|
|
}
|
|
if v.is_none(a) || v.is_none(b) {
|
|
return error('cannot use none with +')
|
|
}
|
|
if v.is_arr(a) || v.is_arr(b) {
|
|
return error('cannot add arrays with +')
|
|
}
|
|
if v.is_struct(a) || v.is_struct(b) {
|
|
return error('cannot add structs with +')
|
|
}
|
|
if v.is_float(a) || v.is_float(b) {
|
|
return v.push_float(v.to_f64(a) + v.to_f64(b))
|
|
}
|
|
return v.enc_int(v.dec_int(a) + v.dec_int(b))
|
|
}
|
|
|
|
// to_f64 promotes an integer or float tagged value to f64.
|
|
fn (mut v Vm) to_f64(x i64) f64 {
|
|
if v.is_float(x) {
|
|
return v.fval(x)
|
|
}
|
|
return f64(v.dec_int(x))
|
|
}
|
|
|
|
fn (mut v Vm) alloc_str(s string) i64 {
|
|
v.strings << s
|
|
return v.mkstr(v.strings.len - 1)
|
|
}
|
|
|
|
fn (mut v Vm) num_str(x i64) string {
|
|
if v.is_float(x) {
|
|
return fmt_float(v.fval(x))
|
|
}
|
|
return v.dec_int(x).str()
|
|
}
|
|
|
|
// fmt_float renders an f64 nicely: integral values lose the trailing ".0",
|
|
// -0 collapses to 0, and most floats avoid V's default scientific notation.
|
|
fn fmt_float(f f64) string {
|
|
if f == 0.0 {
|
|
return '0'
|
|
}
|
|
if math.is_nan(f) {
|
|
return 'NaN'
|
|
}
|
|
if math.is_inf(f, 1) {
|
|
return 'Inf'
|
|
}
|
|
if math.is_inf(f, -1) {
|
|
return '-Inf'
|
|
}
|
|
if f == math.floor(f) && math.abs(f) < 1e18 {
|
|
return i64(f).str()
|
|
}
|
|
if math.abs(f) < 1e18 {
|
|
mut s := '${f:.14f}'
|
|
s = s.trim_right('0').trim_right('.')
|
|
if s == '' {
|
|
return '0'
|
|
}
|
|
return s
|
|
}
|
|
return f.str()
|
|
}
|
|
|
|
fn (mut v Vm) arith(a i64, b i64, op string) !i64 {
|
|
if v.is_none(a) || v.is_none(b) {
|
|
return error('cannot use none with "${op}"')
|
|
}
|
|
if v.is_str(a) || v.is_str(b) {
|
|
return error('cannot use strings with "${op}"')
|
|
}
|
|
if v.is_arr(a) || v.is_arr(b) {
|
|
return error('cannot use arrays with "${op}"')
|
|
}
|
|
if v.is_struct(a) || v.is_struct(b) {
|
|
return error('cannot use structs with "${op}"')
|
|
}
|
|
if v.is_float(a) || v.is_float(b) {
|
|
x := v.to_f64(a)
|
|
y := v.to_f64(b)
|
|
match op {
|
|
'-' {
|
|
return v.push_float(x - y)
|
|
}
|
|
'*' {
|
|
return v.push_float(x * y)
|
|
}
|
|
'/' {
|
|
if y == 0.0 {
|
|
return error('division by zero')
|
|
}
|
|
return v.push_float(x / y)
|
|
}
|
|
'%' {
|
|
if y == 0.0 {
|
|
return error('division by zero')
|
|
}
|
|
return v.push_float(math.fmod(x, y))
|
|
}
|
|
else {
|
|
return error('internal: bad arith op "${op}"')
|
|
}
|
|
}
|
|
}
|
|
x := v.dec_int(a)
|
|
y := v.dec_int(b)
|
|
match op {
|
|
'-' {
|
|
return v.enc_int(x - y)
|
|
}
|
|
'*' {
|
|
return v.enc_int(x * y)
|
|
}
|
|
'/' {
|
|
if y == 0 {
|
|
return error('division by zero')
|
|
}
|
|
return v.enc_int(x / y)
|
|
}
|
|
'%' {
|
|
if y == 0 {
|
|
return error('division by zero')
|
|
}
|
|
return v.enc_int(x % y)
|
|
}
|
|
else {
|
|
return error('internal: bad arith op "${op}"')
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut v Vm) cmp(a i64, b i64, op string) !i64 {
|
|
if v.is_none(a) || v.is_none(b) {
|
|
// none equals only none; ordering a none is an error
|
|
if op == '==' {
|
|
return bool_i64(v.is_none(a) && v.is_none(b))
|
|
}
|
|
if op == '!=' {
|
|
return bool_i64(!(v.is_none(a) && v.is_none(b)))
|
|
}
|
|
return error('cannot order none')
|
|
}
|
|
if v.is_arr(a) || v.is_arr(b) {
|
|
// arrays compare by identity (handle equality) with ==/!=
|
|
if op == '==' || op == '!=' {
|
|
return bool_i64(if op == '==' { a == b } else { a != b })
|
|
}
|
|
return error('cannot order arrays')
|
|
}
|
|
if v.is_struct(a) || v.is_struct(b) {
|
|
// structs compare by identity (handle equality) with ==/!=
|
|
if op == '==' || op == '!=' {
|
|
return bool_i64(if op == '==' { a == b } else { a != b })
|
|
}
|
|
return error('cannot order structs')
|
|
}
|
|
if v.is_str(a) && v.is_str(b) {
|
|
sa := v.strings[v.hand(a)]
|
|
sb := v.strings[v.hand(b)]
|
|
return bool_i64(match op {
|
|
'==' { sa == sb }
|
|
'!=' { sa != sb }
|
|
'<' { sa < sb }
|
|
'<=' { sa <= sb }
|
|
'>' { sa > sb }
|
|
'>=' { sa >= sb }
|
|
else { return error('internal: bad cmp op "${op}"') }
|
|
})
|
|
}
|
|
if v.is_str(a) || v.is_str(b) {
|
|
return error('cannot compare a string and a number')
|
|
}
|
|
if v.is_float(a) || v.is_float(b) {
|
|
x := v.to_f64(a)
|
|
y := v.to_f64(b)
|
|
return bool_i64(match op {
|
|
'==' { x == y }
|
|
'!=' { x != y }
|
|
'<' { x < y }
|
|
'<=' { x <= y }
|
|
'>' { x > y }
|
|
'>=' { x >= y }
|
|
else { return error('internal: bad cmp op "${op}"') }
|
|
})
|
|
}
|
|
x := v.dec_int(a)
|
|
y := v.dec_int(b)
|
|
return bool_i64(match op {
|
|
'==' { x == y }
|
|
'!=' { x != y }
|
|
'<' { x < y }
|
|
'<=' { x <= y }
|
|
'>' { x > y }
|
|
'>=' { x >= y }
|
|
else { return error('internal: bad cmp op "${op}"') }
|
|
})
|
|
}
|