mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42: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()
|
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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -137,14 +137,15 @@ mut:
|
||||||
inside_assign bool
|
inside_assign bool
|
||||||
// doing_line_info int // a quick single file run when called with v -line-info (contains line nr to inspect)
|
// doing_line_info int // a quick single file run when called with v -line-info (contains line nr to inspect)
|
||||||
// doing_line_path string // same, but stores the path being parsed
|
// doing_line_path string // same, but stores the path being parsed
|
||||||
is_index_assign bool
|
is_index_assign bool
|
||||||
comptime_call_pos int // needed for correctly checking use before decl for templates
|
comptime_call_pos int // needed for correctly checking use before decl for templates
|
||||||
goto_labels map[string]ast.GotoLabel // to check for unused goto labels
|
goto_labels map[string]ast.GotoLabel // to check for unused goto labels
|
||||||
enum_data_type ast.Type
|
enum_data_type ast.Type
|
||||||
field_data_type ast.Type
|
field_data_type ast.Type
|
||||||
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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
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) {
|
// 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,19 +2570,26 @@ 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 {
|
||||||
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
|
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]}')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2676,19 +2617,26 @@ 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 {
|
||||||
c.g.n_error('${@LOCATION} assignment arithmetic not implemented for type ${node.left_types[i]}')
|
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]}')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
// Absorb the last partial message block
|
// reset pos
|
||||||
s.e0 ^= load_bytes(msg, msg.len)
|
pos = 0
|
||||||
s.e0 ^= pad(msg.len)
|
|
||||||
|
|
||||||
// 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)
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
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() {
|
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)!
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue