compiler: implement typeof(x)

pull/3829/head
Delyan Angelov 2020-02-24 22:45:47 +02:00 committed by GitHub
parent 794ee6fc9d
commit b17ade1257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 124 additions and 6 deletions

View File

@ -843,9 +843,13 @@ fn (p mut Parser) type_decl() {
is_public: is_pub is_public: is_pub
}) })
*/ */
if p.pass == .main {
p.cgen.consts << '//// SUMTYPE: ${p.mod} | parent: ${name} | name: ${parent.name}'
}
// Register the rest of them // Register the rest of them
mut idx := 0 mut idx := 0
mut done := false mut done := false
mut ctype_names := []string
for { for {
// p.tok == .pipe { // p.tok == .pipe {
idx++ idx++
@ -865,6 +869,7 @@ fn (p mut Parser) type_decl() {
t.parent = name t.parent = name
p.table.rewrite_type(t) p.table.rewrite_type(t)
p.cgen.consts << '#define SumType_$child_type_name $idx // DEF2' p.cgen.consts << '#define SumType_$child_type_name $idx // DEF2'
ctype_names << child_type_name
} }
if done { if done {
break break
@ -888,7 +893,15 @@ fn (p mut Parser) type_decl() {
mod: p.mod mod: p.mod
cat: .alias cat: .alias
is_public: is_pub 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 { p.gen_typedef('typedef struct {
void* obj; void* obj;
int typ; int typ;

View File

@ -109,6 +109,7 @@ $c_common_macros
#include <direct.h> // _wgetcwd #include <direct.h> // _wgetcwd
//#include <WinSock2.h> //#include <WinSock2.h>
#ifdef _MSC_VER #ifdef _MSC_VER
// On MSVC these are the same (as long as /volatile:ms is passed) // On MSVC these are the same (as long as /volatile:ms is passed)
#define _Atomic volatile #define _Atomic volatile

View File

@ -68,8 +68,7 @@ fn (p mut Parser) bool_expression() string {
//p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type') //p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type')
T := p.table.find_type(typ) T := p.table.find_type(typ)
if T.parent == expected { if T.parent == expected {
p.cgen.set_placeholder(start_ph, p.cgen.set_placeholder(start_ph, '/*SUM TYPE CAST2*/ ($expected) { .obj = memdup( &($typ[]) { ')
'/*SUM TYPE CAST2*/($expected) { .obj = memdup( &($typ[]) { ')
tt := typ.all_after('_') // TODO tt := typ.all_after('_') // TODO
p.gen('}, sizeof($typ) ), .typ = SumType_${tt} }')//${val}_type }') p.gen('}, sizeof($typ) ), .typ = SumType_${tt} }')//${val}_type }')
} }
@ -824,13 +823,29 @@ fn (p mut Parser) factor() string {
// p.fgen('$sizeof_typ)') // p.fgen('$sizeof_typ)')
return 'int' 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 { .key_nameof {
p.next() p.next()
p.check(.lpar) p.check(.lpar)
mut nameof_typ := p.get_type() mut nameof_typ := p.get_type()
p.check(.rpar) p.check(.rpar)
p.gen('tos3("$nameof_typ")') p.gen('tos3("$nameof_typ")')
// return 'byteptr'
return 'string' return 'string'
} }
.key_offsetof { .key_offsetof {

View File

@ -627,8 +627,22 @@ fn (p mut Parser) cast(typ string) {
if expr_typ == 'bool' { if expr_typ == 'bool' {
p.error('cannot cast `bool` to `$typ`') p.error('cannot cast `bool` to `$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.cgen.set_placeholder(pos, '($typ)(')
} }
}
p.check(.rpar) p.check(.rpar)
p.gen(')') p.gen(')')
} }

View File

@ -127,6 +127,7 @@ pub mut:
is_flag bool // enum bitfield flag is_flag bool // enum bitfield flag
// max_field_len int // max_field_len int
is_generic bool is_generic bool
ctype_names []string
} }
struct TypeNode { struct TypeNode {

View File

@ -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'
}

View File

@ -115,7 +115,7 @@ enum TokenKind {
key_switch key_switch
key_true key_true
key_type key_type
// typeof key_typeof
key_orelse key_orelse
key_union key_union
key_pub key_pub
@ -224,7 +224,7 @@ fn build_token_str() []string {
s[TokenKind.key_import] = 'import' s[TokenKind.key_import] = 'import'
s[TokenKind.key_embed] = 'embed' s[TokenKind.key_embed] = 'embed'
s[TokenKind.key_unsafe] = 'unsafe' s[TokenKind.key_unsafe] = 'unsafe'
// Tokens[key_typeof] = 'typeof' s[TokenKind.key_typeof] = 'typeof'
s[TokenKind.key_enum] = 'enum' s[TokenKind.key_enum] = 'enum'
s[TokenKind.key_interface] = 'interface' s[TokenKind.key_interface] = 'interface'
s[TokenKind.key_pub] = 'pub' s[TokenKind.key_pub] = 'pub'