From b17ade1257cfe086c1742c91deeb6c606037b893 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 24 Feb 2020 22:45:47 +0200 Subject: [PATCH] compiler: implement typeof(x) --- vlib/compiler/aparser.v | 13 ++++++ vlib/compiler/cheaders.v | 1 + vlib/compiler/expression.v | 21 +++++++-- vlib/compiler/gen_c.v | 16 ++++++- vlib/compiler/table.v | 1 + vlib/compiler/tests/typeof_test.v | 74 +++++++++++++++++++++++++++++++ vlib/compiler/token.v | 4 +- 7 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 vlib/compiler/tests/typeof_test.v diff --git a/vlib/compiler/aparser.v b/vlib/compiler/aparser.v index 6575efd666..01b673fe0d 100644 --- a/vlib/compiler/aparser.v +++ b/vlib/compiler/aparser.v @@ -843,9 +843,13 @@ fn (p mut Parser) type_decl() { is_public: is_pub }) */ + if p.pass == .main { + p.cgen.consts << '//// SUMTYPE: ${p.mod} | parent: ${name} | name: ${parent.name}' + } // Register the rest of them mut idx := 0 mut done := false + mut ctype_names := []string for { // p.tok == .pipe { idx++ @@ -865,6 +869,7 @@ fn (p mut Parser) type_decl() { t.parent = name p.table.rewrite_type(t) p.cgen.consts << '#define SumType_$child_type_name $idx // DEF2' + ctype_names << child_type_name } if done { break @@ -888,7 +893,15 @@ fn (p mut Parser) type_decl() { mod: p.mod cat: .alias is_public: is_pub + ctype_names: ctype_names }) + if p.pass == .main { + p.cgen.consts << 'const char * __SumTypeNames__${name}[] = {' + for ctype_name in ctype_names { + p.cgen.consts << ' "$ctype_name",' + } + p.cgen.consts << '};' + } p.gen_typedef('typedef struct { void* obj; int typ; diff --git a/vlib/compiler/cheaders.v b/vlib/compiler/cheaders.v index 5b13f581a3..c9911698c0 100644 --- a/vlib/compiler/cheaders.v +++ b/vlib/compiler/cheaders.v @@ -109,6 +109,7 @@ $c_common_macros #include // _wgetcwd //#include #ifdef _MSC_VER + // On MSVC these are the same (as long as /volatile:ms is passed) #define _Atomic volatile diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index 26241c87f4..ade220f44b 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -68,8 +68,7 @@ fn (p mut Parser) bool_expression() string { //p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type') T := p.table.find_type(typ) if T.parent == expected { - p.cgen.set_placeholder(start_ph, - '/*SUM TYPE CAST2*/($expected) { .obj = memdup( &($typ[]) { ') + p.cgen.set_placeholder(start_ph, '/*SUM TYPE CAST2*/ ($expected) { .obj = memdup( &($typ[]) { ') tt := typ.all_after('_') // TODO p.gen('}, sizeof($typ) ), .typ = SumType_${tt} }')//${val}_type }') } @@ -824,13 +823,29 @@ fn (p mut Parser) factor() string { // p.fgen('$sizeof_typ)') return 'int' } + .key_typeof { + p.next() + p.check(.lpar) + p.cgen.nogen = true + vname := if p.tok == .name && p.peek() == .rpar { p.lit } else { '' } + type_of_var := p.expression() + p.cgen.nogen = false + p.check(.rpar) + is_sum_type := type_of_var in p.table.sum_types + if is_sum_type && vname.len > 0 { + // TODO: make this work for arbitrary sumtype expressions, not just simple vars + p.gen('tos3(__SumTypeNames__${type_of_var}[${vname}.typ - 1])') + }else{ + p.gen('tos3("$type_of_var")') + } + return 'string' + } .key_nameof { p.next() p.check(.lpar) mut nameof_typ := p.get_type() p.check(.rpar) p.gen('tos3("$nameof_typ")') -// return 'byteptr' return 'string' } .key_offsetof { diff --git a/vlib/compiler/gen_c.v b/vlib/compiler/gen_c.v index 3bad77693f..bb2b83b7c1 100644 --- a/vlib/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -627,7 +627,21 @@ fn (p mut Parser) cast(typ string) { if expr_typ == 'bool' { p.error('cannot cast `bool` to `$typ`') } - p.cgen.set_placeholder(pos, '($typ)(') + if typ != expr_typ && typ in p.table.sum_types { + T := p.table.find_type(typ) + if expr_typ in T.ctype_names { + // There is no need for a cast here, since it was already done + // in p.bool_expression, SUM TYPE CAST2 . Besides, doubling the + // cast here causes MSVC to complain with: + // error C2440: 'type cast': cannot convert from 'ExprType' to 'ExprType' + p.cgen.set_placeholder(pos, '(') + }else{ + p.warn('only $T.ctype_names can be casted to `$typ`') + p.error('cannot cast `$expr_typ` to `$typ`') + } + }else{ + p.cgen.set_placeholder(pos, '($typ)(') + } } p.check(.rpar) p.gen(')') diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index e380bd2eec..87fa93cecd 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -127,6 +127,7 @@ pub mut: is_flag bool // enum bitfield flag // max_field_len int is_generic bool + ctype_names []string } struct TypeNode { diff --git a/vlib/compiler/tests/typeof_test.v b/vlib/compiler/tests/typeof_test.v new file mode 100644 index 0000000000..b3a1712a20 --- /dev/null +++ b/vlib/compiler/tests/typeof_test.v @@ -0,0 +1,74 @@ + +fn test_typeof_on_simple_expressions() { + a := 123 + assert typeof(42) == 'int' + assert typeof(3.14) == 'f32' + assert typeof(2+2*10) == 'int' + assert typeof(1.0 * 12.2) == 'f32' + assert typeof(a) == 'int' +} + +fn test_typeof_on_atypes(){ + aint := []int + astring := []string + assert typeof(aint) == 'array_int' + assert typeof(astring) == 'array_string' +} + +struct FooBar { + x int +} + +fn test_typeof_on_structs(){ + assert typeof(FooBar{}) == "FooBar" + astruct_static := [2]FooBar + astruct_dynamic := [FooBar{}, FooBar{}] + assert typeof(astruct_static) == '[2]FooBar' + assert typeof(astruct_dynamic) == 'array_FooBar' +} + +type MySumType = int | f32 | FooBar +pub fn (ms MySumType) str() string { + match ms { + int { return it.str() } + f32 { return it.str() } + //FooBar { return it.x.str() } + else { return 'unknown: ' + typeof(ms) } + } +} + +fn test_typeof_on_sumtypes(){ + a := MySumType(32) + b := MySumType(123.0) + c := MySumType(FooBar{x:43}) + assert typeof(a) == 'int' + assert typeof(b) == 'f32' + assert typeof(c) == 'FooBar' +} + +// + +struct UnaryExpr { a string } +struct BinExpr { a string b string } +struct BoolExpr { z int } +type ExprType = BoolExpr | BinExpr | UnaryExpr + +fn fexpr(k int) ExprType { + match k { + 1 { return UnaryExpr{} } + 2 { return BinExpr{} } + 3 { return BoolExpr{} } + else { return UnaryExpr{} } + } +} + +fn test_typeof_on_sumtypes_of_structs() { + a := fexpr(1) + b := fexpr(2) + c := fexpr(3) + d := ExprType( UnaryExpr{} ) + assert typeof(a) == 'UnaryExpr' + assert typeof(b) == 'BinExpr' + assert typeof(c) == 'BoolExpr' + assert typeof(d) == 'UnaryExpr' +} diff --git a/vlib/compiler/token.v b/vlib/compiler/token.v index e0a4bce0f8..d9900170e2 100644 --- a/vlib/compiler/token.v +++ b/vlib/compiler/token.v @@ -115,7 +115,7 @@ enum TokenKind { key_switch key_true key_type - // typeof + key_typeof key_orelse key_union key_pub @@ -224,7 +224,7 @@ fn build_token_str() []string { s[TokenKind.key_import] = 'import' s[TokenKind.key_embed] = 'embed' s[TokenKind.key_unsafe] = 'unsafe' - // Tokens[key_typeof] = 'typeof' + s[TokenKind.key_typeof] = 'typeof' s[TokenKind.key_enum] = 'enum' s[TokenKind.key_interface] = 'interface' s[TokenKind.key_pub] = 'pub'