From a9b8bc067ff2e538cc66d6a8b1b203d0d884d386 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 2 Jul 2019 00:00:27 +0200 Subject: [PATCH] enums (`mode == .default_mode` syntax); fix `foo.bar[0].baz = val` --- compiler/fn.v | 4 +-- compiler/main.v | 59 ++++++++++++++++++--------------- compiler/parser.v | 67 ++++++++++++++++++++++++-------------- compiler/table.v | 9 ++--- vlib/builtin/struct_test.v | 44 +++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 60 deletions(-) create mode 100644 vlib/builtin/struct_test.v diff --git a/compiler/fn.v b/compiler/fn.v index a9c554009a..7bfc447ede 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -95,7 +95,7 @@ fn (f mut Fn) clear_vars() { // vlib header file? fn (p mut Parser) is_sig() bool { - return (p.pref.build_mode == default_mode || p.pref.build_mode == build) && + return (p.pref.build_mode == .default_mode || p.pref.build_mode == .build) && (p.file_path.contains(TmpPath)) } @@ -338,7 +338,7 @@ fn (p mut Parser) fn_decl() { // Add function definition to the top if !is_c && f.name != 'main' && p.first_run() { // TODO hack to make Volt compile without -embed_vlib - if f.name == 'darwin__nsstring' && p.pref.build_mode == default_mode { + if f.name == 'darwin__nsstring' && p.pref.build_mode == .default_mode { return } p.cgen.fns << fn_decl + ';' diff --git a/compiler/main.v b/compiler/main.v index d8ce1cf2ba..92e8279a27 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -77,7 +77,7 @@ mut: build_mode BuildMode nofmt bool // disable vfmt is_test bool // `v test string_test.v` - is_script bool // single file mode (`v program.v`), `fn main(){}` can be skipped + is_script bool // single file mode (`v program.v`), main function can be skipped is_live bool // for hot code reloading is_so bool is_prof bool // benchmark every function @@ -92,6 +92,7 @@ mut: sanitize bool // use Clang's new "-fsanitize" option } + fn main() { args := os.args // Print the version and exit. @@ -108,7 +109,7 @@ fn main() { return } // TODO quit if the compiler is too old - // u := os.file_last_mod_unix('/var/tmp/alex') + // u := os.file_last_mod_unix('v') // Create a temp directory if it's not there. if !os.file_exists(TmpPath) { os.mkdir(TmpPath) @@ -242,23 +243,23 @@ void reload_so(); void init_consts();') imports_json := v.table.imports.contains('json') // TODO remove global UI hack - if v.os == MAC && ((v.pref.build_mode == embed_vlib && v.table.imports.contains('ui')) || - (v.pref.build_mode == build && v.dir.contains('/ui'))) { + if v.os == MAC && ((v.pref.build_mode == .embed_vlib && v.table.imports.contains('ui')) || + (v.pref.build_mode == .build && v.dir.contains('/ui'))) { cgen.genln('id defaultFont = 0; // main.v') } // TODO remove ugly .c include once V has its own json parser // Embed cjson either in embedvlib or in json.o - if imports_json && v.pref.build_mode == embed_vlib || - (v.pref.build_mode == build && v.out_name.contains('json.o')) { + if imports_json && v.pref.build_mode == .embed_vlib || + (v.pref.build_mode == .build && v.out_name.contains('json.o')) { cgen.genln('#include "cJSON.c" ') } // We need the cjson header for all the json decoding user will do in default mode - if v.pref.build_mode == default_mode { + if v.pref.build_mode == .default_mode { if imports_json { cgen.genln('#include "cJSON.h"') } } - if v.pref.build_mode == embed_vlib || v.pref.build_mode == default_mode { + if v.pref.build_mode == .embed_vlib || v.pref.build_mode == .default_mode { // If we declare these for all modes, then when running `v a.v` we'll get // `/usr/bin/ld: multiple definition of 'total_m'` // TODO @@ -303,7 +304,7 @@ void init_consts();') dd := d.str() cgen.lines.set(defs_pos, dd)// TODO `def.str()` doesn't compile // if v.build_mode in [.default, .embed_vlib] { - if v.pref.build_mode == default_mode || v.pref.build_mode == embed_vlib { + if v.pref.build_mode == .default_mode || v.pref.build_mode == .embed_vlib { // vlib can't have `init_consts()` cgen.genln('void init_consts() { g_str_buf=malloc(1000); ${cgen.consts_init.join_lines()} }') // _STR function can't be defined in vlib @@ -343,7 +344,7 @@ string _STR_TMP(const char *fmt, ...) { } // Make sure the main function exists // Obviously we don't need it in libraries - if v.pref.build_mode != build { + if v.pref.build_mode != .build { if !v.table.main_exists() && !v.pref.is_test { // It can be skipped in single file programs if v.pref.is_script { @@ -424,7 +425,7 @@ fn (c &V) cc_windows_cross() { } } mut libs := '' - if c.pref.build_mode == default_mode { + if c.pref.build_mode == .default_mode { libs = '$TmpPath/vlib/builtin.o' if !os.file_exists(libs) { println('`builtin.o` not found') @@ -462,7 +463,7 @@ fn (c &V) cc_windows_cross() { println('Cross compilation for Windows failed. Make sure you have clang installed.') exit(1) } - if c.pref.build_mode != build { + if c.pref.build_mode != .build { link_cmd := 'lld-link $obj_name $winroot/lib/libcmt.lib ' + '$winroot/lib/libucrt.lib $winroot/lib/kernel32.lib $winroot/lib/libvcruntime.lib ' + '$winroot/lib/uuid.lib' @@ -507,13 +508,13 @@ fn (v mut V) cc() { a << '-g' } mut libs := ''// builtin.o os.o http.o etc - if v.pref.build_mode == build { + if v.pref.build_mode == .build { a << '-c' } - else if v.pref.build_mode == embed_vlib { + else if v.pref.build_mode == .embed_vlib { // } - else if v.pref.build_mode == default_mode { + else if v.pref.build_mode == .default_mode { libs = '$TmpPath/vlib/builtin.o' if !os.file_exists(libs) { println('`builtin.o` not found') @@ -568,7 +569,7 @@ mut args := '' a << '-x objective-c' } // Without these libs compilation will fail on Linux - if v.os == LINUX && v.pref.build_mode != build { + if v.os == LINUX && v.pref.build_mode != .build { a << '-lm -ldl -lpthread' } // Find clang executable @@ -595,7 +596,7 @@ mut args := '' panic('clang error') } // Link it if we are cross compiling and need an executable - if v.os == LINUX && !linux_host && v.pref.build_mode != build { + if v.os == LINUX && !linux_host && v.pref.build_mode != .build { v.out_name = v.out_name.replace('.o', '') obj_file := v.out_name + '.o' println('linux obj_file=$obj_file out_name=$v.out_name') @@ -614,7 +615,9 @@ mut args := '' } println('linux cross compilation done. resulting binary: "$v.out_name"') } - //os.rm('$TmpPath/$v.out_name_c') + if v.out_name_c != 'v.c' { + os.rm('$TmpPath/$v.out_name_c') + } } fn (v &V) v_files_from_dir(dir string) []string { @@ -706,7 +709,7 @@ fn (v mut V) add_user_v_files() { p.parse() } // Parse lib imports - if v.pref.build_mode == default_mode { + if v.pref.build_mode == .default_mode { for i := 0; i < v.table.imports.len; i++ { pkg := v.table.imports[i] vfiles := v.v_files_from_dir('$TmpPath/vlib/$pkg') @@ -741,7 +744,7 @@ fn (v mut V) add_user_v_files() { // If we are in default mode, we don't parse vlib .v files, but header .vh files in // TmpPath/vlib // These were generated by vfmt - if v.pref.build_mode == default_mode || v.pref.build_mode == build { + if v.pref.build_mode == .default_mode || v.pref.build_mode == .build { module_path = '$TmpPath/vlib/$pkg' } vfiles := v.v_files_from_dir(module_path) @@ -793,23 +796,27 @@ fn new_v(args[]string) *V { target_os := get_arg(joined_args, 'os', '') mut out_name := get_arg(joined_args, 'o', 'a.out') // build mode - mut build_mode := default_mode + mut build_mode := BuildMode.default_mode if args.contains('-lib') { - build_mode = build + build_mode = .build // v -lib ~/v/os => os.o base := dir.all_after('/') println('Building module ${base}...') - out_name = '$TmpPath/vlib/${base}.o' + //out_name = '$TmpPath/vlib/${base}.o' + out_name = base + '.o' // Cross compiling? Use separate dirs for each os +/* if target_os != os.user_os() { os.mkdir('$TmpPath/vlib/$target_os') out_name = '$TmpPath/vlib/$target_os/${base}.o' - println('Cross compiling $out_name') + println('target_os=$target_os user_os=${os.user_os()}') + println('!Cross compiling $out_name') } +*/ } // TODO embed_vlib is temporarily the default mode. It's much slower. else if !args.contains('-embed_vlib') { - build_mode = embed_vlib + build_mode = .embed_vlib } // is_test := dir.ends_with('_test.v') @@ -896,7 +903,7 @@ fn new_v(args[]string) *V { for builtin in builtins { mut f := '$lang_dir/vlib/builtin/$builtin' // In default mode we use precompiled vlib.o, point to .vh files with signatures - if build_mode == default_mode || build_mode == build { + if build_mode == .default_mode || build_mode == .build { f = '$TmpPath/vlib/builtin/${builtin}h' } files << f diff --git a/compiler/parser.v b/compiler/parser.v index ed6034ad12..36a89e0e0f 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -46,6 +46,7 @@ mut: inside_const bool expr_var Var assigned_type string + left_type string tmp_cnt int // TODO all these options are copy-pasted from the V struct. Create a Settings struct instead? // is_test bool @@ -589,9 +590,10 @@ fn (p mut Parser) enum_decl(_enum_name string) { mut val := 0 for p.tok == NAME { field := p.check_name() - // name := '${p.pkg}__${enum_name}_$field' - // name := '${enum_name}_$field' - name := '$field' + mut name := '$field' +if enum_name == 'BuildMode' { + name = '${p.pkg}__${enum_name}_$field' +} p.fgenln('') if p.run == RUN_MAIN { p.cgen.consts << '#define $name $val \n' @@ -1178,6 +1180,7 @@ fn (p mut Parser) bool_expression() string { fn (p mut Parser) bterm() string { ph := p.cgen.add_placeholder() mut typ := p.expression() + p.left_type = typ is_str := typ=='string' tok := p.tok // if tok in [ EQ, GT, LT, LE, GE, NE] { @@ -1207,7 +1210,7 @@ fn (p mut Parser) bterm() string { return typ } -// also called on *, & +// also called on *, &, . (enum) fn (p mut Parser) name_expr() string { p.log('\nname expr() pass=$p.run tok=${p.tok.str()} $p.lit') // print('known type:') @@ -1217,6 +1220,7 @@ fn (p mut Parser) name_expr() string { hack_tok := p.tok hack_lit := p.lit // amp + ptr := p.tok == AMP deref := p.tok == MUL if ptr || deref { @@ -1243,6 +1247,17 @@ fn (p mut Parser) name_expr() string { is_c_struct_init = true } } + // enum value? (`color == .green`) + if p.tok == DOT { + //println('got enum dot val $p.left_type pass=$p.run $p.scanner.line_nr left=$p.left_type') + T := p.find_type(p.left_type) + if T.is_enum { + p.check(DOT) + val := p.check_name() + p.gen(p.pkg + '__' + p.left_type + '_' + val) + } + return p.left_type + } // ////////////////////////// // module ? // Allow shadowing (gg = gg.newcontext(); gg.draw_triangle()) @@ -1454,23 +1469,28 @@ fn (p mut Parser) var_expr(v Var) string { } if typ != 'int' { if !p.pref.translated && !is_number_type(typ) { - // if T.parent != 'int' { p.error('cannot ++/-- value of type `$typ`') } } p.gen(p.tok.str()) p.fgen(p.tok.str()) - p.next()// ++ - // allow a := c++ in translated + p.next()// ++/-- + // allow `a := c++` in translated code if p.pref.translated { - return p.index_expr(typ, fn_ph) - // return typ + //return p.index_expr(typ, fn_ph) } else { return 'void' } } typ = p.index_expr(typ, fn_ph) + // TODO hack to allow `foo.bar[0] = 2` + if p.tok == DOT { + for p.tok == DOT { + typ = p.dot(typ, fn_ph) + } + typ = p.index_expr(typ, fn_ph) + } return typ } @@ -1485,9 +1505,9 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string { field_name := p.lit p.fgen(field_name) p.log('dot() field_name=$field_name typ=$str_typ') - if p.fileis('hi_test') { - println('dot() field_name=$field_name typ=$str_typ') - } + //if p.fileis('main.v') { + //println('dot() field_name=$field_name typ=$str_typ prev_tok=${prev_tok.str()}') + //} typ := p.find_type(str_typ) if typ.name.len == 0 { p.error('dot(): cannot find type `$str_typ`') @@ -1500,7 +1520,7 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string { opt_type := typ.name.substr(7, typ.name.len) p.error('unhandled option type: $opt_type?') } - println('dot():') + println('error in dot():') println('fields:') for field in typ.fields { println(field.name) @@ -1564,17 +1584,17 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string { // return typ.name.replace('array_', '') return typ.name.right(6) } - if false && p.tok == LSBR { + //if false && p.tok == LSBR { // if is_indexer { - return p.index_expr(method.typ, method_ph) - } + //return p.index_expr(method.typ, method_ph) + //} return method.typ } fn (p mut Parser) index_expr(typ string, fn_ph int) string { - if p.fileis('int_test') { - println('index expr typ=$typ') - } + //if p.fileis('main.v') { + //println('index expr typ=$typ') + //} // a[0] v := p.expr_var is_map := typ.starts_with('map_') @@ -1688,16 +1708,13 @@ fn (p mut Parser) index_expr(typ string, fn_ph int) string { p.tok == MULT_ASSIGN || p.tok == DIV_ASSIGN || p.tok == XOR_ASSIGN || p.tok == MOD_ASSIGN || p.tok == OR_ASSIGN || p.tok == AND_ASSIGN || p.tok == RIGHT_SHIFT_ASSIGN || p.tok == LEFT_SHIFT_ASSIGN { - if is_map || is_arr { - // Don't generate indexer right away, but assign it to tmp - // p.cgen.start_tmp() - } if is_indexer && is_str && !p.builtin_pkg { p.error('strings are immutable') } // println('111 "$p.cgen.cur_line"') assign_pos := p.cgen.cur_line.len p.assigned_type = typ + p.left_type = typ p.assign_statement(v, fn_ph, is_indexer && (is_map || is_arr)) // m[key] = val if is_indexer && (is_map || is_arr) { @@ -1833,7 +1850,7 @@ fn (p mut Parser) expression() string { p.check_types(p.expression(), typ) return 'int' } - if p.tok == DOT { + if p.tok == DOT { for p.tok == DOT { typ = p.dot(typ, 0) } @@ -2669,7 +2686,7 @@ fn (p mut Parser) chash() { else if hash.contains('embed') { pos := hash.index('embed') + 5 file := hash.right(pos) - if p.pref.build_mode != default_mode { + if p.pref.build_mode != BuildMode.default_mode { p.genln('#include $file') } } diff --git a/compiler/table.v b/compiler/table.v index 44820fe1c7..9a5d727b4c 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -25,11 +25,6 @@ enum AccessMod { PUBLIC_MUT_MUT // public and mutable both inside and outside (not recommended to use, that's why it's so verbose) } -enum TypeCategory { - TYPE_STRUCT - T_CAT_FN -} - struct Type { mut: pkg string @@ -37,8 +32,6 @@ mut: fields []Var methods []Fn parent string - cat TypeCategory - gen_types []string func Fn // For cat == FN (type kek fn()) is_c bool // C.FILE is_interface bool @@ -369,6 +362,7 @@ fn (t &Type) find_method(name string) Fn { return Fn{} } +/* fn (t mut Type) add_gen_type(type_name string) { // println('add_gen_type($s)') if t.gen_types.contains(type_name) { @@ -376,6 +370,7 @@ fn (t mut Type) add_gen_type(type_name string) { } t.gen_types << type_name } +*/ fn (p &Parser) find_type(name string) *Type { typ := p.table.find_type(name) diff --git a/vlib/builtin/struct_test.v b/vlib/builtin/struct_test.v new file mode 100644 index 0000000000..00a972e36b --- /dev/null +++ b/vlib/builtin/struct_test.v @@ -0,0 +1,44 @@ +struct A{ +mut: + val int + nums []int +} + +struct B{ +mut: + a A +} +struct C { +mut: + b B + nums []int + as []A + num int +} + +fn test_struct_levels() { + mut c := C{} + assert c.nums.len == 0 + c.nums << 3 + assert c.nums.len == 1 + assert c.nums[0] == 3 + c.nums[0] = 4 + assert c.nums[0] == 4 + c.b.a.val = 34 + assert c.b.a.val == 34 + c.b.a.nums = [0;0] + c.b.a.nums << 0 + c.b.a.nums << 2 + assert c.b.a.nums.len == 2 + assert c.b.a.nums[0] == 0 + assert c.b.a.nums[1] == 2 + c.b.a.nums [0] = 7 + assert c.b.a.nums[0] == 7 + c.as << A{val:8} + assert c.as.len == 1 + assert c.as[0].val == 8 + c.num = 20 + assert c.num == 20 + c.as[0].val = 10 + assert c.as[0].val == 10 +}