mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
Merge branch 'master' into 64bit-int-1
This commit is contained in:
commit
32aaf30d1e
17 changed files with 580 additions and 302 deletions
|
@ -897,6 +897,7 @@ fn (t Tree) fn_type_decl(node ast.FnTypeDecl) &Node {
|
|||
mut obj := create_object()
|
||||
obj.add_terse('ast_type', t.string_node('FnTypeDecl'))
|
||||
obj.add_terse('name', t.string_node(node.name))
|
||||
obj.add_terse('mod', t.string_node(node.mod))
|
||||
obj.add_terse('is_pub', t.bool_node(node.is_pub))
|
||||
obj.add_terse('typ', t.type_node(node.typ))
|
||||
obj.add('pos', t.pos(node.pos))
|
||||
|
|
|
@ -1521,6 +1521,7 @@ pub mut:
|
|||
pub struct FnTypeDecl {
|
||||
pub:
|
||||
name string
|
||||
mod string
|
||||
is_pub bool
|
||||
typ Type
|
||||
pos token.Pos
|
||||
|
|
|
@ -444,6 +444,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
|||
if c.check_import_sym_conflict(left.name) {
|
||||
c.error('duplicate of an import symbol `${left.name}`', left.pos)
|
||||
}
|
||||
c.check_module_name_conflict(left.name, left.pos)
|
||||
}
|
||||
if node.op == .assign && left_type.has_flag(.option) && right is ast.UnsafeExpr
|
||||
&& right.expr.is_nil() {
|
||||
|
|
|
@ -145,6 +145,7 @@ mut:
|
|||
variant_data_type ast.Type
|
||||
fn_return_type ast.Type
|
||||
orm_table_fields map[string][]ast.StructField // known table structs
|
||||
short_module_names []string // to check for function names colliding with module functions
|
||||
|
||||
v_current_commit_hash string // same as old C.V_CURRENT_COMMIT_HASH
|
||||
assign_stmt_attr string // for `x := [1,2,3] @[freed]`
|
||||
|
@ -331,6 +332,14 @@ pub fn (mut c Checker) change_current_file(file &ast.File) {
|
|||
c.vmod_file_content = ''
|
||||
c.mod = file.mod.name
|
||||
c.is_generated = file.is_generated
|
||||
c.short_module_names = ['builtin']
|
||||
for import_sym in c.file.imports {
|
||||
c.short_module_names << if import_sym.alias == '' {
|
||||
import_sym.mod.all_after_last('.')
|
||||
} else {
|
||||
import_sym.alias
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) check_files(ast_files []&ast.File) {
|
||||
|
@ -5804,6 +5813,16 @@ fn (c &Checker) check_import_sym_conflict(ident string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_module_name_conflict(ident string, pos token.Pos) {
|
||||
if ident.contains('__') {
|
||||
prefix := ident.all_before('__')
|
||||
if prefix in c.short_module_names {
|
||||
c.error('identifier cannot use prefix `${prefix}__` of imported module `${prefix}`',
|
||||
pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update_unresolved_fixed_sizes updates the unresolved type symbols for array fixed return type and alias type.
|
||||
pub fn (mut c Checker) update_unresolved_fixed_sizes() {
|
||||
for mut stmt in c.unresolved_fixed_sizes {
|
||||
|
|
|
@ -104,6 +104,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|||
if !node.is_method && node.mod == 'main' && node.short_name in c.table.builtin_pub_fns {
|
||||
c.error('cannot redefine builtin public function `${node.short_name}`', node.pos)
|
||||
}
|
||||
c.check_module_name_conflict(node.short_name, node.pos)
|
||||
}
|
||||
if node.name == 'main.main' {
|
||||
c.main_fn_decl_node = *node
|
||||
|
|
28
vlib/v/checker/tests/clash_ident_module_name_prefix.out
Normal file
28
vlib/v/checker/tests/clash_ident_module_name_prefix.out
Normal file
|
@ -0,0 +1,28 @@
|
|||
vlib/v/checker/tests/clash_ident_module_name_prefix.vv:7:1: error: identifier cannot use prefix `builtin__` of imported module `builtin`
|
||||
5 | // os is not imported so the os__ prefix should not produce an error
|
||||
6 |
|
||||
7 | fn builtin__string_str() {
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
8 | }
|
||||
9 |
|
||||
vlib/v/checker/tests/clash_ident_module_name_prefix.vv:10:1: error: identifier cannot use prefix `time__` of imported module `time`
|
||||
8 | }
|
||||
9 |
|
||||
10 | fn time__utc() {
|
||||
| ~~~~~~~~~~~~~~
|
||||
11 | }
|
||||
12 |
|
||||
vlib/v/checker/tests/clash_ident_module_name_prefix.vv:17:2: error: identifier cannot use prefix `builtin__` of imported module `builtin`
|
||||
15 |
|
||||
16 | fn main() {
|
||||
17 | builtin__string_str := 'Hello V!'.str()
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
18 | time__now := time.now()
|
||||
19 | os__log := 'Hello V!'
|
||||
vlib/v/checker/tests/clash_ident_module_name_prefix.vv:18:2: error: identifier cannot use prefix `time__` of imported module `time`
|
||||
16 | fn main() {
|
||||
17 | builtin__string_str := 'Hello V!'.str()
|
||||
18 | time__now := time.now()
|
||||
| ~~~~~~~~~
|
||||
19 | os__log := 'Hello V!'
|
||||
20 | println(builtin__string_str)
|
23
vlib/v/checker/tests/clash_ident_module_name_prefix.vv
Normal file
23
vlib/v/checker/tests/clash_ident_module_name_prefix.vv
Normal file
|
@ -0,0 +1,23 @@
|
|||
import time
|
||||
|
||||
// builtin__ prefix should always produce an error
|
||||
// time is imported so the time__ prefix should always produce an error
|
||||
// os is not imported so the os__ prefix should not produce an error
|
||||
|
||||
fn builtin__string_str() {
|
||||
}
|
||||
|
||||
fn time__utc() {
|
||||
}
|
||||
|
||||
fn os__getwd() {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
builtin__string_str := 'Hello V!'.str()
|
||||
time__now := time.now()
|
||||
os__log := 'Hello V!'
|
||||
println(builtin__string_str)
|
||||
println(time__now)
|
||||
println(os__log)
|
||||
}
|
|
@ -1798,10 +1798,8 @@ fn (mut c Amd64) mov(r Register, val i32) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut c Amd64) mul_reg(a Amd64Register, b Amd64Register) {
|
||||
if a != .rax {
|
||||
c.g.n_error('mul always operates on rax')
|
||||
}
|
||||
// rax times b
|
||||
fn (mut c Amd64) mul_reg_rax(b Amd64Register) {
|
||||
match b {
|
||||
.rax {
|
||||
c.g.write8(0x48)
|
||||
|
@ -1844,10 +1842,8 @@ fn (mut c Amd64) imul_reg(r Amd64Register) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut c Amd64) div_reg(a Amd64Register, b Amd64Register) {
|
||||
if a != .rax {
|
||||
c.g.n_error('div always operates on rax')
|
||||
}
|
||||
// rax divided by b
|
||||
fn (mut c Amd64) div_reg_rax(b Amd64Register) {
|
||||
match b {
|
||||
.rax {
|
||||
c.g.write8(0x48)
|
||||
|
@ -1876,8 +1872,9 @@ fn (mut c Amd64) div_reg(a Amd64Register, b Amd64Register) {
|
|||
c.g.println('div ${b}')
|
||||
}
|
||||
|
||||
fn (mut c Amd64) mod_reg(a Amd64Register, b Amd64Register) {
|
||||
c.div_reg(a, b)
|
||||
// rax % b
|
||||
fn (mut c Amd64) mod_reg_rax(b Amd64Register) {
|
||||
c.div_reg_rax(b)
|
||||
c.mov_reg(Amd64Register.rdx, Amd64Register.rax)
|
||||
}
|
||||
|
||||
|
@ -2332,36 +2329,6 @@ fn (mut c Amd64) assign_var(var IdentVar, raw_type ast.Type) {
|
|||
// TODO: may have a problem if the literal is bigger than max_i64: needs u64
|
||||
fn (mut c Amd64) assign_ident_int_lit(node ast.AssignStmt, i i32, int_lit ast.IntegerLiteral, left ast.Ident) {
|
||||
match node.op {
|
||||
.plus_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
|
||||
c.add_reg(Amd64Register.rax, Amd64Register.rdx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.minus_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
|
||||
c.sub_reg(Amd64Register.rax, Amd64Register.rdx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.mult_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
|
||||
c.mul_reg(.rax, .rdx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.div_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
|
||||
c.div_reg(.rax, .rdx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.mod_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rdx, i64(int_lit.val.int()))
|
||||
c.mod_reg(.rax, .rdx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.decl_assign {
|
||||
c.allocate_var(left.name, 8, i64(int_lit.val.int()))
|
||||
}
|
||||
|
@ -2369,56 +2336,23 @@ fn (mut c Amd64) assign_ident_int_lit(node ast.AssignStmt, i i32, int_lit ast.In
|
|||
c.mov64(Amd64Register.rax, i64(int_lit.val.int()))
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.left_shift_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.shl_reg(.rax, .rcx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.right_shift_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.sar_reg(.rax, .rcx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.unsigned_right_shift_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.shr_reg(.rax, .rcx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.xor_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.bitxor_reg(.rax, .rcx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.or_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.bitor_reg(.rax, .rcx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.and_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.bitand_reg(.rax, .rcx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.boolean_and_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.bitand_reg(.rax, .rcx)
|
||||
c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
|
||||
c.bitand_reg(.rax, .rbx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
.boolean_or_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rcx, i64(int_lit.val.int()))
|
||||
c.bitor_reg(.rax, .rcx)
|
||||
c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
|
||||
c.bitor_reg(.rax, .rbx)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
else {
|
||||
c.g.n_error('${@LOCATION} unexpected assignment op ${node.op}')
|
||||
c.mov_var_to_reg(Amd64Register.rax, left)
|
||||
c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
|
||||
c.apply_op_int(.rax, .rbx, node.op)
|
||||
c.mov_reg_to_var(left, Amd64Register.rax)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2636,22 +2570,29 @@ fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.E
|
|||
|
||||
c.mov_ssereg_to_var(ident, .xmm1)
|
||||
} else if left_type.is_int() {
|
||||
c.mov_var_to_reg(Amd64Register.rbx, ident)
|
||||
|
||||
match node.op {
|
||||
.plus_assign { c.add_reg(.rbx, .rax) }
|
||||
.minus_assign { c.sub_reg(.rbx, .rax) }
|
||||
.div_assign { c.div_reg(.rbx, .rax) }
|
||||
.mult_assign { c.mul_reg(.rbx, .rax) }
|
||||
else { c.g.n_error('${@LOCATION} unexpected assignment operator ${node.op} for i32') }
|
||||
}
|
||||
|
||||
c.mov_reg_to_var(ident, Amd64Register.rbx)
|
||||
c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
|
||||
c.mov_var_to_reg(Amd64Register.rax, ident)
|
||||
c.apply_op_int(.rax, .rbx, node.op)
|
||||
c.mov_reg_to_var(ident, Amd64Register.rax)
|
||||
} else {
|
||||
match node.op {
|
||||
.boolean_and_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rbx, ident)
|
||||
c.bitand_reg(.rbx, .rax)
|
||||
c.mov_reg_to_var(ident, Amd64Register.rbx)
|
||||
}
|
||||
.boolean_or_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rbx, ident)
|
||||
c.bitor_reg(.rbx, .rax)
|
||||
c.mov_reg_to_var(ident, Amd64Register.rbx)
|
||||
}
|
||||
else {
|
||||
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
left_type := node.left_types[i]
|
||||
if node.op == .decl_assign {
|
||||
|
@ -2676,22 +2617,29 @@ fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.E
|
|||
|
||||
c.mov_ssereg_to_var(ident, .xmm1)
|
||||
} else if left_type.is_int() {
|
||||
c.mov_var_to_reg(Amd64Register.rbx, ident)
|
||||
|
||||
match node.op {
|
||||
.plus_assign { c.add_reg(.rbx, .rax) }
|
||||
.minus_assign { c.sub_reg(.rbx, .rax) }
|
||||
.div_assign { c.div_reg(.rbx, .rax) }
|
||||
.mult_assign { c.mul_reg(.rbx, .rax) }
|
||||
else { c.g.n_error('${@LOCATION} unexpected assignment operator ${node.op} for i32') }
|
||||
}
|
||||
|
||||
c.mov_reg_to_var(ident, Amd64Register.rbx)
|
||||
c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
|
||||
c.mov_var_to_reg(Amd64Register.rax, ident)
|
||||
c.apply_op_int(.rax, .rbx, node.op)
|
||||
c.mov_reg_to_var(ident, Amd64Register.rax)
|
||||
} else {
|
||||
match node.op {
|
||||
.boolean_and_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rbx, ident)
|
||||
c.bitand_reg(.rbx, .rax)
|
||||
c.mov_reg_to_var(ident, Amd64Register.rbx)
|
||||
}
|
||||
.boolean_or_assign {
|
||||
c.mov_var_to_reg(Amd64Register.rbx, ident)
|
||||
c.bitor_reg(.rbx, .rax)
|
||||
c.mov_reg_to_var(ident, Amd64Register.rbx)
|
||||
}
|
||||
else {
|
||||
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ast.IndexExpr {
|
||||
|
@ -2725,6 +2673,59 @@ fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.E
|
|||
}*/
|
||||
}
|
||||
|
||||
// /!\ for div, mul, mod the left value should always be .rax
|
||||
fn (mut c Amd64) apply_op_int(left_value Amd64Register, right_value Amd64Register, op token.Kind) {
|
||||
match op {
|
||||
.plus_assign {
|
||||
c.add_reg(left_value, right_value)
|
||||
}
|
||||
.minus_assign {
|
||||
c.sub_reg(left_value, right_value)
|
||||
}
|
||||
.div_assign {
|
||||
if left_value != .rax {
|
||||
c.g.n_error('@{LOCATION} div always operates on rax')
|
||||
}
|
||||
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
|
||||
c.div_reg_rax(right_value)
|
||||
}
|
||||
.mult_assign {
|
||||
if left_value != .rax {
|
||||
c.g.n_error('@{LOCATION} mul always operates on rax')
|
||||
}
|
||||
c.mul_reg_rax(right_value)
|
||||
}
|
||||
.xor_assign {
|
||||
c.bitxor_reg(left_value, right_value)
|
||||
}
|
||||
.mod_assign {
|
||||
if left_value != .rax {
|
||||
c.g.n_error('@{LOCATION} mod always operates on rax')
|
||||
}
|
||||
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
|
||||
c.mod_reg_rax(right_value)
|
||||
}
|
||||
.or_assign {
|
||||
c.bitor_reg(left_value, right_value)
|
||||
}
|
||||
.and_assign {
|
||||
c.bitand_reg(left_value, right_value)
|
||||
}
|
||||
.right_shift_assign {
|
||||
c.shr_reg(left_value, right_value)
|
||||
}
|
||||
.left_shift_assign {
|
||||
c.shl_reg(left_value, right_value)
|
||||
}
|
||||
.unsigned_right_shift_assign {
|
||||
c.sar_reg(left_value, right_value)
|
||||
}
|
||||
else {
|
||||
c.g.n_error('${@LOCATION} unexpected operator ${op} for int')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Amd64) gen_type_promotion(from ast.Type, to ast.Type, option Amd64RegisterOption) {
|
||||
if !to.is_pure_float() {
|
||||
return
|
||||
|
@ -3122,52 +3123,6 @@ fn (mut c Amd64) assign_stmt(node ast.AssignStmt) {
|
|||
.decl_assign, .assign {
|
||||
c.mov_store(.rbx, .rcx, size)
|
||||
}
|
||||
.plus_assign {
|
||||
c.add_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.minus_assign {
|
||||
c.sub_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.and_assign {
|
||||
c.bitand_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.mod_assign {
|
||||
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
|
||||
c.mod_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.mult_assign {
|
||||
c.mul_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.div_assign {
|
||||
c.mov(Amd64Register.rdx, i32(0)) // 64bits IDIV uses RDX:RAX
|
||||
c.div_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.xor_assign {
|
||||
c.bitxor_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.or_assign {
|
||||
c.bitor_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.right_shift_assign {
|
||||
c.shr_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.left_shift_assign {
|
||||
c.shl_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.unsigned_right_shift_assign {
|
||||
c.sar_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
.boolean_and_assign {
|
||||
c.bitand_reg(.rax, .rcx)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
|
@ -3177,7 +3132,8 @@ fn (mut c Amd64) assign_stmt(node ast.AssignStmt) {
|
|||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
else {
|
||||
c.g.n_error('${@LOCATION} Unsupported assign instruction (${node.op})')
|
||||
c.apply_op_int(.rax, .rcx, node.op)
|
||||
c.mov_store(.rbx, .rax, size)
|
||||
}
|
||||
}
|
||||
} else if var_type.is_pure_float() {
|
||||
|
@ -4334,7 +4290,7 @@ fn (mut c Amd64) convert_int_to_string(a Register, b Register) {
|
|||
|
||||
c.mov(Amd64Register.rdx, 0) // upperhalf of the dividend
|
||||
c.mov(Amd64Register.rbx, 10)
|
||||
c.div_reg(.rax, .rbx) // rax will be the result of the division
|
||||
c.div_reg_rax(.rbx) // rax will be the result of the division
|
||||
c.add8(.rdx, i32(`0`)) // rdx is the remainder, add 48 to convert it into it's ascii representation
|
||||
|
||||
c.mov_store(.rdi, .rdx, ._8)
|
||||
|
|
|
@ -7,6 +7,88 @@ fn main() {
|
|||
test_alias(100, 9)
|
||||
test_plus_assign()
|
||||
test_minus_assign()
|
||||
op_assigns_test()
|
||||
if_expr_op_assigns_test()
|
||||
}
|
||||
|
||||
fn if_expr_op_assigns_test() {
|
||||
mut b := 1
|
||||
one := 1
|
||||
two := 2
|
||||
three := 3
|
||||
four := 4
|
||||
five := 5
|
||||
ten := 10
|
||||
b += if false {0} else {one}
|
||||
b -= if false {0} else {one}
|
||||
assert b == 1
|
||||
b |= if false {0} else {two}
|
||||
assert b == 3
|
||||
b &= if false {0} else {one}
|
||||
assert b == 1
|
||||
b ^= if false {0} else {five}
|
||||
assert b == 4
|
||||
b %= if false {0} else {three}
|
||||
assert b == 1
|
||||
b *= if false {0} else {ten}
|
||||
b /= if false {0} else {ten}
|
||||
assert b == 1
|
||||
b <<= if false {0} else {four}
|
||||
b >>>= if false {0} else {two}
|
||||
b >>= if false {0} else {two}
|
||||
assert b == 1
|
||||
|
||||
mut var := true
|
||||
t := true
|
||||
f := false
|
||||
var &&= if false {f} else {t}
|
||||
assert var == true
|
||||
var &&= if false {t} else {f}
|
||||
assert var == false
|
||||
var ||= if false {f} else {t}
|
||||
assert var == true
|
||||
var ||= if false {t} else {f}
|
||||
assert var == true
|
||||
}
|
||||
|
||||
fn op_assigns_test() {
|
||||
mut b := 1
|
||||
one := 1
|
||||
two := 2
|
||||
three := 3
|
||||
four := 4
|
||||
five := 5
|
||||
ten := 10
|
||||
b += one
|
||||
b -= one
|
||||
assert b == 1
|
||||
b |= two
|
||||
assert b == 3
|
||||
b &= one
|
||||
assert b == 1
|
||||
b ^= five
|
||||
assert b == 4
|
||||
b %= three
|
||||
assert b == 1
|
||||
b *= ten
|
||||
b /= ten
|
||||
assert b == 1
|
||||
b <<= four
|
||||
b >>>= two
|
||||
b >>= two
|
||||
assert b == 1
|
||||
|
||||
mut var := true
|
||||
t := true
|
||||
f := false
|
||||
var &&= t
|
||||
assert var == true
|
||||
var &&= f
|
||||
assert var == false
|
||||
var ||= t
|
||||
assert var == true
|
||||
var ||= f
|
||||
assert var == true
|
||||
}
|
||||
|
||||
fn test_minus_assign() {
|
||||
|
|
|
@ -2782,6 +2782,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
p.attrs = []
|
||||
return ast.FnTypeDecl{
|
||||
name: fn_name
|
||||
mod: p.mod
|
||||
is_pub: is_pub
|
||||
typ: fn_type
|
||||
pos: decl_pos
|
||||
|
|
|
@ -41,6 +41,9 @@ fn ascon_pnr(mut s State, nr int) {
|
|||
if nr < 1 || nr > 16 {
|
||||
panic('Invalid round number')
|
||||
}
|
||||
// Allocate temporary vars to reduce allocation within loop
|
||||
mut x0 := u64(0)
|
||||
mut y0 := u64(0)
|
||||
// Ascon permutation routine
|
||||
for i := max_nr_perm - nr; i < max_nr_perm; i++ {
|
||||
// 3.2 Constant-Addition Layer step
|
||||
|
@ -56,18 +59,22 @@ fn ascon_pnr(mut s State, nr int) {
|
|||
s.e0 ^= s.e4
|
||||
s.e4 ^= s.e3
|
||||
s.e2 ^= s.e1
|
||||
|
||||
// Set temp vars to values
|
||||
x0 = s.e0
|
||||
y0 = s.e4 ^ (~s.e0 & s.e1)
|
||||
/*
|
||||
t0 := s.e4 ^ (~s.e0 & s.e1)
|
||||
t1 := s.e0 ^ (~s.e1 & s.e2)
|
||||
t2 := s.e1 ^ (~s.e2 & s.e3)
|
||||
t3 := s.e2 ^ (~s.e3 & s.e4)
|
||||
t4 := s.e3 ^ (~s.e4 & s.e0)
|
||||
*/
|
||||
|
||||
s.e0 = t1
|
||||
s.e1 = t2
|
||||
s.e2 = t3
|
||||
s.e3 = t4
|
||||
s.e4 = t0
|
||||
s.e0 = s.e0 ^ (~s.e1 & s.e2) // t1
|
||||
s.e1 = s.e1 ^ (~s.e2 & s.e3) // t2
|
||||
s.e2 = s.e2 ^ (~s.e3 & s.e4) // t3
|
||||
s.e3 = s.e3 ^ (~s.e4 & x0) // t4, change s.e0 to x0
|
||||
s.e4 = y0
|
||||
|
||||
s.e1 ^= s.e0
|
||||
s.e0 ^= s.e4
|
||||
|
|
|
@ -135,20 +135,32 @@ fn (mut d Digest) squeeze(mut dst []u8) int {
|
|||
}
|
||||
|
||||
@[direct_array_access; inline]
|
||||
fn ascon_generic_hash(mut s State, msg_ []u8, size int) []u8 {
|
||||
fn ascon_generic_hash(mut s State, msg []u8, size int) []u8 {
|
||||
// Assumed state was correctly initialized
|
||||
// Absorbing the message
|
||||
mut msg := msg_.clone()
|
||||
for msg.len >= block_size {
|
||||
s.e0 ^= binary.little_endian_u64(msg[0..block_size])
|
||||
unsafe {
|
||||
msg = msg[block_size..]
|
||||
}
|
||||
mut pos := 0
|
||||
// Check if msg has non-null length, if yes, absorb it.
|
||||
// Otherwise, just pad it
|
||||
if _likely_(msg.len > 0) {
|
||||
mut msg_len := msg.len
|
||||
for msg_len >= block_size {
|
||||
s.e0 ^= binary.little_endian_u64(msg[pos..pos + block_size])
|
||||
pos += block_size
|
||||
msg_len -= block_size
|
||||
ascon_pnr(mut s, ascon_prnd_12)
|
||||
}
|
||||
// Absorb the last partial message block
|
||||
s.e0 ^= load_bytes(msg, msg.len)
|
||||
s.e0 ^= pad(msg.len)
|
||||
last_block := unsafe { msg[pos..] }
|
||||
s.e0 ^= u64(0x01) << (8 * last_block.len) // pad(last_block.len)
|
||||
if last_block.len > 0 {
|
||||
s.e0 ^= load_bytes(last_block, last_block.len)
|
||||
}
|
||||
} else {
|
||||
// Otherwise, just pad it
|
||||
s.e0 ^= u64(0x01)
|
||||
}
|
||||
// reset pos
|
||||
pos = 0
|
||||
|
||||
// Squeezing phase
|
||||
//
|
||||
|
@ -156,7 +168,6 @@ fn ascon_generic_hash(mut s State, msg_ []u8, size int) []u8 {
|
|||
// permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] to the state:
|
||||
ascon_pnr(mut s, ascon_prnd_12)
|
||||
mut out := []u8{len: size}
|
||||
mut pos := 0
|
||||
mut clen := out.len
|
||||
for clen >= block_size {
|
||||
binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0)
|
||||
|
|
|
@ -83,13 +83,23 @@ fn set_byte(b u8, i int) u64 {
|
|||
fn load_bytes(bytes []u8, n int) u64 {
|
||||
mut x := u64(0)
|
||||
for i := 0; i < n; i++ {
|
||||
x |= set_byte(bytes[i], i)
|
||||
// This is the same way to store bytes in little-endian way
|
||||
// x |= u64(bytes[0]) << 8*0 // LSB at lowest index
|
||||
// x |= u64(bytes[1]) << 8*1
|
||||
// x |= u64(bytes[2]) << 8*2
|
||||
// x |= u64(bytes[3]) << 8*3
|
||||
// ...etc
|
||||
// x |= u64(bytes[7]) << 8*7 // MSB at highest index
|
||||
x |= u64(bytes[i]) << (8 * i)
|
||||
}
|
||||
return u64le(x)
|
||||
// No need to cast with u64le, its alread le
|
||||
return x
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn store_bytes(mut out []u8, x u64, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
out[i] = get_byte(x, i)
|
||||
// use underlying get_byte directly
|
||||
out[i] = u8(x >> (8 * i))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,16 @@ struct StruWithRequiredAttribute {
|
|||
b int
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
a int @[required]
|
||||
}
|
||||
|
||||
fn test_last_field_requiered() {
|
||||
assert json.decode[Foo]('{"a":0}')! == Foo{
|
||||
a: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn test_skip_and_rename_attributes() {
|
||||
assert json.decode[StruWithJsonAttribute]('{"name": "hola1", "a": 2, "b": 3}')! == StruWithJsonAttribute{
|
||||
a: 2
|
||||
|
|
|
@ -38,7 +38,7 @@ struct StructFieldInfo {
|
|||
is_required bool
|
||||
is_raw bool
|
||||
mut:
|
||||
decoded_with_value_info_node &Node[ValueInfo] = unsafe { nil }
|
||||
is_decoded bool
|
||||
}
|
||||
|
||||
// Decoder represents a JSON decoder.
|
||||
|
@ -368,105 +368,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
}
|
||||
}
|
||||
$if T.unaliased_typ is string {
|
||||
string_info := decoder.current_node.value
|
||||
|
||||
if string_info.value_kind == .string {
|
||||
mut string_buffer := []u8{cap: string_info.length} // might be too long but most json strings don't contain many escape characters anyways
|
||||
|
||||
mut buffer_index := 1
|
||||
mut string_index := 1
|
||||
|
||||
for string_index < string_info.length - 1 {
|
||||
current_byte := decoder.json[string_info.position + string_index]
|
||||
|
||||
if current_byte == `\\` {
|
||||
// push all characters up to this point
|
||||
unsafe {
|
||||
string_buffer.push_many(decoder.json.str + string_info.position +
|
||||
buffer_index, string_index - buffer_index)
|
||||
}
|
||||
|
||||
string_index++
|
||||
|
||||
escaped_char := decoder.json[string_info.position + string_index]
|
||||
|
||||
string_index++
|
||||
|
||||
match escaped_char {
|
||||
`/`, `"`, `\\` {
|
||||
string_buffer << escaped_char
|
||||
}
|
||||
`b` {
|
||||
string_buffer << `\b`
|
||||
}
|
||||
`f` {
|
||||
string_buffer << `\f`
|
||||
}
|
||||
`n` {
|
||||
string_buffer << `\n`
|
||||
}
|
||||
`r` {
|
||||
string_buffer << `\r`
|
||||
}
|
||||
`t` {
|
||||
string_buffer << `\t`
|
||||
}
|
||||
`u` {
|
||||
unicode_point := rune(strconv.parse_uint(decoder.json[
|
||||
string_info.position + string_index..string_info.position +
|
||||
string_index + 4], 16, 32)!)
|
||||
|
||||
string_index += 4
|
||||
|
||||
if unicode_point < 0xD800 { // normal utf-8
|
||||
string_buffer << unicode_point.bytes()
|
||||
} else if unicode_point >= 0xDC00 { // trail surrogate -> invalid
|
||||
decoder.decode_error('Got trail surrogate: ${u32(unicode_point):04X} before head surrogate.')!
|
||||
} else { // head surrogate -> treat as utf-16
|
||||
if string_index > string_info.length - 6 {
|
||||
decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
|
||||
}
|
||||
if decoder.json[string_info.position + string_index..
|
||||
string_info.position + string_index + 2] != '\\u' {
|
||||
decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
|
||||
}
|
||||
|
||||
string_index += 2
|
||||
|
||||
unicode_point2 := rune(strconv.parse_uint(decoder.json[
|
||||
string_info.position + string_index..string_info.position +
|
||||
string_index + 4], 16, 32)!)
|
||||
|
||||
string_index += 4
|
||||
|
||||
if unicode_point2 < 0xDC00 {
|
||||
decoder.decode_error('Expected a trail surrogate after a head surrogate, but got ${u32(unicode_point):04X}.')!
|
||||
}
|
||||
|
||||
final_unicode_point := (unicode_point2 & 0x3FF) +
|
||||
((unicode_point & 0x3FF) << 10) + 0x10000
|
||||
string_buffer << final_unicode_point.bytes()
|
||||
}
|
||||
}
|
||||
else {} // has already been checked
|
||||
}
|
||||
|
||||
buffer_index = string_index
|
||||
} else {
|
||||
string_index++
|
||||
}
|
||||
}
|
||||
|
||||
// push the rest
|
||||
unsafe {
|
||||
string_buffer.push_many(decoder.json.str + string_info.position + buffer_index,
|
||||
string_index - buffer_index)
|
||||
}
|
||||
|
||||
val = string_buffer.bytestr()
|
||||
} else {
|
||||
return decoder.decode_error('Expected string, but got ${string_info.value_kind}')
|
||||
}
|
||||
decoder.decode_string(mut val)!
|
||||
} $else $if T.unaliased_typ is $sumtype {
|
||||
decoder.decode_sumtype(mut val)!
|
||||
return
|
||||
|
@ -509,7 +411,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
for attr in field.attrs {
|
||||
if attr.starts_with('json:') {
|
||||
if attr.len <= 6 {
|
||||
return decoder.decode_error('`json` attribute must have an argument')
|
||||
decoder.decode_error('`json` attribute must have an argument')!
|
||||
}
|
||||
json_name_str = unsafe { attr.str + 6 }
|
||||
json_name_len = attr.len - 6
|
||||
|
@ -615,9 +517,9 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
|
||||
if current_field_info.value.is_skip {
|
||||
if current_field_info.value.is_required == false {
|
||||
return decoder.decode_error('This should not happen. Please, file a bug. `skip` field should not be processed here without a `required` attribute')
|
||||
decoder.decode_error('This should not happen. Please, file a bug. `skip` field should not be processed here without a `required` attribute')!
|
||||
}
|
||||
current_field_info.value.decoded_with_value_info_node = decoder.current_node
|
||||
current_field_info.value.is_decoded = true
|
||||
if decoder.current_node != unsafe { nil } {
|
||||
decoder.current_node = decoder.current_node.next
|
||||
}
|
||||
|
@ -627,7 +529,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
if current_field_info.value.is_raw {
|
||||
$if field.unaliased_typ is $enum {
|
||||
// workaround to avoid the error: enums can only be assigned `int` values
|
||||
return decoder.decode_error('`raw` attribute cannot be used with enum fields')
|
||||
decoder.decode_error('`raw` attribute cannot be used with enum fields')!
|
||||
} $else $if field.typ is ?string {
|
||||
position := decoder.current_node.value.position
|
||||
end := position + decoder.current_node.value.length
|
||||
|
@ -659,7 +561,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
decoder.current_node = decoder.current_node.next
|
||||
}
|
||||
} $else {
|
||||
return decoder.decode_error('`raw` attribute can only be used with string fields')
|
||||
decoder.decode_error('`raw` attribute can only be used with string fields')!
|
||||
}
|
||||
} else {
|
||||
$if field.typ is $option {
|
||||
|
@ -682,7 +584,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
decoder.decode_value(mut val.$(field.name))!
|
||||
}
|
||||
}
|
||||
current_field_info.value.decoded_with_value_info_node = decoder.current_node
|
||||
current_field_info.value.is_decoded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -705,15 +607,15 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
current_field_info = current_field_info.next
|
||||
continue
|
||||
}
|
||||
if current_field_info.value.decoded_with_value_info_node == unsafe { nil } {
|
||||
return decoder.decode_error('missing required field `${unsafe {
|
||||
if !current_field_info.value.is_decoded {
|
||||
decoder.decode_error('missing required field `${unsafe {
|
||||
tos(current_field_info.value.field_name_str, current_field_info.value.field_name_len)
|
||||
}}`')
|
||||
}}`')!
|
||||
}
|
||||
current_field_info = current_field_info.next
|
||||
}
|
||||
} else {
|
||||
return decoder.decode_error('Expected object, but got ${struct_info.value_kind}')
|
||||
decoder.decode_error('Expected object, but got ${struct_info.value_kind}')!
|
||||
}
|
||||
unsafe {
|
||||
struct_fields_info.free()
|
||||
|
@ -723,14 +625,14 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
value_info := decoder.current_node.value
|
||||
|
||||
if value_info.value_kind != .boolean {
|
||||
return decoder.decode_error('Expected boolean, but got ${value_info.value_kind}')
|
||||
decoder.decode_error('Expected boolean, but got ${value_info.value_kind}')!
|
||||
}
|
||||
|
||||
unsafe {
|
||||
val = vmemcmp(decoder.json.str + value_info.position, true_in_string.str,
|
||||
true_in_string.len) == 0
|
||||
}
|
||||
} $else $if T.unaliased_typ is $float || T.unaliased_typ is $int || T.unaliased_typ is $enum {
|
||||
} $else $if T.unaliased_typ is $float || T.unaliased_typ is $int {
|
||||
value_info := decoder.current_node.value
|
||||
|
||||
if value_info.value_kind == .number {
|
||||
|
@ -742,10 +644,12 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
|
||||
unsafe { decoder.decode_number(&val)! }
|
||||
} else {
|
||||
return decoder.decode_error('Expected number, but got ${value_info.value_kind}')
|
||||
decoder.decode_error('Expected number, but got ${value_info.value_kind}')!
|
||||
}
|
||||
} $else $if T.unaliased_typ is $enum {
|
||||
decoder.decode_enum(mut val)!
|
||||
} $else {
|
||||
return decoder.decode_error('cannot decode value with ${typeof(val).name} type')
|
||||
decoder.decode_error('cannot decode value with ${typeof(val).name} type')!
|
||||
}
|
||||
|
||||
if decoder.current_node != unsafe { nil } {
|
||||
|
@ -753,6 +657,108 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut decoder Decoder) decode_string[T](mut val T) ! {
|
||||
string_info := decoder.current_node.value
|
||||
|
||||
if string_info.value_kind == .string {
|
||||
mut string_buffer := []u8{cap: string_info.length} // might be too long but most json strings don't contain many escape characters anyways
|
||||
|
||||
mut buffer_index := 1
|
||||
mut string_index := 1
|
||||
|
||||
for string_index < string_info.length - 1 {
|
||||
current_byte := decoder.json[string_info.position + string_index]
|
||||
|
||||
if current_byte == `\\` {
|
||||
// push all characters up to this point
|
||||
unsafe {
|
||||
string_buffer.push_many(decoder.json.str + string_info.position + buffer_index,
|
||||
string_index - buffer_index)
|
||||
}
|
||||
|
||||
string_index++
|
||||
|
||||
escaped_char := decoder.json[string_info.position + string_index]
|
||||
|
||||
string_index++
|
||||
|
||||
match escaped_char {
|
||||
`/`, `"`, `\\` {
|
||||
string_buffer << escaped_char
|
||||
}
|
||||
`b` {
|
||||
string_buffer << `\b`
|
||||
}
|
||||
`f` {
|
||||
string_buffer << `\f`
|
||||
}
|
||||
`n` {
|
||||
string_buffer << `\n`
|
||||
}
|
||||
`r` {
|
||||
string_buffer << `\r`
|
||||
}
|
||||
`t` {
|
||||
string_buffer << `\t`
|
||||
}
|
||||
`u` {
|
||||
unicode_point := rune(strconv.parse_uint(decoder.json[
|
||||
string_info.position + string_index..string_info.position +
|
||||
string_index + 4], 16, 32)!)
|
||||
|
||||
string_index += 4
|
||||
|
||||
if unicode_point < 0xD800 || unicode_point > 0xDFFF { // normal utf-8
|
||||
string_buffer << unicode_point.bytes()
|
||||
} else if unicode_point >= 0xDC00 { // trail surrogate -> invalid
|
||||
decoder.decode_error('Got trail surrogate: ${u32(unicode_point):04X} before head surrogate.')!
|
||||
} else { // head surrogate -> treat as utf-16
|
||||
if string_index > string_info.length - 6 {
|
||||
decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
|
||||
}
|
||||
if decoder.json[string_info.position + string_index..
|
||||
string_info.position + string_index + 2] != '\\u' {
|
||||
decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
|
||||
}
|
||||
|
||||
string_index += 2
|
||||
|
||||
unicode_point2 := rune(strconv.parse_uint(decoder.json[
|
||||
string_info.position + string_index..string_info.position +
|
||||
string_index + 4], 16, 32)!)
|
||||
|
||||
string_index += 4
|
||||
|
||||
if unicode_point2 < 0xDC00 {
|
||||
decoder.decode_error('Expected a trail surrogate after a head surrogate, but got ${u32(unicode_point):04X}.')!
|
||||
}
|
||||
|
||||
final_unicode_point := (unicode_point2 & 0x3FF) +
|
||||
((unicode_point & 0x3FF) << 10) + 0x10000
|
||||
string_buffer << final_unicode_point.bytes()
|
||||
}
|
||||
}
|
||||
else {} // has already been checked
|
||||
}
|
||||
|
||||
buffer_index = string_index
|
||||
} else {
|
||||
string_index++
|
||||
}
|
||||
}
|
||||
|
||||
// push the rest
|
||||
unsafe {
|
||||
string_buffer.push_many(decoder.json.str + string_info.position + buffer_index,
|
||||
string_index - buffer_index)
|
||||
}
|
||||
|
||||
val = string_buffer.bytestr()
|
||||
} else {
|
||||
decoder.decode_error('Expected string, but got ${string_info.value_kind}')!
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut decoder Decoder) decode_array[T](mut val []T) ! {
|
||||
array_info := decoder.current_node.value
|
||||
|
||||
|
@ -775,7 +781,7 @@ fn (mut decoder Decoder) decode_array[T](mut val []T) ! {
|
|||
val << array_element
|
||||
}
|
||||
} else {
|
||||
return decoder.decode_error('Expected array, but got ${array_info.value_kind}')
|
||||
decoder.decode_error('Expected array, but got ${array_info.value_kind}')!
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -819,7 +825,7 @@ fn (mut decoder Decoder) decode_map[K, V](mut val map[K]V) ! {
|
|||
decoder.decode_value(mut val[key])!
|
||||
}
|
||||
} else {
|
||||
return decoder.decode_error('Expected object, but got ${map_info.value_kind}')
|
||||
decoder.decode_error('Expected object, but got ${map_info.value_kind}')!
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -889,6 +895,36 @@ fn get_number_digits[T](num T) int {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut decoder Decoder) decode_enum[T](mut val T) ! {
|
||||
enum_info := decoder.current_node.value
|
||||
|
||||
if enum_info.value_kind == .number {
|
||||
mut result := 0
|
||||
unsafe { decoder.decode_number(&result)! }
|
||||
|
||||
$for value in T.values {
|
||||
if int(value.value) == result {
|
||||
val = value.value
|
||||
return
|
||||
}
|
||||
}
|
||||
decoder.decode_error('Number value: `${result}` does not match any field in enum: ${typeof(val).name}')!
|
||||
} else if enum_info.value_kind == .string {
|
||||
mut result := ''
|
||||
unsafe { decoder.decode_value(mut result)! }
|
||||
|
||||
$for value in T.values {
|
||||
if value.name == result {
|
||||
val = value.value
|
||||
return
|
||||
}
|
||||
}
|
||||
decoder.decode_error('String value: `${result}` does not match any field in enum: ${typeof(val).name}')!
|
||||
}
|
||||
|
||||
decoder.decode_error('Expected number or string value for enum, got: ${enum_info.value_kind}')!
|
||||
}
|
||||
|
||||
// use pointer instead of mut so enum cast works
|
||||
@[unsafe]
|
||||
fn (mut decoder Decoder) decode_number[T](val &T) ! {
|
||||
|
@ -904,10 +940,6 @@ fn (mut decoder Decoder) decode_number[T](val &T) ! {
|
|||
$if T.unaliased_typ is $float {
|
||||
*val = T(strconv.atof_quick(decoder.json[number_info.position..number_info.position +
|
||||
number_info.length]))
|
||||
} $else $if T.unaliased_typ is $enum {
|
||||
mut result := 0
|
||||
decoder.decode_number(&result)!
|
||||
*val = T(result)
|
||||
} $else { // this part is a minefield
|
||||
mut is_negative := false
|
||||
mut index := 0
|
||||
|
|
95
vlib/x/json2/decoder2/tests/decode_enum_test.v
Normal file
95
vlib/x/json2/decoder2/tests/decode_enum_test.v
Normal file
|
@ -0,0 +1,95 @@
|
|||
import x.json2.decoder2 as json
|
||||
|
||||
enum Bar {
|
||||
a
|
||||
b
|
||||
c = 10
|
||||
}
|
||||
|
||||
type BarAlias = Bar
|
||||
|
||||
fn test_number_decode() {
|
||||
assert json.decode[Bar]('0')! == Bar.a
|
||||
assert json.decode[Bar]('1')! == Bar.b
|
||||
assert json.decode[Bar]('10')! == Bar.c
|
||||
|
||||
assert json.decode[BarAlias]('0')! == Bar.a
|
||||
assert json.decode[BarAlias]('1')! == Bar.b
|
||||
assert json.decode[BarAlias]('10')! == Bar.c
|
||||
}
|
||||
|
||||
fn test_number_decode_fails() {
|
||||
if _ := json.decode[Bar]('2') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Number value: `2` does not match any field in enum: &Bar'
|
||||
}
|
||||
}
|
||||
|
||||
if _ := json.decode[BarAlias]('2') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Number value: `2` does not match any field in enum: &BarAlias'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_string_decode() {
|
||||
assert json.decode[Bar]('"a"')! == Bar.a
|
||||
assert json.decode[Bar]('"b"')! == Bar.b
|
||||
assert json.decode[Bar]('"c"')! == Bar.c
|
||||
|
||||
assert json.decode[BarAlias]('"a"')! == Bar.a
|
||||
assert json.decode[BarAlias]('"b"')! == Bar.b
|
||||
assert json.decode[BarAlias]('"c"')! == Bar.c
|
||||
}
|
||||
|
||||
fn test_string_decode_fails() {
|
||||
if _ := json.decode[Bar]('"d"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: String value: `d` does not match any field in enum: &Bar'
|
||||
}
|
||||
}
|
||||
|
||||
if _ := json.decode[BarAlias]('"d"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: String value: `d` does not match any field in enum: &BarAlias'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_invalid_decode_fails() {
|
||||
if _ := json.decode[Bar]('true') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected number or string value for enum, got: boolean'
|
||||
}
|
||||
}
|
||||
|
||||
if _ := json.decode[BarAlias]('true') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected number or string value for enum, got: boolean'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ import x.json2.decoder2
|
|||
|
||||
fn test_decode_escaped_string() {
|
||||
escaped_strings := ['test', 'test\\sd', 'test\nsd', '\ntest', 'test\\"', 'test\\', 'test\u1234ps',
|
||||
'test\u1234', '\u1234\\\t"', '']
|
||||
'test\u1234', '\u1234\\\t"', '', '\uff0f', 'test \uff0f test', '😀', 'text 😀 text']
|
||||
|
||||
json_string := json2.encode[[]string](escaped_strings)
|
||||
decoded_strings := decoder2.decode[[]string](json_string)!
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue