From 31ba64b4092f828a17468607e3c766643a1c55e5 Mon Sep 17 00:00:00 2001 From: Larpon Date: Tue, 26 May 2020 15:35:11 +0200 Subject: [PATCH] scanner: add support for `@STRUCT` compile time substitution --- vlib/v/scanner/scanner.v | 67 +++++++++++++++++++++++++++++++++++ vlib/v/scanner/scanner_test.v | 41 +++++++++++++++++++-- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index e367f41087..b249f6da9c 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -31,6 +31,7 @@ pub mut: is_started bool fn_name string // needed for @FN mod_name string // needed for @MOD + struct_name string // needed for @STRUCT is_print_line_on_error bool is_print_colored_error bool is_print_rel_paths_on_error bool @@ -174,6 +175,67 @@ fn (mut s Scanner) ident_mod_name() string { return mod_name } +// ident_struct_name look ahead and return name of last encountered struct if possible, otherwise empty string +fn (mut s Scanner) ident_struct_name() string { + start := s.pos + mut pos := s.pos + + // Return last known stuct_name encountered to avoid using high order/anonymous function definitions + if s.current_column() - 2 != 0 { + return s.struct_name + } + + pos++ + + // Eat whitespaces + for pos < s.text.len && s.text[pos].is_space() { + pos++ + } + if pos >= s.text.len { + return '' + } + + // Return if `(` is not the first character after "fn ..." + if s.text[pos] != `(` { + return '' + } + + // Search for closing paranthesis + for pos < s.text.len && s.text[pos] != `)` { + pos++ + } + if pos >= s.text.len { + return '' + } + + pos-- + // Search backwards for end position of struct name + // Eat whitespaces + for pos > start && s.text[pos].is_space() { + pos-- + } + if pos < start { + return '' + } + end_pos := pos + 1 + + // Go back while we have a name character or digit + for pos > start && (util.is_name_char(s.text[pos]) || s.text[pos].is_digit()) { + pos-- + } + if pos < start { + return '' + } + + start_pos := pos + 1 + + if s.text[start_pos].is_digit() || end_pos > s.text.len || end_pos <= start_pos || end_pos <= start || start_pos <= start { + return '' + } + struct_name := s.text[start_pos..end_pos] + return struct_name +} + fn filter_num_sep(txt byteptr, start int, end int) string { unsafe{ mut b := malloc(end - start + 1) // add a byte for the endstring 0 @@ -491,6 +553,7 @@ pub fn (mut s Scanner) scan() token.Token { kind := token.key_to_token(name) if kind == .key_fn { s.fn_name = s.ident_fn_name() + s.struct_name = s.ident_struct_name() } else if kind == .key_module { s.mod_name = s.ident_mod_name() } @@ -676,6 +739,7 @@ pub fn (mut s Scanner) scan() token.Token { name := s.ident_name() // @FN => will be substituted with the name of the current V function // @MOD => will be substituted with the name of the current V module + // @STRUCT => will be substituted with the name of the current V struct // @VEXE => will be substituted with the path to the V compiler // @FILE => will be substituted with the path of the V source file // @LINE => will be substituted with the V line number where it appears (as a string). @@ -690,6 +754,9 @@ pub fn (mut s Scanner) scan() token.Token { if name == 'MOD' { return s.new_token(.string, s.mod_name, 4) } + if name == 'STRUCT' { + return s.new_token(.string, s.struct_name, 7) + } if name == 'VEXE' { vexe := pref.vexe_path() return s.new_token(.string, util.cescaped_path(vexe), 5) diff --git a/vlib/v/scanner/scanner_test.v b/vlib/v/scanner/scanner_test.v index 921c6b0b0c..403f031af9 100644 --- a/vlib/v/scanner/scanner_test.v +++ b/vlib/v/scanner/scanner_test.v @@ -5,6 +5,26 @@ module scanner import v.token + +struct TestStruct { + test string +} + +fn (mut t TestStruct) test_struct() { + assert @STRUCT == 'TestStruct' +} + +fn (mut t TestStruct) test_struct_w_return() string { + assert @STRUCT == 'TestStruct' + return t.test +} + +fn (mut t TestStruct) test_struct_w_high_order(cb fn(int)string) string { + assert @STRUCT == 'TestStruct' + return 'test'+cb(2) +} + + fn test_scan() { text := 'println(2 + 3)' mut scanner := new_scanner(text, .skip_comments) @@ -41,9 +61,24 @@ fn test_scan() { assert 1.23e+10 == 1.23e0010 assert (-1.23e+10) == (1.23e0010 * -1.0) - // test @MOD + // Test @FN + assert @FN == 'test_scan' + + // Test @MOD assert @MOD == 'scanner' - // test @FN - assert @FN == 'test_scan' + // Test @STRUCT + assert @STRUCT == '' + + ts := TestStruct { test: "test" } + ts.test_struct() + r1 := ts.test_struct_w_return() + r2 := ts.test_struct_w_high_order(fn(i int)string{ + assert @STRUCT == '' + return i.str() + }) + assert r1 == 'test' + assert r2 == 'test2' + + }