1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

enums (mode == .default_mode syntax); fix foo.bar[0].baz = val

This commit is contained in:
Alexander Medvednikov 2019-07-02 00:00:27 +02:00
parent 859c8ffdb8
commit a9b8bc067f
5 changed files with 123 additions and 60 deletions

View File

@ -95,7 +95,7 @@ fn (f mut Fn) clear_vars() {
// vlib header file? // vlib header file?
fn (p mut Parser) is_sig() bool { 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)) (p.file_path.contains(TmpPath))
} }
@ -338,7 +338,7 @@ fn (p mut Parser) fn_decl() {
// Add function definition to the top // Add function definition to the top
if !is_c && f.name != 'main' && p.first_run() { if !is_c && f.name != 'main' && p.first_run() {
// TODO hack to make Volt compile without -embed_vlib // 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 return
} }
p.cgen.fns << fn_decl + ';' p.cgen.fns << fn_decl + ';'

View File

@ -77,7 +77,7 @@ mut:
build_mode BuildMode build_mode BuildMode
nofmt bool // disable vfmt nofmt bool // disable vfmt
is_test bool // `v test string_test.v` 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_live bool // for hot code reloading
is_so bool is_so bool
is_prof bool // benchmark every function is_prof bool // benchmark every function
@ -92,6 +92,7 @@ mut:
sanitize bool // use Clang's new "-fsanitize" option sanitize bool // use Clang's new "-fsanitize" option
} }
fn main() { fn main() {
args := os.args args := os.args
// Print the version and exit. // Print the version and exit.
@ -108,7 +109,7 @@ fn main() {
return return
} }
// TODO quit if the compiler is too old // 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. // Create a temp directory if it's not there.
if !os.file_exists(TmpPath) { if !os.file_exists(TmpPath) {
os.mkdir(TmpPath) os.mkdir(TmpPath)
@ -242,23 +243,23 @@ void reload_so();
void init_consts();') void init_consts();')
imports_json := v.table.imports.contains('json') imports_json := v.table.imports.contains('json')
// TODO remove global UI hack // TODO remove global UI hack
if v.os == MAC && ((v.pref.build_mode == embed_vlib && v.table.imports.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'))) { (v.pref.build_mode == .build && v.dir.contains('/ui'))) {
cgen.genln('id defaultFont = 0; // main.v') cgen.genln('id defaultFont = 0; // main.v')
} }
// TODO remove ugly .c include once V has its own json parser // TODO remove ugly .c include once V has its own json parser
// Embed cjson either in embedvlib or in json.o // Embed cjson either in embedvlib or in json.o
if imports_json && v.pref.build_mode == embed_vlib || if imports_json && v.pref.build_mode == .embed_vlib ||
(v.pref.build_mode == build && v.out_name.contains('json.o')) { (v.pref.build_mode == .build && v.out_name.contains('json.o')) {
cgen.genln('#include "cJSON.c" ') cgen.genln('#include "cJSON.c" ')
} }
// We need the cjson header for all the json decoding user will do in default mode // 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 { if imports_json {
cgen.genln('#include "cJSON.h"') 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 // If we declare these for all modes, then when running `v a.v` we'll get
// `/usr/bin/ld: multiple definition of 'total_m'` // `/usr/bin/ld: multiple definition of 'total_m'`
// TODO // TODO
@ -303,7 +304,7 @@ void init_consts();')
dd := d.str() dd := d.str()
cgen.lines.set(defs_pos, dd)// TODO `def.str()` doesn't compile cgen.lines.set(defs_pos, dd)// TODO `def.str()` doesn't compile
// if v.build_mode in [.default, .embed_vlib] { // 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()` // vlib can't have `init_consts()`
cgen.genln('void init_consts() { g_str_buf=malloc(1000); ${cgen.consts_init.join_lines()} }') cgen.genln('void init_consts() { g_str_buf=malloc(1000); ${cgen.consts_init.join_lines()} }')
// _STR function can't be defined in vlib // _STR function can't be defined in vlib
@ -343,7 +344,7 @@ string _STR_TMP(const char *fmt, ...) {
} }
// Make sure the main function exists // Make sure the main function exists
// Obviously we don't need it in libraries // 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 { if !v.table.main_exists() && !v.pref.is_test {
// It can be skipped in single file programs // It can be skipped in single file programs
if v.pref.is_script { if v.pref.is_script {
@ -424,7 +425,7 @@ fn (c &V) cc_windows_cross() {
} }
} }
mut libs := '' mut libs := ''
if c.pref.build_mode == default_mode { if c.pref.build_mode == .default_mode {
libs = '$TmpPath/vlib/builtin.o' libs = '$TmpPath/vlib/builtin.o'
if !os.file_exists(libs) { if !os.file_exists(libs) {
println('`builtin.o` not found') 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.') println('Cross compilation for Windows failed. Make sure you have clang installed.')
exit(1) exit(1)
} }
if c.pref.build_mode != build { if c.pref.build_mode != .build {
link_cmd := 'lld-link $obj_name $winroot/lib/libcmt.lib ' + link_cmd := 'lld-link $obj_name $winroot/lib/libcmt.lib ' +
'$winroot/lib/libucrt.lib $winroot/lib/kernel32.lib $winroot/lib/libvcruntime.lib ' + '$winroot/lib/libucrt.lib $winroot/lib/kernel32.lib $winroot/lib/libvcruntime.lib ' +
'$winroot/lib/uuid.lib' '$winroot/lib/uuid.lib'
@ -507,13 +508,13 @@ fn (v mut V) cc() {
a << '-g' a << '-g'
} }
mut libs := ''// builtin.o os.o http.o etc mut libs := ''// builtin.o os.o http.o etc
if v.pref.build_mode == build { if v.pref.build_mode == .build {
a << '-c' 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' libs = '$TmpPath/vlib/builtin.o'
if !os.file_exists(libs) { if !os.file_exists(libs) {
println('`builtin.o` not found') println('`builtin.o` not found')
@ -568,7 +569,7 @@ mut args := ''
a << '-x objective-c' a << '-x objective-c'
} }
// Without these libs compilation will fail on Linux // 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' a << '-lm -ldl -lpthread'
} }
// Find clang executable // Find clang executable
@ -595,7 +596,7 @@ mut args := ''
panic('clang error') panic('clang error')
} }
// Link it if we are cross compiling and need an executable // 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', '') v.out_name = v.out_name.replace('.o', '')
obj_file := v.out_name + '.o' obj_file := v.out_name + '.o'
println('linux obj_file=$obj_file out_name=$v.out_name') 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"') 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 { fn (v &V) v_files_from_dir(dir string) []string {
@ -706,7 +709,7 @@ fn (v mut V) add_user_v_files() {
p.parse() p.parse()
} }
// Parse lib imports // 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++ { for i := 0; i < v.table.imports.len; i++ {
pkg := v.table.imports[i] pkg := v.table.imports[i]
vfiles := v.v_files_from_dir('$TmpPath/vlib/$pkg') 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 // If we are in default mode, we don't parse vlib .v files, but header .vh files in
// TmpPath/vlib // TmpPath/vlib
// These were generated by vfmt // 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' module_path = '$TmpPath/vlib/$pkg'
} }
vfiles := v.v_files_from_dir(module_path) 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', '') target_os := get_arg(joined_args, 'os', '')
mut out_name := get_arg(joined_args, 'o', 'a.out') mut out_name := get_arg(joined_args, 'o', 'a.out')
// build mode // build mode
mut build_mode := default_mode mut build_mode := BuildMode.default_mode
if args.contains('-lib') { if args.contains('-lib') {
build_mode = build build_mode = .build
// v -lib ~/v/os => os.o // v -lib ~/v/os => os.o
base := dir.all_after('/') base := dir.all_after('/')
println('Building module ${base}...') 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 // Cross compiling? Use separate dirs for each os
/*
if target_os != os.user_os() { if target_os != os.user_os() {
os.mkdir('$TmpPath/vlib/$target_os') os.mkdir('$TmpPath/vlib/$target_os')
out_name = '$TmpPath/vlib/$target_os/${base}.o' 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. // TODO embed_vlib is temporarily the default mode. It's much slower.
else if !args.contains('-embed_vlib') { else if !args.contains('-embed_vlib') {
build_mode = embed_vlib build_mode = .embed_vlib
} }
// //
is_test := dir.ends_with('_test.v') is_test := dir.ends_with('_test.v')
@ -896,7 +903,7 @@ fn new_v(args[]string) *V {
for builtin in builtins { for builtin in builtins {
mut f := '$lang_dir/vlib/builtin/$builtin' mut f := '$lang_dir/vlib/builtin/$builtin'
// In default mode we use precompiled vlib.o, point to .vh files with signatures // 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' f = '$TmpPath/vlib/builtin/${builtin}h'
} }
files << f files << f

View File

@ -46,6 +46,7 @@ mut:
inside_const bool inside_const bool
expr_var Var expr_var Var
assigned_type string assigned_type string
left_type string
tmp_cnt int tmp_cnt int
// TODO all these options are copy-pasted from the V struct. Create a Settings struct instead? // TODO all these options are copy-pasted from the V struct. Create a Settings struct instead?
// is_test bool // is_test bool
@ -589,9 +590,10 @@ fn (p mut Parser) enum_decl(_enum_name string) {
mut val := 0 mut val := 0
for p.tok == NAME { for p.tok == NAME {
field := p.check_name() field := p.check_name()
// name := '${p.pkg}__${enum_name}_$field' mut name := '$field'
// name := '${enum_name}_$field' if enum_name == 'BuildMode' {
name := '$field' name = '${p.pkg}__${enum_name}_$field'
}
p.fgenln('') p.fgenln('')
if p.run == RUN_MAIN { if p.run == RUN_MAIN {
p.cgen.consts << '#define $name $val \n' p.cgen.consts << '#define $name $val \n'
@ -1178,6 +1180,7 @@ fn (p mut Parser) bool_expression() string {
fn (p mut Parser) bterm() string { fn (p mut Parser) bterm() string {
ph := p.cgen.add_placeholder() ph := p.cgen.add_placeholder()
mut typ := p.expression() mut typ := p.expression()
p.left_type = typ
is_str := typ=='string' is_str := typ=='string'
tok := p.tok tok := p.tok
// if tok in [ EQ, GT, LT, LE, GE, NE] { // if tok in [ EQ, GT, LT, LE, GE, NE] {
@ -1207,7 +1210,7 @@ fn (p mut Parser) bterm() string {
return typ return typ
} }
// also called on *, & // also called on *, &, . (enum)
fn (p mut Parser) name_expr() string { fn (p mut Parser) name_expr() string {
p.log('\nname expr() pass=$p.run tok=${p.tok.str()} $p.lit') p.log('\nname expr() pass=$p.run tok=${p.tok.str()} $p.lit')
// print('known type:') // print('known type:')
@ -1217,6 +1220,7 @@ fn (p mut Parser) name_expr() string {
hack_tok := p.tok hack_tok := p.tok
hack_lit := p.lit hack_lit := p.lit
// amp // amp
ptr := p.tok == AMP ptr := p.tok == AMP
deref := p.tok == MUL deref := p.tok == MUL
if ptr || deref { if ptr || deref {
@ -1243,6 +1247,17 @@ fn (p mut Parser) name_expr() string {
is_c_struct_init = true 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 ? // module ?
// Allow shadowing (gg = gg.newcontext(); gg.draw_triangle()) // 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 typ != 'int' {
if !p.pref.translated && !is_number_type(typ) { if !p.pref.translated && !is_number_type(typ) {
// if T.parent != 'int' {
p.error('cannot ++/-- value of type `$typ`') p.error('cannot ++/-- value of type `$typ`')
} }
} }
p.gen(p.tok.str()) p.gen(p.tok.str())
p.fgen(p.tok.str()) p.fgen(p.tok.str())
p.next()// ++ p.next()// ++/--
// allow a := c++ in translated // allow `a := c++` in translated code
if p.pref.translated { if p.pref.translated {
return p.index_expr(typ, fn_ph) //return p.index_expr(typ, fn_ph)
// return typ
} }
else { else {
return 'void' return 'void'
} }
} }
typ = p.index_expr(typ, fn_ph) 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 return typ
} }
@ -1485,9 +1505,9 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
field_name := p.lit field_name := p.lit
p.fgen(field_name) p.fgen(field_name)
p.log('dot() field_name=$field_name typ=$str_typ') p.log('dot() field_name=$field_name typ=$str_typ')
if p.fileis('hi_test') { //if p.fileis('main.v') {
println('dot() field_name=$field_name typ=$str_typ') //println('dot() field_name=$field_name typ=$str_typ prev_tok=${prev_tok.str()}')
} //}
typ := p.find_type(str_typ) typ := p.find_type(str_typ)
if typ.name.len == 0 { if typ.name.len == 0 {
p.error('dot(): cannot find type `$str_typ`') 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) opt_type := typ.name.substr(7, typ.name.len)
p.error('unhandled option type: $opt_type?') p.error('unhandled option type: $opt_type?')
} }
println('dot():') println('error in dot():')
println('fields:') println('fields:')
for field in typ.fields { for field in typ.fields {
println(field.name) 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.replace('array_', '')
return typ.name.right(6) return typ.name.right(6)
} }
if false && p.tok == LSBR { //if false && p.tok == LSBR {
// if is_indexer { // if is_indexer {
return p.index_expr(method.typ, method_ph) //return p.index_expr(method.typ, method_ph)
} //}
return method.typ return method.typ
} }
fn (p mut Parser) index_expr(typ string, fn_ph int) string { fn (p mut Parser) index_expr(typ string, fn_ph int) string {
if p.fileis('int_test') { //if p.fileis('main.v') {
println('index expr typ=$typ') //println('index expr typ=$typ')
} //}
// a[0] // a[0]
v := p.expr_var v := p.expr_var
is_map := typ.starts_with('map_') 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 == 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 == OR_ASSIGN || p.tok == AND_ASSIGN || p.tok == RIGHT_SHIFT_ASSIGN ||
p.tok == LEFT_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 { if is_indexer && is_str && !p.builtin_pkg {
p.error('strings are immutable') p.error('strings are immutable')
} }
// println('111 "$p.cgen.cur_line"') // println('111 "$p.cgen.cur_line"')
assign_pos := p.cgen.cur_line.len assign_pos := p.cgen.cur_line.len
p.assigned_type = typ p.assigned_type = typ
p.left_type = typ
p.assign_statement(v, fn_ph, is_indexer && (is_map || is_arr)) p.assign_statement(v, fn_ph, is_indexer && (is_map || is_arr))
// m[key] = val // m[key] = val
if is_indexer && (is_map || is_arr) { if is_indexer && (is_map || is_arr) {
@ -1833,7 +1850,7 @@ fn (p mut Parser) expression() string {
p.check_types(p.expression(), typ) p.check_types(p.expression(), typ)
return 'int' return 'int'
} }
if p.tok == DOT { if p.tok == DOT {
for p.tok == DOT { for p.tok == DOT {
typ = p.dot(typ, 0) typ = p.dot(typ, 0)
} }
@ -2669,7 +2686,7 @@ fn (p mut Parser) chash() {
else if hash.contains('embed') { else if hash.contains('embed') {
pos := hash.index('embed') + 5 pos := hash.index('embed') + 5
file := hash.right(pos) file := hash.right(pos)
if p.pref.build_mode != default_mode { if p.pref.build_mode != BuildMode.default_mode {
p.genln('#include $file') p.genln('#include $file')
} }
} }

View File

@ -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) 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 { struct Type {
mut: mut:
pkg string pkg string
@ -37,8 +32,6 @@ mut:
fields []Var fields []Var
methods []Fn methods []Fn
parent string parent string
cat TypeCategory
gen_types []string
func Fn // For cat == FN (type kek fn()) func Fn // For cat == FN (type kek fn())
is_c bool // C.FILE is_c bool // C.FILE
is_interface bool is_interface bool
@ -369,6 +362,7 @@ fn (t &Type) find_method(name string) Fn {
return Fn{} return Fn{}
} }
/*
fn (t mut Type) add_gen_type(type_name string) { fn (t mut Type) add_gen_type(type_name string) {
// println('add_gen_type($s)') // println('add_gen_type($s)')
if t.gen_types.contains(type_name) { 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 t.gen_types << type_name
} }
*/
fn (p &Parser) find_type(name string) *Type { fn (p &Parser) find_type(name string) *Type {
typ := p.table.find_type(name) typ := p.table.find_type(name)

View File

@ -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
}