ast: expr.Position; struct field refactoring
parent
8bb11d9035
commit
402e55d115
|
@ -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
|
||||
fields []StructInitField
|
||||
is_short bool
|
||||
mut:
|
||||
typ table.Type
|
||||
expr_types []table.Type
|
||||
expected_types []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{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
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
|
||||
}
|
||||
// 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]
|
||||
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)
|
||||
|
|
|
@ -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| }
|
|
@ -0,0 +1,7 @@
|
|||
struct Test {
|
||||
foo bool
|
||||
}
|
||||
|
||||
fn main() {
|
||||
t := Test{true, false}
|
||||
}
|
|
@ -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| }
|
|
@ -0,0 +1,10 @@
|
|||
struct Test {
|
||||
foo bool
|
||||
}
|
||||
|
||||
fn main() {
|
||||
t := Test{
|
||||
foo: true
|
||||
bar: false
|
||||
}
|
||||
}
|
|
@ -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--
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -964,8 +964,8 @@ 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(', ')
|
||||
}
|
||||
|
|
|
@ -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
|
||||
} else {
|
||||
field_name = p.check_name()
|
||||
field_names << field_name
|
||||
fields << ast.StructInitField{
|
||||
// name will be set later in checker
|
||||
expr: expr
|
||||
pos: expr.position()
|
||||
}
|
||||
if !is_short_syntax {
|
||||
} else {
|
||||
first_field_pos := p.tok.position()
|
||||
field_name = p.check_name()
|
||||
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue