v2: more informative assert output; string interpolation formatting

pull/4124/head
Delyan Angelov 2020-03-26 20:17:14 +02:00 committed by GitHub
parent 6892a3e0a8
commit f489c89987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 263 additions and 181 deletions

View File

@ -99,7 +99,7 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
//////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames) //////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames)
csymbols := backtrace_symbols(&buffer[skipframes], nr_actual_frames) csymbols := backtrace_symbols(&buffer[skipframes], nr_actual_frames)
for i in 0 .. nr_actual_frames { for i in 0 .. nr_actual_frames {
sframes << tos2(csymbols[i]) sframes << tos2( byteptr( voidptr(csymbols[i]) ) )
} }
for sframe in sframes { for sframe in sframes {
executable := sframe.all_before('(') executable := sframe.all_before('(')

View File

@ -0,0 +1,116 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
fn test_strip_margins_no_tabs() {
no_tabs := ['Hello there',
'This is a string',
'With multiple lines',
].join('\n')
no_tabs_stripped := 'Hello there
|This is a string
|With multiple lines'.strip_margin()
assert no_tabs == no_tabs_stripped
}
fn test_strip_margins_text_before() {
text_before := ['There is text',
'before the delimiter',
'that should be removed as well',
].join('\n')
text_before_stripped := 'There is text
f lasj asldfj j lksjdf |before the delimiter
Which is removed hello |that should be removed as well'.strip_margin()
assert text_before_stripped == text_before
}
fn test_strip_margins_white_space_after_delim() {
tabs := [' Tab',
' spaces',
' another tab',
].join('\n')
tabs_stripped := ' Tab
| spaces
| another tab'.strip_margin()
assert tabs == tabs_stripped
}
fn test_strip_margins_alternate_delim() {
alternate_delimiter := ['This has a different delim,',
'but that is ok',
'because everything works',
].join('\n')
alternate_delimiter_stripped := 'This has a different delim,
#but that is ok
#because everything works'.strip_margin(`#`)
assert alternate_delimiter_stripped == alternate_delimiter
}
fn test_strip_margins_multiple_delims_after_first() {
delim_after_first_instance := ['The delimiter used',
'only matters the |||| First time it is seen',
'not any | other | times',
].join('\n')
delim_after_first_instance_stripped := 'The delimiter used
|only matters the |||| First time it is seen
|not any | other | times'.strip_margin()
assert delim_after_first_instance_stripped == delim_after_first_instance
}
fn test_strip_margins_uneven_delims() {
uneven_delims := ['It doesn\'t matter if the delims are uneven,',
'The text will still be delimited correctly.',
'Maybe not everything needs 3 lines?',
'Let us go for 4 then',
].join('\n')
uneven_delims_stripped := 'It doesn\'t matter if the delims are uneven,
|The text will still be delimited correctly.
|Maybe not everything needs 3 lines?
|Let us go for 4 then'.strip_margin()
assert uneven_delims_stripped == uneven_delims
}
fn test_strip_margins_multiple_blank_lines() {
multi_blank_lines := ['Multiple blank lines will be removed.',
' I actually consider this a feature.',
].join('\n')
multi_blank_lines_stripped := 'Multiple blank lines will be removed.
| I actually consider this a feature.'.strip_margin()
assert multi_blank_lines == multi_blank_lines_stripped
}
fn test_strip_margins_end_newline() {
end_with_newline := ['This line will end with a newline',
'Something cool or something.',
'',
].join('\n')
end_with_newline_stripped := 'This line will end with a newline
|Something cool or something.
'.strip_margin()
assert end_with_newline_stripped == end_with_newline
}
fn test_strip_margins_space_delimiter() {
space_delimiter := ['Using a white-space char will',
'revert back to default behavior.',
].join('\n')
space_delimiter_stripped := 'Using a white-space char will
|revert back to default behavior.'.strip_margin(`\n`)
assert space_delimiter == space_delimiter_stripped
}
fn test_strip_margins_crlf() {
crlf := ['This string\'s line endings have CR as well as LFs.',
'This should pass',
'Definitely',
].join('\r\n')
crlf_stripped := 'This string\'s line endings have CR as well as LFs.\r
|This should pass\r
|Definitely'.strip_margin()
assert crlf == crlf_stripped
}

View File

@ -20,11 +20,6 @@ fn test_add() {
assert a.ends_with('bbbbb') assert a.ends_with('bbbbb')
a += '123' a += '123'
assert a.ends_with('3') assert a.ends_with('3')
mut foo := Foo{10, 'hi'}
assert foo.str == 'hi'
assert foo.bar == 10
foo.str += '!'
assert foo.str == 'hi!'
} }
fn test_ends_with() { fn test_ends_with() {
@ -481,20 +476,6 @@ fn test_reverse() {
assert 'a'.reverse() == 'a' assert 'a'.reverse() == 'a'
} }
fn (f Foo) baz() string {
return 'baz'
}
fn test_interpolation() {
num := 7
mut s := 'number=$num'
assert s == 'number=7'
foo := Foo{}
s = 'baz=${foo.baz()}'
assert s == 'baz=baz'
}
fn test_bytes_to_string() { fn test_bytes_to_string() {
mut buf := vcalloc(10) mut buf := vcalloc(10)
buf[0] = `h` buf[0] = `h`
@ -703,115 +684,3 @@ fn test_split_into_lines() {
} }
} }
fn test_strip_margins_no_tabs() {
no_tabs := ['Hello there',
'This is a string',
'With multiple lines',
].join('\n')
no_tabs_stripped := 'Hello there
|This is a string
|With multiple lines'.strip_margin()
assert no_tabs == no_tabs_stripped
}
fn test_strip_margins_text_before() {
text_before := ['There is text',
'before the delimiter',
'that should be removed as well',
].join('\n')
text_before_stripped := 'There is text
f lasj asldfj j lksjdf |before the delimiter
Which is removed hello |that should be removed as well'.strip_margin()
assert text_before_stripped == text_before
}
fn test_strip_margins_white_space_after_delim() {
tabs := [' Tab',
' spaces',
' another tab',
].join('\n')
tabs_stripped := ' Tab
| spaces
| another tab'.strip_margin()
assert tabs == tabs_stripped
}
fn test_strip_margins_alternate_delim() {
alternate_delimiter := ['This has a different delim,',
'but that is ok',
'because everything works',
].join('\n')
alternate_delimiter_stripped := 'This has a different delim,
#but that is ok
#because everything works'.strip_margin(`#`)
assert alternate_delimiter_stripped == alternate_delimiter
}
fn test_strip_margins_multiple_delims_after_first() {
delim_after_first_instance := ['The delimiter used',
'only matters the |||| First time it is seen',
'not any | other | times',
].join('\n')
delim_after_first_instance_stripped := 'The delimiter used
|only matters the |||| First time it is seen
|not any | other | times'.strip_margin()
assert delim_after_first_instance_stripped == delim_after_first_instance
}
fn test_strip_margins_uneven_delims() {
uneven_delims := ['It doesn\'t matter if the delims are uneven,',
'The text will still be delimited correctly.',
'Maybe not everything needs 3 lines?',
'Let us go for 4 then',
].join('\n')
uneven_delims_stripped := 'It doesn\'t matter if the delims are uneven,
|The text will still be delimited correctly.
|Maybe not everything needs 3 lines?
|Let us go for 4 then'.strip_margin()
assert uneven_delims_stripped == uneven_delims
}
fn test_strip_margins_multiple_blank_lines() {
multi_blank_lines := ['Multiple blank lines will be removed.',
' I actually consider this a feature.',
].join('\n')
multi_blank_lines_stripped := 'Multiple blank lines will be removed.
| I actually consider this a feature.'.strip_margin()
assert multi_blank_lines == multi_blank_lines_stripped
}
fn test_strip_margins_end_newline() {
end_with_newline := ['This line will end with a newline',
'Something cool or something.',
'',
].join('\n')
end_with_newline_stripped := 'This line will end with a newline
|Something cool or something.
'.strip_margin()
assert end_with_newline_stripped == end_with_newline
}
fn test_strip_margins_space_delimiter() {
space_delimiter := ['Using a white-space char will',
'revert back to default behavior.',
].join('\n')
space_delimiter_stripped := 'Using a white-space char will
|revert back to default behavior.'.strip_margin(`\n`)
assert space_delimiter == space_delimiter_stripped
}
fn test_strip_margins_crlf() {
crlf := ['This string\'s line endings have CR as well as LFs.',
'This should pass',
'Definitely',
].join('\r\n')
crlf_stripped := 'This string\'s line endings have CR as well as LFs.\r
|This should pass\r
|Definitely'.strip_margin()
assert crlf == crlf_stripped
}

View File

@ -1,36 +1,57 @@
fn test_simple_string_interpolation() {
fn test_simple_string_interpolation(){
a := 'Hello' a := 'Hello'
b := 'World' b := 'World'
res := '$a $b' res := '$a $b'
assert res == 'Hello World' assert res == 'Hello World'
} }
fn test_mixed_string_interpolation() {
num := 7
str := 'abc'
s1 := 'number=$num'
assert s1 == 'number=7'
s2 := 'string=$str'
assert s2 == 'string=abc'
s3 := 'a: $num | b: $str'
assert s3 == 'a: 7 | b: abc'
}
fn test_formatted_string_interpolation() {
x := 'abc'
axb := 'a:$x:b'
assert axb == 'a:abc:b'
x_10 := 'a:${x:10s}:b'
x10_ := 'a:${x:-10s}:b'
assert x_10 == 'a: abc:b'
assert x10_ == 'a:abc :b'
i := 23
si_right := '${i:10d}'
si__left := '${i:-10d}'
assert si_right == ' 23'
assert si__left == '23 '
}
fn test_excape_dollar_in_string() { fn test_excape_dollar_in_string() {
i := 42 i := 42
assert '($i)' == '(42)'
assert '($i)' == '(42)' assert '(\$i)'.contains('i') && !'(\$i)'.contains('42')
assert '(\$i)'.contains('i') && !'(\$i)'.contains('42') assert !'(\\$i)'.contains('i') && '(\\$i)'.contains('42') && '(\\$i)'.contains('\\')
assert !'(\\$i)'.contains('i') && '(\\$i)'.contains('42') && '(\\$i)'.contains('\\') assert '(\\\$i)'.contains('i') && !'(\\\$i)'.contains('42') && '(\\$i)'.contains('\\')
assert '(\\\$i)'.contains('i') && !'(\\\$i)'.contains('42') && '(\\$i)'.contains('\\') assert !'(\\\\$i)'.contains('i') && '(\\\\$i)'.contains('42') && '(\\\\$i)'.contains('\\\\')
assert !'(\\\\$i)'.contains('i') && '(\\\\$i)'.contains('42') && '(\\\\$i)'.contains('\\\\') assert '(${i})' == '(42)'
assert '(\${i})'.contains('i') && !'(\${i})'.contains('42')
assert '(${i})' == '(42)' assert !'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\')
assert '(\${i})'.contains('i') && !'(\${i})'.contains('42') assert '(\\\${i})'.contains('i') && !'(\\\${i})'.contains('42') && '(\\${i})'.contains('\\')
assert !'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\') assert !'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') && '(\\\\${i})'.contains('\\\\')
assert '(\\\${i})'.contains('i') && !'(\\\${i})'.contains('42') && '(\\${i})'.contains('\\') assert i == 42
assert !'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') && '(\\\\${i})'.contains('\\\\')
assert i==42
} }
fn test_implicit_str() { fn test_implicit_str() {
i := 42 i := 42
assert 'int $i' == 'int 42' assert 'int $i' == 'int 42'
assert '$i' == '42' assert '$i' == '42'
check := '$i' == '42'
check := '$i' == '42' assert check
assert check text := '$i' + '42'
assert text == '4242'
text := '$i' + '42'
assert text == '4242'
} }

View File

@ -0,0 +1,25 @@
struct Foo {
bar int
mut:
str string
}
fn (f Foo) baz() string {
return 'baz'
}
fn test_string_method_interpolation() {
foo := Foo{}
s := 'baz=${foo.baz()}'
assert s == 'baz=baz'
}
fn test_adding_to_mutable_string_field() {
mut foo := Foo{10, 'hi'}
assert foo.bar == 10
assert foo.str == 'hi'
foo.str += '!'
eprintln( foo.str )
assert foo.str == 'hi!'
}

View File

@ -59,6 +59,9 @@ pub fn (node &FnDecl) str(t &table.Table) string {
// string representaiton of expr // string representaiton of expr
pub fn (x Expr) str() string { pub fn (x Expr) str() string {
match x { match x {
Ident {
return it.name
}
InfixExpr { InfixExpr {
return '(${it.left.str()} $it.op.str() ${it.right.str()})' return '(${it.left.str()} $it.op.str() ${it.right.str()})'
} }

View File

@ -269,16 +269,7 @@ fn (g mut Gen) stmt(node ast.Stmt) {
// g.writeln('//// stmt start') // g.writeln('//// stmt start')
match node { match node {
ast.AssertStmt { ast.AssertStmt {
g.writeln('// assert') g.gen_assert_stmt(it)
g.write('if ((')
g.expr(it.expr)
g.writeln(')) {')
g.writeln('g_test_oks++;')
// g.writeln('puts("OK $g.fn_decl.name");')
g.writeln('} else {')
g.writeln('g_test_fails++;')
g.writeln('puts("FAILED $g.fn_decl.name $it.pos.line_nr");')
g.writeln('}')
} }
ast.AssignStmt { ast.AssignStmt {
g.gen_assign_stmt(it) g.gen_assign_stmt(it)
@ -366,7 +357,7 @@ fn (g mut Gen) stmt(node ast.Stmt) {
g.writeln('}') g.writeln('}')
} }
ast.ForInStmt { ast.ForInStmt {
g.for_in(it) g.for_in(it)
} }
ast.ForStmt { ast.ForStmt {
g.write('while (') g.write('while (')
@ -522,6 +513,29 @@ fn (g mut Gen) expr_with_cast(expr ast.Expr, got_type table.Type, exp_type table
g.expr(expr) g.expr(expr)
} }
fn (g mut Gen) gen_assert_stmt(a ast.AssertStmt) {
g.writeln('// assert')
g.write('if( ')
g.expr(a.expr)
s_assertion := a.expr.str().replace('"', "\'")
g.write(' )')
if g.is_test {
g.writeln('{')
g.writeln(' g_test_oks++;')
// g.writeln(' println(_STR("OK ${g.file.path}:${a.pos.line_nr}: fn ${g.fn_decl.name}(): assert $s_assertion"));')
g.writeln('}else{')
g.writeln(' g_test_fails++;')
g.writeln(' eprintln(_STR("${g.file.path}:${a.pos.line_nr}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion"));')
g.writeln(' exit(1);')
g.writeln('}')
} else {
g.writeln('{}else{')
g.writeln(' eprintln(_STR("${g.file.path}:${a.pos.line_nr}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion"));')
g.writeln(' exit(1);')
g.writeln('}')
}
}
fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
// g.write('/*assign_stmt*/') // g.write('/*assign_stmt*/')
if assign_stmt.left.len > assign_stmt.right.len { if assign_stmt.left.len > assign_stmt.right.len {
@ -537,7 +551,7 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
else { else {
panic('expected call') panic('expected call')
} }
} }
mr_var_name := 'mr_$assign_stmt.pos.pos' mr_var_name := 'mr_$assign_stmt.pos.pos'
g.expr_var_name = mr_var_name g.expr_var_name = mr_var_name
if table.type_is_optional(return_type) { if table.type_is_optional(return_type) {
@ -738,9 +752,9 @@ fn (g mut Gen) free_scope_vars(pos int) {
continue continue
} }
else { else {
g.writeln('// other' + t) g.writeln('// other ' + t)
} }
} }
g.writeln('string_free($var.name); // autofreed') g.writeln('string_free($var.name); // autofreed')
} }
} }
@ -2031,24 +2045,39 @@ fn (g mut Gen) string_inter_literal(node ast.StringInterLiteral) {
// } // }
// else {} // else {}
// } // }
if node.expr_types[i] == table.string_type {
sfmt := node.expr_fmts[i]
if sfmt.len > 0 {
fspec := sfmt[sfmt.len-1]
if fspec == `s` && node.expr_types[i] != table.string_type {
verror('only V strings can be formatted with a ${sfmt} format')
}
g.write('%' + sfmt[1..])
}else if node.expr_types[i] == table.string_type {
g.write('%.*s') g.write('%.*s')
} }else {
else if node.expr_types[i] == table.int_type {
g.write('%d') g.write('%d')
} }
} }
g.write('", ') g.write('", ')
// Build args // Build args
for i, expr in node.exprs { for i, expr in node.exprs {
if node.expr_types[i] == table.string_type { sfmt := node.expr_fmts[i]
if sfmt.len > 0 {
fspec := sfmt[sfmt.len-1]
if fspec == `s` && node.expr_types[i] == table.string_type {
g.expr(expr)
g.write('.str')
}else{
g.expr(expr)
}
} else if node.expr_types[i] == table.string_type {
// `name.str, name.len,` // `name.str, name.len,`
g.expr(expr) g.expr(expr)
g.write('.len, ') g.write('.len, ')
g.expr(expr) g.expr(expr)
g.write('.str') g.write('.str')
} } else {
else {
g.expr(expr) g.expr(expr)
} }
if i < node.exprs.len - 1 { if i < node.exprs.len - 1 {
@ -2366,15 +2395,36 @@ fn (g &Gen) type_default(typ table.Type) string {
} }
pub fn (g mut Gen) write_tests_main() { pub fn (g mut Gen) write_tests_main() {
g.definitions.writeln('int g_test_oks = 0;')
g.definitions.writeln('int g_test_fails = 0;')
g.writeln('int main() {') g.writeln('int main() {')
g.writeln('\t_vinit();') g.writeln('\t_vinit();')
mut tfuncs := []string
mut tsuite_begin := ''
mut tsuite_end := ''
for _, f in g.table.fns { for _, f in g.table.fns {
if f.name == 'testsuite_begin' {
tsuite_begin = f.name
}
if f.name == 'testsuite_end' {
tsuite_end = f.name
}
if !f.name.starts_with('test_') { if !f.name.starts_with('test_') {
continue continue
} }
g.writeln('\t${f.name}();') tfuncs << f.name
} }
g.writeln('return 0; }') if tsuite_begin.len > 0 {
g.writeln('\t${tsuite_begin}();\n')
}
for t in tfuncs {
g.writeln('\t${t}();')
}
if tsuite_end.len > 0 {
g.writeln('\t${tsuite_end}();\n')
}
g.writeln('\treturn 0;')
g.writeln('}')
} }
fn (g &Gen) is_importing_os() bool { fn (g &Gen) is_importing_os() bool {

View File

@ -186,8 +186,6 @@ extern wchar_t **_wenviron;
//================================== GLOBALS =================================*/ //================================== GLOBALS =================================*/
byte g_str_buf[1024]; byte g_str_buf[1024];
int g_test_fails = 0;
int g_test_oks = 0;
int load_so(byteptr); int load_so(byteptr);
void reload_so(); void reload_so();
void _vinit(); void _vinit();