From 803ded3decd078451fb72625d832228ecbc974c0 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 24 Dec 2019 11:07:26 +0300 Subject: [PATCH] sum type `type Foo = Bar | Baz` --- vlib/compiler/expression.v | 9 +++- vlib/compiler/parser.v | 79 ++++++++++++++++++++++++++++----- vlib/compiler/table.v | 16 ++++++- vlib/compiler/tests/type_test.v | 45 +++++++++++++++++++ 4 files changed, 137 insertions(+), 12 deletions(-) diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index 490031b8c1..de11e6c2dc 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -4,6 +4,8 @@ module compiler fn (p mut Parser) bool_expression() string { + start_ph := p.cgen.add_placeholder() + expected := p.expected_type tok := p.tok typ := p.bterm() mut got_and := false // to catch `a && b || c` in one expression without () @@ -44,6 +46,11 @@ fn (p mut Parser) bool_expression() string { println(tok.str()) p.error('expr() returns empty type') } + if expected != typ && expected in p.table.sum_types { // TODO perf + p.cgen.set_placeholder(start_ph, '/*KUK*/($expected) { .obj = ($typ[]) { ') + p.gen('}, .typ = 1}')//${val}_type }') + + } return typ } @@ -362,7 +369,7 @@ fn (p mut Parser) name_expr() string { //println(q) //println(q[idx]) arg_type := q[idx] - p.gen('($enum_type.name) { .obj = ($arg_type[]) { ') + p.gen('($enum_type.name) { .obj = ($arg_type[]) { ') p.bool_expression() p.check(.rpar) p.gen('}, .typ = ${val}_type }') diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index 8c7801c9cc..6562792291 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -160,7 +160,7 @@ fn (v mut V) new_parser_from_file(path string) Parser { break } } - + if v.compile_defines.len > 0 { for cdefine in v.compile_defines { custom_path_ending := '_d_${cdefine}.v' @@ -171,7 +171,7 @@ fn (v mut V) new_parser_from_file(path string) Parser { } } } - + mut p := v.new_parser(new_scanner_file(path)) p = { p | @@ -793,20 +793,79 @@ fn (p mut Parser) type_decl() { if p.tok == .key_struct { p.error('use `struct $name {` instead of `type $name struct {`') } + if p.tok == .assign { + p.next() + + } parent := p.get_type2() + // Sum type + is_sum := p.tok == .pipe + if is_sum { + // Register the first child (name we already parsed) + /* + p.table.register_type(Type{ + parent: name + name: parent.name // yeah it's not a parent here + mod: p.mod + is_public: is_pub + }) + */ + // Register the rest of them + for p.tok == .pipe { + p.next() + child := p.check_name() + if p.pass == .main { + // Update the type's parent + println('child=$child parent=$name') + mut t := p.table.find_type(child) + if t.name == '' { + p.error('unknown type `$child`') + } + t.parent = name + p.table.rewrite_type(t) + /* + p.table.register_type(Type{ + parent: name + name: child + mod: p.mod + is_public: is_pub + }) + */ + } + } + if p.pass == .decl { + p.table.sum_types << name + println(p.table.sum_types) + } + // Register the actual sum type + println('reging sum $name') + p.table.register_type(Type{ + name: name + mod: p.mod + cat: .alias + is_public: is_pub + }) + p.gen_typedef('typedef struct { +void* obj; +int typ; +} $name; +') + } nt_pair := p.table.cgen_name_type_pair(name, parent.name) // TODO dirty C typedef hacks for DOOM // Unknown type probably means it's a struct, and it's used before the struct is defined, // so specify "struct" _struct := if parent.cat != .array && parent.cat != .func && !p.table.known_type(parent.name) { 'struct' } else { '' } - p.gen_typedef('typedef $_struct $nt_pair; //type alias name="$name" parent=`$parent.name`') - p.table.register_type(Type{ - name: name - parent: parent.name - mod: p.mod - cat: .alias - is_public: is_pub - }) + if !is_sum { + p.gen_typedef('typedef $_struct $nt_pair; //type alias name="$name" parent=`$parent.name`') + p.table.register_type(Type{ + name: name + parent: parent.name + mod: p.mod + cat: .alias + is_public: is_pub + }) + } if p.tok != .key_type { p.fspace() } diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index f4dfed78d6..f74103cbea 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -21,7 +21,8 @@ pub mut: // names []Name max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12 generic_struct_params map[string][]string - tuple_variants map[string][]string + tuple_variants map[string][]string // enum( Bool(BoolExpr) ) + sum_types []string } struct VargAccess { @@ -593,6 +594,9 @@ fn (t &Table) find_type(name_ string) Type { } fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool { + if p.fileis('type_test') { + println('got=$got_ exp=$expected_') + } mut got := got_ mut expected := expected_ // p.log('check types got="$got" exp="$expected" ') @@ -725,6 +729,16 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool { return true } } + // Sum type + println(expected) + if expected in p.table.sum_types { + println('checking sum') + child := p.table.find_type(got) + if child.parent == expected { + println('yep $expected') + return true + } + } if !throw { return false } diff --git a/vlib/compiler/tests/type_test.v b/vlib/compiler/tests/type_test.v index 688bd0669d..4509b11f7f 100644 --- a/vlib/compiler/tests/type_test.v +++ b/vlib/compiler/tests/type_test.v @@ -17,3 +17,48 @@ fn test_person_str() { println(p) assert p.str() == 'Person: Bilbo' } + +struct Foo {} + +struct WTF { + wtf int +} + +struct BoolExpr { + foo int + +} + +struct BinExpr { + +} + +struct UnaryExpr { + +} + +type Expr = Foo | BoolExpr | BinExpr | UnaryExpr + +fn handle_expr(e Expr) { + +} + +fn parse_bool() BoolExpr { + return BoolExpr{} +} + +fn test_sum() { + b := parse_bool() + handle_expr(b) +} + +/* +#define ExprType_BoolExpr 0 +#define ExprType_BinExpr 1 +#define ExprType_UnaryExpr 2 + +struct Expr { + int typ; + void* obj; +} +*/