diff --git a/vlib/json/json_primitives.v b/vlib/json/json_primitives.v index 9ebb18e03f..5adb206b6a 100644 --- a/vlib/json/json_primitives.v +++ b/vlib/json/json_primitives.v @@ -12,6 +12,14 @@ struct C.cJSON { valuestring byteptr } +pub fn decode() voidptr { + +} + +pub fn encode() voidptr { + +} + fn jsdecode_int(root &C.cJSON) int { if isnil(root) { return 0 diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 09d1133cbc..a5e275904c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -251,185 +251,196 @@ fn (c mut Checker) assign_expr(assign_expr mut ast.AssignExpr) { } pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type { - if call_expr.name == 'panic' { - c.returns = true - } c.stmts(call_expr.or_block.stmts) if call_expr.is_method { - left_type := c.expr(call_expr.left) - call_expr.left_type = left_type - left_type_sym := c.table.get_type_symbol(left_type) - method_name := call_expr.name - // TODO: remove this for actual methods, use only for compiler magic - if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse', - 'map', 'slice'] { - if method_name in ['filter', 'map'] { - array_info := left_type_sym.info as table.Array - mut scope := c.file.scope.innermost(call_expr.pos.pos) - scope.update_var_type('it', array_info.elem_type) - } - for i, arg in call_expr.args { - c.expr(arg.expr) - } - // need to return `array_xxx` instead of `array` - call_expr.return_type = left_type - if method_name == 'clone' { - // in ['clone', 'str'] { - call_expr.receiver_type = table.type_to_ptr(left_type) - // call_expr.return_type = call_expr.receiver_type - } else { - call_expr.receiver_type = left_type - } - return left_type - } else if left_type_sym.kind == .array && method_name in ['first', 'last'] { - info := left_type_sym.info as table.Array - call_expr.return_type = info.elem_type - call_expr.receiver_type = left_type - return info.elem_type + return c.call_method(call_expr) + } + return c.call_fn(call_expr) +} + +pub fn (c mut Checker) call_method(call_expr mut ast.CallExpr) table.Type { + left_type := c.expr(call_expr.left) + call_expr.left_type = left_type + left_type_sym := c.table.get_type_symbol(left_type) + method_name := call_expr.name + // TODO: remove this for actual methods, use only for compiler magic + if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse', + 'map', 'slice'] { + if method_name in ['filter', 'map'] { + array_info := left_type_sym.info as table.Array + mut scope := c.file.scope.innermost(call_expr.pos.pos) + scope.update_var_type('it', array_info.elem_type) } - if method := c.table.type_find_method(left_type_sym, method_name) { - no_args := method.args.len - 1 - min_required_args := method.args.len - if method.is_variadic && method.args.len > - 1 { 2 } else { 1 } - if call_expr.args.len < min_required_args { - c.error('too few arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $min_required_args)', - call_expr.pos) - } else if !method.is_variadic && call_expr.args.len > no_args { - c.error('too many arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $no_args)', - call_expr.pos) - return method.return_type - } - // if method_name == 'clone' { - // println('CLONE nr args=$method.args.len') - // } - // call_expr.args << method.args[0].typ - // call_expr.exp_arg_types << method.args[0].typ - for i, arg in call_expr.args { - c.expected_type = if method.is_variadic && i >= method.args.len - 1 { - method.args[method.args.len - 1].typ - } else { - method.args[i + 1].typ - } - call_expr.args[i].typ = c.expr(arg.expr) - } - // TODO: typ optimize.. this node can get processed more than once - if call_expr.expected_arg_types.len == 0 { - for i in 1 .. method.args.len { - call_expr.expected_arg_types << method.args[i].typ - } - } - call_expr.receiver_type = method.args[0].typ - call_expr.return_type = method.return_type + for i, arg in call_expr.args { + c.expr(arg.expr) + } + // need to return `array_xxx` instead of `array` + call_expr.return_type = left_type + if method_name == 'clone' { + // in ['clone', 'str'] { + call_expr.receiver_type = table.type_to_ptr(left_type) + // call_expr.return_type = call_expr.receiver_type + } else { + call_expr.receiver_type = left_type + } + return left_type + } else if left_type_sym.kind == .array && method_name in ['first', 'last'] { + info := left_type_sym.info as table.Array + call_expr.return_type = info.elem_type + call_expr.receiver_type = left_type + return info.elem_type + } + if method := c.table.type_find_method(left_type_sym, method_name) { + no_args := method.args.len - 1 + min_required_args := method.args.len - if method.is_variadic && method.args.len > 1 { 2 } else { 1 } + if call_expr.args.len < min_required_args { + c.error('too few arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $min_required_args)', + call_expr.pos) + } else if !method.is_variadic && call_expr.args.len > no_args { + c.error('too many arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $no_args)', + call_expr.pos) return method.return_type } - // TODO: str methods - if left_type_sym.kind == .map && method_name == 'str' { - call_expr.receiver_type = table.new_type(c.table.type_idxs['map_string']) - call_expr.return_type = table.string_type - return table.string_type - } - if left_type_sym.kind == .array && method_name == 'str' { - call_expr.receiver_type = left_type - call_expr.return_type = table.string_type - return table.string_type - } - c.error('unknown method: ${left_type_sym.name}.$method_name', call_expr.pos) - return table.void_type - } else { - fn_name := call_expr.name - // TODO: impl typeof properly (probably not going to be a fn call) - if fn_name == 'typeof' { - return table.string_type - } - // look for function in format `mod.fn` or `fn` (main/builtin) - mut f := table.Fn{} - mut found := false - // try prefix with current module as it would have never gotten prefixed - if !fn_name.contains('.') && !(call_expr.mod in ['builtin', 'main']) { - name_prefixed := '${call_expr.mod}.$fn_name' - if f1 := c.table.find_fn(name_prefixed) { - call_expr.name = name_prefixed - found = true - f = f1 + // if method_name == 'clone' { + // println('CLONE nr args=$method.args.len') + // } + // call_expr.args << method.args[0].typ + // call_expr.exp_arg_types << method.args[0].typ + for i, arg in call_expr.args { + c.expected_type = if method.is_variadic && i >= method.args.len - 1 { + method.args[method.args.len - 1].typ + } else { + method.args[i + 1].typ } - } - // already prefixed (mod.fn) or C/builtin/main - if !found { - if f1 := c.table.find_fn(fn_name) { - found = true - f = f1 - } - } - // check for arg (var) of fn type - if !found { - scope := c.file.scope.innermost(call_expr.pos.pos) - if var := scope.find_var(fn_name) { - if var.typ != 0 { - vts := c.table.get_type_symbol(var.typ) - if vts.kind == .function { - info := vts.info as table.FnType - f = info.func - found = true - } - } - } - } - if !found { - c.error('unknown fn: $fn_name', call_expr.pos) - return table.void_type - } - call_expr.return_type = f.return_type - if f.is_c || call_expr.is_c { - for arg in call_expr.args { - c.expr(arg.expr) - } - return f.return_type - } - min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len } - if call_expr.args.len < min_required_args { - c.error('too few arguments in call to `$fn_name` ($call_expr.args.len instead of $min_required_args)', - call_expr.pos) - } else if !f.is_variadic && call_expr.args.len > f.args.len { - c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)', - call_expr.pos) - return f.return_type - } - // println can print anything - if fn_name == 'println' || fn_name == 'print' { - c.expected_type = table.string_type - call_expr.args[0].typ = c.expr(call_expr.args[0].expr) - return f.return_type + call_expr.args[i].typ = c.expr(arg.expr) } // TODO: typ optimize.. this node can get processed more than once if call_expr.expected_arg_types.len == 0 { - for arg in f.args { - call_expr.expected_arg_types << arg.typ + for i in 1 .. method.args.len { + call_expr.expected_arg_types << method.args[i].typ } } - for i, call_arg in call_expr.args { - arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] } - c.expected_type = arg.typ - typ := c.expr(call_arg.expr) - call_expr.args[i].typ = typ - typ_sym := c.table.get_type_symbol(typ) - arg_typ_sym := c.table.get_type_symbol(arg.typ) - if !c.table.check(typ, arg.typ) { - // str method, allow type with str method if fn arg is string - if arg_typ_sym.kind == .string && typ_sym.has_method('str') { - continue + call_expr.receiver_type = method.args[0].typ + call_expr.return_type = method.return_type + return method.return_type + } + // TODO: str methods + if left_type_sym.kind == .map && method_name == 'str' { + call_expr.receiver_type = table.new_type(c.table.type_idxs['map_string']) + call_expr.return_type = table.string_type + return table.string_type + } + if left_type_sym.kind == .array && method_name == 'str' { + call_expr.receiver_type = left_type + call_expr.return_type = table.string_type + return table.string_type + } + c.error('unknown method: ${left_type_sym.name}.$method_name', call_expr.pos) + return table.void_type +} + +pub fn (c mut Checker) call_fn(call_expr mut ast.CallExpr) table.Type { + if call_expr.name == 'panic' { + c.returns = true + } + fn_name := call_expr.name + if fn_name == 'typeof' { + // TODO: impl typeof properly (probably not going to be a fn call) + return table.string_type + } + //if c.fileis('json_test.v') { + //println(fn_name) + //} + if fn_name == 'json.encode' { + } + // look for function in format `mod.fn` or `fn` (main/builtin) + mut f := table.Fn{} + mut found := false + // try prefix with current module as it would have never gotten prefixed + if !fn_name.contains('.') && !(call_expr.mod in ['builtin', 'main']) { + name_prefixed := '${call_expr.mod}.$fn_name' + if f1 := c.table.find_fn(name_prefixed) { + call_expr.name = name_prefixed + found = true + f = f1 + } + } + // already prefixed (mod.fn) or C/builtin/main + if !found { + if f1 := c.table.find_fn(fn_name) { + found = true + f = f1 + } + } + // check for arg (var) of fn type + if !found { + scope := c.file.scope.innermost(call_expr.pos.pos) + if var := scope.find_var(fn_name) { + if var.typ != 0 { + vts := c.table.get_type_symbol(var.typ) + if vts.kind == .function { + info := vts.info as table.FnType + f = info.func + found = true } - if typ_sym.kind == .void && arg_typ_sym.kind == .string { - continue - } - if typ_sym.kind == .array_fixed { - } - // println('fixed') - c.error('cannot use type `$typ_sym.str()` as type `$arg_typ_sym.str()` in argument ${i+1} to `$fn_name`', - call_expr.pos) } } + } + if !found { + c.error('unknown fn: $fn_name', call_expr.pos) + return table.void_type + } + call_expr.return_type = f.return_type + if f.is_c || call_expr.is_c { + for arg in call_expr.args { + c.expr(arg.expr) + } return f.return_type } + min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len } + if call_expr.args.len < min_required_args { + c.error('too few arguments in call to `$fn_name` ($call_expr.args.len instead of $min_required_args)', + call_expr.pos) + } else if !f.is_variadic && call_expr.args.len > f.args.len { + c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)', + call_expr.pos) + return f.return_type + } + // println can print anything + if fn_name == 'println' || fn_name == 'print' { + c.expected_type = table.string_type + call_expr.args[0].typ = c.expr(call_expr.args[0].expr) + return f.return_type + } + // TODO: typ optimize.. this node can get processed more than once + if call_expr.expected_arg_types.len == 0 { + for arg in f.args { + call_expr.expected_arg_types << arg.typ + } + } + for i, call_arg in call_expr.args { + arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] } + c.expected_type = arg.typ + typ := c.expr(call_arg.expr) + call_expr.args[i].typ = typ + typ_sym := c.table.get_type_symbol(typ) + arg_typ_sym := c.table.get_type_symbol(arg.typ) + if !c.table.check(typ, arg.typ) { + // str method, allow type with str method if fn arg is string + if arg_typ_sym.kind == .string && typ_sym.has_method('str') { + continue + } + if typ_sym.kind == .void && arg_typ_sym.kind == .string { + continue + } + if typ_sym.kind == .array_fixed { + } + // println('fixed') + c.error('cannot use type `$typ_sym.str()` as type `$arg_typ_sym.str()` in argument ${i+1} to `$fn_name`', + call_expr.pos) + } + } + return f.return_type } pub fn (c mut Checker) check_expr_opt_call(x ast.Expr, xtype table.Type, is_return_used bool) { @@ -1564,3 +1575,7 @@ fn (c mut Checker) warn_or_error(s string, pos token.Position, warn bool) { exit(1) } } + +fn (p Checker) fileis(s string) bool { + return p.file.path.contains(s) +} diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index bfd74413dc..fd565ddf50 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -16,6 +16,7 @@ const ( struct Fmt { out strings.Builder + out_imports strings.Builder table &table.Table mut: indent int @@ -26,21 +27,35 @@ mut: file ast.File did_imports bool is_assign bool + auto_imports []string // automatically inserted imports that the user forgot to specify + import_pos int // position of the imports in the resulting string for later autoimports insertion } pub fn fmt(file ast.File, table &table.Table) string { mut f := Fmt{ out: strings.new_builder(1000) + out_imports: strings.new_builder(200) table: table indent: 0 file: file } f.cur_mod = 'main' for i, stmt in file.stmts { + // TODO `if stmt is ast.Import` + match stmt { + ast.Import { + // Just remember the position of the imports for now + f.import_pos = f.out.len + // f.imports(f.file.imports) + } + else {} + } f.stmt(stmt) } // for comment in file.comments { println('$comment.line_nr $comment.text') } - return f.out.str().trim_space() + '\n' + f.imports(f.file.imports) // now that we have all autoimports, handle them + res := f.out.str().trim_space() + '\n' + return res[..f.import_pos] + f.out_imports.str() + res[f.import_pos..] } /* @@ -86,17 +101,19 @@ fn (f mut Fmt) imports(imports []ast.Import) { return } f.did_imports = true + // f.import_pos = f.out.len if imports.len == 1 { imp_stmt_str := f.imp_stmt_str(imports[0]) - f.writeln('import ${imp_stmt_str}\n') + f.out_imports.writeln('import ${imp_stmt_str}\n') } else if imports.len > 1 { - f.writeln('import (') - f.indent++ + f.out_imports.writeln('import (') + // f.indent++ for imp in imports { - f.writeln(f.imp_stmt_str(imp)) + f.out_imports.write('\t') + f.out_imports.writeln(f.imp_stmt_str(imp)) } - f.indent-- - f.writeln(')\n') + // f.indent-- + f.out_imports.writeln(')\n') } } @@ -306,7 +323,8 @@ fn (f mut Fmt) stmt(node ast.Stmt) { f.writeln('#$it.val') } ast.Import { - f.imports(f.file.imports) + // Imports are handled after the file is formatted, to automatically add necessary modules + // f.imports(f.file.imports) } ast.Module { f.mod(it) @@ -482,19 +500,7 @@ fn (f mut Fmt) expr(node ast.Expr) { f.write(')') } ast.CallExpr { - if it.is_method { - f.expr(it.left) - f.write('.' + it.name + '(') - f.call_args(it.args) - f.write(')') - f.or_expr(it.or_block) - } else { - name := short_module(it.name) - f.write('${name}(') - f.call_args(it.args) - f.write(')') - f.or_expr(it.or_block) - } + f.call_expr(it) } ast.CharLiteral { f.write('`$it.val`') @@ -801,3 +807,40 @@ fn (f mut Fmt) if_expr(it ast.IfExpr) { f.write('}') f.single_line_if = false } + +fn (f mut Fmt) call_expr(node ast.CallExpr) { + if node.is_method { + match node.left { + ast.Ident { + // `time.now()` without `time imported` is processed as a method call with `time` being + // a `node.left` expression. Import `time` automatically. + // TODO fetch all available modules + if it.name in ['time', 'os', 'strings', 'math', 'json'] { + if !(it.name in f.auto_imports) { + f.auto_imports << it.name + f.file.imports << ast.Import{ + mod: it.name + alias: it.name + } + } + println(it.name + '!!') + for imp in f.file.imports { + println(imp.mod) + } + } + } + else {} + } + f.expr(node.left) + f.write('.' + node.name + '(') + f.call_args(node.args) + f.write(')') + f.or_expr(node.or_block) + } else { + name := short_module(node.name) + f.write('${name}(') + f.call_args(node.args) + f.write(')') + f.or_expr(node.or_block) + } +} diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 92a9a83203..d39ee8124d 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -338,6 +338,13 @@ fn (g mut Gen) stmt(node ast.Stmt) { // println('cgen.stmt()') // g.writeln('//// stmt start') match node { + ast.InterfaceDecl { + g.writeln('//interface') + g.writeln('struct $it.name {') + g.writeln('\tvoid* _object;') + g.writeln('\tint _interface_idx;') + g.writeln('};') + } ast.AssertStmt { g.gen_assert_stmt(it) } @@ -2339,6 +2346,7 @@ fn (g Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { field_deps << dep } } + // table.Interface {} else {} } // add type and dependant types to graph diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 3e682551c8..7cab09b436 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -482,7 +482,16 @@ fn (p mut Parser) attribute() ast.Attr { if p.tok.kind == .key_if { p.next() } - name := p.check_name() + mut name := p.check_name() + if p.tok.kind == .colon { + p.next() + if p.tok.kind == .name { + name += p.check_name() + } else if p.tok.kind == .string { + name += p.tok.lit + p.next() + } + } p.check(.rsbr) p.attr = name return ast.Attr{ @@ -1577,6 +1586,10 @@ fn (p mut Parser) struct_decl() ast.StructDecl { } has_default_expr = true } + mut attr := ast.Attr{} + if p.tok.kind == .lsbr { + attr = p.attribute() + } if p.tok.kind == .comment { comment = p.comment() } @@ -1587,6 +1600,7 @@ fn (p mut Parser) struct_decl() ast.StructDecl { comment: comment default_expr: default_expr has_default_expr: has_default_expr + attr: attr.name } fields << table.Field{ name: field_name diff --git a/vlib/v/tests/interface_test.v b/vlib/v/tests/interface_test.v index 1447722686..bbf1977c30 100644 --- a/vlib/v/tests/interface_test.v +++ b/vlib/v/tests/interface_test.v @@ -24,14 +24,16 @@ fn (d Dog) name() string { fn test_todo() {} -/* interface Speaker { name ()string - speak()} + speak() + } +/* interface Speak2er { name ()string - speak()} + speak() + } struct Foo { speaker Speaker