Merge branch 'master' into 64bit-int-1

This commit is contained in:
kbkpbot 2025-09-12 16:12:45 +08:00
commit 32aaf30d1e
17 changed files with 580 additions and 302 deletions

View file

@ -897,6 +897,7 @@ fn (t Tree) fn_type_decl(node ast.FnTypeDecl) &Node {
mut obj := create_object() mut obj := create_object()
obj.add_terse('ast_type', t.string_node('FnTypeDecl')) obj.add_terse('ast_type', t.string_node('FnTypeDecl'))
obj.add_terse('name', t.string_node(node.name)) 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('is_pub', t.bool_node(node.is_pub))
obj.add_terse('typ', t.type_node(node.typ)) obj.add_terse('typ', t.type_node(node.typ))
obj.add('pos', t.pos(node.pos)) obj.add('pos', t.pos(node.pos))

View file

@ -1521,6 +1521,7 @@ pub mut:
pub struct FnTypeDecl { pub struct FnTypeDecl {
pub: pub:
name string name string
mod string
is_pub bool is_pub bool
typ Type typ Type
pos token.Pos pos token.Pos

View file

@ -444,6 +444,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
if c.check_import_sym_conflict(left.name) { if c.check_import_sym_conflict(left.name) {
c.error('duplicate of an import symbol `${left.name}`', left.pos) 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 if node.op == .assign && left_type.has_flag(.option) && right is ast.UnsafeExpr
&& right.expr.is_nil() { && right.expr.is_nil() {

View file

@ -145,6 +145,7 @@ mut:
variant_data_type ast.Type variant_data_type ast.Type
fn_return_type ast.Type fn_return_type ast.Type
orm_table_fields map[string][]ast.StructField // known table structs 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 v_current_commit_hash string // same as old C.V_CURRENT_COMMIT_HASH
assign_stmt_attr string // for `x := [1,2,3] @[freed]` 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.vmod_file_content = ''
c.mod = file.mod.name c.mod = file.mod.name
c.is_generated = file.is_generated 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) { 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 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. // 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() { pub fn (mut c Checker) update_unresolved_fixed_sizes() {
for mut stmt in c.unresolved_fixed_sizes { for mut stmt in c.unresolved_fixed_sizes {

View file

@ -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 { 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.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' { if node.name == 'main.main' {
c.main_fn_decl_node = *node c.main_fn_decl_node = *node

View 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)

View 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)
}

View file

@ -1798,10 +1798,8 @@ fn (mut c Amd64) mov(r Register, val i32) {
} }
} }
fn (mut c Amd64) mul_reg(a Amd64Register, b Amd64Register) { // rax times b
if a != .rax { fn (mut c Amd64) mul_reg_rax(b Amd64Register) {
c.g.n_error('mul always operates on rax')
}
match b { match b {
.rax { .rax {
c.g.write8(0x48) 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) { // rax divided by b
if a != .rax { fn (mut c Amd64) div_reg_rax(b Amd64Register) {
c.g.n_error('div always operates on rax')
}
match b { match b {
.rax { .rax {
c.g.write8(0x48) c.g.write8(0x48)
@ -1876,8 +1872,9 @@ fn (mut c Amd64) div_reg(a Amd64Register, b Amd64Register) {
c.g.println('div ${b}') c.g.println('div ${b}')
} }
fn (mut c Amd64) mod_reg(a Amd64Register, b Amd64Register) { // rax % b
c.div_reg(a, b) fn (mut c Amd64) mod_reg_rax(b Amd64Register) {
c.div_reg_rax(b)
c.mov_reg(Amd64Register.rdx, Amd64Register.rax) 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 // 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) { fn (mut c Amd64) assign_ident_int_lit(node ast.AssignStmt, i i32, int_lit ast.IntegerLiteral, left ast.Ident) {
match node.op { 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 { .decl_assign {
c.allocate_var(left.name, 8, i64(int_lit.val.int())) 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.mov64(Amd64Register.rax, i64(int_lit.val.int()))
c.mov_reg_to_var(left, Amd64Register.rax) 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 { .boolean_and_assign {
c.mov_var_to_reg(Amd64Register.rax, left) c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int())) c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
c.bitand_reg(.rax, .rcx) c.bitand_reg(.rax, .rbx)
c.mov_reg_to_var(left, Amd64Register.rax) c.mov_reg_to_var(left, Amd64Register.rax)
} }
.boolean_or_assign { .boolean_or_assign {
c.mov_var_to_reg(Amd64Register.rax, left) c.mov_var_to_reg(Amd64Register.rax, left)
c.mov64(Amd64Register.rcx, i64(int_lit.val.int())) c.mov64(Amd64Register.rbx, i64(int_lit.val.int()))
c.bitor_reg(.rax, .rcx) c.bitor_reg(.rax, .rbx)
c.mov_reg_to_var(left, Amd64Register.rax) c.mov_reg_to_var(left, Amd64Register.rax)
} }
else { 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) c.mov_ssereg_to_var(ident, .xmm1)
} else if left_type.is_int() { } else if left_type.is_int() {
c.mov_var_to_reg(Amd64Register.rbx, ident) c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
c.mov_var_to_reg(Amd64Register.rax, ident)
match node.op { c.apply_op_int(.rax, .rbx, node.op)
.plus_assign { c.add_reg(.rbx, .rax) } c.mov_reg_to_var(ident, Amd64Register.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)
} else { } 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]}') c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
} }
} }
} }
}
}
else { else {
left_type := node.left_types[i] left_type := node.left_types[i]
if node.op == .decl_assign { 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) c.mov_ssereg_to_var(ident, .xmm1)
} else if left_type.is_int() { } else if left_type.is_int() {
c.mov_var_to_reg(Amd64Register.rbx, ident) c.mov_reg(Amd64Register.rbx, Amd64Register.rax)
c.mov_var_to_reg(Amd64Register.rax, ident)
match node.op { c.apply_op_int(.rax, .rbx, node.op)
.plus_assign { c.add_reg(.rbx, .rax) } c.mov_reg_to_var(ident, Amd64Register.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)
} else { } 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]}') c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
} }
} }
} }
}
}
/* /*
ast.IndexExpr { 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) { fn (mut c Amd64) gen_type_promotion(from ast.Type, to ast.Type, option Amd64RegisterOption) {
if !to.is_pure_float() { if !to.is_pure_float() {
return return
@ -3122,52 +3123,6 @@ fn (mut c Amd64) assign_stmt(node ast.AssignStmt) {
.decl_assign, .assign { .decl_assign, .assign {
c.mov_store(.rbx, .rcx, size) 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 { .boolean_and_assign {
c.bitand_reg(.rax, .rcx) c.bitand_reg(.rax, .rcx)
c.mov_store(.rbx, .rax, size) 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) c.mov_store(.rbx, .rax, size)
} }
else { 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() { } 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.rdx, 0) // upperhalf of the dividend
c.mov(Amd64Register.rbx, 10) 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.add8(.rdx, i32(`0`)) // rdx is the remainder, add 48 to convert it into it's ascii representation
c.mov_store(.rdi, .rdx, ._8) c.mov_store(.rdi, .rdx, ._8)

View file

@ -7,6 +7,88 @@ fn main() {
test_alias(100, 9) test_alias(100, 9)
test_plus_assign() test_plus_assign()
test_minus_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() { fn test_minus_assign() {

View file

@ -2782,6 +2782,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
p.attrs = [] p.attrs = []
return ast.FnTypeDecl{ return ast.FnTypeDecl{
name: fn_name name: fn_name
mod: p.mod
is_pub: is_pub is_pub: is_pub
typ: fn_type typ: fn_type
pos: decl_pos pos: decl_pos

View file

@ -41,6 +41,9 @@ fn ascon_pnr(mut s State, nr int) {
if nr < 1 || nr > 16 { if nr < 1 || nr > 16 {
panic('Invalid round number') panic('Invalid round number')
} }
// Allocate temporary vars to reduce allocation within loop
mut x0 := u64(0)
mut y0 := u64(0)
// Ascon permutation routine // Ascon permutation routine
for i := max_nr_perm - nr; i < max_nr_perm; i++ { for i := max_nr_perm - nr; i < max_nr_perm; i++ {
// 3.2 Constant-Addition Layer step // 3.2 Constant-Addition Layer step
@ -56,18 +59,22 @@ fn ascon_pnr(mut s State, nr int) {
s.e0 ^= s.e4 s.e0 ^= s.e4
s.e4 ^= s.e3 s.e4 ^= s.e3
s.e2 ^= s.e1 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) t0 := s.e4 ^ (~s.e0 & s.e1)
t1 := s.e0 ^ (~s.e1 & s.e2) t1 := s.e0 ^ (~s.e1 & s.e2)
t2 := s.e1 ^ (~s.e2 & s.e3) t2 := s.e1 ^ (~s.e2 & s.e3)
t3 := s.e2 ^ (~s.e3 & s.e4) t3 := s.e2 ^ (~s.e3 & s.e4)
t4 := s.e3 ^ (~s.e4 & s.e0) t4 := s.e3 ^ (~s.e4 & s.e0)
*/
s.e0 = t1 s.e0 = s.e0 ^ (~s.e1 & s.e2) // t1
s.e1 = t2 s.e1 = s.e1 ^ (~s.e2 & s.e3) // t2
s.e2 = t3 s.e2 = s.e2 ^ (~s.e3 & s.e4) // t3
s.e3 = t4 s.e3 = s.e3 ^ (~s.e4 & x0) // t4, change s.e0 to x0
s.e4 = t0 s.e4 = y0
s.e1 ^= s.e0 s.e1 ^= s.e0
s.e0 ^= s.e4 s.e0 ^= s.e4

View file

@ -135,20 +135,32 @@ fn (mut d Digest) squeeze(mut dst []u8) int {
} }
@[direct_array_access; inline] @[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 // Assumed state was correctly initialized
// Absorbing the message // Absorbing the message
mut msg := msg_.clone() mut pos := 0
for msg.len >= block_size { // Check if msg has non-null length, if yes, absorb it.
s.e0 ^= binary.little_endian_u64(msg[0..block_size]) // Otherwise, just pad it
unsafe { if _likely_(msg.len > 0) {
msg = msg[block_size..] 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) ascon_pnr(mut s, ascon_prnd_12)
} }
// Absorb the last partial message block // Absorb the last partial message block
s.e0 ^= load_bytes(msg, msg.len) last_block := unsafe { msg[pos..] }
s.e0 ^= pad(msg.len) 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 // Squeezing phase
// //
@ -156,7 +168,6 @@ fn ascon_generic_hash(mut s State, msg_ []u8, size int) []u8 {
// permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] to the state: // permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] to the state:
ascon_pnr(mut s, ascon_prnd_12) ascon_pnr(mut s, ascon_prnd_12)
mut out := []u8{len: size} mut out := []u8{len: size}
mut pos := 0
mut clen := out.len mut clen := out.len
for clen >= block_size { for clen >= block_size {
binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0) binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0)

View file

@ -83,13 +83,23 @@ fn set_byte(b u8, i int) u64 {
fn load_bytes(bytes []u8, n int) u64 { fn load_bytes(bytes []u8, n int) u64 {
mut x := u64(0) mut x := u64(0)
for i := 0; i < n; i++ { 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) { fn store_bytes(mut out []u8, x u64, n int) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
out[i] = get_byte(x, i) // use underlying get_byte directly
out[i] = u8(x >> (8 * i))
} }
} }

View file

@ -38,6 +38,16 @@ struct StruWithRequiredAttribute {
b int 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() { fn test_skip_and_rename_attributes() {
assert json.decode[StruWithJsonAttribute]('{"name": "hola1", "a": 2, "b": 3}')! == StruWithJsonAttribute{ assert json.decode[StruWithJsonAttribute]('{"name": "hola1", "a": 2, "b": 3}')! == StruWithJsonAttribute{
a: 2 a: 2

View file

@ -38,7 +38,7 @@ struct StructFieldInfo {
is_required bool is_required bool
is_raw bool is_raw bool
mut: mut:
decoded_with_value_info_node &Node[ValueInfo] = unsafe { nil } is_decoded bool
} }
// Decoder represents a JSON decoder. // 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 { $if T.unaliased_typ is string {
string_info := decoder.current_node.value decoder.decode_string(mut val)!
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}')
}
} $else $if T.unaliased_typ is $sumtype { } $else $if T.unaliased_typ is $sumtype {
decoder.decode_sumtype(mut val)! decoder.decode_sumtype(mut val)!
return return
@ -509,7 +411,7 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
for attr in field.attrs { for attr in field.attrs {
if attr.starts_with('json:') { if attr.starts_with('json:') {
if attr.len <= 6 { 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_str = unsafe { attr.str + 6 }
json_name_len = attr.len - 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_skip {
if current_field_info.value.is_required == false { 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 } { if decoder.current_node != unsafe { nil } {
decoder.current_node = decoder.current_node.next 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 current_field_info.value.is_raw {
$if field.unaliased_typ is $enum { $if field.unaliased_typ is $enum {
// workaround to avoid the error: enums can only be assigned `int` values // 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 { } $else $if field.typ is ?string {
position := decoder.current_node.value.position position := decoder.current_node.value.position
end := position + decoder.current_node.value.length 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 decoder.current_node = decoder.current_node.next
} }
} $else { } $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 { } else {
$if field.typ is $option { $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))! 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 break
} }
} }
@ -705,15 +607,15 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
current_field_info = current_field_info.next current_field_info = current_field_info.next
continue continue
} }
if current_field_info.value.decoded_with_value_info_node == unsafe { nil } { if !current_field_info.value.is_decoded {
return decoder.decode_error('missing required field `${unsafe { decoder.decode_error('missing required field `${unsafe {
tos(current_field_info.value.field_name_str, current_field_info.value.field_name_len) tos(current_field_info.value.field_name_str, current_field_info.value.field_name_len)
}}`') }}`')!
} }
current_field_info = current_field_info.next current_field_info = current_field_info.next
} }
} else { } 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 { unsafe {
struct_fields_info.free() struct_fields_info.free()
@ -723,14 +625,14 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
value_info := decoder.current_node.value value_info := decoder.current_node.value
if value_info.value_kind != .boolean { 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 { unsafe {
val = vmemcmp(decoder.json.str + value_info.position, true_in_string.str, val = vmemcmp(decoder.json.str + value_info.position, true_in_string.str,
true_in_string.len) == 0 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 value_info := decoder.current_node.value
if value_info.value_kind == .number { 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)! } unsafe { decoder.decode_number(&val)! }
} else { } 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 { } $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 } { 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) ! { fn (mut decoder Decoder) decode_array[T](mut val []T) ! {
array_info := decoder.current_node.value array_info := decoder.current_node.value
@ -775,7 +781,7 @@ fn (mut decoder Decoder) decode_array[T](mut val []T) ! {
val << array_element val << array_element
} }
} else { } 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])! decoder.decode_value(mut val[key])!
} }
} else { } 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 // use pointer instead of mut so enum cast works
@[unsafe] @[unsafe]
fn (mut decoder Decoder) decode_number[T](val &T) ! { 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 { $if T.unaliased_typ is $float {
*val = T(strconv.atof_quick(decoder.json[number_info.position..number_info.position + *val = T(strconv.atof_quick(decoder.json[number_info.position..number_info.position +
number_info.length])) 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 } $else { // this part is a minefield
mut is_negative := false mut is_negative := false
mut index := 0 mut index := 0

View 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'
}
}
}

View file

@ -3,7 +3,7 @@ import x.json2.decoder2
fn test_decode_escaped_string() { fn test_decode_escaped_string() {
escaped_strings := ['test', 'test\\sd', 'test\nsd', '\ntest', 'test\\"', 'test\\', 'test\u1234ps', 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) json_string := json2.encode[[]string](escaped_strings)
decoded_strings := decoder2.decode[[]string](json_string)! decoded_strings := decoder2.decode[[]string](json_string)!