From 26ef99293da4f6d9cb0d4bc22637ae52c6d6e7df Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 7 Jul 2019 22:30:15 +0200 Subject: [PATCH] new enum syntax; update the compiler (`p.tok == .name`) --- compiler/cgen.v | 4 +- compiler/fn.v | 152 ++++---- compiler/jsgen.v | 4 +- compiler/main.v | 92 +++-- compiler/parser.v | 851 ++++++++++++++++++++++----------------------- compiler/scanner.v | 148 ++++---- compiler/table.v | 30 +- compiler/token.v | 388 ++++++++++----------- 8 files changed, 835 insertions(+), 834 deletions(-) diff --git a/compiler/cgen.v b/compiler/cgen.v index dc400dec5d..d02530f570 100644 --- a/compiler/cgen.v +++ b/compiler/cgen.v @@ -45,7 +45,7 @@ fn new_cgen(out_name_c string) *CGen { } fn (g mut CGen) genln(s string) { - if g.nogen || g.run == RUN_DECLS { + if g.nogen || g.run == .decl { return } if g.is_tmp { @@ -61,7 +61,7 @@ fn (g mut CGen) genln(s string) { } fn (g mut CGen) gen(s string) { - if g.nogen || g.run == RUN_DECLS { + if g.nogen || g.run == .decl { return } if g.is_tmp { diff --git a/compiler/fn.v b/compiler/fn.v index 164ee8335a..a4551d81ee 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -114,7 +114,7 @@ fn new_fn(pkg string, is_public bool) *Fn { // Function signatures are added to the top of the .c file in the first run. fn (p mut Parser) fn_decl() { p.fgen('fn ') - is_pub := p.tok == PUB + is_pub := p.tok == .key_pub is_live := p.attr == 'live' && !p.pref.is_so if is_live && !p.pref.is_live { p.error('run `v -live program.v` if you want to use [live] functions') @@ -124,15 +124,15 @@ fn (p mut Parser) fn_decl() { } p.returns = false p.next() - mut f := new_fn(p.pkg, is_pub) + mut f := new_fn(p.mod, is_pub) // Method receiver mut receiver_typ := '' - if p.tok == LPAR { + if p.tok == .lpar { f.is_method = true - p.check(LPAR) + p.check(.lpar) receiver_name := p.check_name() - is_mut := p.tok == MUT - is_amp := p.tok == AMP + is_mut := p.tok == .key_mut + is_amp := p.tok == .amp if is_mut || is_amp { p.check_space(p.tok) } @@ -142,9 +142,9 @@ fn (p mut Parser) fn_decl() { p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)') } // Don't allow modifying types from a different module - if !p.first_run() && !p.builtin_pkg && T.pkg != p.pkg { - println('T.pkg=$T.pkg') - println('pkg=$p.pkg') + if !p.first_run() && !p.builtin_pkg && T.mod != p.mod { + println('T.mod=$T.mod') + println('pkg=$p.mod') p.error('cannot define new methods on non-local type `$receiver_typ`') } // (a *Foo) instead of (a mut Foo) is a common mistake @@ -156,7 +156,7 @@ fn (p mut Parser) fn_decl() { if is_mut || is_amp { receiver_typ += '*' } - p.check(RPAR) + p.check(.rpar) p.fspace() receiver := Var { name: receiver_name @@ -170,7 +170,7 @@ fn (p mut Parser) fn_decl() { f.args << receiver f.register_var(receiver) } - if p.tok == PLUS || p.tok == MINUS || p.tok == MUL { + if p.tok == .plus || p.tok == .minus || p.tok == .mul { f.name = p.tok.str() p.next() } @@ -178,14 +178,14 @@ fn (p mut Parser) fn_decl() { f.name = p.check_name() } // C function header def? (fn C.NSMakeRect(int,int,int,int)) - is_c := f.name == 'C' && p.tok == DOT + is_c := f.name == 'C' && p.tok == .dot // Just fn signature? only builtin.v + default build mode // is_sig := p.builtin_pkg && p.pref.build_mode == default_mode // is_sig := p.pref.build_mode == default_mode && (p.builtin_pkg || p.file.contains(LANG_TMP)) is_sig := p.is_sig() // println('\n\nfn decl !!is_sig=$is_sig name=$f.name $p.builtin_pkg') if is_c { - p.check(DOT) + p.check(.dot) f.name = p.check_name() f.is_c = true } @@ -198,7 +198,7 @@ fn (p mut Parser) fn_decl() { } } // simple_name := f.name - // println('!SIMPLE=$simple_name') + // println('!SIMP.le=$simple_name') // user.register() => User_register() has_receiver := receiver_typ.len > 0 if receiver_typ != '' { @@ -206,7 +206,7 @@ fn (p mut Parser) fn_decl() { } // full pkg function name // os.exit ==> os__exit() - if !is_c && !p.builtin_pkg && p.pkg != 'main' && receiver_typ.len == 0 { + if !is_c && !p.builtin_pkg && p.mod != 'main' && receiver_typ.len == 0 { f.name = p.prepend_pkg(f.name) } if p.first_run() && p.table.known_fn(f.name) && receiver_typ.len == 0 { @@ -218,42 +218,42 @@ fn (p mut Parser) fn_decl() { } // Generic? mut is_generic := false - if p.tok == LT { + if p.tok == .lt { p.next() gen_type := p.check_name() if gen_type != 'T' { p.error('only `T` is allowed as a generic type for now') } - p.check(GT) + p.check(.gt) is_generic = true } // Args (...) p.fn_args(mut f) // Returns an error? - if p.tok == NOT { + if p.tok == .not { p.next() f.returns_error = true } // Returns a type? mut typ := 'void' - if p.tok == NAME || p.tok == MUL || p.tok == AMP || p.tok == LSBR || - p.tok == QUESTION { + if p.tok == .name || p.tok == .mul || p.tok == .amp || p.tok == .lsbr || + p.tok == .question { p.fgen(' ') // TODO In - // if p.tok in [ NAME, MUL, AMP, LSBR ] { + // if p.tok in [ .name, .mul, .amp, .lsbr ] { typ = p.get_type() } // Translated C code can have empty functions (just definitions) is_fn_header := !is_c && !is_sig && (p.pref.translated || p.pref.is_test) && - (p.tok != LCBR)// || (p.tok == NAME && p.peek() != LCBR)) + (p.tok != .lcbr)// || (p.tok == .name && p.peek() != .lcbr)) if is_fn_header { f.is_decl = true - // println('GOT fn header $f.name') + // println('.key_goT fn header $f.name') } // { required only in normal function declarations if !is_c && !is_sig && !is_fn_header { p.fgen(' ') - p.check(LCBR) + p.check(.lcbr) } // Register option ? type if typ.starts_with('Option_') { @@ -262,7 +262,7 @@ fn (p mut Parser) fn_decl() { // Register function f.typ = typ mut str_args := f.str_args(p.table) - // println('FN DECL $f.name typ=$f.typ str_args="$str_args"') + // println('FN .decL $f.name typ=$f.typ str_args="$str_args"') // Special case for main() args if f.name == 'main' && !has_receiver { if str_args != '' || typ != 'void' { @@ -305,7 +305,7 @@ fn (p mut Parser) fn_decl() { // println('fn decl !!!!!!! REG PH $receiver_typ') ttyp := Type { name: receiver_typ.replace('*', '') - pkg: p.pkg + mod: p.mod is_placeholder: true } p.table.register_type2(ttyp) @@ -328,7 +328,7 @@ fn (p mut Parser) fn_decl() { } } // Live code reloading? Load all fns from .so - if is_live && p.first_run() && p.pkg == 'main' { + if is_live && p.first_run() && p.mod == 'main' { //println('ADDING SO FN $fn_name_cgen') p.cgen.so_fns << fn_name_cgen fn_name_cgen = '(* $fn_name_cgen )' @@ -374,7 +374,7 @@ pthread_create(&_thread_so , NULL, &reload_so, NULL); ') } // println('is_c=$is_c name=$f.name') if is_c || is_sig || is_fn_header { - // println('IS SIG RETURNING tok=${p.strtok()}') + // println('IS SIG .key_returnING tok=${p.strtok()}') p.fgenln('\n') return } @@ -395,15 +395,15 @@ pthread_create(&_thread_so , NULL, &reload_so, NULL); ') p.error('$f.name must return "$typ"') } // {} closed correctly? scope_level should be 0 - if p.pkg == 'main' { + if p.mod == 'main' { // println(p.cur_fn.scope_level) } if p.cur_fn.scope_level > 2 { // p.error('unclosed {') } // Make sure all vars in this function are used (only in main for now) - // if p.builtin_pkg || p.pkg == 'os' ||p.pkg=='http'{ - if p.pkg != 'main' { + // if p.builtin_pkg || p.mod == 'os' ||p.mod=='http'{ + if p.mod != 'main' { p.genln('}') p.fgenln('\n') return @@ -442,7 +442,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type // println('\nfn_call $f.name is_method=$f.is_method receiver_type=$f.receiver_type') // p.print_tok() mut thread_name := '' - // Normal function => just its name, method => TYPE_FNNAME + // Normal function => just its name, method => TYPE_FN.name mut fn_name := f.name if f.is_method { receiver_type = receiver_type.replace('*', '') @@ -454,7 +454,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type p.genln('$arg_struct_name * $tmp_struct = malloc(sizeof($arg_struct_name));') mut arg_struct := 'typedef struct $arg_struct_name { ' p.next() - p.check(LPAR) + p.check(.lpar) // str_args contains the args for the wrapper function: // wrapper(arg_struct * arg) { fn("arg->a, arg->b"); } mut str_args := '' @@ -473,7 +473,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type p.expression() p.genln(';') if i < f.args.len - 1 { - p.check(COMMA) + p.check(.comma) str_args += ',' } } @@ -486,7 +486,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type // Create thread object tmp_nr := p.get_tmp_counter() thread_name = '_thread$tmp_nr' - if p.os != WINDOWS { + if p.os != .windows { p.genln('pthread_t $thread_name;') } tmp2 := p.get_tmp() @@ -495,17 +495,17 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type parg = ' $tmp_struct' } // Call the wrapper - if p.os == WINDOWS { + if p.os == .windows { p.genln(' CreateThread(0,0, $wrapper_name, $parg, 0,0);') } else { p.genln('int $tmp2 = pthread_create(& $thread_name, NULL, $wrapper_name, $parg);') } - p.check(RPAR) + p.check(.rpar) } fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type string) { - if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.pkg != p.pkg { + if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.pkg != p.mod { p.error('function `$f.name` is private') } p.calling_c = f.is_c @@ -533,9 +533,9 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin receiver := f.args.first() if receiver.is_mut && !p.expr_var.is_mut { println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut') - p.error('`$p.expr_var.name` is immutable') + p.error('`$p.expr_var.name` is imkey_mut') } - // if receiver is mutable or a ref (&), generate & for the first arg + // if receiver is key_mut or a ref (&), generate & for the first arg if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) { method_call += '& /* ? */' } @@ -566,8 +566,8 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin // for declaration // return an updated Fn object with args[] field set fn (p mut Parser) fn_args(f mut Fn) { - p.check(LPAR) - // TODO defer p.check(RPAR) + p.check(.lpar) + // TODO defer p.check(.rpar) if f.is_interface { int_arg := Var { typ: f.receiver_typ @@ -575,9 +575,9 @@ fn (p mut Parser) fn_args(f mut Fn) { f.args << int_arg } // Just register fn arg types - types_only := p.tok == MUL || (p.peek() == COMMA && p.table.known_type(p.lit)) || p.peek() == RPAR// (int, string) + types_only := p.tok == .mul || (p.peek() == .comma && p.table.known_type(p.lit)) || p.peek() == .rpar// (int, string) if types_only { - for p.tok != RPAR { + for p.tok != .rpar { typ := p.get_type() v := Var { typ: typ @@ -587,24 +587,24 @@ fn (p mut Parser) fn_args(f mut Fn) { } // f.register_var(v) f.args << v - if p.tok == COMMA { + if p.tok == .comma { p.next() } } } // (a int, b,c string) syntax - for p.tok != RPAR { + for p.tok != .rpar { mut names := [ p.check_name() ] // a,b,c int syntax - for p.tok == COMMA { - p.check(COMMA) + for p.tok == .comma { + p.check(.comma) p.fspace() names << p.check_name() } p.fspace() - is_mut := p.tok == MUT + is_mut := p.tok == .key_mut if is_mut { p.next() } @@ -628,17 +628,17 @@ fn (p mut Parser) fn_args(f mut Fn) { f.register_var(v) f.args << v } - if p.tok == COMMA { + if p.tok == .comma { p.next() } - if p.tok == DOTDOT { + if p.tok == .dotdot { f.args << Var { name: '..' } p.next() } } - p.check(RPAR) + p.check(.rpar) } fn (p mut Parser) fn_call_args(f *Fn) *Fn { @@ -646,16 +646,16 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn { // println('fn_call_args() name=$f.name args.len=$f.args.len') // C func. # of args is not known // if f.name.starts_with('c_') { - p.check(LPAR) + p.check(.lpar) if f.is_c { - for p.tok != RPAR { + for p.tok != .rpar { p.bool_expression() - if p.tok == COMMA { + if p.tok == .comma { p.gen(', ') - p.check(COMMA) + p.check(.comma) } } - p.check(RPAR) + p.check(.rpar) return f } // Receiver - first arg @@ -673,22 +673,27 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn { break } ph := p.cgen.add_placeholder() - // ) here means not enough args were supplied - if p.tok == RPAR { + // `)` here means that not enough args were provided + if p.tok == .rpar { str_args := f.str_args(p.table)// TODO this is C args p.error('not enough arguments in call to `$f.name ($str_args)`') } - // If `arg` is mutable, the caller needs to provide MUT: + // If `arg` is key_mut, the caller needs to provide `mut`: // `arr := [1,2,3]; reverse(mut arr);` if arg.is_mut { - if p.tok != MUT { - p.error('`$arg.name` is a mutable argument, you need to provide `mut`: `$f.name(...mut a...)`') + if p.tok != .key_mut { + p.error('`$arg.name` is a key_mut argument, you need to provide `mut`: `$f.name(...mut a...)`') } - if p.peek() != NAME { - p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`') + if p.peek() != .name { + p.error('`$arg.name` is a key_mut argument, you need to provide a variable to modify: `$f.name(... mut a...)`') } - p.check(MUT) + p.check(.key_mut) } + p.expected_type = arg.typ + //TT := p.table.find_type(arg.typ) + //if arg.typ.parent == 'int' { + //p.expected_type = +//} typ := p.bool_expression() // Optimize `println`: replace it with `printf` to avoid extra allocations and // function calls. `println(777)` => `printf("%d\n", 777)` @@ -748,7 +753,7 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn { if ! (expected == 'void*' && got == 'int') && ! (expected == 'byte*' && got.contains(']byte')) && ! (expected == 'byte*' && got == 'string') { - p.cgen.set_placeholder(ph, '& /*11 EXP:"$expected" GOT:"$got" */') + p.cgen.set_placeholder(ph, '& /*11 EXP:"$expected" .key_goT:"$got" */') } } } @@ -767,10 +772,10 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn { if i < f.args.len - 1 { // Handle 0 args passed to varargs is_vararg := i == f.args.len - 2 && f.args[i + 1].name == '..' - if p.tok != COMMA && !is_vararg { + if p.tok != .comma && !is_vararg { p.error('wrong number of arguments for $i,$arg.name fn `$f.name`: expected $f.args.len, but got less') } - if p.tok == COMMA { + if p.tok == .comma { p.fgen(', ') } if !is_vararg { @@ -783,20 +788,19 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn { if f.args.len > 0 { last_arg := f.args.last() if last_arg.name == '..' { - println('GOT VAR ARGS AFTER') - for p.tok != RPAR { - if p.tok == COMMA { + for p.tok != .rpar { + if p.tok == .comma { p.gen(',') - p.check(COMMA) + p.check(.comma) } p.bool_expression() } } } - if p.tok == COMMA { + if p.tok == .comma { p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more') } - p.check(RPAR) + p.check(.rpar) // p.gen(')') } diff --git a/compiler/jsgen.v b/compiler/jsgen.v index 5b62c098ce..987a4237e2 100644 --- a/compiler/jsgen.v +++ b/compiler/jsgen.v @@ -28,7 +28,7 @@ fn (p mut Parser) gen_json_for_type(typ Type) { // println('gen_json_for_type( $typ.name )') // Register decoder fn mut dec_fn := Fn { - pkg: p.pkg + pkg: p.mod typ: 'Option_$typ.name' name: js_dec_name(t) } @@ -45,7 +45,7 @@ fn (p mut Parser) gen_json_for_type(typ Type) { p.table.register_fn(dec_fn) // Register encoder fn mut enc_fn := Fn { - pkg: p.pkg + pkg: p.mod typ: 'cJSON*' name: js_enc_name(t) } diff --git a/compiler/main.v b/compiler/main.v index 42a887dd62..db761488b7 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -32,35 +32,31 @@ const ( TmpPath = vtmp_path() ) -enum Os { - MAC - LINUX - WINDOWS +enum OS { + mac + linux + windows } enum Pass { // A very short pass that only looks at imports in the begginning of each file - RUN_IMPORTS + imports // First pass, only parses and saves declarations (fn signatures, consts, types). // Skips function bodies. // We need this because in V things can be used before they are declared. - RUN_DECLS + decl // Second pass, parses function bodies and generates C or machine code. - RUN_MAIN -} - -/* -// TODO rename to: -enum Pass { - imports - decls main } -*/ + +fn mykek(o OS) { + + +} struct V { mut: - os Os // the OS to build for + os OS // the OS to build for out_name_c string // name of the temporary C file files []string // all V files that need to be parsed and compiled dir string // directory (or file) being compiled (TODO rename to path?) @@ -125,7 +121,7 @@ fn main() { } */ // Just fmt and exit - if args.contains('fmt') { + if 'fmt' in args { file := args.last() if !os.file_exists(file) { println('"$file" does not exist') @@ -138,7 +134,7 @@ fn main() { println('vfmt is temporarily disabled') return } - // V with no args? REPL + // No args? REPL if args.len < 2 || (args.len == 2 && args[1] == '-') { run_repl() return @@ -149,7 +145,7 @@ fn main() { println(args) } // Generate the docs and exit - if args.contains('doc') { + if 'doc' in args { // v.gen_doc_html_for_module(args.last()) exit(0) } @@ -167,11 +163,11 @@ fn (v mut V) compile() { } // First pass (declarations) for file in v.files { - mut p := v.new_parser(file, RUN_DECLS) + mut p := v.new_parser(file, Pass.decl) p.parse() } // Main pass - cgen.run = RUN_MAIN + cgen.run = Pass.main if v.pref.is_play { cgen.genln('#define VPLAY (1) ') } @@ -233,13 +229,13 @@ typedef map map_string; #define false 0 #endif -//============================== HELPER C MACROS =============================*/ +//============================== HELPER C.MACROS =============================*/ #define _PUSH(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array__push(arr, &tmp);} #define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array__push_many(arr, tmp.data, tmp.len);} #define _IN(typ, val, arr) array_##typ##_contains(arr, val) #define ALLOC_INIT(type, ...) (type *)memdup((type[]){ __VA_ARGS__ }, sizeof(type)) -#define UTF8_CHAR_LEN( byte ) (( 0xE5000000 >> (( byte >> 3 ) & 0x1e )) & 3 ) + 1 +#define UTF8_.chartoken_.leN( byte ) (( 0xE5000000 >> (( byte >> 3 ) & 0x1e )) & 3 ) + 1 //================================== GLOBALS =================================*/ //int V_ZERO = 0; @@ -249,7 +245,7 @@ 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')) || + 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') } @@ -284,7 +280,7 @@ void init_consts();') cgen.genln('this line will be replaced with definitions') defs_pos := cgen.lines.len - 1 for file in v.files { - mut p := v.new_parser(file, RUN_MAIN) + mut p := v.new_parser(file, Pass.main) p.parse() // p.g.gen_x64() // Format all files (don't format automatically generated vlib headers) @@ -294,7 +290,7 @@ void init_consts();') } v.log('Done parsing.') // Write everything - mut d := strings.new_builder(10000)// Just to avoid some unnecessary allocations + mut d := strings.new_builder(10000)// Avoid unnecessary allocations d.writeln(cgen.includes.join_lines()) d.writeln(cgen.typedefs.join_lines()) d.writeln(cgen.types.join_lines()) @@ -524,7 +520,7 @@ fn (c &V) cc_windows_cross() { fn (v mut V) cc() { // Cross compiling for Windows - if v.os == WINDOWS { + if v.os == .windows { $if !windows { v.cc_windows_cross() return @@ -580,7 +576,7 @@ mut args := '' } // Cross compiling linux sysroot := '/Users/alex/tmp/lld/linuxroot/' - if v.os == LINUX && !linux_host { + if v.os == .linux && !linux_host { // Build file.o a << '-c --sysroot=$sysroot -target x86_64-linux-gnu' // Right now `out_name` can be `file`, not `file.o` @@ -597,17 +593,17 @@ mut args := '' a << '"$TmpPath/$v.out_name_c"' // } // Min macos version is mandatory I think? - if v.os == MAC { + if v.os == .mac { a << '-mmacosx-version-min=10.7' } a << flags a << libs // macOS code can include objective C TODO remove once objective C is replaced with C - if v.os == MAC { + if v.os == .mac { 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 @@ -634,7 +630,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') @@ -680,20 +676,20 @@ fn (v &V) v_files_from_dir(dir string) []string { if file.ends_with('_test.v') { continue } - if file.ends_with('_win.v') && v.os != WINDOWS { + if file.ends_with('_win.v') && v.os != .windows { continue } - if file.ends_with('_lin.v') && v.os != LINUX { + if file.ends_with('_lin.v') && v.os != .linux { continue } - if file.ends_with('_mac.v') && v.os != MAC { + if file.ends_with('_mac.v') && v.os != .mac { lin_file := file.replace('_mac.v', '_lin.v') // println('lin_file="$lin_file"') // If there are both _mav.v and _lin.v, don't use _mav.v if os.file_exists('$dir/$lin_file') { continue } - else if v.os == WINDOWS { + else if v.os == .windows { continue } else { @@ -719,7 +715,7 @@ fn (v mut V) add_user_v_files() { if is_test_with_imports { user_files << dir pos := dir.last_index('/') - dir = dir.left(pos) + '/'// TODO WHY IS THIS NEEDED? + dir = dir.left(pos) + '/'// TODO WHY IS THIS .neEDED? } if dir.ends_with('.v') { // Just compile one file and get parent dir @@ -743,7 +739,7 @@ fn (v mut V) add_user_v_files() { } // Parse user imports for file in user_files { - mut p := v.new_parser(file, RUN_IMPORTS) + mut p := v.new_parser(file, Pass.imports) p.parse() } // Parse lib imports @@ -753,7 +749,7 @@ fn (v mut V) add_user_v_files() { vfiles := v.v_files_from_dir('$TmpPath/vlib/$pkg') // Add all imports referenced by these libs for file in vfiles { - mut p := v.new_parser(file, RUN_IMPORTS) + mut p := v.new_parser(file, Pass.imports) p.parse() } } @@ -771,7 +767,7 @@ fn (v mut V) add_user_v_files() { vfiles := v.v_files_from_dir(import_path) // Add all imports referenced by these libs for file in vfiles { - mut p := v.new_parser(file, RUN_IMPORTS) + mut p := v.new_parser(file, Pass.imports) p.parse() } } @@ -890,24 +886,24 @@ fn new_v(args[]string) *V { base := os.getwd().all_after('/') out_name = base.trim_space() } - mut _os := MAC + mut _os := OS.mac // No OS specifed? Use current system if target_os == '' { $if linux { - _os = LINUX + _os = .linux } $if mac { - _os = MAC + _os = .mac } $if windows { - _os = WINDOWS + _os = .windows } } else { switch target_os { - case 'linux': _os = LINUX - case 'windows': _os = WINDOWS - case 'mac': _os = MAC + case 'linux': _os = .linux + case 'windows': _os = .windows + case 'mac': _os = .mac } } builtins := [ @@ -960,7 +956,7 @@ fn new_v(args[]string) *V { 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 { - f = '$TmpPath/vlib/builtin/${builtin}h' + //f = '$TmpPath/vlib/builtin/${builtin}h' } files << f } diff --git a/compiler/parser.v b/compiler/parser.v index a7d82bcd9c..b47f0e2755 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -19,7 +19,7 @@ mut: ptr bool ref bool parent_fn string // Variables can only be defined in functions - pkg string // module where this var is stored TODO rename to `mod` + mod string // module where this var is stored line_nr int access_mod AccessMod is_global bool // __global (translated from C only) @@ -41,12 +41,12 @@ mut: cgen *CGen table *Table run Pass // TODO rename `run` to `pass` - os Os - pkg string + os OS + mod string inside_const bool expr_var Var assigned_type string - left_type string + expected_type string tmp_cnt int is_script bool pref *Preferences // Setting and Preferences shared from V struct @@ -111,27 +111,27 @@ fn (p mut Parser) parse() { p.log('\nparse() run=$p.run file=$p.file_name tok=${p.strtok()}')// , "script_file=", script_file) // `module main` is not required if it's a single file program if p.is_script || p.pref.is_test { - p.pkg = 'main' + p.mod = 'main' // User may still specify `module main` - if p.tok == PACKAGE { + if p.tok == .key_module { p.next() p.fgen('module ') - p.pkg = p.check_name() + p.mod = p.check_name() } } else { - p.check(PACKAGE) + p.check(.key_module) p.fspace() - p.pkg = p.check_name() + p.mod = p.check_name() } p.fgenln('\n') - p.builtin_pkg = p.pkg == 'builtin' - p.can_chash = p.pkg == 'gg' || p.pkg == 'glm' || p.pkg == 'gl' || - p.pkg == 'http' || p.pkg == 'glfw' || p.pkg=='ui' // TODO tmp remove + p.builtin_pkg = p.mod == 'builtin' + p.can_chash = p.mod == 'gg' || p.mod == 'glm' || p.mod == 'gl' || + p.mod == 'http' || p.mod == 'glfw' || p.mod=='ui' // TODO tmp remove // Import pass - the first and the smallest pass that only analyzes imports - p.table.register_package(p.pkg) - if p.run == RUN_IMPORTS { - for p.tok == IMPORT && p.peek() != CONST { + p.table.register_package(p.mod) + if p.run == .imports { + for p.tok == .key_import && p.peek() != .key_const { p.import_statement() } if p.table.imports.contains('builtin') { @@ -142,17 +142,17 @@ fn (p mut Parser) parse() { // Go through every top level token or throw a compilation error if a non-top level token is met for { switch p.tok { - case IMPORT: - if p.peek() == CONST { + case Token.key_import: + if p.peek() == .key_const { p.const_decl() } else { // TODO remove imported consts from the language p.import_statement() } - case ENUM: + case Token.key_enum: p.next() - if p.tok == NAME { + if p.tok == .name { p.fgen('enum ') name := p.check_name() p.fgen(' ') @@ -165,38 +165,38 @@ fn (p mut Parser) parse() { p.enum_decl('int') } else { - p.check(NAME) + p.check(.name) } - case PUB: - if p.peek() == FUNC { + case Token.key_pub: + if p.peek() == .func { p.fn_decl() - } else if p.peek() == STRUCT { + } else if p.peek() == .key_struct { p.error('structs can\'t be declared public *yet*') // TODO public structs } else { p.error('wrong pub keyword usage') } - case FUNC: + case Token.func: p.fn_decl() - case TIP: + case Token.typ: p.type_decl() - case LSBR: + case Token.lsbr: // `[` can only mean an [attribute] before a function // or a struct definition p.attribute() - case STRUCT, INTERFACE, UNION, LSBR: + case Token.key_struct, Token.key_interface, Token.key_union, Token.lsbr: p.struct_decl() - case CONST: + case Token.key_const: p.const_decl() - case HASH: + case Token.hash: // insert C code, TODO this is going to be removed ASAP // some libraries (like UI) still have lots of C code // # puts("hello"); p.chash() - case DOLLAR: + case Token.dollar: // $if, $else p.comp_time() - case GLOBAL: + case Token.key_global: if !p.pref.translated && !p.pref.is_live && !p.builtin_pkg && !p.building_v() { //p.error('__global is only allowed in translated code') } @@ -206,7 +206,7 @@ fn (p mut Parser) parse() { p.register_global(name, typ) // p.genln(p.table.cgen_name_type_pair(name, typ)) mut g := p.table.cgen_name_type_pair(name, typ) - if p.tok == ASSIGN { + if p.tok == .assign { p.next() // p.gen(' = ') g += ' = ' @@ -218,7 +218,7 @@ fn (p mut Parser) parse() { // p.genln('; // global') g += '; // global' p.cgen.consts << g - case EOF: + case Token.eof: p.log('end of parse()') if p.is_script && !p.pref.is_test { p.cur_fn = MainFn @@ -274,11 +274,11 @@ fn (p mut Parser) parse() { } fn (p mut Parser) import_statement() { - p.check(IMPORT) + p.check(.key_import) // `import ()` - if p.tok == LPAR { - p.check(LPAR) - for p.tok != RPAR && p.tok != EOF { + if p.tok == .lpar { + p.check(.lpar) + for p.tok != .rpar && p.tok != .eof { pkg := p.lit.trim_space() p.next() if p.table.imports.contains(pkg) { @@ -287,11 +287,11 @@ fn (p mut Parser) import_statement() { p.table.imports << pkg p.table.register_package(pkg) } - p.check(RPAR) + p.check(.rpar) return } // `import foo` - if p.tok != NAME { + if p.tok != .name { p.error('bad import format') } mut pkg := p.lit.trim_space() @@ -321,36 +321,36 @@ fn (p mut Parser) import_statement() { } fn (p mut Parser) const_decl() { - is_import := p.tok == IMPORT + is_import := p.tok == .key_import p.inside_const = true if is_import { p.next() } - p.check(CONST) + p.check(.key_const) p.fspace() - p.check(LPAR) + p.check(.lpar) p.fgenln('') p.fmt_inc() - for p.tok == NAME { + for p.tok == .name { // `Age = 20` mut name := p.check_name() if p.pref.is_play && ! (name[0] >= `A` && name[0] <= `Z`) { p.error('const name must be capitalized') } - // Imported consts (like GL_TRIANGLES) dont need pkg prepended (gl__GL_TRIANGLES) + // Imported consts (like GL_TRIANG.leS) dont need pkg prepended (gl__GL_TRIANG.leS) if !is_import { name = p.prepend_pkg(name) } mut typ := 'int' if !is_import { - p.check_space(ASSIGN) + p.check_space(.assign) typ = p.expression() } if p.first_run() && !is_import && p.table.known_const(name) { p.error('redefinition of `$name`') } - p.table.register_const(name, typ, p.pkg, is_import) - if p.run == RUN_MAIN && !is_import { + p.table.register_const(name, typ, p.mod, is_import) + if p.run == .main && !is_import { // TODO hack // cur_line has const's value right now. if it's just a number, then optimize generation: // output a #define so that we don't pollute the binary with unnecessary global vars @@ -373,7 +373,7 @@ fn (p mut Parser) const_decl() { p.fgenln('') } p.fmt_dec() - p.check(RPAR) + p.check(.rpar) p.fgenln('\n') p.inside_const = false } @@ -381,7 +381,7 @@ fn (p mut Parser) const_decl() { // `type myint int` // `type onclickfn fn(voidptr) int` fn (p mut Parser) type_decl() { - p.check(TIP) + p.check(.typ) name := p.check_name() parent := p.get_type() nt_pair := p.table.cgen_name_type_pair(name, parent) @@ -418,20 +418,20 @@ fn (p mut Parser) struct_decl() { mut objc_parent := '' mut is_objc := false// V can generate Objective C for integration with Cocoa // [attr] - if p.tok == LSBR { - p.check(LSBR) + if p.tok == .lsbr { + p.check(.lsbr) // `[interface:ParentInterface]` - is_objc = p.tok == INTERFACE + is_objc = p.tok == .key_interface p.next() if is_objc { - p.check(COLON) + p.check(.colon) objc_parent = p.check_name() } - p.check(RSBR) + p.check(.rsbr) } - is_interface := p.tok == INTERFACE - is_union := p.tok == UNION - is_struct := p.tok == STRUCT + is_interface := p.tok == .key_interface + is_union := p.tok == .key_union + is_struct := p.tok == .key_struct p.fgen(p.tok.str() + ' ') // Get type name p.next() @@ -442,16 +442,16 @@ fn (p mut Parser) struct_decl() { if is_interface && !name.ends_with('er') { p.error('interface names temporarily have to end with `er` (e.g. `Speaker`, `Reader`)') } - is_c := name == 'C' && p.tok == DOT + is_c := name == 'C' && p.tok == .dot if is_c { - p.check(DOT) + p.check(.dot) name = p.check_name() } // Specify full type name - if !is_c && !p.builtin_pkg && p.pkg != 'main' { + if !is_c && !p.builtin_pkg && p.mod != 'main' { name = p.prepend_pkg(name) } - if p.run == RUN_DECLS && p.table.known_type(name) { + if p.run == .decl && p.table.known_type(name) { p.error('`$name` redeclared') } // Generate type definitions @@ -467,7 +467,7 @@ fn (p mut Parser) struct_decl() { } } // V used to have 'type Foo struct', many Go users might use this syntax - if !is_c && p.tok == STRUCT { + if !is_c && p.tok == .key_struct { p.error('use `struct $name {` instead of `type $name struct {`') } // Register the type @@ -476,55 +476,55 @@ fn (p mut Parser) struct_decl() { if typ.is_placeholder { is_ph = true typ.name = name - typ.pkg = p.pkg + typ.mod = p.mod typ.is_c = is_c typ.is_placeholder = false } else { typ = &Type { name: name - pkg: p.pkg + mod: p.mod is_c: is_c is_interface: is_interface } } // Struct `C.Foo` declaration, no body // println('EEEE $is_c $is_struct') - if is_c && is_struct && p.tok != LCBR { + if is_c && is_struct && p.tok != .lcbr { // println('skipping struct header $name') p.table.register_type2(typ) return } p.fgen(' ') - p.check(LCBR) + p.check(.lcbr) // Struct fields mut is_pub := false mut is_mut := false mut names := []string// to avoid dup names TODO alloc perf // mut is_mut_mut := false - for p.tok != RCBR { - if p.tok == PUB { + for p.tok != .rcbr { + if p.tok == .key_pub { if is_pub { p.error('structs can only have one `pub:`, all public fields have to be grouped') } is_pub = true p.fmt_dec() - p.check(PUB) - if p.tok != MUT { - p.check(COLON) + p.check(.key_pub) + if p.tok != .key_mut { + p.check(.colon) } p.fmt_inc() p.fgenln('') } - if p.tok == MUT { + if p.tok == .key_mut { if is_mut { - p.error('structs can only have one `mut:`, all private mutable fields have to be grouped') + p.error('structs can only have one `mut:`, all private key_mut fields have to be grouped') } is_mut = true p.fmt_dec() - p.check(MUT) - if p.tok != MUT { - p.check(COLON) + p.check(.key_mut) + if p.tok != .key_mut { + p.check(.colon) } p.fmt_inc() p.fgenln('') @@ -532,7 +532,7 @@ fn (p mut Parser) struct_decl() { // if is_pub { // } // (mut) user *User - // if p.tok == PLUS { + // if p.tok == .plus { // p.next() // } // Check dups @@ -548,10 +548,10 @@ fn (p mut Parser) struct_decl() { continue } // `pub` access mod - access_mod := if is_pub{PUBLIC} else { PRIVATE} + access_mod := if is_pub{AccessMod.public} else { AccessMod.private} p.fgen(' ') field_type := p.get_type() - is_atomic := p.tok == ATOMIC + is_atomic := p.tok == .key_atomic if is_atomic { p.next() p.gen_type('_Atomic ') @@ -561,10 +561,10 @@ fn (p mut Parser) struct_decl() { } // [ATTR] mut attr := '' - if p.tok == LSBR { + if p.tok == .lsbr { p.next() attr = p.check_name() - p.check(RSBR) + p.check(.rsbr) } typ.add_field(field_name, field_type, is_mut, attr, access_mod) p.fgenln('') @@ -572,7 +572,7 @@ fn (p mut Parser) struct_decl() { if !is_ph && p.first_run() { p.table.register_type2(typ) } - p.check(RCBR) + p.check(.rcbr) if !is_c { p.gen_type('}; ') } @@ -585,59 +585,59 @@ fn (p mut Parser) struct_decl() { fn (p mut Parser) enum_decl(_enum_name string) { mut enum_name := _enum_name // Specify full type name - if !p.builtin_pkg && p.pkg != 'main' { + if !p.builtin_pkg && p.mod != 'main' { enum_name = p.prepend_pkg(enum_name) } - p.table.register_type2(&Type { - name: enum_name - pkg: p.pkg - parent: 'int' - is_enum: true - }) // Skip empty enums if enum_name != 'int' { p.cgen.typedefs << 'typedef int $enum_name ;\n' } - p.check(LCBR) + p.check(.lcbr) mut val := 0 - for p.tok == NAME { + mut fields := []string + for p.tok == .name { field := p.check_name() - mut name := '$field' -if enum_name == 'BuildMode' { - name = '${p.pkg}__${enum_name}_$field' -} + fields << field + name := '${p.mod}__${enum_name}_$field' p.fgenln('') - if p.run == RUN_MAIN { + if p.run == .main { p.cgen.consts << '#define $name $val \n' } - if p.tok == COMMA { + if p.tok == .comma { p.next() } - p.table.register_const(name, enum_name, p.pkg, false) + p.table.register_const(name, enum_name, p.mod, false) val++ } - p.check(RCBR) + p.table.register_type2(&Type { + name: enum_name + mod: p.mod + parent: 'int' + is_enum: true + enum_vals: fields + }) + p.check(.rcbr) p.fgenln('\n') } // check_name checks for a name token and returns its literal fn (p mut Parser) check_name() string { name := p.lit - p.check(NAME) + p.check(.name) return name } fn (p mut Parser) check_string() string { s := p.lit - p.check(STRING) + p.check(.strtoken) return s } fn (p &Parser) strtok() string { - if p.tok == NAME { + if p.tok == .name { return p.lit } - if p.tok == STRING { + if p.tok == .strtoken { return '"$p.lit"' } res := p.tok.str() @@ -665,12 +665,12 @@ fn (p mut Parser) check(expected Token) { print_backtrace() p.error(s) } - if expected == RCBR { + if expected == .rcbr { p.fmt_dec() } p.fgen(p.strtok()) // vfmt: increase indentation on `{` unless it's `{}` - if expected == LCBR && p.scanner.text[p.scanner.pos + 1] != `}` { + if expected == .lcbr && p.scanner.text[p.scanner.pos + 1] != `}` { p.fgenln('') p.fmt_inc() } @@ -711,7 +711,7 @@ fn (p mut Parser) error(s string) { } fn (p &Parser) first_run() bool { - return p.run == RUN_DECLS + return p.run == .decl } // TODO return Type instead of string? @@ -721,11 +721,11 @@ fn (p mut Parser) get_type() string { mut nr_muls := 0 mut typ := '' // fn type - if p.tok == FUNC { + if p.tok == .func { if debug { - println('\nget_type() GOT FN TYP line=$p.scanner.line_nr') + println('\nget_type() .key_goT FN TYP line=$p.scanner.line_nr') } - mut f := Fn{name: '_', pkg: p.pkg} + mut f := Fn{name: '_', pkg: p.mod} p.next() line_nr := p.scanner.line_nr p.fn_args(mut f) @@ -743,7 +743,7 @@ fn (p mut Parser) get_type() string { // Register anon fn type fn_typ := Type { name: f.typ_str()// 'fn (int, int) string' - pkg: p.pkg + mod: p.mod func: f } p.table.register_type2(fn_typ) @@ -752,58 +752,58 @@ fn (p mut Parser) get_type() string { // arrays ([]int) mut is_arr := false mut is_arr2 := false// [][]int TODO remove this and allow unlimited levels of arrays - is_question := p.tok == QUESTION + is_question := p.tok == .question if is_question { - p.check(QUESTION) + p.check(.question) } - if p.tok == LSBR { - p.check(LSBR) + if p.tok == .lsbr { + p.check(.lsbr) // [10]int - if p.tok == INT { + if p.tok == .integer { typ = '[$p.lit]' p.next() } else { is_arr = true } - p.check(RSBR) + p.check(.rsbr) // [10][3]int - if p.tok == LSBR { + if p.tok == .lsbr { p.next() - if p.tok == INT { + if p.tok == .integer { typ += '[$p.lit]' - p.check(INT) + p.check(.integer) } else { is_arr2 = true } - p.check(RSBR) + p.check(.rsbr) } } // map[string]int - if !p.builtin_pkg && p.tok == NAME && p.lit == 'map' { + if !p.builtin_pkg && p.tok == .name && p.lit == 'map' { p.next() - p.check(LSBR) + p.check(.lsbr) key_type := p.check_name() if key_type != 'string' { p.error('maps only support string keys for now') } - p.check(RSBR) + p.check(.rsbr) val_type := p.check_name() typ= 'map_$val_type' p.register_map(typ) return typ } // - for p.tok == MUL { + for p.tok == .mul { mul = true nr_muls++ - p.check(MUL) + p.check(.mul) } - if p.tok == AMP { + if p.tok == .amp { mul = true nr_muls++ - p.check(AMP) + p.check(.amp) } typ += p.lit if !p.is_struct_init { @@ -811,23 +811,23 @@ fn (p mut Parser) get_type() string { p.fgen(p.lit) } // C.Struct import - if p.lit == 'C' && p.peek() == DOT { + if p.lit == 'C' && p.peek() == .dot { p.next() - p.check(DOT) + p.check(.dot) typ = p.lit } else { // Package specified? (e.g. gx.Image) - if p.peek() == DOT { + if p.peek() == .dot { p.next() - p.check(DOT) + p.check(.dot) typ += '__$p.lit' } mut t := p.table.find_type(typ) // "typ" not found? try "pkg__typ" if t.name == '' && !p.builtin_pkg { // && !p.first_run() { - if !typ.contains('array_') && p.pkg != 'main' && !typ.contains('__') && + if !typ.contains('array_') && p.mod != 'main' && !typ.contains('__') && !typ.starts_with('[') { typ = p.prepend_pkg(typ) } @@ -860,10 +860,10 @@ fn (p mut Parser) get_type() string { p.register_array(typ) } p.next() - if p.tok == QUESTION || is_question { + if p.tok == .question || is_question { typ = 'Option_$typ' p.table.register_type_with_parent(typ, 'Option') - if p.tok == QUESTION { + if p.tok == .question { p.next() } } @@ -872,7 +872,7 @@ fn (p mut Parser) get_type() string { return 'byte*' } if typ == 'voidptr' { - //if !p.builtin_pkg && p.pkg != 'os' && p.pkg != 'gx' && p.pkg != 'gg' && !p.pref.translated { + //if !p.builtin_pkg && p.mod != 'os' && p.mod != 'gx' && p.mod != 'gg' && !p.pref.translated { //p.error('voidptr can only be used in unsafe code') //} return 'void*' @@ -884,11 +884,11 @@ fn (p mut Parser) get_type() string { } fn (p &Parser) print_tok() { - if p.tok == NAME { + if p.tok == .name { println(p.lit) return } - if p.tok == STRING { + if p.tok == .strtoken { println('"$p.lit"') return } @@ -915,7 +915,8 @@ fn (p mut Parser) statements_no_curly_end() string { } mut i := 0 mut last_st_typ := '' - for p.tok != RCBR && p.tok != EOF && p.tok != CASE && p.tok != DEFAULT { + for p.tok != .rcbr && p.tok != .eof && p.tok != .key_case && + p.tok != .key_default { // println(p.tok.str()) // p.print_tok() last_st_typ = p.statement(true) @@ -930,12 +931,12 @@ fn (p mut Parser) statements_no_curly_end() string { p.error('more than 50 000 statements in function `$p.cur_fn.name`') } } - if p.tok != CASE && p.tok != DEFAULT { + if p.tok != .key_case && p.tok != .key_default { // p.next() - p.check(RCBR) + p.check(.rcbr) } else { - // p.check(RCBR) + // p.check(.rcbr) } //p.fmt_dec() // println('close scope line=$p.scanner.line_nr') @@ -969,22 +970,22 @@ fn (p mut Parser) statement(add_semi bool) string { tok := p.tok mut q := '' switch tok { - case NAME: + case .name: next := p.peek() if p.pref.is_verbose { println(next.str()) } // goto_label: - if p.peek() == COLON { + if p.peek() == .colon { p.fmt_dec() label := p.check_name() p.fmt_inc() p.genln(label + ':') - p.check(COLON) + p.check(.colon) return '' } // `a := 777` - else if p.peek() == DECL_ASSIGN { + else if p.peek() == .decl_assign { p.log('var decl') p.var_decl() } @@ -995,47 +996,47 @@ fn (p mut Parser) statement(add_semi bool) string { // `a + 3`, `a(7)` or maybe just `a` q = p.bool_expression() } - case GOTO: - p.check(GOTO) + case Token.key_goto: + p.check(.key_goto) p.fgen(' ') label := p.check_name() p.genln('goto $label;') return '' - case HASH: + case Token.hash: p.chash() return '' - case DOLLAR: + case Token.dollar: p.comp_time() - case IF: + case Token.key_if: p.if_st(false) - case FOR: + case Token.key_for: p.for_st() - case SWITCH, MATCH: + case Token.key_switch: p.switch_statement() - case MUT, STATIC: + case Token.key_mut, Token.key_static: p.var_decl() - case RETURN: + case Token.key_return: p.return_st() - case LCBR:// {} block + case Token.lcbr:// {} block p.next() p.genln('{') p.statements() return '' - case CONTINUE: + case Token.key_continue: if p.for_expr_cnt == 0 { p.error('`continue` statement outside `for`') } p.genln('continue') - p.check(CONTINUE) - case BREAK: + p.check(.key_continue) + case Token.key_break: if p.for_expr_cnt == 0 { p.error('`break` statement outside `for`') } p.genln('break') - p.check(BREAK) - case GO: + p.check(.key_break) + case Token.key_go: p.go_statement() - case ASSERT: + case Token.key_assert: p.assert_statement() default: // An expression as a statement @@ -1048,7 +1049,7 @@ fn (p mut Parser) statement(add_semi bool) string { return typ } // ? : uses , as statement separators - if p.inside_if_expr && p.tok != RCBR { + if p.inside_if_expr && p.tok != .rcbr { p.gen(', ') } if add_semi && !p.inside_if_expr { @@ -1064,15 +1065,15 @@ fn (p mut Parser) assign_statement(v Var, ph int, is_map bool) { p.log('assign_statement() name=$v.name tok=') tok := p.tok if !v.is_mut && !v.is_arg && !p.pref.translated && !v.is_global{ - p.error('`$v.name` is immutable') + p.error('`$v.name` is imkey_mut') } is_str := v.typ == 'string' switch tok { - case ASSIGN: + case Token.assign: if !is_map { p.gen(' = ') } - case PLUS_ASSIGN: + case Token.plus_assign: if is_str { p.gen('= string_add($v.name, ')// TODO can't do `foo.bar += '!'` } @@ -1099,7 +1100,7 @@ fn (p mut Parser) assign_statement(v Var, ph int, is_map bool) { p.scanner.line_nr-- p.error('cannot use type `$expr_type` as type `$p.assigned_type` in assignment') } - if is_str && tok == PLUS_ASSIGN { + if is_str && tok == .plus_assign { p.gen(')') } // p.assigned_var = '' @@ -1110,14 +1111,14 @@ fn (p mut Parser) assign_statement(v Var, ph int, is_map bool) { } fn (p mut Parser) var_decl() { - is_mut := p.tok == MUT || p.prev_tok == FOR - is_static := p.tok == STATIC - if p.tok == MUT { - p.check(MUT) + is_mut := p.tok == .key_mut || p.prev_tok == .key_for + is_static := p.tok == .key_static + if p.tok == .key_mut { + p.check(.key_mut) p.fspace() } - if p.tok == STATIC { - p.check(STATIC) + if p.tok == .key_static { + p.check(.key_static) p.fspace() } // println('var decl tok=${p.strtok()} ismut=$is_mut') @@ -1128,13 +1129,13 @@ fn (p mut Parser) var_decl() { v := p.cur_fn.find_var(name) p.error('redefinition of `$name`') } - p.check_space(DECL_ASSIGN) // := + p.check_space(.decl_assign) // := // Generate expression to tmp because we need its type first - // [TYP NAME =] bool_expression() + // [TYP .name =] bool_expression() pos := p.cgen.add_placeholder() mut typ := p.bool_expression() // Option check ? or { - or_else := p.tok == OR_ELSE + or_else := p.tok == .key_orelse tmp := p.get_tmp() if or_else { // Option_User tmp = get_user(1); @@ -1145,7 +1146,7 @@ fn (p mut Parser) var_decl() { p.gen(';') typ = typ.replace('Option_', '') p.next() - p.check(LCBR) + p.check(.lcbr) p.genln('if (!$tmp .ok) {') p.register_var(Var { name: 'err' @@ -1156,7 +1157,7 @@ fn (p mut Parser) var_decl() { p.genln('string err = $tmp . error;') p.statements() p.genln('$typ $name = *($typ*) $tmp . data;') - if !p.returns && p.prev_tok2 != CONTINUE && p.prev_tok2 != BREAK { + if !p.returns && p.prev_tok2 != .key_continue && p.prev_tok2 != .key_break { p.error('`or` block must return/continue/break/panic') } } @@ -1179,7 +1180,7 @@ fn (p mut Parser) var_decl() { fn (p mut Parser) bool_expression() string { tok := p.tok typ := p.bterm() - for p.tok == AND || p.tok == OR { + for p.tok == .and || p.tok == .ortok { p.gen(' ${p.tok.str()} ') p.check_space(p.tok) p.check_types(p.bterm(), typ) @@ -1196,11 +1197,11 @@ 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 + p.expected_type = typ is_str := typ=='string' tok := p.tok - // if tok in [ EQ, GT, LT, LE, GE, NE] { - if tok == EQ || tok == GT || tok == LT || tok == LE || tok == GE || tok == NE { + // if tok in [ .eq, .gt, .lt, .le, .ge, .ne] { + if tok == .eq || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne { p.fgen(' ${p.tok.str()} ') if is_str { p.gen(',') @@ -1214,12 +1215,12 @@ fn (p mut Parser) bterm() string { if is_str { p.gen(')') switch tok { - case EQ: p.cgen.set_placeholder(ph, 'string_eq(') - case NE: p.cgen.set_placeholder(ph, 'string_ne(') - case LE: p.cgen.set_placeholder(ph, 'string_le(') - case GE: p.cgen.set_placeholder(ph, 'string_ge(') - case GT: p.cgen.set_placeholder(ph, 'string_gt(') - case LT: p.cgen.set_placeholder(ph, 'string_lt(') + case Token.eq: p.cgen.set_placeholder(ph, 'string_eq(') + case Token.ne: p.cgen.set_placeholder(ph, 'string_ne(') + case Token.le: p.cgen.set_placeholder(ph, 'string_le(') + case Token.ge: p.cgen.set_placeholder(ph, 'string_ge(') + case Token.gt: p.cgen.set_placeholder(ph, 'string_gt(') + case Token.lt: p.cgen.set_placeholder(ph, 'string_lt(') } } } @@ -1238,8 +1239,8 @@ fn (p mut Parser) name_expr() string { ph := p.cgen.add_placeholder() // amp - ptr := p.tok == AMP - deref := p.tok == MUL + ptr := p.tok == .amp + deref := p.tok == .mul if ptr || deref { p.next() } @@ -1252,28 +1253,32 @@ fn (p mut Parser) name_expr() string { p.fgen(name) // known_type := p.table.known_type(name) orig_name := name - is_c := name == 'C' && p.peek() == DOT + is_c := name == 'C' && p.peek() == .dot mut is_c_struct_init := is_c && ptr// a := &C.mycstruct{} if is_c { p.next() - p.check(DOT) + p.check(.dot) name = p.lit p.fgen(name) // Currently struct init is set to true only we have `&C.Foo{}`, handle `C.Foo{}`: - if !is_c_struct_init && p.peek() == LCBR { + if !is_c_struct_init && p.peek() == .lcbr { is_c_struct_init = true } } // enum value? (`color == .green`) - if p.tok == DOT { + 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) + T := p.find_type(p.expected_type) if T.is_enum { - p.check(DOT) + p.check(.dot) val := p.check_name() - p.gen(p.pkg + '__' + p.left_type + '_' + val) + // Make sure this enum value exists + if !T.has_enum_val(val) { + p.error('enum `$T.name` does not have value `$val`') + } + p.gen(p.mod + '__' + p.expected_type + '_' + val) } - return p.left_type + return p.expected_type } // ////////////////////////// // module ? @@ -1282,7 +1287,7 @@ fn (p mut Parser) name_expr() string { // println('"$name" is a known pkg') pkg := name p.next() - p.check(DOT) + p.check(.dot) name = p.lit p.fgen(name) name = prepend_pkg(pkg, name) @@ -1316,11 +1321,11 @@ fn (p mut Parser) name_expr() string { } return typ } - // if known_type || is_c_struct_init || (p.first_run() && p.peek() == LCBR) { + // if known_type || is_c_struct_init || (p.first_run() && p.peek() == .lcbr) { // known type? int(4.5) or Color.green (enum) if p.table.known_type(name) { // float(5), byte(0), (*int)(ptr) etc - if !is_c && ( p.peek() == LPAR || (deref && p.peek() == RPAR) ) { + if !is_c && ( p.peek() == .lpar || (deref && p.peek() == .rpar) ) { if deref { name += '*' } @@ -1330,26 +1335,26 @@ fn (p mut Parser) name_expr() string { p.gen('(') mut typ := p.cast(name) p.gen(')') - for p.tok == DOT { + for p.tok == .dot { typ = p.dot(typ, ph) } return typ } // Color.green - else if p.peek() == DOT { + else if p.peek() == .dot { enum_type := p.table.find_type(name) if !enum_type.is_enum { p.error('`$name` is not an enum') } p.next() - p.check(DOT) + p.check(.dot) val := p.lit // println('enum val $val') - p.gen(p.pkg + '__' + enum_type.name + '_' + val)// `color = main__Color_green` + p.gen(p.mod + '__' + enum_type.name + '_' + val)// `color = main__Color_green` p.next() return enum_type.name } - else if p.peek() == LCBR { + else if p.peek() == .lcbr { // go back to name start (pkg.name) p.scanner.pos = hack_pos p.tok = hack_tok @@ -1416,7 +1421,7 @@ fn (p mut Parser) name_expr() string { } // no () after func, so func is an argument, just gen its name // TODO verify this and handle errors - if p.peek() != LPAR { + if p.peek() != .lpar { p.gen(p.table.cgen_name(f)) p.next() return 'void*' @@ -1428,9 +1433,9 @@ fn (p mut Parser) name_expr() string { p.log('calling function') p.fn_call(f, 0, '', '') // dot after a function call: `get_user().age` - if p.tok == DOT { + if p.tok == .dot { mut typ := '' - for p.tok == DOT { + for p.tok == .dot { // println('dot #$dc') typ = p.dot(f.typ, ph) } @@ -1462,27 +1467,27 @@ fn (p mut Parser) var_expr(v Var) string { } // users[0] before dot so that we can have // users[0].name - if p.tok == LSBR { + if p.tok == .lsbr { typ = p.index_expr(typ, fn_ph) // ////println('QQQQ KEK $typ') } // a.b.c().d chain // mut dc := 0 - for p.tok == DOT { + for p.tok ==.dot { // println('dot #$dc') typ = p.dot(typ, fn_ph) p.log('typ after dot=$typ') // print('tok after dot()') // p.print_tok() // dc++ - if p.tok == LSBR { + if p.tok == .lsbr { // typ = p.index_expr(typ, fn_ph, v) } } // a++ and a-- - if p.tok == INC || p.tok == DEC { + if p.tok == .inc || p.tok == .dec { if !v.is_mut && !v.is_arg && !p.pref.translated { - p.error('`$v.name` is immutable') + p.error('`$v.name` is imkey_mut') } if typ != 'int' { if !p.pref.translated && !is_number_type(typ) { @@ -1502,8 +1507,8 @@ fn (p mut Parser) var_expr(v Var) string { } typ = p.index_expr(typ, fn_ph) // TODO hack to allow `foo.bar[0] = 2` - if p.tok == DOT { - for p.tok == DOT { + if p.tok == .dot { + for p.tok == .dot { typ = p.dot(typ, fn_ph) } typ = p.index_expr(typ, fn_ph) @@ -1518,7 +1523,7 @@ fn (p &Parser) fileis(s string) bool { // user.name => `str_typ` is `User` // user.company.name => `str_typ` is `Company` fn (p mut Parser) dot(str_typ string, method_ph int) string { - p.check(DOT) + p.check(.dot) field_name := p.lit p.fgen(field_name) p.log('dot() field_name=$field_name typ=$str_typ') @@ -1558,50 +1563,43 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string { field := p.table.find_field(typ, field_name) // Is the next token `=`, `+=` etc? (Are we modifying the field?) next := p.peek() - modifying := next.is_assign() || next == INC || next == DEC + modifying := next.is_assign() || next == .inc || next == .dec is_vi := p.fileis('vi') if !p.builtin_pkg && !p.pref.translated && modifying && !field.is_mut && !is_vi { - p.error('cannot modify immutable field `$field_name` (type `$typ.name`)') + p.error('cannot modify imkey_mut field `$field_name` (type `$typ.name`)') } - if !p.builtin_pkg && p.pkg != typ.pkg { + if !p.builtin_pkg && p.mod != typ.mod { } - // if p.pref.is_play && field.access_mod == PRIVATE && !p.builtin_pkg && p.pkg != typ.pkg { + // if p.pref.is_play && field.access_mod ==.private && !p.builtin_pkg && p.mod != typ.mod { // Don't allow `arr.data` - if field.access_mod == PRIVATE && !p.builtin_pkg && !p.pref.translated && p.pkg != typ.pkg { + if field.access_mod == .private && !p.builtin_pkg && !p.pref.translated && p.mod != typ.mod { // println('$typ.name :: $field.name ') // println(field.access_mod) p.error('cannot refer to unexported field `$field_name` (type `$typ.name`)') } - // if field.access_mod == PUBLIC && p.peek() == ASSIGN && !p.builtin_pkg && p.pkg != typ.pkg { + // if field.access_mod ==.public && p.peek() == .assign && !p.builtin_pkg && p.mod != typ.mod { // Don't allow `str.len = 0` - if field.access_mod == PUBLIC && !p.builtin_pkg && p.pkg != typ.pkg { - // if field.name == 'age' { - // println('HOHOH') - // println(next.str()) - // } + if field.access_mod == .public && !p.builtin_pkg && p.mod != typ.mod { if !field.is_mut && !p.pref.translated && modifying { - p.error('cannot modify public immutable field `$field_name` (type `$typ.name`)') + p.error('cannot modify public imkey_mut field `$field_name` (type `$typ.name`)') } } - p.gen('${dot}${field_name}') - // p.gen(dot + field_name) + p.gen(dot + field_name) p.next() return field.typ } // method - // mut method := typ.find_method(field_name) mut method := p.table.find_method(typ, field_name) p.fn_call(method, method_ph, '', str_typ) - // Methods returning "array" (like slice_fast) should return "array_string" + // Methods returning `array` should return `array_string` if method.typ == 'array' && typ.name.starts_with('array_') { return typ.name } - // Array Methods returning `voidptr` (like `last()`) should return element type + // Array methods returning `voidptr` (like `last()`) should return element type if method.typ == 'void*' && typ.name.starts_with('array_') { - // 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) //} @@ -1619,14 +1617,14 @@ fn (p mut Parser) index_expr(typ string, fn_ph int) string { is_arr0 := typ.starts_with('array_') is_arr := is_arr0 || typ == 'array' is_ptr := typ == 'byte*' || typ == 'byteptr' || typ.contains('*') - is_indexer := p.tok == LSBR + is_indexer := p.tok == .lsbr mut close_bracket := false if is_indexer { is_fixed_arr := typ[0] == `[` if !is_str && !is_arr && !is_map && !is_ptr && !is_fixed_arr { p.error('Cant [] non-array/string/map. Got type "$typ"') } - p.check(LSBR) + p.check(.lsbr) // Get element type (set `typ` to it) if is_str { typ = 'byte' @@ -1682,7 +1680,7 @@ fn (p mut Parser) index_expr(typ string, fn_ph int) string { typ = 'void*' } // No bounds check in translated from C code - if p.pref.translated { + if p.pref.translated && !p.builtin_pkg{ // Cast void* to typ*: add (typ*) to the beginning of the assignment : // ((int*)a.data = ... p.cgen.set_placeholder(fn_ph, '(($typ*)(') @@ -1713,25 +1711,24 @@ fn (p mut Parser) index_expr(typ string, fn_ph int) string { else { p.expression() } - p.check(RSBR) + p.check(.rsbr) // if (is_str && p.builtin_pkg) || is_ptr || is_fixed_arr && ! (is_ptr && is_arr) { if close_bracket { p.gen(']/*r$typ $v.is_mut*/') } } // TODO if p.tok in ... - // if p.tok in [ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN] - if p.tok == ASSIGN || p.tok == PLUS_ASSIGN || p.tok == MINUS_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 == LEFT_SHIFT_ASSIGN { + // if p.tok in [.assign, .plus_assign, .minus_assign] + if p.tok == .assign || p.tok == .plus_assign || p.tok == .minus_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 == .righ_shift_assign || + p.tok == .left_shift_assign { if is_indexer && is_str && !p.builtin_pkg { - p.error('strings are immutable') + p.error('strings are imkey_mut') } - // println('111 "$p.cgen.cur_line"') assign_pos := p.cgen.cur_line.len p.assigned_type = typ - p.left_type = typ + p.expected_type = typ p.assign_statement(v, fn_ph, is_indexer && (is_map || is_arr)) // m[key] = val if is_indexer && (is_map || is_arr) { @@ -1779,7 +1776,7 @@ fn (p mut Parser) index_expr(typ string, fn_ph int) string { p.cgen.insert_before('$typ $tmp = $def; bool $tmp_ok = map_get($index_expr, & $tmp);') } else if is_arr { - if p.pref.translated { + if p.pref.translated && !p.builtin_pkg { p.gen('$index_expr ]') } else { @@ -1812,19 +1809,19 @@ fn (p mut Parser) expression() string { mut typ := p.term() is_str := typ=='string' // a << b ==> array2_push(&a, b) - if p.tok == LEFT_SHIFT { + if p.tok == .left_shift { if typ.contains('array_') { // Can't pass integer literal, because push requires a void* // a << 7 => int tmp = 7; array_push(&a, &tmp); // _PUSH(&a, expression(), tmp, string) tmp := p.get_tmp() tmp_typ := typ.right(6)// skip "array_" - p.check_space(LEFT_SHIFT) + p.check_space(.left_shift) // Get the value we are pushing p.gen(', (') - // Immutable? Can we push? + // Imkey_mut? Can we push? if !p.expr_var.is_mut && !p.pref.translated { - p.error('`$p.expr_var.name` is immutable (can\'t <<)') + p.error('`$p.expr_var.name` is imkey_mut (can\'t <<)') } expr_type := p.expression() // Two arrays of the same type? @@ -1836,7 +1833,7 @@ fn (p mut Parser) expression() string { p.check_types(expr_type, tmp_typ) // Pass tmp var info to the _PUSH macro // Prepend tmp initialisation and push call - // Don't dereference if it's already a mutable array argument (`fn foo(mut []int)`) + // Don't dereference if it's already a key_mut array argument (`fn foo(mut []int)`) push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'} p.cgen.set_placeholder(ph, push_call) p.gen('), $tmp, $tmp_typ)') @@ -1851,9 +1848,9 @@ fn (p mut Parser) expression() string { } } // a in [1,2,3] - if p.tok == IN { + if p.tok == .key_in { p.fgen(' ') - p.check(IN) + p.check(.key_in) p.fgen(' ') p.gen(', ') arr_typ := p.expression() @@ -1869,24 +1866,24 @@ fn (p mut Parser) expression() string { p.gen(')') return 'bool' } - if p.tok == RIGHT_SHIFT { + if p.tok == .righ_shift { p.next() p.gen(' >> ') p.check_types(p.expression(), typ) return 'int' } - if p.tok == DOT { - for p.tok == DOT { + if p.tok == .dot { + for p.tok == .dot { typ = p.dot(typ, ph) } } // + - | - for p.tok == PLUS || p.tok == MINUS || p.tok == PIPE || p.tok == AMP || p.tok == XOR { - // for p.tok in [PLUS, MINUS, PIPE, AMP, XOR] { + for p.tok == .plus || p.tok == .minus || p.tok == .pipe || p.tok == .amp || p.tok == .xor { + // for p.tok in [.plus, .minus, .pipe, .amp, .xor] { tok_op := p.tok is_num := typ == 'void*' || typ == 'byte*' || is_number_type(typ) p.check_space(p.tok) - if is_str && tok_op == PLUS { + if is_str && tok_op == .plus { p.cgen.set_placeholder(ph, 'string_add(') p.gen(',') } @@ -1904,13 +1901,13 @@ fn (p mut Parser) expression() string { } } p.check_types(p.term(), typ) - if is_str && tok_op == PLUS { + if is_str && tok_op == .plus { p.gen(')') } // Make sure operators are used with correct types if !p.pref.translated && !is_str && !is_num { T := p.table.find_type(typ) - if tok_op == PLUS { + if tok_op == .plus { if T.has_method('+') { p.cgen.set_placeholder(ph, typ + '_plus(') p.gen(')') @@ -1919,7 +1916,7 @@ fn (p mut Parser) expression() string { p.error('operator + not defined on `$typ`') } } - else if tok_op == MINUS { + else if tok_op == .minus { if T.has_method('-') { p.cgen.set_placeholder(ph, '${typ}_minus(') p.gen(')') @@ -1943,22 +1940,22 @@ fn (p mut Parser) term() string { println('2: $line_nr') } // `*` on a newline? Can't be multiplication, only dereference - if p.tok == MUL && line_nr != p.scanner.line_nr { + if p.tok == .mul && line_nr != p.scanner.line_nr { return typ } - for p.tok == MUL || p.tok == DIV || p.tok == MOD { + for p.tok == .mul || p.tok == .div || p.tok == .mod { tok := p.tok - is_div := tok == DIV - is_mod := tok == MOD - // is_mul := tok == MOD + is_div := tok == .div + is_mod := tok == .mod + // is_mul := tok == .mod p.next() p.gen(tok.str())// + ' /*op2*/ ') p.fgen(' ' + tok.str() + ' ') - if is_div && p.tok == INT && p.lit == '0' { + if is_div && p.tok == .integer && p.lit == '0' { p.error('division by zero') } if is_mod && (is_float_type(typ) || !is_number_type(typ)) { - p.error('operator MOD requires integer types') + p.error('operator .mod requires integer types') } p.check_types(p.unary(), typ) } @@ -1969,14 +1966,14 @@ fn (p mut Parser) unary() string { mut typ := '' tok := p.tok switch tok { - case NOT: + case Token.not: p.gen('!') - p.check(NOT) + p.check(.not) typ = 'bool' p.bool_expression() - case BIT_NOT: + case Token.bit_not: p.gen('~') - p.check(BIT_NOT) + p.check(.bit_not) typ = p.bool_expression() default: typ = p.factor() @@ -1988,7 +1985,7 @@ fn (p mut Parser) factor() string { mut typ := '' tok := p.tok switch tok { - case INT: + case .integer: typ = 'int' // Check if float (`1.0`, `1e+3`) but not if is hexa if (p.lit.contains('.') || p.lit.contains('e')) && @@ -1998,43 +1995,43 @@ fn (p mut Parser) factor() string { } p.gen(p.lit) p.fgen(p.lit) - case MINUS: + case Token.minus: p.gen('-') p.fgen('-') p.next() return p.factor() // Variable - case SIZEOF: + case Token.key_sizeof: p.gen('sizeof(') p.fgen('sizeof(') p.next() - p.check(LPAR) + p.check(.lpar) mut sizeof_typ := p.get_type() if sizeof_typ.ends_with('*') { // Move * from the end to the beginning, as C requires sizeof_typ = '*' + sizeof_typ.left(sizeof_typ.len - 1) } - p.check(RPAR) + p.check(.rpar) p.gen('$sizeof_typ)') p.fgen('$sizeof_typ)') return 'int' - case AMP: + case Token.amp: return p.name_expr() - case DOT: + case Token.dot: return p.name_expr()// `.green` (enum) - case MUL: + case Token.mul: return p.name_expr() - case NAME: + case Token.name: // map[string]int - if p.lit == 'map' && p.peek() == LSBR { + if p.lit == 'map' && p.peek() == .lsbr { return p.map_init() } - if p.lit == 'json' && p.peek() == DOT { + if p.lit == 'json' && p.peek() == .dot { return p.js_decode() } typ = p.name_expr() return typ - case DEFAULT: + case Token.key_default: p.next() p.next() name := p.check_name() @@ -2044,50 +2041,50 @@ fn (p mut Parser) factor() string { p.gen('default(T)') p.next() return 'T' - case LPAR: + case Token.lpar: p.gen('(/*lpar*/') - p.check(LPAR) + p.check(.lpar) typ = p.bool_expression() // Hack. If this `)` referes to a ptr cast `(*int__)__`, it was already checked // TODO: fix parser so that it doesn't think it's a par expression when it sees `(` in // __(__*int)( if !p.ptr_cast { - p.check(RPAR) + p.check(.rpar) } p.ptr_cast = false p.gen(')') return typ - case CHAR: + case Token.chartoken: p.char_expr() typ = 'byte' return typ - case STRING: + case Token.strtoken: p.string_expr() typ = 'string' return typ - case FALSE: + case Token.key_false: typ = 'bool' p.gen('0') p.fgen('false') - case TRUE: + case Token.key_true: typ = 'bool' p.gen('1') p.fgen('true') - case LSBR: + case Token.lsbr: // `[1,2,3]` or `[]` or `[20]byte` // TODO have to return because arrayInit does next() // everything should do next() return p.array_init() - case LCBR: + case Token.lcbr: // { user | name :'new name' } return p.assoc() - case IF: + case Token.key_if: typ = p.if_st(true) return typ default: next := p.peek() println('PREV=${p.prev_tok.str()}') - println('NEXT=${next.str()}') + println('.neXT=${next.str()}') p.error('unexpected token: `${p.tok.str()}`') } p.next()// TODO everything should next() @@ -2103,18 +2100,18 @@ fn (p mut Parser) assoc() string { p.error('unknown variable `$name`') } var := p.cur_fn.find_var(name) - p.check(PIPE) + p.check(.pipe) p.gen('($var.typ){') mut fields := []string// track the fields user is setting, the rest will be copied from the old object - for p.tok != RCBR { + for p.tok != .rcbr { field := p.check_name() fields << field p.gen('.$field = ') - p.check(COLON) + p.check(.colon) p.bool_expression() p.gen(',') - if p.tok != RCBR { - p.check(COMMA) + if p.tok != .rcbr { + p.check(.comma) } } // Copy the rest of the fields @@ -2126,7 +2123,7 @@ fn (p mut Parser) assoc() string { } p.gen('.$f = $name . $f,') } - p.check(RCBR) + p.check(.rcbr) p.gen('}') return var.typ } @@ -2170,15 +2167,15 @@ fn format_str(str string) string { } fn (p mut Parser) string_expr() { - // println('STRING EXPR') + // println('.strtoken EXPR') str := p.lit p.fgen('\'$str\'') // No ${}, just return simple string - if p.peek() != DOLLAR { + if p.peek() != .dollar { // println('before format: "$str"') f := format_str(str) // println('after format: "$str"') - if p.calling_c || p.pref.translated { + if p.calling_c || (p.pref.translated && p.mod == 'main') { p.gen('"$f"') } else { @@ -2190,14 +2187,14 @@ fn (p mut Parser) string_expr() { // tmp := p.get_tmp() mut args := '"' mut format := '"' - for p.tok == STRING { + for p.tok == .strtoken { // Add the string between %d's format += format_str(p.lit) p.next()// skip $ - if p.tok != DOLLAR { + if p.tok != .dollar { continue } - // Handle DOLLAR + // Handle .dollar p.next() // Get bool expr inside a temp var p.cgen.start_tmp() @@ -2214,11 +2211,11 @@ fn (p mut Parser) string_expr() { args += '.len, ${val}.s.str' } // Custom format? ${t.hour:02d} - custom := p.tok == COLON + custom := p.tok == .colon if custom { format += '%' p.next() - if p.tok == DOT { + if p.tok == .dot { format += '.' p.next() } @@ -2242,7 +2239,7 @@ fn (p mut Parser) string_expr() { } // println: don't allocate a new string, just print it. cur_line := p.cgen.cur_line.trim_space() - if cur_line.contains('println (') && p.tok != PLUS && + if cur_line.contains('println (') && p.tok != .plus && !cur_line.contains('string_add') && !cur_line.contains('eprintln') { p.cgen.cur_line = cur_line.replace('println (', 'printf(') p.gen('$format\\n$args') @@ -2250,7 +2247,7 @@ fn (p mut Parser) string_expr() { } // '$age'! means the user wants this to be a tmp string (uses global buffer, no allocation, // won't be used again) - if p.tok == NOT { + if p.tok == .not { p.next() p.gen('_STR_TMP($format$args)') } @@ -2263,43 +2260,43 @@ fn (p mut Parser) string_expr() { // m := map[string]int{} fn (p mut Parser) map_init() string { p.next() - p.check(LSBR) + p.check(.lsbr) key_type := p.check_name() if key_type != 'string' { p.error('only string key maps allowed for now') } - p.check(RSBR) + p.check(.rsbr) val_type := p.check_name() if !p.table.known_type(val_type) { p.error('map init unknown type "$val_type"') } p.gen('new_map(1, sizeof($val_type))') - p.check(LCBR) - p.check(RCBR) + p.check(.lcbr) + p.check(.rcbr) return 'map_$val_type' } // [1,2,3] fn (p mut Parser) array_init() string { - p.check(LSBR) - is_integer := p.tok == INT + p.check(.lsbr) + is_integer := p.tok == .integer lit := p.lit mut typ := '' new_arr_ph := p.cgen.add_placeholder() mut i := 0 pos := p.cgen.cur_line.len// remember cur line to fetch first number in cgen for [0; 10] - for p.tok != RSBR { + for p.tok != .rsbr { val_typ := p.bool_expression() // Get type of the first expression if i == 0 { typ = val_typ // fixed width array initialization? (`arr := [20]byte`) - if is_integer && p.tok == RSBR && p.peek() == NAME { + if is_integer && p.tok == .rsbr && p.peek() == .name { nextc := p.scanner.text[p.scanner.pos + 1] // TODO whitespace hack // Make sure there's no space in `[10]byte` if !nextc.is_space() { - p.check(RSBR) + p.check(.rsbr) name := p.check_name() if p.table.known_type(name) { p.cgen.cur_line = '' @@ -2317,15 +2314,15 @@ fn (p mut Parser) array_init() string { p.error('bad array element type `$val_typ` instead of `$typ`') } } - if p.tok != RSBR && p.tok != SEMICOLON { + if p.tok != .rsbr && p.tok != .semicolon { p.gen(', ') - p.check(COMMA) + p.check(.comma) p.fspace() } i++ // Repeat (a = [0;5] ) - if i == 1 && p.tok == SEMICOLON { - p.check_space(SEMICOLON) + if i == 1 && p.tok == .semicolon { + p.check_space(.semicolon) val := p.cgen.cur_line.right(pos) // p.cgen.cur_line = '' p.cgen.cur_line = p.cgen.cur_line.left(pos) @@ -2340,27 +2337,27 @@ fn (p mut Parser) array_init() string { } p.check_types(p.bool_expression(), 'int') p.gen(', sizeof($typ) )') - p.check(RSBR) + p.check(.rsbr) return 'array_$typ' } } - p.check(RSBR) + p.check(.rsbr) // type after `]`? (e.g. "[]string") - if p.tok != NAME && i == 0 { + if p.tok != .name && i == 0 { p.error('specify array type: `[]typ` instead of `[]`') } - if p.tok == NAME && i == 0 { + if p.tok == .name && i == 0 { // vals.len == 0 { typ = p.get_type() - // println('GOT TYP after [] $typ') + // println('.key_goT TYP after [] $typ') } // ! after array => no malloc and no copy - no_alloc := p.tok == NOT + no_alloc := p.tok == .not if no_alloc { p.next() } // [1,2,3]!! => [3]int{1,2,3} - is_fixed_size := p.tok == NOT + is_fixed_size := p.tok == .not if is_fixed_size { p.next() p.gen(' }') @@ -2427,7 +2424,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.cgen.lines[p.cgen.lines.len-1] = '' p.cgen.lines[p.cgen.lines.len-2] = '' } - p.check(LCBR) + p.check(.lcbr) // tmp := p.get_tmp() if !ptr { if p.is_c_struct_init { @@ -2441,10 +2438,10 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { else { // TODO tmp hack for 0 pointers init // &User{!} ==> 0 - if p.tok == NOT { + if p.tok == .not { p.next() p.gen('0') - p.check(RCBR) + p.check(.rcbr) return typ } no_star := typ.replace('*', '') @@ -2455,9 +2452,9 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { // Remember which fields were set, so that we dont have to zero them later mut inited_fields := []string peek := p.peek() - if peek == COLON || p.tok == RCBR { + if peek == .colon || p.tok == .rcbr { t := p.table.find_type(typ) - for p.tok != RCBR { + for p.tok != .rcbr { field := p.check_name() if !t.has_field(field) { p.error('`$t.name` has no field `$field`') @@ -2465,13 +2462,13 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { f := t.find_field(field) inited_fields << field p.gen('.$field = ') - p.check(COLON) + p.check(.colon) p.fspace() p.check_types(p.bool_expression(), f.typ) - if p.tok == COMMA { + if p.tok == .comma { p.next() } - if p.tok != RCBR { + if p.tok != .rcbr { p.gen(',') } p.fgenln('') @@ -2513,7 +2510,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.error('field value #${i+1} `$ffield.name` has type `$ffield.typ`, got `$expr_typ` ') } if i < T.fields.len - 1 { - if p.tok != COMMA { + if p.tok != .comma { p.error('too few values in `$typ` literal (${i+1} instead of $T.fields.len)') } p.gen(',') @@ -2522,10 +2519,10 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { } // Allow `user := User{1,2,3,}` // The final comma will be removed by vfmt, since we are not calling `p.fgen()` - if p.tok == COMMA { + if p.tok == .comma { p.next() } - if p.tok != RCBR { + if p.tok != .rcbr { p.error('too many fields initialized: `$typ` has $T.fields.len field(s)') } } @@ -2533,7 +2530,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { if ptr { p.gen(')') } - p.check(RCBR) + p.check(.rcbr) p.is_struct_init = false return typ } @@ -2543,14 +2540,14 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { fn (p mut Parser) cast(typ string) string { p.next() pos := p.cgen.add_placeholder() - if p.tok == RPAR { + if p.tok == .rpar { // skip `)` if it's `(*int)(ptr)`, not `int(a)` p.ptr_cast = true p.next() } - p.check(LPAR) + p.check(.lpar) expr_typ := p.bool_expression() - p.check(RPAR) + p.check(.rpar) // `string(buffer)` => `tos2(buffer)` if typ == 'string' && (expr_typ == 'byte*' || expr_typ == 'byteptr') { p.cgen.set_placeholder(pos, 'tos2(') @@ -2588,13 +2585,13 @@ fn os_name_to_ifdef(name string) string { } fn (p mut Parser) comp_time() { - p.check(DOLLAR) - if p.tok == IF { - p.check(IF) + p.check(.dollar) + if p.tok == .key_if { + p.check(.key_if) p.fspace() - not := p.tok == NOT + not := p.tok == .not if not { - p.check(NOT) + p.check(.not) } name := p.check_name() p.fspace() @@ -2606,9 +2603,9 @@ fn (p mut Parser) comp_time() { else { p.genln('#ifdef $ifdef_name') } - p.check(LCBR) + p.check(.lcbr) p.statements_no_curly_end() - if ! (p.tok == DOLLAR && p.peek() == ELSE) { + if ! (p.tok == .dollar && p.peek() == .key_else) { p.genln('#endif') } } @@ -2618,34 +2615,34 @@ fn (p mut Parser) comp_time() { p.error('unknown platform `$name`') } } - else if p.tok == FOR { + else if p.tok == .key_for { p.next() name := p.check_name() if name != 'field' { p.error('for field only') } - p.check(IN) + p.check(.key_in) p.check_name() - p.check(DOT) + p.check(.dot) p.check_name()// fields - p.check(LCBR) - // for p.tok != RCBR && p.tok != EOF { + p.check(.lcbr) + // for p.tok != .rcbr && p.tok != .eof { res_name := p.check_name() println(res_name) - p.check(DOT) - p.check(DOLLAR) - p.check(NAME) - p.check(ASSIGN) + p.check(.dot) + p.check(.dollar) + p.check(.name) + p.check(.assign) p.cgen.start_tmp() p.bool_expression() val := p.cgen.end_tmp() println(val) - p.check(RCBR) + p.check(.rcbr) // } } - else if p.tok == ELSE { + else if p.tok == .key_else { p.next() - p.check(LCBR) + p.check(.lcbr) p.genln('#else') p.statements_no_curly_end() p.genln('#endif') @@ -2667,13 +2664,13 @@ fn (p mut Parser) chash() { mut flag := hash.right(5) // No the right os? Skip! // mut ok := true - if hash.contains('linux') && p.os != LINUX { + if hash.contains('linux') && p.os != .linux { return } - else if hash.contains('darwin') && p.os != MAC { + else if hash.contains('darwin') && p.os != .mac { return } - else if hash.contains('windows') && p.os != WINDOWS { + else if hash.contains('windows') && p.os != .windows { return } // Remove "linux" etc from flag @@ -2743,10 +2740,10 @@ fn (p mut Parser) if_st(is_expr bool) string { p.genln('/*if*/') } p.fgen(' ') - p.check(LCBR) + p.check(.lcbr) mut typ := '' // if { if hack - if p.tok == IF && p.inside_if_expr { + if p.tok == .key_if && p.inside_if_expr { println('AAAWWFAFAF') typ = p.factor() println('QWEWQE typ=$typ') @@ -2756,11 +2753,11 @@ fn (p mut Parser) if_st(is_expr bool) string { typ = p.statements() } // println('IF TYp=$typ') - if p.tok == ELSE { + if p.tok == .key_else { p.fgenln('') - p.check(ELSE) + p.check(.key_else) p.fspace() - if p.tok == IF { + if p.tok == .key_if { p.gen(' else ') return p.if_st(is_expr) // return '' @@ -2772,7 +2769,7 @@ fn (p mut Parser) if_st(is_expr bool) string { p.genln(' else { ') p.genln('/*else if*/') } - p.check(LCBR) + p.check(.lcbr) // statements() returns the type of the last statement typ = p.statements() p.inside_if_expr = false @@ -2789,32 +2786,32 @@ fn (p mut Parser) if_st(is_expr bool) string { } fn (p mut Parser) for_st() { - p.check(FOR) + p.check(.key_for) p.fgen(' ') p.for_expr_cnt++ next_tok := p.peek() debug := p.scanner.file_path.contains('r_draw') if debug { - println('\n\nFOR {') + println('\n\nF.ortok {') } p.cur_fn.open_scope() - if p.tok == LCBR { + if p.tok == .lcbr { // Infinite loop p.gen('while (1) {') } - else if p.tok == MUT { + else if p.tok == .key_mut { p.error('`mut` is not required in for loops') } // for i := 0; i < 10; i++ { - else if next_tok == DECL_ASSIGN || next_tok == ASSIGN || p.tok == SEMICOLON { + else if next_tok == .decl_assign || next_tok == .assign || p.tok == .semicolon { if debug { println('for 1') } p.genln('for (') - if next_tok == DECL_ASSIGN { + if next_tok == .decl_assign { p.var_decl() } - else if p.tok != SEMICOLON { + else if p.tok != .semicolon { // allow `for ;; i++ {` // Allow `for i = 0; i < ...` p.statement(false) @@ -2822,19 +2819,19 @@ fn (p mut Parser) for_st() { if debug { println('for 2') } - p.check(SEMICOLON) + p.check(.semicolon) p.gen(' ; ') p.fgen(' ') - if p.tok != SEMICOLON { + if p.tok != .semicolon { p.bool_expression() } if debug { println('for 3') } - p.check(SEMICOLON) + p.check(.semicolon) p.gen(' ; ') p.fgen(' ') - if p.tok != LCBR { + if p.tok != .lcbr { p.statement(false) } if debug { @@ -2843,17 +2840,17 @@ fn (p mut Parser) for_st() { p.genln(') { ') } // for i, val in array - else if p.peek() == COMMA { + else if p.peek() == .comma { // for i, val in array { ==> // // array_int tmp = array; // for (int i = 0; i < tmp.len; i++) { // int val = tmp[i]; i := p.check_name() - p.check(COMMA) + p.check(.comma) val := p.check_name() p.fgen(' ') - p.check(IN) + p.check(.key_in) p.fgen(' ') tmp := p.get_tmp() p.cgen.start_tmp() @@ -2881,10 +2878,10 @@ fn (p mut Parser) for_st() { p.genln('$var_typ $val = (($var_typ *) $tmp . data)[$i];') } // `for val in vals` - else if p.peek() == IN { + else if p.peek() == .key_in { val := p.check_name() p.fgen(' ') - p.check(IN) + p.check(.key_in) p.fspace() tmp := p.get_tmp() p.cgen.start_tmp() @@ -2892,11 +2889,11 @@ fn (p mut Parser) for_st() { expr := p.cgen.end_tmp() // println('if in:') // println(p.strtok()) - is_range := p.tok == DOTDOT + is_range := p.tok == .dotdot mut range_end := '' if is_range { p.check_types(typ, 'int') - p.check_space(DOTDOT) + p.check_space(.dotdot) p.cgen.start_tmp() p.check_types(p.bool_expression(), 'int') range_end = p.cgen.end_tmp() @@ -2951,24 +2948,24 @@ fn (p mut Parser) for_st() { p.genln(') {') } p.fspace() - p.check(LCBR) + p.check(.lcbr) p.statements() p.cur_fn.close_scope() p.for_expr_cnt-- } fn (p mut Parser) switch_statement() { - p.check(SWITCH) + p.check(.key_switch) p.cgen.start_tmp() typ := p.bool_expression() expr := p.cgen.end_tmp() - p.check(LCBR) + p.check(.lcbr) mut i := 0 - for p.tok == CASE || p.tok == DEFAULT { - if p.tok == DEFAULT { + for p.tok == .key_case || p.tok == .key_default { + if p.tok == .key_default { p.genln('else { // default:') - p.check(DEFAULT) - p.check(COLON) + p.check(.key_default) + p.check(.colon) p.statements() break } @@ -2988,17 +2985,17 @@ fn (p mut Parser) switch_statement() { else { p.gen('($expr == ') } - if p.tok == CASE || p.tok == DEFAULT { + if p.tok == .key_case || p.tok == .key_default { p.check(p.tok) } p.bool_expression() - if p.tok != COMMA { + if p.tok != .comma { break } - p.check(COMMA) + p.check(.comma) got_comma = true } - p.check(COLON) + p.check(.colon) p.gen(')) {') p.genln('/* case */') p.statements() @@ -3010,7 +3007,7 @@ fn (p mut Parser) assert_statement() { if p.first_run() { return } - p.check(ASSERT) + p.check(.key_assert) p.fspace() tmp := p.get_tmp() p.gen('bool $tmp = ') @@ -3019,7 +3016,7 @@ fn (p mut Parser) assert_statement() { filename := p.file_path p.genln(';\n if (!$tmp) { - puts("\\x1B[31mFAILED: $p.cur_fn.name() in $filename:$p.scanner.line_nr\\x1B[0m"); + puts("\\x1B[31mFAI.leD: $p.cur_fn.name() in $filename:$p.scanner.line_nr\\x1B[0m"); g_test_ok = 0 ; // TODO // Maybe print all vars in a test function if it fails? @@ -3035,11 +3032,11 @@ fn (p mut Parser) return_st() { if p.cur_fn.name == 'main' { p.gen(' 0') } - p.check(RETURN) + p.check(.key_return) p.fgen(' ') fn_returns := p.cur_fn.typ != 'void' if fn_returns { - if p.tok == RCBR { + if p.tok == .rcbr { p.error('`$p.cur_fn.name` needs to return `$p.cur_fn.typ`') } else { @@ -3056,8 +3053,8 @@ fn (p mut Parser) return_st() { } else { // Don't allow `return val` in functions that don't return anything - // if p.tok != RCBR && p.tok != HASH { - if false && p.tok == NAME || p.tok == INT { + // if p.tok != .rcbr && p.tok != .hash { + if false && p.tok == .name || p.tok == .integer { p.error('function `$p.cur_fn.name` does not return a value') } } @@ -3069,19 +3066,19 @@ fn prepend_pkg(pkg, name string) string { } fn (p &Parser) prepend_pkg(name string) string { - return prepend_pkg(p.pkg, name) + return prepend_pkg(p.mod, name) } fn (p mut Parser) go_statement() { - p.check(GO) + p.check(.key_go) // TODO copypasta of name_expr() ? // Method - if p.peek() == DOT { + if p.peek() == .dot { var_name := p.lit v := p.cur_fn.find_var(var_name) p.cur_fn.mark_var_used(v) p.next() - p.check(DOT) + p.check(.dot) typ := p.table.find_type(v.typ) mut method := p.table.find_method(typ, p.lit) p.async_fn_call(method, 0, var_name, v.typ) @@ -3106,8 +3103,8 @@ fn (p mut Parser) register_var(v Var) { // user:=jsdecode(User, user_json_string) fn (p mut Parser) js_decode() string { - p.check(NAME)// json - p.check(DOT) + p.check(.name)// json + p.check(.dot) op := p.check_name() if op == 'decode' { // User tmp2; tmp2.foo = 0; tmp2.bar = 0;// I forgot to zero vals before => huge bug @@ -3116,13 +3113,13 @@ fn (p mut Parser) js_decode() string { // return // } // User u = *(User*) tmp3 . data; // TODO remove this (generated in or {} block handler) - p.check(LPAR) + p.check(.lpar) typ := p.get_type() - p.check(COMMA) + p.check(.comma) p.cgen.start_tmp() p.check_types(p.bool_expression(), 'string') expr := p.cgen.end_tmp() - p.check(RPAR) + p.check(.rpar) tmp := p.get_tmp() cjson_tmp := p.get_tmp() mut decl := '$typ $tmp; ' @@ -3145,13 +3142,13 @@ fn (p mut Parser) js_decode() string { return opt_type } else if op == 'encode' { - p.check(LPAR) + p.check(.lpar) p.cgen.start_tmp() typ := p.bool_expression() T := p.table.find_type(typ) p.gen_json_for_type(T) expr := p.cgen.end_tmp() - p.check(RPAR) + p.check(.rpar) p.gen('json__json_print(json__jsencode_$typ($expr))') return 'string' } @@ -3183,15 +3180,15 @@ fn (p &Parser) building_v() bool { } fn (p mut Parser) attribute() { - p.check(LSBR) + p.check(.lsbr) p.attr = p.check_name() - p.check(RSBR) - if p.tok == FUNC { + p.check(.rsbr) + if p.tok == .func { p.fn_decl() p.attr = '' return } - else if p.tok == STRUCT { + else if p.tok == .key_struct { p.struct_decl() p.attr = '' return diff --git a/compiler/scanner.v b/compiler/scanner.v index 57c5407e2c..5e9428c20c 100644 --- a/compiler/scanner.v +++ b/compiler/scanner.v @@ -148,7 +148,7 @@ fn (s mut Scanner) skip_whitespace() { s.pos++ } // if s.pos == s.text.len { - // return scan_res(EOF, '') + // return scan_res(.eof, '') // } } @@ -175,14 +175,14 @@ fn (s mut Scanner) scan() ScanRes { } s.started = true if s.pos >= s.text.len { - return scan_res(EOF, '') + return scan_res(.eof, '') } // skip whitespace if !s.inside_string { s.skip_whitespace() } if s.is_fmt && s.text[s.pos] == `\n` { - return scan_res(NL, '') + return scan_res(.nl, '') } // End of $var, start next string if !s.is_fmt && s.dollar_end { @@ -190,16 +190,16 @@ fn (s mut Scanner) scan() ScanRes { if s.text[s.pos] == SINGLE_QUOTE { // fmt.Println("ENDDD") s.dollar_end = false - return scan_res(STRING, '') + return scan_res(.strtoken, '') } s.dollar_end = false - return scan_res(STRING, s.ident_string()) + return scan_res(.strtoken, s.ident_string()) } s.skip_whitespace() // end of file if s.pos >= s.text.len { - // println('scan(): returning EOF (pos >= len)') - return scan_res(EOF, '') + // println('scan(): returning .eof (pos >= len)') + return scan_res(.eof, '') } // handle each char c := s.text[s.pos] @@ -211,7 +211,7 @@ fn (s mut Scanner) scan() ScanRes { if is_name_char(c) { name := s.ident_name() // tmp hack to detect . in ${} - // Check if not EOF to prevent panic + // Check if not .eof to prevent panic next_char := if s.pos + 1 < s.text.len { s.text[s.pos + 1] } else { `\0` } // println('!!! got name=$name next_char=$next_char') if is_key(name) { @@ -232,7 +232,7 @@ fn (s mut Scanner) scan() ScanRes { } } if s.dollar_start && next_char != `.` { - // println('INSIDE STRING .dollar var=$name') + // println('INSIDE .strtoken .dollar var=$name') s.dollar_end = true s.dollar_start = false } @@ -241,128 +241,128 @@ fn (s mut Scanner) scan() ScanRes { //If a single letter name at the start of the file, increment //Otherwise the scanner would be stuck at s.pos = 0 } - return scan_res(NAME, name) + return scan_res(.name, name) } // number, `.123` else if c.is_digit() || c == `.` && nextc.is_digit() { num := s.ident_number() - return scan_res(INT, num) + return scan_res(.integer, num) } // all other tokens switch c { case `+`: if nextc == `+` { s.pos++ - return scan_res(INC, '') + return scan_res(.inc, '') } else if nextc == `=` { s.pos++ - return scan_res(PLUS_ASSIGN, '') + return scan_res(.plus_assign, '') } - return scan_res(PLUS, '') + return scan_res(.plus, '') case `-`: if nextc == `-` { s.pos++ - return scan_res(DEC, '') + return scan_res(.dec, '') } else if nextc == `=` { s.pos++ - return scan_res(MINUS_ASSIGN, '') + return scan_res(.minus_assign, '') } - return scan_res(MINUS, '') + return scan_res(.minus, '') case `*`: if nextc == `=` { s.pos++ - return scan_res(MULT_ASSIGN, '') + return scan_res(.mult_assign, '') } - return scan_res(MUL, '') + return scan_res(.mul, '') case `^`: if nextc == `=` { s.pos++ - return scan_res(XOR_ASSIGN, '') + return scan_res(.xor_assign, '') } - return scan_res(XOR, '') + return scan_res(.xor, '') case `%`: if nextc == `=` { s.pos++ - return scan_res(MOD_ASSIGN, '') + return scan_res(.mod_assign, '') } - return scan_res(MOD, '') + return scan_res(.mod, '') case `?`: - return scan_res(QUESTION, '') + return scan_res(.question, '') case SINGLE_QUOTE: - return scan_res(STRING, s.ident_string()) + return scan_res(.strtoken, s.ident_string()) // TODO allow double quotes // case QUOTE: - // return scan_res(STRING, s.ident_string()) + // return scan_res(.strtoken, s.ident_string()) case `\``: - return scan_res(CHAR, s.ident_char()) + return scan_res(.chartoken, s.ident_char()) case `(`: - return scan_res(LPAR, '') + return scan_res(.lpar, '') case `)`: - return scan_res(RPAR, '') + return scan_res(.rpar, '') case `[`: - return scan_res(LSBR, '') + return scan_res(.lsbr, '') case `]`: - return scan_res(RSBR, '') + return scan_res(.rsbr, '') case `{`: // Skip { in ${ in strings if s.inside_string { return s.scan() } - return scan_res(LCBR, '') + return scan_res(.lcbr, '') case `$`: - return scan_res(DOLLAR, '') + return scan_res(.dollar, '') case `}`: // s = `hello $name kek` // s = `hello ${name} kek` if s.inside_string { s.pos++ - // TODO UNNEEDED? + // TODO UN.neEDED? if s.text[s.pos] == SINGLE_QUOTE { s.inside_string = false - return scan_res(STRING, '') + return scan_res(.strtoken, '') } - return scan_res(STRING, s.ident_string()) + return scan_res(.strtoken, s.ident_string()) } else { - return scan_res(RCBR, '') + return scan_res(.rcbr, '') } case `&`: if nextc == `=` { s.pos++ - return scan_res(AND_ASSIGN, '') + return scan_res(.and_assign, '') } if nextc == `&` { s.pos++ - return scan_res(AND, '') + return scan_res(.and, '') } - return scan_res(AMP, '') + return scan_res(.amp, '') case `|`: if nextc == `|` { s.pos++ - return scan_res(OR, '') + return scan_res(.ortok, '') } if nextc == `=` { s.pos++ - return scan_res(OR_ASSIGN, '') + return scan_res(.or_assign, '') } - return scan_res(PIPE, '') + return scan_res(.pipe, '') case `,`: - return scan_res(COMMA, '') + return scan_res(.comma, '') case `\r`: if nextc == `\n` { s.pos++ - return scan_res(NL, '') + return scan_res(.nl, '') } case `\n`: - return scan_res(NL, '') + return scan_res(.nl, '') case `.`: if nextc == `.` { s.pos++ - return scan_res(DOTDOT, '') + return scan_res(.dotdot, '') } - return scan_res(DOT, '') + return scan_res(.dot, '') case `#`: start := s.pos + 1 for s.pos < s.text.len && s.text[s.pos] != `\n` { @@ -374,74 +374,74 @@ fn (s mut Scanner) scan() ScanRes { // fmt needs NL after # s.pos-- } - return scan_res(HASH, hash.trim_space()) + return scan_res(.hash, hash.trim_space()) case `>`: if nextc == `=` { s.pos++ - return scan_res(GE, '') + return scan_res(.ge, '') } else if nextc == `>` { if s.pos + 2 < s.text.len && s.text[s.pos + 2] == `=` { s.pos += 2 - return scan_res(RIGHT_SHIFT_ASSIGN, '') + return scan_res(.righ_shift_assign, '') } s.pos++ - return scan_res(RIGHT_SHIFT, '') + return scan_res(.righ_shift, '') } else { - return scan_res(GT, '') + return scan_res(.gt, '') } case `<`: if nextc == `=` { s.pos++ - return scan_res(LE, '') + return scan_res(.le, '') } else if nextc == `<` { if s.pos + 2 < s.text.len && s.text[s.pos + 2] == `=` { s.pos += 2 - return scan_res(LEFT_SHIFT_ASSIGN, '') + return scan_res(.left_shift_assign, '') } s.pos++ - return scan_res(LEFT_SHIFT, '') + return scan_res(.left_shift, '') } else { - return scan_res(LT, '') + return scan_res(.lt, '') } case `=`: if nextc == `=` { s.pos++ - return scan_res(EQ, '') + return scan_res(.eq, '') } else { - return scan_res(ASSIGN, '') + return scan_res(.assign, '') } case `:`: if nextc == `=` { s.pos++ - return scan_res(DECL_ASSIGN, '') + return scan_res(.decl_assign, '') } else { - return scan_res(COLON, '') + return scan_res(.colon, '') } case `;`: - return scan_res(SEMICOLON, '') + return scan_res(.semicolon, '') case `!`: if nextc == `=` { s.pos++ - return scan_res(NE, '') + return scan_res(.ne, '') } else { - return scan_res(NOT, '') + return scan_res(.not, '') } case `~`: - return scan_res(BIT_NOT, '') + return scan_res(.bit_not, '') case `/`: if nextc == `=` { s.pos++ - return scan_res(DIV_ASSIGN, '') + return scan_res(.div_assign, '') } if nextc == `/` { - // debug("!!!!!!GOT LINE COM") + // debug("!!!!!!.key_goT LI.ne COM") start := s.pos + 1 for s.pos < s.text.len && s.text[s.pos] != `\n`{ s.pos++ @@ -458,7 +458,7 @@ fn (s mut Scanner) scan() ScanRes { // Skip comment return s.scan() } - return scan_res(LINE_COM, s.line_comment) + return scan_res(.line_com, s.line_comment) } // Multiline comments if nextc == `*` { @@ -488,16 +488,16 @@ fn (s mut Scanner) scan() ScanRes { comm := s.text.substr(start, end) s.fgenln(comm) if s.is_fmt { - return scan_res(MLINE_COM, comm) + return scan_res(.mline_com, comm) } // Skip if not in fmt mode return s.scan() } - return scan_res(DIV, '') + return scan_res(.div, '') } $if windows { if c == `\0` { - return scan_res(EOF, '') + return scan_res(.eof, '') } } println('(char code=$c) pos=$s.pos len=$s.text.len') @@ -506,7 +506,7 @@ fn (s mut Scanner) scan() ScanRes { msg += ', use \' to denote strings' } s.error(msg) - return scan_res(EOF, '') + return scan_res(.eof, '') } fn (s &Scanner) error(msg string) { @@ -637,7 +637,7 @@ fn (s mut Scanner) ident_char() string { fn (p mut Parser) peek() Token { for { tok := p.scanner.peek() - if tok != NL { + if tok != .nl { return tok } } @@ -681,7 +681,7 @@ fn (s mut Scanner) debug_tokens() { println('') } // allToks += "\n" - if tok == EOF { + if tok == .eof { println('============ END OF DEBUG TOKENS ==================') // fmt.Println("========"+s.file+"========\n", allToks) break diff --git a/compiler/table.v b/compiler/table.v index 67f9a711e2..b4db0c0380 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -18,24 +18,25 @@ mut: } enum AccessMod { - PRIVATE // private immutable - PRIVET_MUT // private mutable - PUBLIC // public immmutable (readonly) - PUBLIC_MUT // public, but mutable only in this module - PUBLIC_MUT_MUT // public and mutable both inside and outside (not recommended to use, that's why it's so verbose) + private // private imkey_mut + private_mut // private key_mut + public // public immkey_mut (readonly) + public_mut // public, but key_mut only in this module + public_mut_mut // public and key_mut both inside and outside (not recommended to use, that's why it's so verbose) } struct Type { mut: - pkg string + mod string name string fields []Var methods []Fn parent string func Fn // For cat == FN (type kek fn()) - is_c bool // C.FILE + is_c bool // C.FI.le is_interface bool is_enum bool + enum_vals []string // This field is used for types that are not defined yet but are known to exist. // It allows having things like `fn (f Foo) bar()` before `Foo` is defined. // This information is needed in the first pass. @@ -117,8 +118,7 @@ fn new_table(obfuscate bool) *Table { t.register_type_with_parent('i32', 'int') t.register_type_with_parent('u32', 'int') t.register_type_with_parent('byte', 'int') - // t.register_type_with_parent('i64', 'int') - t.register_type('i64') + t.register_type_with_parent('i64', 'int') t.register_type_with_parent('u64', 'int') t.register_type('byteptr') t.register_type('intptr') @@ -159,13 +159,13 @@ fn (table &Table) known_pkg(pkg string) bool { return pkg in table.packages } -fn (t mut Table) register_const(name, typ, pkg string, is_imported bool) { +fn (t mut Table) register_const(name, typ, mod string, is_imported bool) { t.consts << Var { name: name typ: typ is_const: true is_import_const: is_imported - pkg: pkg + mod: mod } } @@ -176,7 +176,7 @@ fn (p mut Parser) register_global(name, typ string) { typ: typ is_const: true is_global: true - pkg: p.pkg + mod: p.mod } } @@ -237,7 +237,7 @@ fn (p mut Parser) register_type_with_parent(strtyp, parent string) { typ := Type { name: strtyp parent: parent - pkg: p.pkg + mod: p.mod } p.table.register_type2(typ) } @@ -297,6 +297,10 @@ fn (t &Type) has_field(name string) bool { return (field.name != '') } +fn (t &Type) has_enum_val(name string) bool { + return name in t.enum_vals +} + fn (t &Type) find_field(name string) Var { for field in t.fields { if field.name == name { diff --git a/compiler/token.v b/compiler/token.v index 309125e57a..57c679f8a4 100644 --- a/compiler/token.v +++ b/compiler/token.v @@ -5,214 +5,212 @@ module main enum Token { - EOF - NAME - INT - STRING - CHAR - PLUS - MINUS - MUL - DIV - MOD - XOR - PIPE - INC - DEC - AND - OR - NOT - BIT_NOT - QUESTION - COMMA - SEMICOLON - COLON - AMP - HASH - DOLLAR - LEFT_SHIFT - RIGHT_SHIFT + eof + name + integer + strtoken + chartoken + plus + minus + mul + div + mod + xor + pipe + inc + dec + and + ortok + not + bit_not + question + comma + semicolon + colon + amp + hash + dollar + left_shift + righ_shift // = := += -= - ASSIGN - DECL_ASSIGN - PLUS_ASSIGN - MINUS_ASSIGN - DIV_ASSIGN - MULT_ASSIGN - XOR_ASSIGN - MOD_ASSIGN - OR_ASSIGN - AND_ASSIGN - RIGHT_SHIFT_ASSIGN - LEFT_SHIFT_ASSIGN + assign + decl_assign + plus_assign + minus_assign + div_assign + mult_assign + xor_assign + mod_assign + or_assign + and_assign + righ_shift_assign + left_shift_assign // {} () [] - LCBR - RCBR - LPAR - RPAR - LSBR - RSBR + lcbr + rcbr + lpar + rpar + lsbr + rsbr // == != <= < >= > - EQ - NE - GT - LT - GE - LE + eq + ne + gt + lt + ge + le // comments - LINE_COM - MLINE_COM - NL - DOT - DOTDOT + line_com + mline_com + nl + dot + dotdot // keywords keyword_beg - PACKAGE - // MODULE - STRUCT - IF - ELSE - RETURN - GO - CONST - IMPORT_CONST - MUT - TIP - ENUM - FOR - SWITCH + key_module + key_struct + key_if + key_else + key_return + key_go + key_const + key_import_const + key_mut + typ + key_enum + key_for + key_switch MATCH - CASE - FUNC - TRUE - FALSE - CONTINUE - BREAK - EMBED - IMPORT - TYPEOF - DEFAULT - ENDIF - ASSERT - SIZEOF - IN - ATOMIC - INTERFACE - OR_ELSE - GLOBAL - UNION - PUB - GOTO - STATIC + key_case + func + key_true + key_false + key_continue + key_break + key_embed + key_import + //typeof + key_default + key_assert + key_sizeof + key_in + key_atomic + key_interface + key_orelse + key_global + key_union + key_pub + key_goto + key_static keyword_end } // build_keys genereates a map with keywords' string values: -// Keywords['return'] == .return +// Keywords['return'] == .key_return fn build_keys() map_int { mut res := map[string]int{} - for t := int(keyword_beg) + 1; t < int(keyword_end); t++ { + for t := int(Token.keyword_beg) + 1; t < int(Token.keyword_end); t++ { key := TOKENSTR[t] res[key] = int(t) } return res } +// TODO remove once we have `enum Token { name('name') if('if') ... }` fn build_token_str() []string { mut s := [''; NrTokens] - s[keyword_beg] = '' - s[keyword_end] = '' - s[EOF] = 'EOF' - s[NAME] = 'NAME' - s[INT] = 'INT' - s[STRING] = 'STR' - s[CHAR] = 'CHAR' - s[PLUS] = '+' - s[MINUS] = '-' - s[MUL] = '*' - s[DIV] = '/' - s[MOD] = '%' - s[XOR] = '^' - s[BIT_NOT] = '~' - s[PIPE] = '|' - s[HASH] = '#' - s[AMP] = '&' - s[INC] = '++' - s[DEC] = '--' - s[AND] = '&&' - s[OR] = '||' - s[NOT] = '!' - s[DOT] = '.' - s[DOTDOT] = '..' - s[COMMA] = ',' - s[SEMICOLON] = ';' - s[COLON] = ':' - s[ASSIGN] = '=' - s[DECL_ASSIGN] = ':=' - s[PLUS_ASSIGN] = '+=' - s[MINUS_ASSIGN] = '-=' - s[MULT_ASSIGN] = '*=' - s[DIV_ASSIGN] = '/=' - s[XOR_ASSIGN] = '^=' - s[MOD_ASSIGN] = '%=' - s[OR_ASSIGN] = '|=' - s[AND_ASSIGN] = '&=' - s[RIGHT_SHIFT_ASSIGN] = '>>=' - s[LEFT_SHIFT_ASSIGN] = '<<=' - s[LCBR] = '{' - s[RCBR] = '}' - s[LPAR] = '(' - s[RPAR] = ')' - s[LSBR] = '[' - s[RSBR] = ']' - s[EQ] = '==' - s[NE] = '!=' - s[GT] = '>' - s[LT] = '<' - s[GE] = '>=' - s[LE] = '<=' - s[QUESTION] = '?' - s[LEFT_SHIFT] = '<<' - s[RIGHT_SHIFT] = '>>' - s[LINE_COM] = '//' - s[NL] = 'NLL' - s[DOLLAR] = '$' - s[ASSERT] = 'assert' - s[STRUCT] = 'struct' - s[IF] = 'if' - s[ELSE] = 'else' - s[RETURN] = 'return' - s[PACKAGE] = 'module' - s[SIZEOF] = 'sizeof' - s[GO] = 'go' - s[GOTO] = 'goto' - s[CONST] = 'const' - s[MUT] = 'mut' - s[TIP] = 'type' - s[FOR] = 'for' - s[SWITCH] = 'switch' - s[MATCH] = 'match' - s[CASE] = 'case' - s[FUNC] = 'fn' - s[TRUE] = 'true' - s[FALSE] = 'false' - s[CONTINUE] = 'continue' - s[BREAK] = 'break' - s[IMPORT] = 'import' - s[EMBED] = 'embed' - s[TYPEOF] = 'typeof' - s[DEFAULT] = 'default' - s[ENDIF] = 'endif' - s[ENUM] = 'enum' - s[INTERFACE] = 'interface' - s[PUB] = 'pub' - s[IMPORT_CONST] = 'import_const' - s[IN] = 'in' - s[ATOMIC] = 'atomic' - s[OR_ELSE] = 'or' - s[GLOBAL] = '__global' - s[UNION] = 'union' - s[STATIC] = 'static' + s[Token.keyword_beg] = '' + s[Token.keyword_end] = '' + s[Token.eof] = '.eof' + s[Token.name] = '.name' + s[Token.integer] = '.integer' + s[Token.strtoken] = 'STR' + s[Token.chartoken] = '.chartoken' + s[Token.plus] = '+' + s[Token.minus] = '-' + s[Token.mul] = '*' + s[Token.div] = '/' + s[Token.mod] = '%' + s[Token.xor] = '^' + s[Token.bit_not] = '~' + s[Token.pipe] = '|' + s[Token.hash] = '#' + s[Token.amp] = '&' + s[Token.inc] = '++' + s[Token.dec] = '--' + s[Token.and] = '&&' + s[Token.ortok] = '||' + s[Token.not] = '!' + s[Token.dot] = '.' + s[Token.dotdot] = '..' + s[Token.comma] = ',' + s[Token.semicolon] = ';' + s[Token.colon] = ':' + s[Token.assign] = '=' + s[Token.decl_assign] = ':=' + s[Token.plus_assign] = '+=' + s[Token.minus_assign] = '-=' + s[Token.mult_assign] = '*=' + s[Token.div_assign] = '/=' + s[Token.xor_assign] = '^=' + s[Token.mod_assign] = '%=' + s[Token.or_assign] = '|=' + s[Token.and_assign] = '&=' + s[Token.righ_shift_assign] = '>>=' + s[Token.left_shift_assign] = '<<=' + s[Token.lcbr] = '{' + s[Token.rcbr] = '}' + s[Token.lpar] = '(' + s[Token.rpar] = ')' + s[Token.lsbr] = '[' + s[Token.rsbr] = ']' + s[Token.eq] = '==' + s[Token.ne] = '!=' + s[Token.gt] = '>' + s[Token.lt] = '<' + s[Token.ge] = '>=' + s[Token.le] = '<=' + s[Token.question] = '?' + s[Token.left_shift] = '<<' + s[Token.righ_shift] = '>>' + s[Token.line_com] = '//' + s[Token.nl] = 'NLL' + s[Token.dollar] = '$' + s[Token.key_assert] = 'assert' + s[Token.key_struct] = 'struct' + s[Token.key_if] = 'if' + s[Token.key_else] = 'else' + s[Token.key_return] = 'return' + s[Token.key_module] = 'module' + s[Token.key_sizeof] = 'sizeof' + s[Token.key_go] = 'go' + s[Token.key_goto] = 'goto' + s[Token.key_const] = 'const' + s[Token.key_mut] = 'mut' + s[Token.typ] = 'type' + s[Token.key_for] = 'for' + s[Token.key_switch] = 'switch' + //Tokens[MATCH] = 'match' + s[Token.key_case] = 'case' + s[Token.func] = 'fn' + s[Token.key_true] = 'true' + s[Token.key_false] = 'false' + s[Token.key_continue] = 'continue' + s[Token.key_break] = 'break' + s[Token.key_import] = 'import' + s[Token.key_embed] = 'embed' + //Tokens[TYP.eof] = 'typeof' + s[Token.key_default] = 'default' + s[Token.key_enum] = 'enum' + s[Token.key_interface] = 'interface' + s[Token.key_pub] = 'pub' + s[Token.key_import_const] = 'import_const' + s[Token.key_in] = 'in' + s[Token.key_atomic] = 'atomic' + s[Token.key_orelse] = 'or' + s[Token.key_global] = '__global' + s[Token.key_union] = 'union' + s[Token.key_static] = 'static' return s } @@ -236,17 +234,19 @@ fn (t Token) str() string { } fn (t Token) is_decl() bool { - // TODO return t in [FUNC ,TIP, CONST, IMPORT_CONST ,AT ,EOF] - return t == ENUM || t == INTERFACE || t == FUNC || t == STRUCT || t == TIP || - t == CONST || t == IMPORT_CONST || t == PUB || t == EOF + // TODO return t in [.func ,.typ, .key_const, .key_import_.key_const ,AT ,.eof] + return t == .key_enum || t == .key_interface || t == .func || + t == .key_struct || t == .typ || + t == .key_const || t == .key_import_const || t == .key_pub || t == .eof } const ( AssignTokens = [ - ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN, - MULT_ASSIGN, DIV_ASSIGN, XOR_ASSIGN, MOD_ASSIGN, - OR_ASSIGN, AND_ASSIGN, RIGHT_SHIFT_ASSIGN, - LEFT_SHIFT_ASSIGN + Token.assign, Token.plus_assign, Token.minus_assign, + Token.mult_assign, Token.div_assign, Token.xor_assign, + Token.mod_assign, + Token.or_assign, Token.and_assign, Token.righ_shift_assign, + Token.left_shift_assign ] )