Compare commits

...

34 Commits

Author SHA1 Message Date
Alexander Medvednikov b81f06e369
checker: remove already handled "undefined" errors
ci/woodpecker/push/vc Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
2022-04-25 19:07:31 +02:00
Larpon 1cc8072efa
regex: rephrase doc string (#14159) 2022-04-25 19:07:31 +02:00
yuyi 2d381d69ea
checker: fix error for match aliases (#14157) 2022-04-25 19:07:31 +02:00
yuyi 549735fbff
checker: check error for array.index() argument (#14147) 2022-04-25 19:07:31 +02:00
yuyi d67251c1e0
cgen: fix error for printing multi fixed array (#14141) 2022-04-25 19:07:30 +02:00
Larpon 8a5ba0dfc8
cli: add missing doc strings (#14158) 2022-04-25 19:07:30 +02:00
yuyi b314b8d8bf
cgen: fix error for anon fn decl inside ternary (#14150) 2022-04-25 19:07:30 +02:00
Delyan Angelov 81963b51ab
tests: add ability to check the output of commands for .starts_with, .ends_with and .contains strings, in `v test-all` 2022-04-25 19:07:30 +02:00
Delyan Angelov b43c538bc0
eval: enable `v interpret examples/hanoi.v` 2022-04-25 19:07:30 +02:00
Nick Treleaven 13902a827b
checker: error if smaller signed == unsigned (#14078) 2022-04-25 19:07:30 +02:00
Delyan Angelov 5b58f4efbf
cgen: fix pushing to an array of string pointers (fix #14156) 2022-04-25 19:07:29 +02:00
yuyi 47e7582af8
parser, checker, cgen: fix error for fn call using anon fn call argument (#14155) 2022-04-25 19:07:29 +02:00
Delyan Angelov b849651197
ci: bump test retries for go_anon_fn_variable_call_test.v to 3 2022-04-25 19:07:29 +02:00
fleur 2134fb3e3e
cgen: add callconv support for fns from ptr (#14151) 2022-04-25 19:07:29 +02:00
Haren S cde15abc0a
builder: improve macOS 10.5 and PPC support (#14152) 2022-04-25 19:07:29 +02:00
stackotter 745e75ab25
vfmt: fix array_init line wrapping (#14154) 2022-04-25 19:07:29 +02:00
yuyi 6690dfa208
checker, cgen: fix printing in 'for v in a' (#14146) 2022-04-25 19:07:28 +02:00
Alexander Medvednikov f3dff7c1c0
tmpl: minor fixes and optimizations 2022-04-25 19:07:28 +02:00
Delyan Angelov 20ded157bf
ci: bump vtest retry to 6 for channel_select_test.v (fails sporadically in the windows-gcc job) 2022-04-25 19:07:28 +02:00
Delyan Angelov 1f471a4628
vfmt: ignore parser warnings, cleanup code 2022-04-25 19:07:28 +02:00
yuyi 0b7c3a6035
checker: simplify checking array.contains() argument (#14133) 2022-04-25 19:07:28 +02:00
yuyi 0a30a90924
parser: minor cleanup in stmt() (#14136) 2022-04-25 19:07:28 +02:00
Claudio Cesar de Sá 7325727e38
examples: add 2 more graph search examples (DFS and BFS), move them into `examples/graphs` (#14131) 2022-04-25 19:07:27 +02:00
yuyi e0963381ec
checker, cgen: fix generic empty interface to multi struct (#14132) 2022-04-25 19:07:27 +02:00
Alexander Medvednikov 2c59d47fc1
cgen: fix enum switch bug in translated code 2022-04-25 19:07:27 +02:00
tzSharing 08f45023e8
gg: fix gg.draw_rounded_rect_filled() graphical abnormalities (#14128) 2022-04-25 19:07:27 +02:00
Larpon a7e8ca70dc
os: add behavior info to `is_dir_empty` documentation (#14110) 2022-04-25 19:07:27 +02:00
yuyi 6eea50c955
fmt: remove extra parentheses (#14125) 2022-04-25 19:07:27 +02:00
Delyan Angelov 34961a23b4
ast: allow `a := match x { 101 { ... for {...} ... y }` 2022-04-25 19:07:26 +02:00
yuyi dc9068b4d3
cgen: fix error for if expr with nested match expr (#14122) 2022-04-25 19:07:26 +02:00
yuyi 052c8e0282
parser: fix generic anon fn decl with type only argument (#14124) 2022-04-25 19:07:26 +02:00
yuyi 0a12fd7212
checker: fix error for fn with array of aliases argument (#14123) 2022-04-25 19:07:26 +02:00
yuyi eca95dcedc
checker: check array builtin method's arguments (#14119) 2022-04-25 19:07:26 +02:00
yuyi 8824f5f103
checker: check map builtin method's arguments (#14120) 2022-04-25 19:07:26 +02:00
74 changed files with 1142 additions and 241 deletions

View File

@ -61,9 +61,7 @@ fn main() {
if term_colors { if term_colors {
os.setenv('VCOLORS', 'always', true) os.setenv('VCOLORS', 'always', true)
} }
if foptions.is_verbose { foptions.vlog('vfmt foptions: $foptions')
eprintln('vfmt foptions: $foptions')
}
if foptions.is_worker { if foptions.is_worker {
// -worker should be added by a parent vfmt process. // -worker should be added by a parent vfmt process.
// We launch a sub process for each file because // We launch a sub process for each file because
@ -109,9 +107,7 @@ fn main() {
mut worker_command_array := cli_args_no_files.clone() mut worker_command_array := cli_args_no_files.clone()
worker_command_array << ['-worker', util.quote_path(fpath)] worker_command_array << ['-worker', util.quote_path(fpath)]
worker_cmd := worker_command_array.join(' ') worker_cmd := worker_command_array.join(' ')
if foptions.is_verbose { foptions.vlog('vfmt worker_cmd: $worker_cmd')
eprintln('vfmt worker_cmd: $worker_cmd')
}
worker_result := os.execute(worker_cmd) worker_result := os.execute(worker_cmd)
// Guard against a possibly crashing worker process. // Guard against a possibly crashing worker process.
if worker_result.exit_code != 0 { if worker_result.exit_code != 0 {
@ -151,43 +147,43 @@ fn main() {
} }
} }
fn (foptions &FormatOptions) format_file(file string) { fn setup_preferences_and_table() (&pref.Preferences, &ast.Table) {
table := ast.new_table()
mut prefs := pref.new_preferences() mut prefs := pref.new_preferences()
prefs.is_fmt = true prefs.is_fmt = true
prefs.skip_warnings = true
return prefs, table
}
fn (foptions &FormatOptions) vlog(msg string) {
if foptions.is_verbose { if foptions.is_verbose {
eprintln('vfmt2 running fmt.fmt over file: $file') eprintln(msg)
} }
table := ast.new_table() }
// checker := checker.new_checker(table, prefs)
fn (foptions &FormatOptions) format_file(file string) {
foptions.vlog('vfmt2 running fmt.fmt over file: $file')
prefs, table := setup_preferences_and_table()
file_ast := parser.parse_file(file, table, .parse_comments, prefs) file_ast := parser.parse_file(file, table, .parse_comments, prefs)
// checker.check(file_ast) // checker.new_checker(table, prefs).check(file_ast)
formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug) formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug)
file_name := os.file_name(file) file_name := os.file_name(file)
ulid := rand.ulid() ulid := rand.ulid()
vfmt_output_path := os.join_path(vtmp_folder, 'vfmt_${ulid}_$file_name') vfmt_output_path := os.join_path(vtmp_folder, 'vfmt_${ulid}_$file_name')
os.write_file(vfmt_output_path, formatted_content) or { panic(err) } os.write_file(vfmt_output_path, formatted_content) or { panic(err) }
if foptions.is_verbose { foptions.vlog('fmt.fmt worked and $formatted_content.len bytes were written to $vfmt_output_path .')
eprintln('fmt.fmt worked and $formatted_content.len bytes were written to $vfmt_output_path .')
}
eprintln('$formatted_file_token$vfmt_output_path') eprintln('$formatted_file_token$vfmt_output_path')
} }
fn (foptions &FormatOptions) format_pipe() { fn (foptions &FormatOptions) format_pipe() {
mut prefs := pref.new_preferences() foptions.vlog('vfmt2 running fmt.fmt over stdin')
prefs.is_fmt = true prefs, table := setup_preferences_and_table()
if foptions.is_verbose {
eprintln('vfmt2 running fmt.fmt over stdin')
}
input_text := os.get_raw_lines_joined() input_text := os.get_raw_lines_joined()
table := ast.new_table()
// checker := checker.new_checker(table, prefs)
file_ast := parser.parse_text(input_text, '', table, .parse_comments, prefs) file_ast := parser.parse_text(input_text, '', table, .parse_comments, prefs)
// checker.check(file_ast) // checker.new_checker(table, prefs).check(file_ast)
formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug) formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug)
print(formatted_content) print(formatted_content)
if foptions.is_verbose { foptions.vlog('fmt.fmt worked and $formatted_content.len bytes were written to stdout.')
eprintln('fmt.fmt worked and $formatted_content.len bytes were written to stdout.')
}
} }
fn print_compiler_options(compiler_params &pref.Preferences) { fn print_compiler_options(compiler_params &pref.Preferences) {
@ -234,9 +230,7 @@ fn (mut foptions FormatOptions) post_process_file(file string, formatted_file_pa
return return
} }
diff_cmd := foptions.find_diff_cmd() diff_cmd := foptions.find_diff_cmd()
if foptions.is_verbose { foptions.vlog('Using diff command: $diff_cmd')
eprintln('Using diff command: $diff_cmd')
}
diff := diff.color_compare_files(diff_cmd, file, formatted_file_path) diff := diff.color_compare_files(diff_cmd, file, formatted_file_path)
if diff.len > 0 { if diff.len > 0 {
println(diff) println(diff)

View File

@ -49,17 +49,26 @@ enum RunCommandKind {
const expect_nothing = '<nothing>' const expect_nothing = '<nothing>'
const starts_with_nothing = '<nothing>'
const ends_with_nothing = '<nothing>'
const contains_nothing = '<nothing>'
struct Command { struct Command {
mut: mut:
line string line string
label string // when set, the label will be printed *before* cmd.line is executed label string // when set, the label will be printed *before* cmd.line is executed
ecode int ecode int
okmsg string okmsg string
errmsg string errmsg string
rmfile string rmfile string
runcmd RunCommandKind = .system runcmd RunCommandKind = .system
expect string = expect_nothing expect string = expect_nothing
output string starts_with string = starts_with_nothing
ends_with string = ends_with_nothing
contains string = contains_nothing
output string
} }
fn get_all_commands() []Command { fn get_all_commands() []Command {
@ -99,6 +108,14 @@ fn get_all_commands() []Command {
runcmd: .execute runcmd: .execute
expect: 'Hello, World!\n' expect: 'Hello, World!\n'
} }
res << Command{
line: '$vexe interpret examples/hanoi.v'
okmsg: 'V can interpret hanoi.v'
runcmd: .execute
starts_with: 'Disc 1 from A to C...\n'
ends_with: 'Disc 1 from A to C...\n'
contains: 'Disc 7 from A to C...\n'
}
res << Command{ res << Command{
line: '$vexe -o - examples/hello_world.v | grep "#define V_COMMIT_HASH" > /dev/null' line: '$vexe -o - examples/hello_world.v | grep "#define V_COMMIT_HASH" > /dev/null'
okmsg: 'V prints the generated source code to stdout with `-o -` .' okmsg: 'V prints the generated source code to stdout with `-o -` .'
@ -252,23 +269,56 @@ fn (mut cmd Command) run() {
spent := sw.elapsed().milliseconds() spent := sw.elapsed().milliseconds()
// //
mut is_failed := false mut is_failed := false
mut is_failed_expected := false
mut is_failed_starts_with := false
mut is_failed_ends_with := false
mut is_failed_contains := false
if cmd.ecode != 0 { if cmd.ecode != 0 {
is_failed = true is_failed = true
} }
if cmd.expect != expect_nothing { if cmd.expect != expect_nothing {
if cmd.output != cmd.expect { if cmd.output != cmd.expect {
is_failed = true is_failed = true
is_failed_expected = true
}
}
if cmd.starts_with != starts_with_nothing {
if !cmd.output.starts_with(cmd.starts_with) {
is_failed = true
is_failed_starts_with = true
}
}
if cmd.ends_with != ends_with_nothing {
if !cmd.output.ends_with(cmd.ends_with) {
is_failed = true
is_failed_ends_with = true
}
}
if cmd.contains != contains_nothing {
if !cmd.output.contains(cmd.contains) {
is_failed = true
is_failed_contains = true
} }
} }
// //
run_label := if is_failed { term.failed('FAILED') } else { term_highlight('OK') } run_label := if is_failed { term.failed('FAILED') } else { term_highlight('OK') }
println('> Running: "$cmd.line" took: $spent ms ... $run_label') println('> Running: "$cmd.line" took: $spent ms ... $run_label')
// //
if is_failed && cmd.expect != expect_nothing { if is_failed && is_failed_expected {
if cmd.output != cmd.expect { eprintln('> expected:\n$cmd.expect')
eprintln('> expected:\n$cmd.expect') eprintln('> output:\n$cmd.output')
eprintln('> output:\n$cmd.output') }
} if is_failed && is_failed_starts_with {
eprintln('> expected to start with:\n$cmd.starts_with')
eprintln('> output:\n${cmd.output#[..cmd.starts_with.len]}')
}
if is_failed && is_failed_ends_with {
eprintln('> expected to end with:\n$cmd.ends_with')
eprintln('> output:\n${cmd.output#[-cmd.starts_with.len..]}')
}
if is_failed && is_failed_contains {
eprintln('> expected to contain:\n$cmd.contains')
eprintln('> output:\n$cmd.output')
} }
if vtest_nocleanup { if vtest_nocleanup {
return return

View File

@ -32,8 +32,7 @@ const (
struct App { struct App {
minutes_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw, minutes_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw,
tp + tp + 1 * th, center - tw, tp + 1 * th]
1 * th, center - tw, tp + 1 * th]
hours_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw, tp + 2 * th, hours_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw, tp + 2 * th,
center - tw, tp + 2 * th] center - tw, tp + 2 * th]
hours3_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw, tp + 3 * th, hours3_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw, tp + 3 * th,

View File

@ -1,3 +1,18 @@
fn main() {
graph := {
'A': ['B', 'C']
'B': ['A', 'D', 'E']
'C': ['A', 'F']
'D': ['B']
'E': ['B', 'F']
'F': ['C', 'E']
}
println('Graph: $graph')
path := breadth_first_search_path(graph, 'A', 'F')
println('The shortest path from node A to node F is: $path')
assert path == ['A', 'C', 'F']
}
// Breadth-First Search (BFS) allows you to find the shortest distance between two nodes in the graph. // Breadth-First Search (BFS) allows you to find the shortest distance between two nodes in the graph.
fn breadth_first_search_path(graph map[string][]string, vertex string, target string) []string { fn breadth_first_search_path(graph map[string][]string, vertex string, target string) []string {
mut path := []string{} mut path := []string{}
@ -24,18 +39,3 @@ fn breadth_first_search_path(graph map[string][]string, vertex string, target st
} }
return path return path
} }
fn main() {
graph := {
'A': ['B', 'C']
'B': ['A', 'D', 'E']
'C': ['A', 'F']
'D': ['B']
'E': ['B', 'F']
'F': ['C', 'E']
}
println('Graph: $graph')
path := breadth_first_search_path(graph, 'A', 'F')
println('The shortest path from node A to node F is: $path')
assert path == ['A', 'C', 'F']
}

View File

@ -0,0 +1,92 @@
// Author: ccs
// I follow literally code in C, done many years ago
fn main() {
// Adjacency matrix as a map
graph := {
'A': ['B', 'C']
'B': ['A', 'D', 'E']
'C': ['A', 'F']
'D': ['B']
'E': ['B', 'F']
'F': ['C', 'E']
}
println('Graph: $graph')
path := breadth_first_search_path(graph, 'A', 'F')
println('\n The shortest path from node A to node F is: $path.reverse()')
}
// Breadth-First Search (BFS) allows you to find the shortest distance between two nodes in the graph.
fn breadth_first_search_path(graph map[string][]string, start string, target string) []string {
mut path := []string{} // ONE PATH with SUCCESS = array
mut queue := []string{} // a queue ... many paths
// all_nodes := graph.keys() // get a key of this map
n_nodes := graph.len // numbers of nodes of this graph
// a map to store all the nodes visited to avoid cycles
// start all them with False, not visited yet
mut visited := a_map_nodes_bool(n_nodes) // a map fully
// false ==> not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false}
queue << start // first arrival
for queue.len != 0 {
mut node := departure(mut queue) // get the front node and remove it
if visited[node] == false { // check if this node is already visited
// if no ... test it searchinf for a final node
visited[node] = true // means: visit this node
if node == target {
path = build_path_reverse(graph, start, node, visited)
return path
}
// Expansion of node removed from queue
print('\n Expansion of node $node (true/false): ${graph[node]}')
// take all nodes from the node
for vertex in graph[node] { // println("\n ...${vertex}")
// not explored yet
if visited[vertex] == false {
queue << vertex
}
}
print('\n QUEUE: $queue (only not visited) \n Visited: $visited')
}
}
path = ['Path not found, problem in the Graph, start or end nodes! ']
return path
}
// Creating a map for VISITED nodes ...
// starting by false ===> means this node was not visited yet
fn a_map_nodes_bool(size int) map[string]bool {
mut my_map := map[string]bool{} // look this map ...
base := u8(65)
mut key := base.ascii_str()
for i in 0 .. size {
key = u8(base + i).ascii_str()
my_map[key] = false
}
return my_map
}
// classical removing of a node from the start of a queue
fn departure(mut queue []string) string {
mut x := queue[0]
queue.delete(0)
return x
}
// Based in the current node that is final, search for its parent, already visited, up to the root or start node
fn build_path_reverse(graph map[string][]string, start string, final string, visited map[string]bool) []string {
print('\n\n Nodes visited (true) or no (false): $visited')
array_of_nodes := graph.keys()
mut current := final
mut path := []string{}
path << current
for (current != start) {
for i in array_of_nodes {
if (current in graph[i]) && (visited[i] == true) {
current = i
break // the first ocurrence is enough
}
}
path << current // update the path tracked
}
return path
}

View File

@ -0,0 +1,103 @@
// Author: ccs
// I follow literally code in C, done many years ago
fn main() {
// Adjacency matrix as a map
// Example 01
graph_01 := {
'A': ['B', 'C']
'B': ['A', 'D', 'E']
'C': ['A', 'F']
'D': ['B']
'E': ['F', 'B', 'F']
'F': ['C', 'E']
}
// Example 02
graph_02 := {
'A': ['B', 'C', 'D']
'B': ['E']
'C': ['F']
'D': ['E']
'E': ['H']
'F': ['H']
'G': ['H']
'H': ['E', 'F', 'G']
}
// println('Graph: $graph')
path_01 := depth_first_search_path(graph_01, 'A', 'F')
println('\n Graph_01: a first path from node A to node F is: $path_01.reverse()')
path_02 := depth_first_search_path(graph_02, 'A', 'H')
println('\n Graph_02: a first path from node A to node F is: $path_02.reverse()')
}
// Depth-First Search (BFS) allows you to find a path between two nodes in the graph.
fn depth_first_search_path(graph map[string][]string, start string, target string) []string {
mut path := []string{} // ONE PATH with SUCCESS = array
mut stack := []string{} // a stack ... many nodes
// all_nodes := graph.keys() // get a key of this map
n_nodes := graph.len // numbers of nodes of this graph
mut visited := a_map_nodes_bool(n_nodes) // a map fully
// false ... not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false}
stack << start // first push on the stack
for stack.len > 0 {
mut node := stack.pop() // get the top node and remove it from the stack
// check if this node is already visited
if visited[node] == false {
// if no ... test it searchin for a final node
visited[node] = true // means: node visited
if node == target {
path = build_path_reverse(graph, start, node, visited)
return path
}
// Exploring of node removed from stack and add its relatives
print('\n Exploring of node $node (true/false): ${graph[node]}')
// graph[node].reverse() take a classical choice for DFS
// at most os left in this case.
// use vertex in graph[node] the choice is right
// take all nodes from the node
for vertex in graph[node].reverse() {
// println("\n ...${vertex}")
// not explored yet
if visited[vertex] == false {
stack << vertex
}
}
print('\n Stack: $stack (only not visited) \n Visited: $visited')
}
}
path = ['Path not found, problem in the Graph, start or end nodes! ']
return path
}
// Creating a map for nodes not VISITED visited ...
// starting by false ===> means this node was not visited yet
fn a_map_nodes_bool(size int) map[string]bool {
mut my_map := map[string]bool{} // look this map ...
for i in 0 .. size {
my_map[u8(65 + i).ascii_str()] = false
}
return my_map
}
// Based in the current node that is final, search for his parent, that is already visited, up to the root or start node
fn build_path_reverse(graph map[string][]string, start string, final string, visited map[string]bool) []string {
print('\n\n Nodes visited (true) or no (false): $visited')
array_of_nodes := graph.keys()
mut current := final
mut path := []string{}
path << current
for current != start {
for i in array_of_nodes {
if (current in graph[i]) && (visited[i] == true) {
current = i
break // the first ocurrence is enough
}
}
path << current // updating the path tracked
}
return path
}

View File

@ -931,7 +931,7 @@ fn test_u64_keys() {
m[i]++ m[i]++
assert m[i] == i + 1 assert m[i] == i + 1
} }
assert m.len == end assert u64(m.len) == end
keys := m.keys() keys := m.keys()
for i in u64(0) .. end { for i in u64(0) .. end {
assert keys[i] == i assert keys[i] == i

View File

@ -919,7 +919,7 @@ fn test_u64_keys() {
m[i]++ m[i]++
assert m[i] == i + 1 assert m[i] == i + 1
} }
assert m.len == end assert u64(m.len) == end
keys := m.keys() keys := m.keys()
for i in u64(0) .. end { for i in u64(0) .. end {
assert keys[i] == i assert keys[i] == i

View File

@ -28,6 +28,7 @@ fn help_cmd() Command {
} }
} }
// print_help_for_command outputs the help message of `help_cmd`.
pub fn print_help_for_command(help_cmd Command) ? { pub fn print_help_for_command(help_cmd Command) ? {
if help_cmd.args.len > 0 { if help_cmd.args.len > 0 {
mut cmd := help_cmd.parent mut cmd := help_cmd.parent
@ -54,6 +55,7 @@ pub fn print_help_for_command(help_cmd Command) ? {
} }
} }
// help_message returns a generated help message as a `string` for the `Command`.
pub fn (cmd Command) help_message() string { pub fn (cmd Command) help_message() string {
mut help := '' mut help := ''
help += 'Usage: $cmd.full_name()' help += 'Usage: $cmd.full_name()'

View File

@ -51,8 +51,7 @@ const (
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd], 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd],
'Test With Truncation'.bytes(), 'Test With Truncation'.bytes(), 'Test Using Larger Than Block-Size Key - Hash Key First'.bytes(),
'Test Using Larger Than Block-Size Key - Hash Key First'.bytes(),
'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'.bytes()] 'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'.bytes()]
) )

View File

@ -278,72 +278,114 @@ pub fn (ctx &Context) draw_rounded_rect_empty(x f32, y f32, w f32, h f32, radius
// `x`,`y` is the top-left corner of the rectangle. // `x`,`y` is the top-left corner of the rectangle.
// `w` is the width, `h` is the height . // `w` is the width, `h` is the height .
// `radius` is the radius of the corner-rounding in pixels. // `radius` is the radius of the corner-rounding in pixels.
// `c` is the color of the outline. // `c` is the color of the filled.
pub fn (ctx &Context) draw_rounded_rect_filled(x f32, y f32, w f32, h f32, radius f32, c gx.Color) { pub fn (ctx &Context) draw_rounded_rect_filled(x f32, y f32, w f32, h f32, radius f32, c gx.Color) {
assert w > 0 && h > 0 && radius >= 0
if c.a != 255 {
sgl.load_pipeline(ctx.timage_pip)
}
sgl.c4b(c.r, c.g, c.b, c.a) sgl.c4b(c.r, c.g, c.b, c.a)
sgl.begin_triangle_strip()
mut theta := f32(0)
mut xx := f32(0) mut xx := f32(0)
mut yy := f32(0) mut yy := f32(0)
r := radius * ctx.scale mut radians := f32(0)
mut new_radius := radius
if w >= h && radius > h / 2 {
new_radius = h / 2
} else if radius > w / 2 {
new_radius = w / 2
}
r := new_radius * ctx.scale
nx := x * ctx.scale nx := x * ctx.scale
ny := y * ctx.scale ny := y * ctx.scale
width := w * ctx.scale width := w * ctx.scale
height := h * ctx.scale height := h * ctx.scale
segments := 2 * math.pi * r
segdiv := segments / 4 // left top quarter
rb := 0 sgl.begin_triangle_strip()
lb := int(rb + segdiv) ltx := nx + r
lt := int(lb + segdiv) lty := ny + r
rt := int(lt + segdiv) for i in 0 .. 91 {
// left top if r == 0 {
lx := nx + r break
ly := ny + r }
for i in lt .. rt { radians = f32(math.radians(i))
theta = 2 * f32(math.pi) * f32(i) / segments xx = r * math.cosf(radians)
xx = r * math.cosf(theta) yy = r * math.sinf(radians)
yy = r * math.sinf(theta) sgl.v2f(ltx - xx, lty - yy)
sgl.v2f(xx + lx, yy + ly) sgl.v2f(ltx, lty)
sgl.v2f(lx, ly)
} }
// right top sgl.end()
mut rx := nx + width - r
mut ry := ny + r // right top quarter
for i in rt .. int(segments) { sgl.begin_triangle_strip()
theta = 2 * f32(math.pi) * f32(i) / segments rtx := nx + width - r
xx = r * math.cosf(theta) rty := ny + r
yy = r * math.sinf(theta) for i in 0 .. 91 {
sgl.v2f(xx + rx, yy + ry) if r == 0 {
sgl.v2f(rx, ry) break
}
radians = f32(math.radians(i))
xx = r * math.cosf(radians)
yy = r * math.sinf(radians)
sgl.v2f(rtx + xx, rty - yy)
sgl.v2f(rtx, rty)
} }
// right bottom sgl.end()
mut rbx := rx
mut rby := ny + height - r // right bottom quarter
for i in rb .. lb { sgl.begin_triangle_strip()
theta = 2 * f32(math.pi) * f32(i) / segments rbx := nx + width - r
xx = r * math.cosf(theta) rby := ny + height - r
yy = r * math.sinf(theta) for i in 0 .. 91 {
sgl.v2f(xx + rbx, yy + rby) if r == 0 {
break
}
radians = f32(math.radians(i))
xx = r * math.cosf(radians)
yy = r * math.sinf(radians)
sgl.v2f(rbx + xx, rby + yy)
sgl.v2f(rbx, rby) sgl.v2f(rbx, rby)
} }
// left bottom sgl.end()
mut lbx := lx
mut lby := ny + height - r // left bottom quarter
for i in lb .. lt { sgl.begin_triangle_strip()
theta = 2 * f32(math.pi) * f32(i) / segments lbx := nx + r
xx = r * math.cosf(theta) lby := ny + height - r
yy = r * math.sinf(theta) for i in 0 .. 91 {
sgl.v2f(xx + lbx, yy + lby) if r == 0 {
break
}
radians = f32(math.radians(i))
xx = r * math.cosf(radians)
yy = r * math.sinf(radians)
sgl.v2f(lbx - xx, lby + yy)
sgl.v2f(lbx, lby) sgl.v2f(lbx, lby)
} }
sgl.v2f(lx + xx, ly)
sgl.v2f(lx, ly)
sgl.end() sgl.end()
// Separate drawing is to prevent transparent color overlap
// top rectangle
sgl.begin_quads()
sgl.v2f(ltx, ny)
sgl.v2f(rtx, ny)
sgl.v2f(rtx, rty)
sgl.v2f(ltx, lty)
sgl.end()
// middle rectangle
sgl.begin_quads()
sgl.v2f(nx, ny + r)
sgl.v2f(rtx + r, rty)
sgl.v2f(rbx + r, rby)
sgl.v2f(nx, lby)
sgl.end()
// bottom rectangle
sgl.begin_quads() sgl.begin_quads()
sgl.v2f(lx, ly)
sgl.v2f(rx, ry)
sgl.v2f(rbx, rby)
sgl.v2f(lbx, lby) sgl.v2f(lbx, lby)
sgl.v2f(rbx, rby)
sgl.v2f(rbx, ny + height)
sgl.v2f(lbx, ny + height)
sgl.end() sgl.end()
} }

View File

@ -242,8 +242,7 @@ const (
cookie: [&http.Cookie{ cookie: [&http.Cookie{
name: 'cookie-1' name: 'cookie-1'
value: 'v1' value: 'v1'
}, }, &http.Cookie{
&http.Cookie{
name: 'cookie-2' name: 'cookie-2'
value: 'v2' value: 'v2'
}, },
@ -294,8 +293,7 @@ const (
header: { header: {
'Set-Cookie': ['ASP.NET_SessionId=foo; path=/; HttpOnly'] 'Set-Cookie': ['ASP.NET_SessionId=foo; path=/; HttpOnly']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'ASP.NET_SessionId' name: 'ASP.NET_SessionId'
value: 'foo' value: 'foo'
path: '/' path: '/'
@ -307,8 +305,7 @@ const (
header: { header: {
'Set-Cookie': ['samesitedefault=foo; SameSite'] 'Set-Cookie': ['samesitedefault=foo; SameSite']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'samesitedefault' name: 'samesitedefault'
value: 'foo' value: 'foo'
same_site: .same_site_default_mode same_site: .same_site_default_mode
@ -319,8 +316,7 @@ const (
header: { header: {
'Set-Cookie': ['samesitelax=foo; SameSite=Lax'] 'Set-Cookie': ['samesitelax=foo; SameSite=Lax']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'samesitelax' name: 'samesitelax'
value: 'foo' value: 'foo'
same_site: .same_site_lax_mode same_site: .same_site_lax_mode
@ -331,8 +327,7 @@ const (
header: { header: {
'Set-Cookie': ['samesitestrict=foo; SameSite=Strict'] 'Set-Cookie': ['samesitestrict=foo; SameSite=Strict']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'samesitestrict' name: 'samesitestrict'
value: 'foo' value: 'foo'
same_site: .same_site_strict_mode same_site: .same_site_strict_mode
@ -343,8 +338,7 @@ const (
header: { header: {
'Set-Cookie': ['samesitenone=foo; SameSite=None'] 'Set-Cookie': ['samesitenone=foo; SameSite=None']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'samesitenone' name: 'samesitenone'
value: 'foo' value: 'foo'
same_site: .same_site_none_mode same_site: .same_site_none_mode
@ -357,8 +351,7 @@ const (
header: { header: {
'Set-Cookie': ['special-1=a z'] 'Set-Cookie': ['special-1=a z']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-1' name: 'special-1'
value: 'a z' value: 'a z'
raw: 'special-1=a z' raw: 'special-1=a z'
@ -368,8 +361,7 @@ const (
header: { header: {
'Set-Cookie': ['special-2=" z"'] 'Set-Cookie': ['special-2=" z"']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-2' name: 'special-2'
value: ' z' value: ' z'
raw: 'special-2=" z"' raw: 'special-2=" z"'
@ -379,8 +371,7 @@ const (
header: { header: {
'Set-Cookie': ['special-3="a "'] 'Set-Cookie': ['special-3="a "']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-3' name: 'special-3'
value: 'a ' value: 'a '
raw: 'special-3="a "' raw: 'special-3="a "'
@ -390,8 +381,7 @@ const (
header: { header: {
'Set-Cookie': ['special-4=" "'] 'Set-Cookie': ['special-4=" "']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-4' name: 'special-4'
value: ' ' value: ' '
raw: 'special-4=" "' raw: 'special-4=" "'
@ -401,8 +391,7 @@ const (
header: { header: {
'Set-Cookie': ['special-5=a,z'] 'Set-Cookie': ['special-5=a,z']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-5' name: 'special-5'
value: 'a,z' value: 'a,z'
raw: 'special-5=a,z' raw: 'special-5=a,z'
@ -412,8 +401,7 @@ const (
header: { header: {
'Set-Cookie': ['special-6=",z"'] 'Set-Cookie': ['special-6=",z"']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-6' name: 'special-6'
value: ',z' value: ',z'
raw: 'special-6=",z"' raw: 'special-6=",z"'
@ -423,8 +411,7 @@ const (
header: { header: {
'Set-Cookie': ['special-7=","'] 'Set-Cookie': ['special-7=","']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-7' name: 'special-7'
value: ',' value: ','
raw: 'special-8=","' raw: 'special-8=","'

View File

@ -223,7 +223,7 @@ pub fn (mut ws Client) parse_frame_header() ?Frame {
buffer[bytes_read] = rbuff[0] buffer[bytes_read] = rbuff[0]
bytes_read++ bytes_read++
// parses the first two header bytes to get basic frame information // parses the first two header bytes to get basic frame information
if bytes_read == u64(websocket.header_len_offset) { if bytes_read == websocket.header_len_offset {
frame.fin = (buffer[0] & 0x80) == 0x80 frame.fin = (buffer[0] & 0x80) == 0x80
frame.rsv1 = (buffer[0] & 0x40) == 0x40 frame.rsv1 = (buffer[0] & 0x40) == 0x40
frame.rsv2 = (buffer[0] & 0x20) == 0x20 frame.rsv2 = (buffer[0] & 0x20) == 0x20
@ -249,7 +249,7 @@ pub fn (mut ws Client) parse_frame_header() ?Frame {
break break
} }
} }
if frame.payload_len == 126 && bytes_read == u64(websocket.extended_payload16_end_byte) { if frame.payload_len == 126 && bytes_read == websocket.extended_payload16_end_byte {
frame.header_len += 2 frame.header_len += 2
frame.payload_len = 0 frame.payload_len = 0
frame.payload_len |= int(u32(buffer[2]) << 8) frame.payload_len |= int(u32(buffer[2]) << 8)
@ -259,7 +259,7 @@ pub fn (mut ws Client) parse_frame_header() ?Frame {
break break
} }
} }
if frame.payload_len == 127 && bytes_read == u64(websocket.extended_payload64_end_byte) { if frame.payload_len == 127 && bytes_read == websocket.extended_payload64_end_byte {
frame.header_len += 8 frame.header_len += 8
// these shift operators needs 64 bit on clang with -prod flag // these shift operators needs 64 bit on clang with -prod flag
mut payload_len := u64(0) mut payload_len := u64(0)

View File

@ -162,6 +162,7 @@ pub fn rmdir_all(path string) ? {
} }
// is_dir_empty will return a `bool` whether or not `path` is empty. // is_dir_empty will return a `bool` whether or not `path` is empty.
// Note that it will return `true` if `path` does not exist.
[manualfree] [manualfree]
pub fn is_dir_empty(path string) bool { pub fn is_dir_empty(path string) bool {
items := ls(path) or { return true } items := ls(path) or { return true }

View File

@ -41,7 +41,7 @@ fn test_open_file() {
mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) } mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) }
file.write_string(hello) or { panic(err) } file.write_string(hello) or { panic(err) }
file.close() file.close()
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_file(filename) or { panic('error reading file $filename') } read_hello := os.read_file(filename) or { panic('error reading file $filename') }
assert hello == read_hello assert hello == read_hello
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
@ -58,7 +58,7 @@ fn test_open_file_binary() {
bytes := hello.bytes() bytes := hello.bytes()
unsafe { file.write_ptr(bytes.data, bytes.len) } unsafe { file.write_ptr(bytes.data, bytes.len) }
file.close() file.close()
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_bytes(filename) or { panic('error reading file $filename') } read_hello := os.read_bytes(filename) or { panic('error reading file $filename') }
assert bytes == read_hello assert bytes == read_hello
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
@ -100,7 +100,7 @@ fn test_create_file() ? {
filename := './test1.txt' filename := './test1.txt'
hello := 'hello world!' hello := 'hello world!'
create_and_write_to_file(filename, hello) ? create_and_write_to_file(filename, hello) ?
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
} }
@ -138,7 +138,7 @@ fn test_write_and_read_string_to_file() {
filename := './test1.txt' filename := './test1.txt'
hello := 'hello world!' hello := 'hello world!'
os.write_file(filename, hello) or { panic(err) } os.write_file(filename, hello) or { panic(err) }
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_file(filename) or { panic('error reading file $filename') } read_hello := os.read_file(filename) or { panic('error reading file $filename') }
assert hello == read_hello assert hello == read_hello
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
@ -157,7 +157,7 @@ fn test_write_and_read_bytes() {
// compare the length of the array with the file size (have to match). // compare the length of the array with the file size (have to match).
unsafe { file_write.write_ptr(payload.data, 5) } unsafe { file_write.write_ptr(payload.data, 5) }
file_write.close() file_write.close()
assert payload.len == os.file_size(file_name) assert u64(payload.len) == os.file_size(file_name)
mut file_read := os.open(os.real_path(file_name)) or { mut file_read := os.open(os.real_path(file_name)) or {
eprintln('failed to open file $file_name') eprintln('failed to open file $file_name')
return return
@ -355,6 +355,12 @@ fn test_mv() {
assert !os.is_dir(expected) assert !os.is_dir(expected)
} }
fn test_is_dir_empty() {
// Test that is_dir_empty returns true on
// non-existent directories ***as stated in it's doc string***
assert os.is_dir_empty('dir that does not exist at all')
}
fn test_cp_all() { fn test_cp_all() {
// fileX -> dir/fileX // fileX -> dir/fileX
// Note: clean up of the files happens inside the cleanup_leftovers function // Note: clean up of the files happens inside the cleanup_leftovers function
@ -786,7 +792,7 @@ fn test_truncate() ? {
mut f := os.create(filename) ? mut f := os.create(filename) ?
f.write_string(hello) ? f.write_string(hello) ?
f.close() f.close()
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
newlen := u64(40000) newlen := u64(40000)
os.truncate(filename, newlen) or { panic(err) } os.truncate(filename, newlen) or { panic(err) }
assert newlen == os.file_size(filename) assert newlen == os.file_size(filename)

View File

@ -14,7 +14,8 @@ import strings
* Inits * Inits
* *
******************************************************************************/ ******************************************************************************/
// regex create a regex object from the query string, retunr RE object and errors as re_err, err_pos // regex_base returns a regex object (`RE`) generated from `pattern` string and
// detailed information in re_err, err_pos, if an error occurred.
pub fn regex_base(pattern string) (RE, int, int) { pub fn regex_base(pattern string) (RE, int, int) {
// init regex // init regex
mut re := RE{} mut re := RE{}

View File

@ -1,13 +1,11 @@
/*
* ATTENTION! Do not use this file as an example!
* For that, please look at `channel_select_2_test.v` or `channel_select_3_test.v`
*
* This test case uses the implementation in `sync/channels.v` directly
* in order to test it independently from the support in the core language
*/
module sync module sync
// vtest retry: 6
// ATTENTION! Do not use this file as an example!
// For that, please look at `channel_select_2_test.v` or `channel_select_3_test.v`
// This test case uses the implementation in `sync/channels.v` directly
// in order to test it independently from the support in the core language
import time import time
fn do_rec_i64(mut ch Channel) { fn do_rec_i64(mut ch Channel) {

View File

@ -18,7 +18,7 @@ fn test_count_10_times_1_cycle_should_result_10_cycles_with_sync() {
go count_one_cycle(mut counter, mut wg) go count_one_cycle(mut counter, mut wg)
} }
wg.wait() wg.wait()
assert counter.counter == desired_iterations assert counter.counter == u64(desired_iterations)
eprintln(' with synchronization the counter is: ${counter.counter:10} , expectedly == ${desired_iterations:10}') eprintln(' with synchronization the counter is: ${counter.counter:10} , expectedly == ${desired_iterations:10}')
} }

View File

@ -1821,6 +1821,9 @@ pub fn (stmt Stmt) check_c_expr() ? {
AssignStmt { AssignStmt {
return return
} }
ForCStmt, ForInStmt, ForStmt {
return
}
ExprStmt { ExprStmt {
if stmt.expr.is_expr() { if stmt.expr.is_expr() {
return return

View File

@ -206,6 +206,14 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
if v.pref.os == .macos && os.exists('/opt/procursus') { if v.pref.os == .macos && os.exists('/opt/procursus') {
ccoptions.linker_flags << '-Wl,-rpath,/opt/procursus/lib' ccoptions.linker_flags << '-Wl,-rpath,/opt/procursus/lib'
} }
mut user_darwin_version := 999_999_999
mut user_darwin_ppc := false
$if macos {
user_darwin_version = os.uname().release.split('.')[0].int()
if os.uname().machine == 'Power Macintosh' {
user_darwin_ppc = true
}
}
ccoptions.debug_mode = v.pref.is_debug ccoptions.debug_mode = v.pref.is_debug
ccoptions.guessed_compiler = v.pref.ccompiler ccoptions.guessed_compiler = v.pref.ccompiler
if ccoptions.guessed_compiler == 'cc' && v.pref.is_prod { if ccoptions.guessed_compiler == 'cc' && v.pref.is_prod {
@ -252,7 +260,10 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
} }
if ccoptions.is_cc_gcc { if ccoptions.is_cc_gcc {
if ccoptions.debug_mode { if ccoptions.debug_mode {
debug_options = ['-g', '-no-pie'] debug_options = ['-g']
if user_darwin_version > 9 {
debug_options << '-no-pie'
}
} }
optimization_options = ['-O3', '-fno-strict-aliasing', '-flto'] optimization_options = ['-O3', '-fno-strict-aliasing', '-flto']
} }
@ -334,7 +345,7 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
} }
// macOS code can include objective C TODO remove once objective C is replaced with C // macOS code can include objective C TODO remove once objective C is replaced with C
if v.pref.os == .macos || v.pref.os == .ios { if v.pref.os == .macos || v.pref.os == .ios {
if !ccoptions.is_cc_tcc { if !ccoptions.is_cc_tcc && !user_darwin_ppc {
ccoptions.source_args << '-x objective-c' ccoptions.source_args << '-x objective-c'
} }
} }

View File

@ -335,7 +335,8 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
left_elem_type := c.table.unaliased_type(left_info.elem_type) left_elem_type := c.table.unaliased_type(left_info.elem_type)
right_info := right_sym.info as ast.Array right_info := right_sym.info as ast.Array
right_elem_type := c.table.unaliased_type(right_info.elem_type) right_elem_type := c.table.unaliased_type(right_info.elem_type)
if left_info.nr_dims == right_info.nr_dims && left_elem_type == right_elem_type { if left_type_unwrapped.nr_muls() == right_type_unwrapped.nr_muls()
&& left_info.nr_dims == right_info.nr_dims && left_elem_type == right_elem_type {
continue continue
} }
} }

View File

@ -204,7 +204,9 @@ pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type,
} }
return return
} }
return error('cannot use `$got_typ_str` as `$expected_typ_str`') if got != ast.void_type {
return error('cannot use `$got_typ_str` as `$expected_typ_str`')
}
} }
// helper method to check if the type is of the same module. // helper method to check if the type is of the same module.
@ -689,3 +691,38 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
c.need_recheck_generic_fns = true c.need_recheck_generic_fns = true
} }
} }
pub fn (c &Checker) sizeof_integer(a ast.Type) int {
t := if a in ast.unsigned_integer_type_idxs { a.flip_signedness() } else { a }
r := match t {
ast.char_type_idx, ast.i8_type_idx {
1
}
ast.i16_type_idx {
2
}
ast.int_type_idx {
4
}
ast.rune_type_idx {
4
}
ast.i64_type_idx {
8
}
ast.isize_type_idx {
if c.pref.m64 { 8 } else { 4 }
}
ast.int_literal_type {
s := c.table.type_to_str(a)
panic('`$s` has unknown size')
0
}
else {
s := c.table.type_to_str(a)
panic('`$s` is not an integer')
0
}
}
return r
}

View File

@ -640,6 +640,18 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
rt := c.table.sym(right_type).name rt := c.table.sym(right_type).name
c.error('negative value cannot be compared with `$rt`', node.left.pos) c.error('negative value cannot be compared with `$rt`', node.left.pos)
} }
} else if is_left_type_signed != is_right_type_signed
&& left_type != ast.int_literal_type_idx
&& right_type != ast.int_literal_type_idx {
ls := c.sizeof_integer(left_type)
rs := c.sizeof_integer(right_type)
// prevent e.g. `u32 == i16` but not `u16 == i32` as max_u16 fits in i32
// TODO u32 == i32, change < to <=
if (is_left_type_signed && ls < rs) || (is_right_type_signed && rs < ls) {
lt := c.table.sym(left_type).name
rt := c.table.sym(right_type).name
c.error('`$lt` cannot be compared with `$rt`', node.pos)
}
} }
} }
} }
@ -1663,7 +1675,9 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
c.inside_selector_expr = old_selector_expr c.inside_selector_expr = old_selector_expr
c.using_new_err_struct = using_new_err_struct_save c.using_new_err_struct = using_new_err_struct_save
if typ == ast.void_type_idx { if typ == ast.void_type_idx {
c.error('`void` type has no fields', node.pos) // This means that the field has an undefined type.
// This error was handled before.
// c.error('`void` type has no fields', node.pos)
return ast.void_type return ast.void_type
} }
node.expr_type = typ node.expr_type = typ
@ -3683,8 +3697,8 @@ pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type {
for mut expr is ast.ParExpr { for mut expr is ast.ParExpr {
expr = expr.expr expr = expr.expr
} }
if expr in [ast.BoolLiteral, ast.CallExpr, ast.CharLiteral, ast.FloatLiteral, if expr in [ast.BoolLiteral, ast.CallExpr, ast.CharLiteral, ast.FloatLiteral, ast.IntegerLiteral,
ast.IntegerLiteral, ast.InfixExpr, ast.StringLiteral, ast.StringInterLiteral] { ast.InfixExpr, ast.StringLiteral, ast.StringInterLiteral] {
c.error('cannot take the address of $expr', node.pos) c.error('cannot take the address of $expr', node.pos)
} }
if mut node.right is ast.IndexExpr { if mut node.right is ast.IndexExpr {

View File

@ -578,6 +578,17 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
found = true found = true
return ast.string_type return ast.string_type
} }
if !found && node.left is ast.CallExpr {
c.expr(node.left)
expr := node.left as ast.CallExpr
sym := c.table.sym(expr.return_type)
if sym.kind == .function {
info := sym.info as ast.FnType
node.return_type = info.func.return_type
found = true
func = info.func
}
}
// already prefixed (mod.fn) or C/builtin/main // already prefixed (mod.fn) or C/builtin/main
if !found { if !found {
if f := c.table.find_fn(fn_name) { if f := c.table.find_fn(fn_name) {
@ -895,6 +906,16 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
if param.typ.has_flag(.generic) { if param.typ.has_flag(.generic) {
continue continue
} }
if param_typ_sym.kind == .array && arg_typ_sym.kind == .array {
param_info := param_typ_sym.info as ast.Array
param_elem_type := c.table.unaliased_type(param_info.elem_type)
arg_info := arg_typ_sym.info as ast.Array
arg_elem_type := c.table.unaliased_type(arg_info.elem_type)
if param.typ.nr_muls() == arg_typ.nr_muls()
&& param_info.nr_dims == arg_info.nr_dims && param_elem_type == arg_elem_type {
continue
}
}
if c.pref.translated || c.file.is_translated { if c.pref.translated || c.file.is_translated {
// TODO duplicated logic in check_types() (check_types.v) // TODO duplicated logic in check_types() (check_types.v)
// Allow enums to be used as ints and vice versa in translated code // Allow enums to be used as ints and vice versa in translated code
@ -1389,6 +1410,19 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
// if arg_typ_sym.kind == .string && typ_sym.has_method('str') { // if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
// continue // continue
// } // }
param_typ_sym := c.table.sym(exp_arg_typ)
arg_typ_sym := c.table.sym(got_arg_typ)
if param_typ_sym.kind == .array && arg_typ_sym.kind == .array {
param_info := param_typ_sym.info as ast.Array
param_elem_type := c.table.unaliased_type(param_info.elem_type)
arg_info := arg_typ_sym.info as ast.Array
arg_elem_type := c.table.unaliased_type(arg_info.elem_type)
if exp_arg_typ.nr_muls() == got_arg_typ.nr_muls()
&& param_info.nr_dims == arg_info.nr_dims
&& param_elem_type == arg_elem_type {
continue
}
}
if got_arg_typ != ast.void_type { if got_arg_typ != ast.void_type {
c.error('$err.msg() in argument ${i + 1} to `${left_sym.name}.$method_name`', c.error('$err.msg() in argument ${i + 1} to `${left_sym.name}.$method_name`',
arg.pos) arg.pos)
@ -1743,6 +1777,9 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast.
mut ret_type := ast.void_type mut ret_type := ast.void_type
match method_name { match method_name {
'clone', 'move' { 'clone', 'move' {
if node.args.len != 0 {
c.error('`.${method_name}()` does not have any arguments', node.args[0].pos)
}
if method_name[0] == `m` { if method_name[0] == `m` {
c.fail_if_immutable(node.left) c.fail_if_immutable(node.left)
} }
@ -1754,6 +1791,9 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast.
ret_type = ret_type.clear_flag(.shared_f) ret_type = ret_type.clear_flag(.shared_f)
} }
'keys' { 'keys' {
if node.args.len != 0 {
c.error('`.keys()` does not have any arguments', node.args[0].pos)
}
info := left_sym.info as ast.Map info := left_sym.info as ast.Map
typ := c.table.find_or_register_array(info.key_type) typ := c.table.find_or_register_array(info.key_type)
ret_type = ast.Type(typ) ret_type = ast.Type(typ)
@ -1878,6 +1918,9 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
c.check_map_and_filter(false, elem_typ, node) c.check_map_and_filter(false, elem_typ, node)
node.return_type = ast.bool_type node.return_type = ast.bool_type
} else if method_name == 'clone' { } else if method_name == 'clone' {
if node.args.len != 0 {
c.error('`.clone()` does not have any arguments', node.args[0].pos)
}
// need to return `array_xxx` instead of `array` // need to return `array_xxx` instead of `array`
// in ['clone', 'str'] { // in ['clone', 'str'] {
node.receiver_type = left_type.ref() node.receiver_type = left_type.ref()
@ -1895,19 +1938,27 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
// c.warn('use `value in arr` instead of `arr.contains(value)`', node.pos) // c.warn('use `value in arr` instead of `arr.contains(value)`', node.pos)
if node.args.len != 1 { if node.args.len != 1 {
c.error('`.contains()` expected 1 argument, but got $node.args.len', node.pos) c.error('`.contains()` expected 1 argument, but got $node.args.len', node.pos)
} else { } else if !left_sym.has_method('contains') {
arg_typ := ast.mktyp(c.expr(node.args[0].expr)) arg_typ := c.expr(node.args[0].expr)
elem_typ_str := c.table.type_to_str(elem_typ) c.check_expected_call_arg(arg_typ, elem_typ, node.language, node.args[0]) or {
arg_typ_str := c.table.type_to_str(arg_typ) c.error('$err.msg() in argument 1 to `.contains()`', node.args[0].pos)
if !left_sym.has_method('contains') && elem_typ_str != arg_typ_str {
c.error('`.contains()` expected `$elem_typ_str` argument, but got `$arg_typ_str`',
node.pos)
} }
} }
node.return_type = ast.bool_type node.return_type = ast.bool_type
} else if method_name == 'index' { } else if method_name == 'index' {
if node.args.len != 1 {
c.error('`.index()` expected 1 argument, but got $node.args.len', node.pos)
} else if !left_sym.has_method('index') {
arg_typ := c.expr(node.args[0].expr)
c.check_expected_call_arg(arg_typ, elem_typ, node.language, node.args[0]) or {
c.error('$err.msg() in argument 1 to `.index()`', node.args[0].pos)
}
}
node.return_type = ast.int_type node.return_type = ast.int_type
} else if method_name in ['first', 'last', 'pop'] { } else if method_name in ['first', 'last', 'pop'] {
if node.args.len != 0 {
c.error('`.${method_name}()` does not have any arguments', node.args[0].pos)
}
node.return_type = array_info.elem_type node.return_type = array_info.elem_type
if method_name == 'pop' { if method_name == 'pop' {
c.fail_if_immutable(node.left) c.fail_if_immutable(node.left)

View File

@ -95,6 +95,9 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
node.scope.update_var_type(node.key_var, key_type) node.scope.update_var_type(node.key_var, key_type)
} }
mut value_type := c.table.value_type(typ) mut value_type := c.table.value_type(typ)
if sym.kind == .string {
value_type = ast.byte_type
}
if value_type == ast.void_type || typ.has_flag(.optional) { if value_type == ast.void_type || typ.has_flag(.optional) {
if typ != ast.void_type { if typ != ast.void_type {
c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.pos()) c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.pos())

View File

@ -296,7 +296,7 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) {
c.smartcast_if_conds(node.right, mut scope) c.smartcast_if_conds(node.right, mut scope)
} else if node.op == .key_is { } else if node.op == .key_is {
right_expr := node.right right_expr := node.right
mut right_type := match right_expr { right_type := match right_expr {
ast.TypeNode { ast.TypeNode {
right_expr.typ right_expr.typ
} }
@ -308,11 +308,10 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) {
ast.Type(0) ast.Type(0)
} }
} }
right_type = c.unwrap_generic(right_type)
if right_type != ast.Type(0) { if right_type != ast.Type(0) {
left_sym := c.table.sym(node.left_type) left_sym := c.table.sym(node.left_type)
right_sym := c.table.sym(right_type) right_sym := c.table.sym(right_type)
mut expr_type := c.unwrap_generic(c.expr(node.left)) mut expr_type := c.expr(node.left)
if left_sym.kind == .aggregate { if left_sym.kind == .aggregate {
expr_type = (left_sym.info as ast.Aggregate).sum_type expr_type = (left_sym.info as ast.Aggregate).sum_type
} }
@ -322,7 +321,7 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) {
} else { } else {
return return
} }
} else if !c.check_types(right_type, expr_type) { } else if !c.check_types(right_type, expr_type) && left_sym.kind != .sum_type {
expect_str := c.table.type_to_str(right_type) expect_str := c.table.type_to_str(right_type)
expr_str := c.table.type_to_str(expr_type) expr_str := c.table.type_to_str(expr_type)
c.error('cannot use type `$expect_str` as type `$expr_str`', node.pos) c.error('cannot use type `$expect_str` as type `$expr_str`', node.pos)

View File

@ -162,9 +162,10 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
c.expected_type = node.expected_type c.expected_type = node.expected_type
low_expr := expr.low low_expr := expr.low
high_expr := expr.high high_expr := expr.high
final_cond_sym := c.table.final_sym(node.cond_type)
if low_expr is ast.IntegerLiteral { if low_expr is ast.IntegerLiteral {
if high_expr is ast.IntegerLiteral if high_expr is ast.IntegerLiteral
&& (cond_type_sym.is_int() || cond_type_sym.info is ast.Enum) { && (final_cond_sym.is_int() || final_cond_sym.info is ast.Enum) {
low = low_expr.val.i64() low = low_expr.val.i64()
high = high_expr.val.i64() high = high_expr.val.i64()
if low > high { if low > high {
@ -174,7 +175,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
c.error('mismatched range types', low_expr.pos) c.error('mismatched range types', low_expr.pos)
} }
} else if low_expr is ast.CharLiteral { } else if low_expr is ast.CharLiteral {
if high_expr is ast.CharLiteral && cond_type_sym.kind in [.u8, .char, .rune] { if high_expr is ast.CharLiteral && final_cond_sym.kind in [.u8, .char, .rune] {
low = low_expr.val[0] low = low_expr.val[0]
high = high_expr.val[0] high = high_expr.val[0]
if low > high { if low > high {

View File

@ -34,7 +34,7 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
mut got_types := []ast.Type{} mut got_types := []ast.Type{}
mut expr_idxs := []int{} mut expr_idxs := []int{}
for i, expr in node.exprs { for i, expr in node.exprs {
typ := c.expr(expr) mut typ := c.expr(expr)
if typ == ast.void_type { if typ == ast.void_type {
c.error('`$expr` used as value', node.pos) c.error('`$expr` used as value', node.pos)
} }
@ -46,6 +46,13 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
expr_idxs << i expr_idxs << i
} }
} else { } else {
if expr is ast.Ident {
if expr.obj is ast.Var {
if expr.obj.smartcasts.len > 0 {
typ = c.unwrap_generic(expr.obj.smartcasts.last())
}
}
}
got_types << typ got_types << typ
expr_idxs << i expr_idxs << i
} }

View File

@ -0,0 +1,28 @@
vlib/v/checker/tests/array_builtin_method_args_err.vv:4:18: error: `.clone()` does not have any arguments
2 | arr := [1, 2, 3]
3 |
4 | a1 := arr.clone(22)
| ~~
5 | println(a1)
6 |
vlib/v/checker/tests/array_builtin_method_args_err.vv:7:18: error: `.first()` does not have any arguments
5 | println(a1)
6 |
7 | a2 := arr.first('a2')
| ~~~~
8 | println(a2)
9 |
vlib/v/checker/tests/array_builtin_method_args_err.vv:10:17: error: `.last()` does not have any arguments
8 | println(a2)
9 |
10 | a3 := arr.last(1)
| ^
11 | println(a3)
12 |
vlib/v/checker/tests/array_builtin_method_args_err.vv:13:16: error: `.pop()` does not have any arguments
11 | println(a3)
12 |
13 | a4 := arr.pop(2)
| ^
14 | println(a4)
15 | }

View File

@ -0,0 +1,15 @@
fn main() {
arr := [1, 2, 3]
a1 := arr.clone(22)
println(a1)
a2 := arr.first('a2')
println(a2)
a3 := arr.last(1)
println(a3)
a4 := arr.pop(2)
println(a4)
}

View File

@ -1,8 +1,8 @@
vlib/v/checker/tests/array_contains_args_err.vv:3:17: error: `.contains()` expected `int` argument, but got `[]int` vlib/v/checker/tests/array_contains_args_err.vv:3:26: error: cannot use `[]int` as `int` in argument 1 to `.contains()`
1 | fn main() { 1 | fn main() {
2 | arr := [0] 2 | arr := [0]
3 | mut ret := [0].contains([0]) 3 | mut ret := [0].contains([0])
| ~~~~~~~~~~~~~ | ~~~
4 | ret = [0].contains() 4 | ret = [0].contains()
5 | ret = [0, 1, 2].contains(0, 1, 2) 5 | ret = [0, 1, 2].contains(0, 1, 2)
vlib/v/checker/tests/array_contains_args_err.vv:4:12: error: `.contains()` expected 1 argument, but got 0 vlib/v/checker/tests/array_contains_args_err.vv:4:12: error: `.contains()` expected 1 argument, but got 0
@ -19,17 +19,17 @@ vlib/v/checker/tests/array_contains_args_err.vv:5:18: error: `.contains()` expec
| ~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~
6 | ret = [0].contains('a') 6 | ret = [0].contains('a')
7 | ret = [0].contains(arr) 7 | ret = [0].contains(arr)
vlib/v/checker/tests/array_contains_args_err.vv:6:12: error: `.contains()` expected `int` argument, but got `string` vlib/v/checker/tests/array_contains_args_err.vv:6:21: error: cannot use `string` as `int` in argument 1 to `.contains()`
4 | ret = [0].contains() 4 | ret = [0].contains()
5 | ret = [0, 1, 2].contains(0, 1, 2) 5 | ret = [0, 1, 2].contains(0, 1, 2)
6 | ret = [0].contains('a') 6 | ret = [0].contains('a')
| ~~~~~~~~~~~~~ | ~~~
7 | ret = [0].contains(arr) 7 | ret = [0].contains(arr)
8 | println(ret) 8 | println(ret)
vlib/v/checker/tests/array_contains_args_err.vv:7:12: error: `.contains()` expected `int` argument, but got `[]int` vlib/v/checker/tests/array_contains_args_err.vv:7:21: error: cannot use `[]int` as `int` in argument 1 to `.contains()`
5 | ret = [0, 1, 2].contains(0, 1, 2) 5 | ret = [0, 1, 2].contains(0, 1, 2)
6 | ret = [0].contains('a') 6 | ret = [0].contains('a')
7 | ret = [0].contains(arr) 7 | ret = [0].contains(arr)
| ~~~~~~~~~~~~~ | ~~~
8 | println(ret) 8 | println(ret)
9 | } 9 | }

View File

@ -0,0 +1,35 @@
vlib/v/checker/tests/array_index_args_err.vv:3:23: error: cannot use `[]int` as `int` in argument 1 to `.index()`
1 | fn main() {
2 | arr := [0]
3 | mut ret := [0].index([0])
| ~~~
4 | ret = [0].index()
5 | ret = [0, 1, 2].index(0, 1, 2)
vlib/v/checker/tests/array_index_args_err.vv:4:12: error: `.index()` expected 1 argument, but got 0
2 | arr := [0]
3 | mut ret := [0].index([0])
4 | ret = [0].index()
| ~~~~~~~
5 | ret = [0, 1, 2].index(0, 1, 2)
6 | ret = [0].index('a')
vlib/v/checker/tests/array_index_args_err.vv:5:18: error: `.index()` expected 1 argument, but got 3
3 | mut ret := [0].index([0])
4 | ret = [0].index()
5 | ret = [0, 1, 2].index(0, 1, 2)
| ~~~~~~~~~~~~~~
6 | ret = [0].index('a')
7 | ret = [0].index(arr)
vlib/v/checker/tests/array_index_args_err.vv:6:18: error: cannot use `string` as `int` in argument 1 to `.index()`
4 | ret = [0].index()
5 | ret = [0, 1, 2].index(0, 1, 2)
6 | ret = [0].index('a')
| ~~~
7 | ret = [0].index(arr)
8 | println(ret)
vlib/v/checker/tests/array_index_args_err.vv:7:18: error: cannot use `[]int` as `int` in argument 1 to `.index()`
5 | ret = [0, 1, 2].index(0, 1, 2)
6 | ret = [0].index('a')
7 | ret = [0].index(arr)
| ~~~
8 | println(ret)
9 | }

View File

@ -0,0 +1,9 @@
fn main() {
arr := [0]
mut ret := [0].index([0])
ret = [0].index()
ret = [0, 1, 2].index(0, 1, 2)
ret = [0].index('a')
ret = [0].index(arr)
println(ret)
}

View File

@ -17,10 +17,45 @@ vlib/v/checker/tests/compare_unsigned_signed.vv:10:16: error: `u8` cannot be com
10 | _ = u8(-1) == -1 // false! 10 | _ = u8(-1) == -1 // false!
| ~~ | ~~
11 | _ = -1 == u16(-1) // false! 11 | _ = -1 == u16(-1) // false!
12 | } 12 |
vlib/v/checker/tests/compare_unsigned_signed.vv:11:6: error: negative value cannot be compared with `u16` vlib/v/checker/tests/compare_unsigned_signed.vv:11:6: error: negative value cannot be compared with `u16`
9 | // unsigned == literal 9 | // unsigned == literal
10 | _ = u8(-1) == -1 // false! 10 | _ = u8(-1) == -1 // false!
11 | _ = -1 == u16(-1) // false! 11 | _ = -1 == u16(-1) // false!
| ~~ | ~~
12 | } 12 |
13 | // smaller unsigned == signed, OK
vlib/v/checker/tests/compare_unsigned_signed.vv:18:12: error: `i8` cannot be compared with `u16`
16 |
17 | // smaller signed == unsigned, NG
18 | _ = i8(0) == u16(0)
| ~~
19 | _ = i16(0) != u32(0)
20 | _ = int(0) == u64(0)
vlib/v/checker/tests/compare_unsigned_signed.vv:19:13: error: `i16` cannot be compared with `u32`
17 | // smaller signed == unsigned, NG
18 | _ = i8(0) == u16(0)
19 | _ = i16(0) != u32(0)
| ~~
20 | _ = int(0) == u64(0)
21 | _ = i32(0) == u64(0) // FIXME
vlib/v/checker/tests/compare_unsigned_signed.vv:20:13: error: `int` cannot be compared with `u64`
18 | _ = i8(0) == u16(0)
19 | _ = i16(0) != u32(0)
20 | _ = int(0) == u64(0)
| ~~
21 | _ = i32(0) == u64(0) // FIXME
22 | // swap order
vlib/v/checker/tests/compare_unsigned_signed.vv:23:13: error: `u16` cannot be compared with `i8`
21 | _ = i32(0) == u64(0) // FIXME
22 | // swap order
23 | _ = u16(0) == i8(0)
| ~~
24 | _ = u64(0) == i16(0)
25 | }
vlib/v/checker/tests/compare_unsigned_signed.vv:24:13: error: `u64` cannot be compared with `i16`
22 | // swap order
23 | _ = u16(0) == i8(0)
24 | _ = u64(0) == i16(0)
| ~~
25 | }

View File

@ -9,4 +9,17 @@ fn main() {
// unsigned == literal // unsigned == literal
_ = u8(-1) == -1 // false! _ = u8(-1) == -1 // false!
_ = -1 == u16(-1) // false! _ = -1 == u16(-1) // false!
// smaller unsigned == signed, OK
_ = u16(-1) == int(-1)
_ = int(-1) != u8(-1)
// smaller signed == unsigned, NG
_ = i8(0) == u16(0)
_ = i16(0) != u32(0)
_ = int(0) == u64(0)
_ = i32(0) == u64(0) // FIXME
// swap order
_ = u16(0) == i8(0)
_ = u64(0) == i16(0)
} }

View File

@ -1,10 +1,3 @@
vlib/v/checker/tests/if_match_expr.vv:8:6: error: `if` expression branch has unsupported statement (`v.ast.ForStmt`)
6 | if true {1} else {-1} // result
7 | } else {
8 | for {break}
| ^
9 | {}
10 | match true {true {} else {}} // statement not expression
vlib/v/checker/tests/if_match_expr.vv:9:2: error: `if` expression branch has unsupported statement (`v.ast.Block`) vlib/v/checker/tests/if_match_expr.vv:9:2: error: `if` expression branch has unsupported statement (`v.ast.Block`)
7 | } else { 7 | } else {
8 | for {break} 8 | for {break}

View File

@ -0,0 +1,21 @@
vlib/v/checker/tests/map_builtin_method_args_err.vv:4:16: error: `.clone()` does not have any arguments
2 | mut m := {11: 22}
3 |
4 | a1 := m.clone(11)
| ~~
5 | println(a1)
6 |
vlib/v/checker/tests/map_builtin_method_args_err.vv:7:15: error: `.move()` does not have any arguments
5 | println(a1)
6 |
7 | a2 := m.move(11)
| ~~
8 | println(a2)
9 |
vlib/v/checker/tests/map_builtin_method_args_err.vv:10:15: error: `.keys()` does not have any arguments
8 | println(a2)
9 |
10 | a3 := m.keys('aaa')
| ~~~~~
11 | println(a3)
12 | }

View File

@ -0,0 +1,12 @@
fn main() {
mut m := {11: 22}
a1 := m.clone(11)
println(a1)
a2 := m.move(11)
println(a2)
a3 := m.keys('aaa')
println(a3)
}

View File

@ -511,8 +511,13 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
return res return res
} }
else { ast.AnonFn, ast.ArrayDecompose, ast.AsCast, ast.Assoc, ast.AtExpr, ast.CTempVar,
e.error('unhandled expression $expr.type_name()') ast.ChanInit, ast.Comment, ast.ComptimeCall, ast.ComptimeSelector, ast.ComptimeType,
ast.ConcatExpr, ast.DumpExpr, ast.EmptyExpr, ast.EnumVal, ast.GoExpr, ast.IfGuardExpr,
ast.IndexExpr, ast.IsRefType, ast.Likely, ast.LockExpr, ast.MapInit, ast.MatchExpr,
ast.NodeError, ast.None, ast.OffsetOf, ast.OrExpr, ast.RangeExpr, ast.SelectExpr,
ast.SqlExpr, ast.TypeNode, ast.TypeOf, ast.UnsafeExpr {
e.error('unhandled expression ${typeof(expr).name}')
} }
} }
return empty return empty

View File

@ -1462,7 +1462,7 @@ pub fn (mut f Fmt) array_init(node ast.ArrayInit) {
|| f.line_len + expr.pos().len > fmt.max_len[3] || f.line_len + expr.pos().len > fmt.max_len[3]
} }
} }
line_break := f.array_init_break[f.array_init_depth - 1] mut line_break := f.array_init_break[f.array_init_depth - 1]
mut penalty := if line_break { 0 } else { 4 } mut penalty := if line_break { 0 } else { 4 }
if penalty > 0 { if penalty > 0 {
if i == 0 if i == 0
@ -1480,14 +1480,26 @@ pub fn (mut f Fmt) array_init(node ast.ArrayInit) {
} }
single_line_expr := expr_is_single_line(expr) single_line_expr := expr_is_single_line(expr)
if single_line_expr { if single_line_expr {
estr := f.node_str(expr) mut estr := ''
if !is_new_line && !f.buffering && f.line_len + estr.len > fmt.max_len.last() { if !is_new_line && !f.buffering && f.line_len + expr.pos().len > fmt.max_len.last() {
if inc_indent {
estr = f.node_str(expr)
}
f.writeln('') f.writeln('')
is_new_line = true is_new_line = true
if !inc_indent { if !inc_indent {
f.indent++ f.indent++
inc_indent = true inc_indent = true
f.write_indent()
f.empty_line = false
estr = f.node_str(expr)
} }
if i == 0 {
f.array_init_break[f.array_init_depth - 1] = true
line_break = true
}
} else {
estr = f.node_str(expr)
} }
if !is_new_line && i > 0 { if !is_new_line && i > 0 {
f.write(' ') f.write(' ')
@ -2315,7 +2327,11 @@ pub fn (mut f Fmt) par_expr(node ast.ParExpr) {
f.par_level++ f.par_level++
f.write('(') f.write('(')
} }
f.expr(node.expr) mut expr := node.expr
for mut expr is ast.ParExpr {
expr = expr.expr
}
f.expr(expr)
if requires_paren { if requires_paren {
f.par_level-- f.par_level--
f.write(')') f.write(')')

View File

@ -6,8 +6,7 @@ const (
header: { header: {
'Set-Cookie': ['special-7=","'] 'Set-Cookie': ['special-7=","']
} }
cookies: [ cookies: [&http.Cookie{
&http.Cookie{
name: 'special-7' name: 'special-7'
value: ',' value: ','
raw: 'special-8=","' raw: 'special-8=","'

View File

@ -12,6 +12,13 @@ fn wrapping_tests() {
'elit. Donec varius purus leo, vel maximus diam', 'elit. Donec varius purus leo, vel maximus diam',
'finibus sed. Etiam eu urna ante. Nunc quis vehicula', 'finibus sed. Etiam eu urna ante. Nunc quis vehicula',
'velit. Sed at mauris et quam ornare tristique.'] 'velit. Sed at mauris et quam ornare tristique.']
multi_level := [
[1, 2, 3],
[
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius purus leo, vel maximus diam',
'finibus sed. Etiam eu urna ante. Nunc quis vehicula velit. Sed at mauris et quam ornare tristique.',
],
]
} }
fn array_init_without_commas() { fn array_init_without_commas() {

View File

@ -9,6 +9,10 @@ fn main() {
fn wrapping_tests() { fn wrapping_tests() {
my_arr := ['Lorem ipsum dolor sit amet, consectetur adipiscing', 'elit. Donec varius purus leo, vel maximus diam', 'finibus sed. Etiam eu urna ante. Nunc quis vehicula', 'velit. Sed at mauris et quam ornare tristique.'] my_arr := ['Lorem ipsum dolor sit amet, consectetur adipiscing', 'elit. Donec varius purus leo, vel maximus diam', 'finibus sed. Etiam eu urna ante. Nunc quis vehicula', 'velit. Sed at mauris et quam ornare tristique.']
multi_level := [
[1, 2, 3],
['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius purus leo, vel maximus diam', 'finibus sed. Etiam eu urna ante. Nunc quis vehicula velit. Sed at mauris et quam ornare tristique.'],
]
} }
fn array_init_without_commas() { fn array_init_without_commas() {

View File

@ -0,0 +1,6 @@
fn main() {
_, _ := (22 > 11), (43 > 22)
_ := (10 + 11)
_ := (11 * 2)
_ := (11 * 2)
}

View File

@ -0,0 +1,6 @@
fn main() {
_, _ := (((22 > 11))), (43 > 22)
_ := ((10 + 11))
_ := ((((((11 * 2))))))
_ := ((11 * 2))
}

View File

@ -78,7 +78,7 @@ fn (mut g Gen) array_init(node ast.ArrayInit) {
g.inside_lambda = false g.inside_lambda = false
return return
} }
need_tmp_var := g.inside_call && !g.inside_struct_init need_tmp_var := g.inside_call && !g.inside_struct_init && node.exprs.len == 0
mut stmt_str := '' mut stmt_str := ''
mut tmp_var := '' mut tmp_var := ''
if need_tmp_var { if need_tmp_var {

View File

@ -314,11 +314,32 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
} }
func := right_sym.info as ast.FnType func := right_sym.info as ast.FnType
ret_styp := g.typ(func.func.return_type) ret_styp := g.typ(func.func.return_type)
g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (')
mut call_conv := ''
mut msvc_call_conv := ''
for attr in func.func.attrs {
match attr.name {
'callconv' {
if g.is_cc_msvc {
msvc_call_conv = '__$attr.arg '
} else {
call_conv = '$attr.arg'
}
}
else {}
}
}
call_conv_attribute_suffix := if call_conv.len != 0 {
'__attribute__(($call_conv))'
} else {
''
}
g.write('$ret_styp ($msvc_call_conv*${g.get_ternary_name(ident.name)}) (')
def_pos := g.definitions.len def_pos := g.definitions.len
g.fn_decl_params(func.func.params, voidptr(0), false) g.fn_decl_params(func.func.params, voidptr(0), false)
g.definitions.go_back(g.definitions.len - def_pos) g.definitions.go_back(g.definitions.len - def_pos)
g.write(')') g.write(')$call_conv_attribute_suffix')
} else { } else {
if is_decl { if is_decl {
if is_inside_ternary { if is_inside_ternary {

View File

@ -3286,7 +3286,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
if field_sym.kind == .sum_type { if field_sym.kind == .sum_type {
g.write('*') g.write('*')
} }
cast_sym := g.table.sym(typ) cast_sym := g.table.sym(g.unwrap_generic(typ))
if i != 0 { if i != 0 {
dot := if field.typ.is_ptr() { '->' } else { '.' } dot := if field.typ.is_ptr() { '->' } else { '.' }
sum_type_deref_field += ')$dot' sum_type_deref_field += ')$dot'
@ -5185,11 +5185,13 @@ fn (mut g Gen) enum_val(node ast.EnumVal) {
// g.write('${it.mod}${it.enum_name}_$it.val') // g.write('${it.mod}${it.enum_name}_$it.val')
// g.enum_expr(node) // g.enum_expr(node)
styp := g.typ(g.table.unaliased_type(node.typ)) styp := g.typ(g.table.unaliased_type(node.typ))
if node.typ.is_number() { // && g.inside_switch
if g.pref.translated && node.typ.is_number() {
// Mostly in translated code, when C enums are used as ints in switches // Mostly in translated code, when C enums are used as ints in switches
// g.write('/*enum val is_number $node.mod styp=$styp*/') g.write('/*enum val is_number $node.mod styp=$styp*/_const_main__$node.val')
} else {
g.write('${styp}__$node.val')
} }
g.write('${styp}__$node.val')
} }
fn (mut g Gen) as_cast(node ast.AsCast) { fn (mut g Gen) as_cast(node ast.AsCast) {

View File

@ -379,7 +379,10 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
defer { defer {
g.tmp_count = ctmp g.tmp_count = ctmp
} }
prev_inside_ternary := g.inside_ternary
g.inside_ternary = 0
g.stmts(node.stmts) g.stmts(node.stmts)
g.inside_ternary = prev_inside_ternary
if node.is_noreturn { if node.is_noreturn {
g.writeln('\twhile(1);') g.writeln('\twhile(1);')
} }
@ -637,11 +640,12 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
// see my comment in parser near anon_fn // see my comment in parser near anon_fn
if node.left is ast.AnonFn { if node.left is ast.AnonFn {
g.expr(node.left) g.expr(node.left)
} } else if node.left is ast.IndexExpr && node.name == '' {
if node.left is ast.IndexExpr && node.name == '' {
g.is_fn_index_call = true g.is_fn_index_call = true
g.expr(node.left) g.expr(node.left)
g.is_fn_index_call = false g.is_fn_index_call = false
} else if node.left is ast.CallExpr && node.name == '' {
g.expr(node.left)
} }
if node.should_be_skipped { if node.should_be_skipped {
return return
@ -941,7 +945,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
g.gen_expr_to_string(node.left, rec_type) g.gen_expr_to_string(node.left, rec_type)
return return
} else if node.left.obj.smartcasts.len > 0 { } else if node.left.obj.smartcasts.len > 0 {
rec_type = node.left.obj.smartcasts.last() rec_type = g.unwrap_generic(node.left.obj.smartcasts.last())
cast_sym := g.table.sym(rec_type) cast_sym := g.table.sym(rec_type)
if cast_sym.info is ast.Aggregate { if cast_sym.info is ast.Aggregate {
rec_type = cast_sym.info.types[g.aggregate_type_idx] rec_type = cast_sym.info.types[g.aggregate_type_idx]
@ -1331,7 +1335,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
if expr.obj is ast.Var { if expr.obj is ast.Var {
typ = expr.obj.typ typ = expr.obj.typ
if expr.obj.smartcasts.len > 0 { if expr.obj.smartcasts.len > 0 {
typ = expr.obj.smartcasts.last() typ = g.unwrap_generic(expr.obj.smartcasts.last())
cast_sym := g.table.sym(typ) cast_sym := g.table.sym(typ)
if cast_sym.info is ast.Aggregate { if cast_sym.info is ast.Aggregate {
typ = cast_sym.info.types[g.aggregate_type_idx] typ = cast_sym.info.types[g.aggregate_type_idx]
@ -2040,6 +2044,12 @@ fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang as
g.expr(arg.expr) g.expr(arg.expr)
g.write('->val') g.write('->val')
return return
} else if arg.expr is ast.ArrayInit {
if arg.expr.is_fixed {
if !arg.expr.has_it {
g.write('(${g.typ(arg.expr.typ)})')
}
}
} }
g.expr_with_cast(arg.expr, arg_typ, expected_type) g.expr_with_cast(arg.expr, arg_typ, expected_type)
if needs_closing { if needs_closing {

View File

@ -299,14 +299,15 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
} else { } else {
node.cond node.cond
} }
field_accessor := if node.cond_type.is_ptr() { '->' } else { '.' }
i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var } i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var }
g.write('for (int $i = 0; $i < ') g.write('for (int $i = 0; $i < ')
g.expr(cond) g.expr(cond)
g.writeln('.len; ++$i) {') g.writeln('${field_accessor}len; ++$i) {')
if node.val_var != '_' { if node.val_var != '_' {
g.write('\tbyte ${c_name(node.val_var)} = ') g.write('\tu8 ${c_name(node.val_var)} = ')
g.expr(cond) g.expr(cond)
g.writeln('.str[$i];') g.writeln('${field_accessor}str[$i];')
} }
} else if node.kind == .struct_ { } else if node.kind == .struct_ {
cond_type_sym := g.table.sym(node.cond_type) cond_type_sym := g.table.sym(node.cond_type)

View File

@ -20,6 +20,9 @@ fn (mut g Gen) need_tmp_var_in_if(node ast.IfExpr) bool {
if is_noreturn_callexpr(stmt.expr) { if is_noreturn_callexpr(stmt.expr) {
return true return true
} }
if stmt.expr is ast.MatchExpr {
return true
}
if stmt.expr is ast.CallExpr { if stmt.expr is ast.CallExpr {
if stmt.expr.is_method { if stmt.expr.is_method {
left_sym := g.table.sym(stmt.expr.receiver_type) left_sym := g.table.sym(stmt.expr.receiver_type)

View File

@ -708,7 +708,8 @@ fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) {
g.write(', _MOV(($elem_type_str[]){ ') g.write(', _MOV(($elem_type_str[]){ ')
} }
// if g.autofree // if g.autofree
needs_clone := array_info.elem_type.idx() == ast.string_type_idx && !g.is_builtin_mod needs_clone := !g.is_builtin_mod && array_info.elem_type.idx() == ast.string_type_idx
&& array_info.elem_type.nr_muls() == 0
if needs_clone { if needs_clone {
g.write('string_clone(') g.write('string_clone(')
} }

View File

@ -61,8 +61,7 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) {
} }
g.inside_match_optional = true g.inside_match_optional = true
} }
if node.cond in [ast.Ident, ast.SelectorExpr, ast.IntegerLiteral, ast.StringLiteral, if node.cond in [ast.Ident, ast.SelectorExpr, ast.IntegerLiteral, ast.StringLiteral, ast.FloatLiteral] {
ast.FloatLiteral] {
cond_var = g.expr_string(node.cond) cond_var = g.expr_string(node.cond)
} else { } else {
line := if is_expr { line := if is_expr {

View File

@ -156,7 +156,7 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int) {
if g.comptime_var_type_map.len > 0 || g.comptime_for_method.len > 0 { if g.comptime_var_type_map.len > 0 || g.comptime_for_method.len > 0 {
exp_typ = expr.obj.typ exp_typ = expr.obj.typ
} else if expr.obj.smartcasts.len > 0 { } else if expr.obj.smartcasts.len > 0 {
exp_typ = expr.obj.smartcasts.last() exp_typ = g.unwrap_generic(expr.obj.smartcasts.last())
cast_sym := g.table.sym(exp_typ) cast_sym := g.table.sym(exp_typ)
if cast_sym.info is ast.Aggregate { if cast_sym.info is ast.Aggregate {
exp_typ = cast_sym.info.types[g.aggregate_type_idx] exp_typ = cast_sym.info.types[g.aggregate_type_idx]

View File

@ -2426,8 +2426,8 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) {
g.inside_ternary = true g.inside_ternary = true
} }
if node.cond in [ast.Ident, ast.SelectorExpr, ast.IntegerLiteral, ast.StringLiteral, if node.cond in [ast.Ident, ast.SelectorExpr, ast.IntegerLiteral, ast.StringLiteral, ast.FloatLiteral,
ast.FloatLiteral, ast.CallExpr, ast.EnumVal] { ast.CallExpr, ast.EnumVal] {
cond_var = CondExpr{node.cond} cond_var = CondExpr{node.cond}
} else { } else {
s := g.new_tmp_var() s := g.new_tmp_var()

View File

@ -783,9 +783,10 @@ fn (mut p Parser) fn_args() ([]ast.Param, bool, bool) {
} else { } else {
p.tok.lit p.tok.lit
} }
is_generic_type := p.tok.kind == .name && p.tok.lit.len == 1 && p.tok.lit[0].is_capital()
types_only := p.tok.kind in [.amp, .ellipsis, .key_fn, .lsbr] types_only := p.tok.kind in [.amp, .ellipsis, .key_fn, .lsbr]
|| (p.peek_tok.kind == .comma && p.table.known_type(argname)) || (p.peek_tok.kind == .comma && (p.table.known_type(argname) || is_generic_type))
|| p.peek_tok.kind == .dot || p.peek_tok.kind == .rpar || p.peek_tok.kind == .dot || p.peek_tok.kind == .rpar
|| (p.tok.kind == .key_mut && (p.peek_token(2).kind == .comma || (p.tok.kind == .key_mut && (p.peek_token(2).kind == .comma
|| p.peek_token(2).kind == .rpar || (p.peek_tok.kind == .name || p.peek_token(2).kind == .rpar || (p.peek_tok.kind == .name

View File

@ -791,7 +791,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
spos := p.tok.pos() spos := p.tok.pos()
name := p.check_name() name := p.check_name()
if name in p.label_names { if name in p.label_names {
p.error_with_pos('duplicate label `$name`', spos) return p.error_with_pos('duplicate label `$name`', spos)
} }
p.label_names << name p.label_names << name
p.next() p.next()
@ -812,7 +812,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
return stmt return stmt
} }
else { else {
p.error_with_pos('unknown kind of For statement', for_pos) return p.error_with_pos('unknown kind of For statement', for_pos)
} }
} }
} }
@ -2327,6 +2327,18 @@ pub fn (mut p Parser) name_expr() ast.Expr {
p.error_with_pos('unexpected $p.prev_tok', p.prev_tok.pos()) p.error_with_pos('unexpected $p.prev_tok', p.prev_tok.pos())
} }
node = p.call_expr(language, mod) node = p.call_expr(language, mod)
if p.tok.kind == .lpar && p.prev_tok.line_nr == p.tok.line_nr {
p.next()
pos := p.tok.pos()
args := p.call_args()
p.check(.rpar)
node = ast.CallExpr{
left: node
args: args
pos: pos
scope: p.scope
}
}
} }
} else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital)) } else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital))
&& (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) && (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital))

View File

@ -256,30 +256,27 @@ fn vweb_tmpl_${fn_name}() string {
match state { match state {
.html { .html {
if line.starts_with('span.') && line.ends_with('{') { line_t := line.trim_space()
if line_t.starts_with('span.') && line.ends_with('{') {
// `span.header {` => `<span class='header'>` // `span.header {` => `<span class='header'>`
class := line.find_between('span.', '{').trim_space() class := line.find_between('span.', '{').trim_space()
source.writeln('<span class="$class">') source.writeln('<span class="$class">')
in_span = true in_span = true
continue continue
} } else if line_t.starts_with('.') && line.ends_with('{') {
if line.trim_space().starts_with('.') && line.ends_with('{') {
// `.header {` => `<div class='header'>` // `.header {` => `<div class='header'>`
class := line.find_between('.', '{').trim_space() class := line.find_between('.', '{').trim_space()
trimmed := line.trim_space() trimmed := line.trim_space()
source.write_string(strings.repeat(`\t`, line.len - trimmed.len)) // add the necessary indent to keep <div><div><div> code clean source.write_string(strings.repeat(`\t`, line.len - trimmed.len)) // add the necessary indent to keep <div><div><div> code clean
source.writeln('<div class="$class">') source.writeln('<div class="$class">')
continue continue
} } else if line_t.starts_with('#') && line.ends_with('{') {
if line.starts_with('#') && line.ends_with('{') {
// `#header {` => `<div id='header'>` // `#header {` => `<div id='header'>`
class := line.find_between('#', '{').trim_space() class := line.find_between('#', '{').trim_space()
source.writeln('<div id="$class">') source.writeln('<div id="$class">')
continue continue
} } else if line_t == '}' {
if line.trim_space() == '}' { source.write_string(strings.repeat(`\t`, line.len - line_t.len)) // add the necessary indent to keep <div><div><div> code clean
trimmed := line.trim_space()
source.write_string(strings.repeat(`\t`, line.len - trimmed.len)) // add the necessary indent to keep <div><div><div> code clean
if in_span { if in_span {
source.writeln('</span>') source.writeln('</span>')
in_span = false in_span = false

View File

@ -0,0 +1,25 @@
fn foofun(op rune) fn () string {
return match op {
`1` {
fn () string {
return '1 passed'
}
}
`2` {
fn () string {
return '2 passed'
}
}
else {
fn () string {
return 'Nor 1 or 2 passed'
}
}
}
}
fn test_anon_fn_decl_inside_ternary() {
a := foofun(`1`)
println(a())
assert a() == '1 passed'
}

View File

@ -0,0 +1,19 @@
fn foofun1(op string) fn () string {
return fn () string {
return 'x passed'
}
}
fn foofun2(op string) fn () int {
return fn () int {
return 22
}
}
fn test_fn_call_using_anon_fn_call_arg() {
println(main.foofun1('1')())
assert main.foofun1('1')() == 'x passed'
println(main.foofun2('1')())
assert main.foofun2('1')() == 22
}

View File

@ -0,0 +1,24 @@
fn show_array_of_u8(data []u8) string {
println(data)
return '$data'
}
struct Foo {}
fn (f Foo) show_array_of_u8(data []u8) string {
println(data)
return '$data'
}
fn test_fn_with_array_of_aliases_argument() {
a := [byte(1), 2, 3]
s1 := show_array_of_u8(a)
println(s1)
assert s1 == '[0x01, 0x02, 0x03]'
foo := Foo{}
s2 := foo.show_array_of_u8(a)
println(s2)
assert s2 == '[0x01, 0x02, 0x03]'
}

View File

@ -0,0 +1,30 @@
interface Any {}
struct ConcreteA {
a int
}
struct ConcreteB {
b int
}
struct Container {
concrete_a Any
concrete_b Any
}
fn cast_struct<T>(any_struct Any) &T {
if any_struct is T {
return any_struct
}
panic('cannot cast')
}
fn test_generic_empty_interface_to_multi_struct() {
concrete_a := cast_struct<ConcreteA>(ConcreteA{12345})
concrete_b := cast_struct<ConcreteB>(ConcreteB{54321})
println(concrete_a.a)
println(concrete_b.b)
assert concrete_a.a == 12345
assert concrete_b.b == 54321
}

View File

@ -0,0 +1,13 @@
fn test_generics_anon_fn_decl_with_type_only_arg() {
ret := func_b<int>(11, 22, add)
println(ret)
assert ret == 33
}
fn add(a int, b int) int {
return a + b
}
fn func_b<T>(x T, y T, f fn (T, T) T) T {
return f(x, y)
}

View File

@ -1,3 +1,5 @@
// vtest retry: 3
fn sum1(a int, b int) int { fn sum1(a int, b int) int {
sum_func1 := fn (a int, b int) int { sum_func1 := fn (a int, b int) int {
return a + b return a + b

View File

@ -0,0 +1,12 @@
fn test_if_expr_with_nested_match_expr() {
a := if true {
match `a` {
`a` { 0 }
else { 1 }
}
} else {
3
}
println(a)
assert a == 0
}

View File

@ -42,9 +42,9 @@ fn test_cmp_u32_and_signed() {
fn test_cmp_signed_and_u64() { fn test_cmp_signed_and_u64() {
// == // ==
assert int(1) == u64(1) // assert int(1) == u64(1)
// != // !=
assert int(1) != u64(2) // assert int(1) != u64(2)
// > // >
assert !(int(1) > u64(1)) assert !(int(1) > u64(1))
assert int(1) > u64(0) assert int(1) > u64(0)
@ -63,9 +63,9 @@ fn test_cmp_signed_and_u64() {
fn test_cmp_u64_and_signed() { fn test_cmp_u64_and_signed() {
// == // ==
assert u64(1) == int(1) // assert u64(1) == int(1)
// != // !=
assert u64(2) != int(1) // assert u64(2) != int(1)
// > // >
assert !(u64(1) > int(1)) assert !(u64(1) > int(1))
assert u64(1) > int(0) assert u64(1) > int(0)

View File

@ -0,0 +1,11 @@
97
98
99
abc
23
77
abc
> k: abc | v: xyz
> k: def | v: jkl
abc
abc

View File

@ -0,0 +1,30 @@
interface Any {}
fn abc(a Any) {
if a is string {
for x in a {
println(x)
}
}
if a is []u8 {
for x in a {
println(x)
}
}
if a is map[string]string {
for k, v in a {
println('> k: $k | v: $v')
}
}
println(@FN)
}
fn main() {
abc('abc')
abc([u8(23), 77])
abc({
'abc': 'xyz'
'def': 'jkl'
})
abc(123)
}

View File

@ -0,0 +1 @@
[[1, 2], [3, 3]]

View File

@ -0,0 +1,4 @@
fn main() {
a := [[1, 2]!, [3, 3]!]!
println([[a[0][0], a[0][1]]!, [a[1][0], a[1][1]]!]!)
}

View File

@ -0,0 +1,9 @@
fn test_match_aliases() {
a := byte(97)
ret := match a {
`0`...`9`, `a`...`f` { 'OK' }
else { 'NOT OK' }
}
println(ret)
assert ret == 'OK'
}

View File

@ -0,0 +1,58 @@
fn test_match_with_for_in_loop() {
a := 101
b := match a {
101 {
mut aa := []int{}
for i in 0 .. 3 {
aa << i
}
aa
}
else {
[0]
}
}
println(b)
assert b == [0, 1, 2]
}
fn test_match_with_for_c_loop() {
a := 101
b := match a {
101 {
mut aa := []int{}
for i := 0; i < 3; i++ {
aa << i
}
aa
}
else {
[0]
}
}
println(b)
assert b == [0, 1, 2]
}
fn test_match_with_for_loop() {
a := 101
b := match a {
101 {
mut aa := []int{}
mut i := 0
for {
aa << i
i++
if i == 3 {
break
}
}
aa
}
else {
[0]
}
}
println(b)
assert b == [0, 1, 2]
}

View File

@ -58,13 +58,13 @@ fn test_shift_operators() {
assert e == a assert e == a
mut e3 := u64(1) mut e3 := u64(1)
e3 <<= u32(i) e3 <<= u32(i)
assert e3 == b assert e3 == u64(b)
e3 >>= u32(i) e3 >>= u32(i)
assert e == a assert e == a
e3 <<= u64(i) e3 <<= u64(i)
assert e3 == b assert e3 == u64(b)
e3 >>= u64(i) e3 >>= u64(i)
assert e3 == a assert e3 == u64(a)
// Test shifts with custom int types // Test shifts with custom int types
x := MyInt(2) x := MyInt(2)
assert x << 2 == 8 assert x << 2 == 8

View File

@ -1,7 +1,18 @@
fn test_str_array_of_reference() { fn test_creating_an_array_of_string_reference() {
names := ['John', 'Paul', 'George', 'Ringo'] names := ['John', 'Paul', 'George', 'Ringo']
a := unsafe { [&names[0], &names[1]] } a := unsafe { [&names[0], &names[1]] }
println(a[0]) println(a[0])
println(a) println(a)
assert '$a' == "[&'John', &'Paul']" assert '$a' == "[&'John', &'Paul']"
assert typeof(a[0]).name == '&string'
}
fn test_pushing_to_an_array_of_string_references() {
mut a := []&string{}
v1 := 'abc'
v2 := 'def'
a << &v1
a << &v2
assert *(a[0]) == 'abc'
assert *(a[1]) == 'def'
} }