mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: remove comp time '@' expansion from scanner (#6746)
This commit is contained in:
parent
1b1d17cfb5
commit
785bf40f67
@ -9,12 +9,12 @@ import v.errors
|
||||
|
||||
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
|
||||
|
||||
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CTempVar | CallExpr |
|
||||
CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral |
|
||||
Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
|
||||
MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr |
|
||||
SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit |
|
||||
Type | TypeOf | UnsafeExpr
|
||||
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | CTempVar |
|
||||
CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal |
|
||||
FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral |
|
||||
Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr |
|
||||
RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
|
||||
StructInit | Type | TypeOf | UnsafeExpr
|
||||
|
||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
|
||||
EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
|
||||
@ -937,6 +937,16 @@ pub mut:
|
||||
return_type table.Type
|
||||
}
|
||||
|
||||
// @FN, @STRUCT, @MOD etc. See full list in token.valid_at_tokens
|
||||
pub struct AtExpr {
|
||||
pub:
|
||||
name string
|
||||
pos token.Position
|
||||
kind token.AtKind
|
||||
pub mut:
|
||||
val string
|
||||
}
|
||||
|
||||
pub struct ComptimeCall {
|
||||
pub:
|
||||
method_name string
|
||||
@ -1019,7 +1029,7 @@ pub fn (expr Expr) position() token.Position {
|
||||
AnonFn {
|
||||
return expr.decl.pos
|
||||
}
|
||||
ArrayInit, AsCast, Assoc, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, EnumVal, FloatLiteral, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr, None, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr {
|
||||
ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, EnumVal, FloatLiteral, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr, None, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr {
|
||||
return expr.pos
|
||||
}
|
||||
IfGuardExpr {
|
||||
|
@ -186,6 +186,9 @@ pub fn (x Expr) str() string {
|
||||
CastExpr {
|
||||
return '${x.typname}($x.expr.str())'
|
||||
}
|
||||
AtExpr {
|
||||
return '$x.val'
|
||||
}
|
||||
CallExpr {
|
||||
sargs := args2str(x.args)
|
||||
if x.is_method {
|
||||
|
@ -2,7 +2,9 @@
|
||||
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
||||
module checker
|
||||
|
||||
import os
|
||||
import v.ast
|
||||
import v.vmod
|
||||
import v.table
|
||||
import v.token
|
||||
import v.pref
|
||||
@ -26,38 +28,39 @@ const (
|
||||
)
|
||||
|
||||
pub struct Checker {
|
||||
pref &pref.Preferences // Preferences shared from V struct
|
||||
pref &pref.Preferences // Preferences shared from V struct
|
||||
pub mut:
|
||||
table &table.Table
|
||||
file ast.File
|
||||
nr_errors int
|
||||
nr_warnings int
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
error_lines []int // to avoid printing multiple errors for the same line
|
||||
expected_type table.Type
|
||||
cur_fn &ast.FnDecl // current function
|
||||
const_decl string
|
||||
const_deps []string
|
||||
const_names []string
|
||||
global_names []string
|
||||
locked_names []string // vars that are currently locked
|
||||
rlocked_names []string // vars that are currently read-locked
|
||||
in_for_count int // if checker is currently in a for loop
|
||||
table &table.Table
|
||||
file ast.File
|
||||
nr_errors int
|
||||
nr_warnings int
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
error_lines []int // to avoid printing multiple errors for the same line
|
||||
expected_type table.Type
|
||||
cur_fn &ast.FnDecl // current function
|
||||
const_decl string
|
||||
const_deps []string
|
||||
const_names []string
|
||||
global_names []string
|
||||
locked_names []string // vars that are currently locked
|
||||
rlocked_names []string // vars that are currently read-locked
|
||||
in_for_count int // if checker is currently in a for loop
|
||||
// checked_ident string // to avoid infinite checker loops
|
||||
returns bool
|
||||
scope_returns bool
|
||||
mod string // current module name
|
||||
is_builtin_mod bool // are we in `builtin`?
|
||||
inside_unsafe bool
|
||||
skip_flags bool // should `#flag` and `#include` be skipped
|
||||
cur_generic_type table.Type
|
||||
returns bool
|
||||
scope_returns bool
|
||||
mod string // current module name
|
||||
is_builtin_mod bool // are we in `builtin`?
|
||||
inside_unsafe bool
|
||||
skip_flags bool // should `#flag` and `#include` be skipped
|
||||
cur_generic_type table.Type
|
||||
mut:
|
||||
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
||||
inside_sql bool // to handle sql table fields pseudo variables
|
||||
cur_orm_ts table.TypeSymbol
|
||||
error_details []string
|
||||
generic_funcs []&ast.FnDecl
|
||||
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
||||
inside_sql bool // to handle sql table fields pseudo variables
|
||||
cur_orm_ts table.TypeSymbol
|
||||
error_details []string
|
||||
generic_funcs []&ast.FnDecl
|
||||
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path*
|
||||
}
|
||||
|
||||
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
||||
@ -2729,6 +2732,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
||||
ast.Comment {
|
||||
return table.void_type
|
||||
}
|
||||
ast.AtExpr {
|
||||
return c.at_expr(mut node)
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
node.sym = c.table.get_type_symbol(c.unwrap_generic(c.expr(node.left)))
|
||||
if node.is_vweb {
|
||||
@ -2997,6 +3003,63 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type {
|
||||
return node.typ
|
||||
}
|
||||
|
||||
fn (mut c Checker) at_expr(mut node ast.AtExpr) table.Type {
|
||||
match node.kind {
|
||||
.fn_name {
|
||||
node.val = c.cur_fn.name.all_after_last('.')
|
||||
}
|
||||
.mod_name {
|
||||
node.val = c.cur_fn.mod
|
||||
}
|
||||
.struct_name {
|
||||
if c.cur_fn.is_method {
|
||||
node.val = c.table.type_to_str(c.cur_fn.receiver.typ).all_after_last('.')
|
||||
} else {
|
||||
node.val = ''
|
||||
}
|
||||
}
|
||||
.vexe_path {
|
||||
node.val = pref.vexe_path()
|
||||
}
|
||||
.file_path {
|
||||
node.val = os.real_path(c.file.path)
|
||||
}
|
||||
.line_nr {
|
||||
node.val = (node.pos.line_nr + 1).str()
|
||||
}
|
||||
.column_nr {
|
||||
_, column := util.filepath_pos_to_source_and_column(c.file.path, node.pos)
|
||||
node.val = (column + 1).str()
|
||||
}
|
||||
.vhash {
|
||||
node.val = util.vhash()
|
||||
}
|
||||
.vmod_file {
|
||||
if c.vmod_file_content.len == 0 {
|
||||
mut mcache := vmod.get_cache()
|
||||
vmod_file_location := mcache.get_by_file(c.file.path)
|
||||
if vmod_file_location.vmod_file.len == 0 {
|
||||
c.error('@VMOD_FILE can be used only in projects, that have v.mod file',
|
||||
node.pos)
|
||||
}
|
||||
vmod_content := os.read_file(vmod_file_location.vmod_file) or {
|
||||
''
|
||||
}
|
||||
$if windows {
|
||||
c.vmod_file_content = vmod_content.replace('\r\n', '\n')
|
||||
} $else {
|
||||
c.vmod_file_content = vmod_content
|
||||
}
|
||||
}
|
||||
node.val = c.vmod_file_content
|
||||
}
|
||||
.unknown, ._end_ {
|
||||
c.error('unknown @ identifier: $node.name', node.pos)
|
||||
}
|
||||
}
|
||||
return table.string_type
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
|
||||
// TODO: move this
|
||||
if c.const_deps.len > 0 {
|
||||
|
@ -762,6 +762,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||
}
|
||||
f.write(')')
|
||||
}
|
||||
ast.AtExpr {
|
||||
f.at_expr(node)
|
||||
}
|
||||
ast.CallExpr {
|
||||
f.call_expr(node)
|
||||
}
|
||||
@ -1387,6 +1390,10 @@ pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut f Fmt) at_expr(node ast.AtExpr) {
|
||||
f.write(node.name)
|
||||
}
|
||||
|
||||
pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
|
||||
/*
|
||||
if node.args.len == 1 && node.expected_arg_types.len == 1 && node.args[0].expr is ast.StructInit &&
|
||||
|
@ -2122,6 +2122,9 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||
ast.CharLiteral {
|
||||
g.write("'$node.val'")
|
||||
}
|
||||
ast.AtExpr {
|
||||
g.comp_at(node)
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
g.comptime_call(node)
|
||||
}
|
||||
|
@ -101,6 +101,15 @@ fn cgen_attrs(attrs []table.Attr) []string {
|
||||
return res
|
||||
}
|
||||
|
||||
fn (mut g Gen) comp_at(node ast.AtExpr) {
|
||||
if node.kind == .vmod_file {
|
||||
val := cnewlines(node.val.replace('\r', ''))
|
||||
g.write('tos_lit("$val")')
|
||||
} else {
|
||||
g.write('tos_lit("$node.val")')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) comp_if(node ast.IfExpr) {
|
||||
line := if node.is_expr {
|
||||
stmt_str := g.go_before_stmt(0)
|
||||
|
@ -657,6 +657,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
||||
g.gen_typeof_expr(node)
|
||||
// TODO: Should this print the V type or the JS type?
|
||||
}
|
||||
ast.AtExpr {
|
||||
g.write('"$node.val"')
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
// TODO
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import os
|
||||
import v.ast
|
||||
import v.pref
|
||||
import v.table
|
||||
import v.token
|
||||
import vweb.tmpl
|
||||
|
||||
// #flag darwin -I.
|
||||
@ -150,6 +151,29 @@ fn (mut p Parser) comp_for() ast.CompFor {
|
||||
}
|
||||
}
|
||||
|
||||
// @FN, @STRUCT, @MOD etc. See full list in token.valid_at_tokens
|
||||
fn (mut p Parser) at() ast.AtExpr {
|
||||
name := p.tok.lit
|
||||
kind := match name {
|
||||
'@FN' { token.AtKind.fn_name }
|
||||
'@MOD' { token.AtKind.mod_name }
|
||||
'@STRUCT' { token.AtKind.struct_name }
|
||||
'@VEXE' { token.AtKind.vexe_path }
|
||||
'@FILE' { token.AtKind.file_path }
|
||||
'@LINE' { token.AtKind.line_nr }
|
||||
'@COLUMN' { token.AtKind.column_nr }
|
||||
'@VHASH' { token.AtKind.vhash }
|
||||
'@VMOD_FILE' { token.AtKind.vmod_file }
|
||||
else { token.AtKind.unknown }
|
||||
}
|
||||
p.next()
|
||||
return ast.AtExpr{
|
||||
name: name
|
||||
pos: p.tok.position()
|
||||
kind: kind
|
||||
}
|
||||
}
|
||||
|
||||
// TODO import warning bug
|
||||
const (
|
||||
todo_delete_me = pref.OS.linux
|
||||
|
@ -45,6 +45,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||
// .enum_val
|
||||
node = p.enum_val()
|
||||
}
|
||||
.at {
|
||||
node = p.at()
|
||||
}
|
||||
.dollar {
|
||||
match p.peek_tok.kind {
|
||||
.name { return p.vweb() }
|
||||
|
@ -7,7 +7,6 @@ import os
|
||||
import v.token
|
||||
import v.pref
|
||||
import v.util
|
||||
import v.vmod
|
||||
|
||||
const (
|
||||
single_quote = `\'`
|
||||
@ -30,10 +29,6 @@ pub mut:
|
||||
line_comment string
|
||||
// prev_tok TokenKind
|
||||
is_started bool
|
||||
fn_name string // needed for @FN
|
||||
mod_name string // needed for @MOD
|
||||
struct_name string // needed for @STRUCT
|
||||
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path*
|
||||
is_print_line_on_error bool
|
||||
is_print_colored_error bool
|
||||
is_print_rel_paths_on_error bool
|
||||
@ -177,138 +172,6 @@ fn (mut s Scanner) ident_name() string {
|
||||
return name
|
||||
}
|
||||
|
||||
// ident_fn_name looks ahead and returns name of the function if possible, otherwise an empty string
|
||||
fn (s &Scanner) ident_fn_name() string {
|
||||
start := s.pos
|
||||
mut pos := s.pos
|
||||
pos++
|
||||
if s.current_column() - 2 != 0 {
|
||||
return s.fn_name
|
||||
}
|
||||
has_struct_name := s.struct_name != ''
|
||||
if has_struct_name {
|
||||
for pos < s.text.len && s.text[pos] != `(` {
|
||||
pos++
|
||||
}
|
||||
if pos >= s.text.len {
|
||||
return ''
|
||||
}
|
||||
pos++
|
||||
}
|
||||
for pos < s.text.len && s.text[pos] != `(` {
|
||||
pos++
|
||||
}
|
||||
if pos >= s.text.len {
|
||||
return ''
|
||||
}
|
||||
pos--
|
||||
// Eat whitespaces
|
||||
for pos > start && s.text[pos].is_space() {
|
||||
pos--
|
||||
}
|
||||
if pos < start {
|
||||
return ''
|
||||
}
|
||||
end_pos := pos + 1
|
||||
pos--
|
||||
// Search for the start position
|
||||
for pos > start && util.is_func_char(s.text[pos]) {
|
||||
pos--
|
||||
}
|
||||
pos++
|
||||
start_pos := pos
|
||||
if pos <= start || pos >= s.text.len {
|
||||
return ''
|
||||
}
|
||||
if s.text[start_pos].is_digit() || end_pos > s.text.len || end_pos <= start_pos ||
|
||||
end_pos <= start || start_pos < start {
|
||||
return ''
|
||||
}
|
||||
fn_name := s.text[start_pos..end_pos]
|
||||
return fn_name
|
||||
}
|
||||
|
||||
// ident_mod_name look ahead and return name of module this file belongs to if possible, otherwise empty string
|
||||
fn (s &Scanner) ident_mod_name() string {
|
||||
start := s.pos
|
||||
mut pos := s.pos
|
||||
pos++
|
||||
// Eat whitespaces
|
||||
for pos < s.text.len && s.text[pos].is_space() {
|
||||
pos++
|
||||
}
|
||||
if pos >= s.text.len {
|
||||
return ''
|
||||
}
|
||||
start_pos := pos
|
||||
// Search for next occurrence of a whitespace or newline
|
||||
for pos < s.text.len && !s.text[pos].is_space() && !util.is_nl(s.text[pos]) {
|
||||
pos++
|
||||
}
|
||||
if pos >= s.text.len {
|
||||
return ''
|
||||
}
|
||||
end_pos := pos
|
||||
if end_pos > s.text.len || end_pos <= start_pos || end_pos <= start || start_pos <= start {
|
||||
return ''
|
||||
}
|
||||
mod_name := s.text[start_pos..end_pos]
|
||||
return mod_name
|
||||
}
|
||||
|
||||
// ident_struct_name look ahead and return name of last encountered struct if possible, otherwise empty string
|
||||
fn (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 parenthesis
|
||||
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
|
||||
@ -699,12 +562,6 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||
next_char := s.look_ahead(1)
|
||||
kind := token.keywords[name]
|
||||
if kind != .unknown {
|
||||
if kind == .key_fn {
|
||||
s.struct_name = s.ident_struct_name()
|
||||
s.fn_name = s.ident_fn_name()
|
||||
} else if kind == .key_module {
|
||||
s.mod_name = s.ident_mod_name()
|
||||
}
|
||||
return s.new_token(kind, name, name.len)
|
||||
}
|
||||
// 'asdf $b' => "b" is the last name in the string, dont start parsing string
|
||||
@ -898,61 +755,9 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||
if s.is_fmt {
|
||||
return s.new_token(.name, '@' + name, name.len + 1)
|
||||
}
|
||||
// @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).
|
||||
// @COLUMN => will be substituted with the column where it appears (as a string).
|
||||
// @VHASH => will be substituted with the shortened commit hash of the V compiler (as a string).
|
||||
// @VMOD_FILE => will be substituted with the contents of the nearest v.mod file (as a string).
|
||||
// This allows things like this:
|
||||
// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN)
|
||||
// ... which is useful while debugging/tracing
|
||||
if name == 'FN' {
|
||||
return s.new_token(.string, s.fn_name, 3)
|
||||
}
|
||||
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)
|
||||
}
|
||||
if name == 'FILE' {
|
||||
fpath := os.real_path(s.file_path)
|
||||
return s.new_token(.string, util.cescaped_path(fpath), 5)
|
||||
}
|
||||
if name == 'LINE' {
|
||||
return s.new_token(.string, (s.line_nr + 1).str(), 5)
|
||||
}
|
||||
if name == 'COLUMN' {
|
||||
return s.new_token(.string, s.current_column().str(), 7)
|
||||
}
|
||||
if name == 'VHASH' {
|
||||
return s.new_token(.string, util.vhash(), 6)
|
||||
}
|
||||
if name == 'VMOD_FILE' {
|
||||
if s.vmod_file_content.len == 0 {
|
||||
mut mcache := vmod.get_cache()
|
||||
vmod_file_location := mcache.get_by_file(s.file_path)
|
||||
if vmod_file_location.vmod_file.len == 0 {
|
||||
s.error('@VMOD_FILE can be used only in projects, that have v.mod file')
|
||||
}
|
||||
vmod_content := os.read_file(vmod_file_location.vmod_file) or {
|
||||
''
|
||||
}
|
||||
$if windows {
|
||||
s.vmod_file_content = vmod_content.replace('\r\n', '\n')
|
||||
} $else {
|
||||
s.vmod_file_content = vmod_content
|
||||
}
|
||||
}
|
||||
return s.new_token(.string, s.vmod_file_content, 10)
|
||||
// @FN, @STRUCT, @MOD etc. See full list in token.valid_at_tokens
|
||||
if '@' + name in token.valid_at_tokens {
|
||||
return s.new_token(.at, '@' + name, name.len + 1)
|
||||
}
|
||||
if !token.is_key(name) {
|
||||
s.error('@ must be used before keywords (e.g. `@type string`)')
|
||||
|
@ -20,6 +20,29 @@ fn (mut t TestStruct) test_struct_w_high_order(cb fn (int) string) string {
|
||||
return 'test' + cb(2)
|
||||
}
|
||||
|
||||
struct Abc {
|
||||
}
|
||||
|
||||
fn (a Another) method() string {
|
||||
println(@STRUCT)
|
||||
return @STRUCT
|
||||
}
|
||||
|
||||
struct Another {
|
||||
}
|
||||
|
||||
fn (a Abc) method() string {
|
||||
println(@STRUCT)
|
||||
return @STRUCT
|
||||
}
|
||||
|
||||
fn test_at_struct_ordering() {
|
||||
a := Abc{}
|
||||
assert a.method() == 'Abc'
|
||||
b := Another{}
|
||||
assert b.method() == 'Another'
|
||||
}
|
||||
|
||||
struct TestFn {
|
||||
}
|
||||
|
||||
@ -44,7 +67,15 @@ fn fn_name_mod_level_high_order(cb fn (int)) {
|
||||
fn test_at_file() {
|
||||
// Test @FILE
|
||||
f := os.file_name(@FILE)
|
||||
assert f == 'scanner_at_literals_test.v'
|
||||
$if windows {
|
||||
// TODO all after Drive letter
|
||||
// no_drive := f.all_after(':')
|
||||
// TODO assert the variable name on Windows???
|
||||
// assert no_drive == 'scanner_at_literals_test.v'
|
||||
assert true
|
||||
} $else {
|
||||
assert f == 'scanner_at_literals_test.v'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_at_fn() {
|
||||
|
@ -43,12 +43,12 @@ pub enum Kind {
|
||||
amp
|
||||
hash
|
||||
dollar
|
||||
at // @
|
||||
str_dollar
|
||||
left_shift
|
||||
right_shift
|
||||
not_in // !in
|
||||
not_is // !is
|
||||
// at // @
|
||||
assign // =
|
||||
decl_assign // :=
|
||||
plus_assign // +=
|
||||
@ -137,6 +137,40 @@ const (
|
||||
.right_shift_assign, .left_shift_assign]
|
||||
nr_tokens = int(Kind._end_)
|
||||
)
|
||||
|
||||
// @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).
|
||||
// @COLUMN => will be substituted with the column where it appears (as a string).
|
||||
// @VHASH => will be substituted with the shortened commit hash of the V compiler (as a string).
|
||||
// @VMOD_FILE => will be substituted with the contents of the nearest v.mod file (as a string).
|
||||
// This allows things like this:
|
||||
// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN)
|
||||
// ... which is useful while debugging/tracing
|
||||
//
|
||||
// @VROOT is special and handled in places like '#include ...'
|
||||
// @<type> is allowed for keyword variable names. E.g. 'type'
|
||||
pub enum AtKind {
|
||||
unknown
|
||||
fn_name
|
||||
mod_name
|
||||
struct_name
|
||||
vexe_path
|
||||
file_path
|
||||
line_nr
|
||||
column_nr
|
||||
vhash
|
||||
vmod_file
|
||||
_end_
|
||||
}
|
||||
const (
|
||||
valid_at_tokens = ['@FN','@MOD','@STRUCT','@VEXE','@FILE','@LINE','@COLUMN','@VHASH','@VMOD_FILE']
|
||||
//valid_at_tokens_len = int(AtKind._end_)
|
||||
)
|
||||
|
||||
// build_keys genereates a map with keywords' string values:
|
||||
// Keywords['return'] == .key_return
|
||||
fn build_keys() map[string]Kind {
|
||||
@ -178,7 +212,6 @@ fn build_token_str() []string {
|
||||
s[Kind.comma] = ','
|
||||
s[Kind.not_in] = '!in'
|
||||
s[Kind.not_is] = '!is'
|
||||
// s[Kind.at] = '@'
|
||||
s[Kind.semicolon] = ';'
|
||||
s[Kind.colon] = ':'
|
||||
s[Kind.arrow] = '<-'
|
||||
@ -212,6 +245,7 @@ fn build_token_str() []string {
|
||||
s[Kind.comment] = '// comment'
|
||||
s[Kind.nl] = 'NLL'
|
||||
s[Kind.dollar] = '$'
|
||||
s[Kind.at] = '@'
|
||||
s[Kind.str_dollar] = '$2'
|
||||
s[Kind.key_assert] = 'assert'
|
||||
s[Kind.key_struct] = 'struct'
|
||||
|
@ -79,18 +79,7 @@ pub fn formatted_error(kind string, omsg string, filepath string, pos token.Posi
|
||||
}
|
||||
}
|
||||
//
|
||||
source := read_file(filepath) or {
|
||||
''
|
||||
}
|
||||
mut p := imax(0, imin(source.len - 1, pos.pos))
|
||||
if source.len > 0 {
|
||||
for ; p >= 0; p-- {
|
||||
if source[p] == `\r` || source[p] == `\n` {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
column := imax(0, pos.pos - p - 1)
|
||||
source, column := filepath_pos_to_source_and_column(filepath, pos)
|
||||
position := '$path:${pos.line_nr + 1}:${imax(1, column + 1)}:'
|
||||
scontext := source_context(kind, source, column, pos).join('\n')
|
||||
final_position := bold(position)
|
||||
@ -101,6 +90,24 @@ pub fn formatted_error(kind string, omsg string, filepath string, pos token.Posi
|
||||
return '$final_position $final_kind $final_msg$final_context'.trim_space()
|
||||
}
|
||||
|
||||
pub fn filepath_pos_to_source_and_column(filepath string, pos token.Position) (string, int) {
|
||||
// TODO: optimize this; may be use a cache.
|
||||
// The column should not be so computationally hard to get.
|
||||
source := read_file(filepath) or {
|
||||
''
|
||||
}
|
||||
mut p := imax(0, imin(source.len - 1, pos.pos))
|
||||
if source.len > 0 {
|
||||
for ; p >= 0; p-- {
|
||||
if source[p] == `\n` || source[p] == `\r` {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
column := imax(0, pos.pos - p - 1)
|
||||
return source, column
|
||||
}
|
||||
|
||||
pub fn source_context(kind string, source string, column int, pos token.Position) []string {
|
||||
mut clines := []string{}
|
||||
if source.len == 0 {
|
||||
|
Loading…
Reference in New Issue
Block a user