ast: expr.Position; struct field refactoring

pull/4460/head
Daniel Däschle 2020-04-17 02:38:39 +02:00 committed by GitHub
parent 8bb11d9035
commit 402e55d115
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 223 additions and 173 deletions

View File

@ -157,15 +157,23 @@ pub:
field_names []string
}
pub struct StructInitField {
pub:
name string
expr Expr
pos token.Position
mut:
typ table.Type
expected_type table.Type
}
pub struct StructInit {
pub:
pos token.Position
fields []string
exprs []Expr
pos token.Position
fields []StructInitField
is_short bool
mut:
typ table.Type
expr_types []table.Type
expected_types []table.Type
typ table.Type
}
// import statement
@ -723,3 +731,86 @@ pub fn expr_is_call(expr Expr) bool {
}
}
}
fn (expr Expr) position() token.Position {
// all uncommented have to be implemented
match mut expr {
ArrayInit {
return it.pos
}
AsCast {
return it.pos
}
// ast.Ident { }
AssignExpr {
return it.pos
}
// ast.CastExpr { }
Assoc {
return it.pos
}
// ast.BoolLiteral { }
CallExpr {
return it.pos
}
// ast.CharLiteral { }
EnumVal {
return it.pos
}
// ast.FloatLiteral { }
IfExpr {
return it.pos
}
// ast.IfGuardExpr { }
IndexExpr {
return it.pos
}
InfixExpr {
left_pos := it.left.position()
right_pos := it.right.position()
if left_pos.pos == 0 || right_pos.pos == 0 {
return it.pos
}
return token.Position{
line_nr: it.pos.line_nr
pos: left_pos.pos
len: right_pos.pos - left_pos.pos + right_pos.len
}
}
IntegerLiteral {
return it.pos
}
MapInit {
return it.pos
}
MatchExpr {
return it.pos
}
PostfixExpr {
return it.pos
}
// ast.None { }
PrefixExpr {
return it.pos
}
// ast.ParExpr { }
SelectorExpr {
return it.pos
}
// ast.SizeOf { }
StringLiteral {
return it.pos
}
StringInterLiteral {
return it.pos
}
// ast.Type { }
StructInit {
return it.pos
}
// ast.TypeOf { }
else {
return token.Position{}
}
}
}

View File

@ -123,56 +123,51 @@ pub fn (c mut Checker) struct_init(struct_init mut ast.StructInit) table.Type {
// string & array are also structs but .kind of string/array
.struct_, .string, .array {
info := type_sym.info as table.Struct
is_short_syntax := struct_init.fields.len == 0
if struct_init.exprs.len > info.fields.len {
if struct_init.is_short && struct_init.fields.len > info.fields.len {
c.error('too many fields', struct_init.pos)
}
mut inited_fields := []string
for i, expr in struct_init.exprs {
if is_short_syntax && i >= info.fields.len {
// It doesn't make sense to check for fields that don't exist.
// We should just stop here.
break
}
// struct_field info.
field_name := if is_short_syntax { info.fields[i].name } else { struct_init.fields[i] }
if field_name in inited_fields {
c.error('duplicate field name in struct literal: `$field_name`', struct_init.pos)
continue
}
inited_fields << field_name
mut field := if is_short_syntax {
info.fields[i]
for i, field in struct_init.fields {
mut info_field := table.Field{}
mut field_name := ''
if struct_init.is_short {
if i >= info.fields.len {
// It doesn't make sense to check for fields that don't exist.
// We should just stop here.
break
}
info_field = info.fields[i]
field_name = info_field.name
struct_init.fields[i].name = field_name
} else {
// There is no guarantee that `i` will not be out of bounds of `info.fields`
// So we just use an empty field as placeholder here.
table.Field{}
}
if !is_short_syntax {
mut found_field := false
field_name = field.name
mut exists := false
for f in info.fields {
if f.name == field_name {
field = f
found_field = true
info_field = f
exists = true
break
}
}
if !found_field {
c.error('struct init: no such field `$field_name` for struct `$type_sym.name`',
struct_init.pos)
if !exists {
c.error('struct init: no such field `$field.name` for struct `$type_sym.name`', field.pos)
continue
}
if field_name in inited_fields {
c.error('duplicate field name in struct literal: `$field_name`', field.pos)
continue
}
}
c.expected_type = field.typ
expr_type := c.expr(expr)
inited_fields << field_name
c.expected_type = info_field.typ
expr_type := c.expr(field.expr)
expr_type_sym := c.table.get_type_symbol(expr_type)
field_type_sym := c.table.get_type_symbol(field.typ)
if !c.table.check(expr_type, field.typ) {
c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$field.name`',
struct_init.pos)
field_type_sym := c.table.get_type_symbol(info_field.typ)
if !c.table.check(expr_type, info_field.typ) {
c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`', field.pos)
}
struct_init.expr_types << expr_type
struct_init.expected_types << field.typ
struct_init.fields[i].typ = expr_type
struct_init.fields[i].expected_type = info_field.typ
}
// Check uninitialized refs
for field in info.fields {
@ -206,7 +201,7 @@ pub fn (c mut Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
if infix_expr.op == .left_shift {
if left.kind != .array && !left.is_int() {
// c.error('<< can only be used with numbers and arrays', infix_expr.pos)
c.error('cannot shift type $right.name into $left.name', expr_pos(infix_expr.right))
c.error('cannot shift type $right.name into $left.name', infix_expr.right.position())
return table.void_type
}
if left.kind == .array {
@ -220,7 +215,7 @@ pub fn (c mut Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
// []T << []T
return table.void_type
}
c.error('cannot shift type $right.name into $left.name', expr_pos(infix_expr.right))
c.error('cannot shift type $right.name into $left.name', infix_expr.right.position())
return table.void_type
}
}
@ -279,7 +274,7 @@ fn (c mut Checker) assign_expr(assign_expr mut ast.AssignExpr) {
left_type_sym := c.table.get_type_symbol(left_type)
right_type_sym := c.table.get_type_symbol(right_type)
c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`',
expr_pos(assign_expr.val))
assign_expr.val.position())
}
c.check_expr_opt_call(assign_expr.val, right_type, true)
}
@ -662,7 +657,7 @@ pub fn (c mut Checker) enum_decl(decl ast.EnumDecl) {
ast.IntegerLiteral {}
ast.PrefixExpr {}
else {
mut pos := expr_pos(field.expr)
mut pos := field.expr.position()
if pos.pos == 0 {
pos = field.pos
}
@ -999,7 +994,7 @@ fn (c mut Checker) stmt(node ast.Stmt) {
value_type := c.table.value_type(typ)
if value_type == table.void_type {
typ_sym := c.table.get_type_symbol(typ)
c.error('for in: cannot index `$typ_sym.name`', expr_pos(it.cond))
c.error('for in: cannot index `$typ_sym.name`', it.cond.position())
}
it.cond_type = typ
it.kind = sym.kind
@ -1011,7 +1006,7 @@ fn (c mut Checker) stmt(node ast.Stmt) {
}
ast.GoStmt {
if !is_call_expr(it.call_expr) {
c.error('expression in `go` must be a function call', expr_pos(it.call_expr))1
c.error('expression in `go` must be a function call', it.call_expr.position())
}
c.expr(it.call_expr)
}
@ -1196,89 +1191,6 @@ pub fn (c mut Checker) expr(node ast.Expr) table.Type {
return table.void_type
}
fn expr_pos(node ast.Expr) token.Position {
// all uncommented have to be implemented
match mut node {
ast.ArrayInit {
return it.pos
}
ast.AsCast {
return it.pos
}
// ast.Ident { }
ast.AssignExpr {
return it.pos
}
// ast.CastExpr { }
ast.Assoc {
return it.pos
}
// ast.BoolLiteral { }
ast.CallExpr {
return it.pos
}
// ast.CharLiteral { }
ast.EnumVal {
return it.pos
}
// ast.FloatLiteral { }
ast.IfExpr {
return it.pos
}
// ast.IfGuardExpr { }
ast.IndexExpr {
return it.pos
}
ast.InfixExpr {
left_pos := expr_pos(it.left)
right_pos := expr_pos(it.right)
if left_pos.pos == 0 || right_pos.pos == 0 {
return it.pos
}
return token.Position{
line_nr: it.pos.line_nr
pos: left_pos.pos
len: right_pos.pos - left_pos.pos + right_pos.len
}
}
ast.IntegerLiteral {
return it.pos
}
ast.MapInit {
return it.pos
}
ast.MatchExpr {
return it.pos
}
ast.PostfixExpr {
return it.pos
}
// ast.None { }
ast.PrefixExpr {
return it.pos
}
// ast.ParExpr { }
ast.SelectorExpr {
return it.pos
}
// ast.SizeOf { }
ast.StringLiteral {
return it.pos
}
ast.StringInterLiteral {
return it.pos
}
// ast.Type { }
ast.StructInit {
return it.pos
}
// ast.TypeOf { }
else {
return token.Position{}
}
}
}
pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type {
if ident.name == c.var_decl_name { // c.checked_ident {
c.error('unresolved: `$ident.name`', ident.pos)

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/inout/short_struct_too_many.v:6:7: error: too many fields
4|
5| fn main() {
6| t := Test{true, false}
~~~~~~~~~~~~~~~~~
7| }

View File

@ -0,0 +1,7 @@
struct Test {
foo bool
}
fn main() {
t := Test{true, false}
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/inout/struct_unknown_field.v:8:9: error: struct init: no such field `bar` for struct `Test`
6| t := Test{
7| foo: true
8| bar: false
^
9| }
10| }

View File

@ -0,0 +1,10 @@
struct Test {
foo bool
}
fn main() {
t := Test{
foo: true
bar: false
}
}

View File

@ -695,15 +695,15 @@ fn (f mut Fmt) expr(node ast.Expr) {
if name == 'void' {
name = ''
}
if it.fields.len == 0 && it.exprs.len == 0 {
if it.fields.len == 0 {
// `Foo{}` on one line if there are no fields
f.write('$name{}')
} else if it.fields.len == 0 {
// `Foo{1,2,3}` (short syntax )
f.write('$name{')
for i, expr in it.exprs {
f.expr(expr)
if i < it.exprs.len - 1 {
for i, field in it.fields {
f.expr(field.expr)
if i < it.fields.len - 1 {
f.write(', ')
}
}
@ -712,8 +712,8 @@ fn (f mut Fmt) expr(node ast.Expr) {
f.writeln('$name{')
f.indent++
for i, field in it.fields {
f.write('$field: ')
f.expr(it.exprs[i])
f.write('$field.name: ')
f.expr(field.expr)
f.writeln('')
}
f.indent--

View File

@ -1918,22 +1918,22 @@ fn (var g Gen) struct_init(struct_init ast.StructInit) {
} else {
g.writeln('($styp){')
}
var fields := []string
// var fields := []string
var inited_fields := []string // TODO this is done in checker, move to ast node
if struct_init.fields.len == 0 && struct_init.exprs.len > 0 {
/*if struct_init.fields.len == 0 && struct_init.exprs.len > 0 {
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
for f in info.fields {
fields << f.name
}
} else {
fields = struct_init.fields
}
}*/
// User set fields
for i, field in fields {
field_name := c_name(field)
inited_fields << field
for i, field in struct_init.fields {
field_name := c_name(field.name)
inited_fields << field.name
g.write('\t.$field_name = ')
g.expr_with_cast(struct_init.exprs[i], struct_init.expr_types[i], struct_init.expected_types[i])
g.expr_with_cast(field.expr, field.typ, field.expected_type)
g.writeln(',')
}
// The rest of the fields are zeroed.

View File

@ -27,7 +27,7 @@ struct JsGen {
pref &pref.Preferences
doc &JsDoc
mut:
constants strings.Builder // all global V constants
constants strings.Builder // all global V constants
file ast.File
tmp_count int
inside_ternary bool
@ -178,7 +178,7 @@ fn (g mut JsGen) to_js_typ(typ string) string {
}
'voidptr' {
styp = 'Object'
}
}
'byteptr' {
styp = 'string'
}
@ -196,7 +196,7 @@ fn (g mut JsGen) to_js_typ(typ string) string {
}
}
return styp
}
}
pub fn (g &JsGen) save() {}
@ -385,12 +385,12 @@ fn (g mut JsGen) expr(node ast.Expr) {
}
ast.InfixExpr {
g.expr(it.left)
mut op := it.op.str()
// in js == is non-strict & === is strict, always do strict
if op == '==' { op = '===' }
else if op == '!=' { op = '!==' }
g.write(' $op ')
g.expr(it.right)
}
@ -461,7 +461,7 @@ fn (g mut JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
.struct_ {
g.expr(expr)
if sym.has_method('str') {
g.write('.str()')
g.write('.str()')
}
}
else {
@ -543,7 +543,7 @@ fn (g mut JsGen) gen_assign_stmt(it ast.AssignStmt) {
val := it.right[i]
ident_var_info := ident.var_info()
mut styp := g.typ(ident_var_info.typ)
match val {
ast.EnumVal {
// we want the type of the enum value not the enum
@ -554,11 +554,11 @@ fn (g mut JsGen) gen_assign_stmt(it ast.AssignStmt) {
styp = ''
} else {}
}
if !g.inside_loop && styp.len > 0 {
g.writeln(g.doc.gen_typ(styp, ident.name))
}
if g.inside_loop || ident.is_mut {
g.write('let ')
} else {
@ -726,7 +726,7 @@ fn (g mut JsGen) gen_method_decl(it ast.FnDecl) {
g.write(')();')
}
g.writeln('')
g.fn_decl = 0
}
@ -835,7 +835,7 @@ fn (g mut JsGen) fn_args(args []table.Arg, is_variadic bool) {
fn (g mut JsGen) gen_go_stmt(node ast.GoStmt) {
// x := node.call_expr as ast.CallEpxr // TODO
match node.call_expr {
ast.CallExpr {
ast.CallExpr {
mut name := it.name
if it.is_method {
receiver_sym := g.table.get_type_symbol(it.receiver_type)
@ -964,10 +964,10 @@ fn (g mut JsGen) gen_struct_init(it ast.StructInit) {
g.writeln('new ${type_sym.name}({')
g.inc_indent()
for i, field in it.fields {
g.write('$field: ')
g.expr(it.exprs[i])
g.write('$field.name: ')
g.expr(field.expr)
if i < it.fields.len - 1 {
g.write(', ')
g.write(', ')
}
g.writeln('')
}
@ -975,7 +975,7 @@ fn (g mut JsGen) gen_struct_init(it ast.StructInit) {
g.write('})')
}
fn (g mut JsGen) gen_ident(node ast.Ident) {
fn (g mut JsGen) gen_ident(node ast.Ident) {
if node.kind == .constant {
g.write('CONSTANTS.')
}
@ -1061,4 +1061,4 @@ fn fn_has_go(it ast.FnDecl) bool {
}
}
return has_go
}
}

View File

@ -570,6 +570,7 @@ pub fn (var p Parser) parse_ident(is_c, is_js bool) ast.Ident {
}
fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
first_pos := p.tok.position()
typ := if short_syntax { table.void_type } else { p.parse_type() }
p.expr_mod = ''
// sym := p.table.get_type_symbol(typ)
@ -577,8 +578,7 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
if !short_syntax {
p.check(.lcbr)
}
var field_names := []string
var exprs := []ast.Expr
var fields := []ast.StructInitField
var i := 0
is_short_syntax := p.peek_tok.kind != .colon && p.tok.kind != .rcbr // `Vec{a,b,c}
// p.warn(is_short_syntax.str())
@ -587,15 +587,27 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
var field_name := ''
if is_short_syntax {
expr := p.expr(0)
exprs << expr
fields << ast.StructInitField{
// name will be set later in checker
expr: expr
pos: expr.position()
}
} else {
first_field_pos := p.tok.position()
field_name = p.check_name()
field_names << field_name
}
if !is_short_syntax {
p.check(.colon)
expr := p.expr(0)
exprs << expr
last_field_pos := expr.position()
field_pos := token.Position{
line_nr: first_field_pos.line_nr
pos: first_field_pos.pos
len: last_field_pos.pos - first_field_pos.pos + last_field_pos.len
}
fields << ast.StructInitField{
name: field_name
expr: expr
pos: field_pos
}
}
i++
if p.tok.kind == .comma {
@ -603,15 +615,20 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
}
p.check_comment()
}
node := ast.StructInit{
typ: typ
exprs: exprs
fields: field_names
pos: p.tok.position()
}
last_pos := p.tok.position()
if !short_syntax {
p.check(.rcbr)
}
node := ast.StructInit{
typ: typ
fields: fields
pos: token.Position{
line_nr: first_pos.line_nr
pos: first_pos.pos
len: last_pos.pos - first_pos.pos + last_pos.len
}
is_short: is_short_syntax
}
return node
}