repl: reproduce the void print function error (#13372)

pull/13381/head
Vincenzo Palazzo 2022-02-06 07:05:25 +01:00 committed by GitHub
parent f23d2c8cf4
commit 4e13ee22e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 30 deletions

View File

@ -17,13 +17,14 @@ mut:
in_func bool // are we inside a new custom user function in_func bool // are we inside a new custom user function
line string // the current line entered by the user line string // the current line entered by the user
// //
modules []string // all the import modules modules []string // all the import modules
includes []string // all the #include statements includes []string // all the #include statements
functions []string // all the user function declarations functions []string // all the user function declarations
functions_name []string // all the user function names functions_name []string // all the user function names
lines []string // all the other lines/statements lines []string // all the other lines/statements
temp_lines []string // all the temporary expressions/printlns temp_lines []string // all the temporary expressions/printlns
vstartup_lines []string // lines in the `VSTARTUP` file vstartup_lines []string // lines in the `VSTARTUP` file
eval_func_lines []string // same line of the `VSTARTUP` file, but used to test fn type
} }
const is_stdin_a_pipe = (os.is_atty(0) == 0) const is_stdin_a_pipe = (os.is_atty(0) == 0)
@ -32,14 +33,41 @@ const vexe = os.getenv('VEXE')
const vstartup = os.getenv('VSTARTUP') const vstartup = os.getenv('VSTARTUP')
enum FnType {
@none
void
fn_type
}
fn new_repl() Repl { fn new_repl() Repl {
return Repl{ return Repl{
readline: readline.Readline{} readline: readline.Readline{}
modules: ['os', 'time', 'math'] modules: ['os', 'time', 'math']
vstartup_lines: os.read_file(vstartup) or { '' }.trim_right('\n\r').split_into_lines() vstartup_lines: os.read_file(vstartup) or { '' }.trim_right('\n\r').split_into_lines()
// Test file used to check if a function as a void return or a
// value return.
eval_func_lines: os.read_file(vstartup) or { '' }.trim_right('\n\r').split_into_lines()
} }
} }
fn endline_if_missed(line string) string {
if line.ends_with('\n') {
return line
}
return line + '\n'
}
fn repl_help() {
println(version.full_v_version(false))
println('
|help Displays this information.
|list Show the program so far.
|reset Clears the accumulated program, so you can start a fresh.
|Ctrl-C, Ctrl-D, exit Exits the REPL.
|clear Clears the screen.
'.strip_margin())
}
fn (mut r Repl) checks() bool { fn (mut r Repl) checks() bool {
mut in_string := false mut in_string := false
was_indent := r.indent > 0 was_indent := r.indent > 0
@ -67,20 +95,41 @@ fn (mut r Repl) checks() bool {
return r.in_func || (was_indent && r.indent <= 0) || r.indent > 0 return r.in_func || (was_indent && r.indent <= 0) || r.indent > 0
} }
fn (r &Repl) function_call(line string) bool { fn (r &Repl) function_call(line string) (bool, FnType) {
for function in r.functions_name { for function in r.functions_name {
is_function_definition := line.replace(' ', '').starts_with('$function:=') is_function_definition := line.replace(' ', '').starts_with('$function:=')
if line.starts_with(function) && !is_function_definition { if line.starts_with(function) && !is_function_definition {
return true // TODO(vincenzopalazzo) store the type of the function here
fntype := r.check_fn_type_kind(line)
return true, fntype
} }
} }
return false
if line.contains(':=') {
// an assignment to a variable:
// `z := abc()`
return false, FnType.@none
}
// Check if it is a Vlib call
// TODO(vincenzopalazzo): auto import the module?
if r.is_function_call(line) {
fntype := r.check_fn_type_kind(line)
return true, fntype
}
return false, FnType.@none
}
// TODO(vincenzopalazzo) Remove this fancy check and add a regex
fn (r &Repl) is_function_call(line string) bool {
return !line.starts_with('[') && line.contains('.') && line.contains('(')
&& (line.ends_with(')') || line.ends_with('?'))
} }
fn (r &Repl) current_source_code(should_add_temp_lines bool, not_add_print bool) string { fn (r &Repl) current_source_code(should_add_temp_lines bool, not_add_print bool) string {
mut all_lines := []string{} mut all_lines := []string{}
for mod in r.modules { for mod in r.modules {
all_lines << 'import $mod\n' all_lines << endline_if_missed('import $mod')
} }
if vstartup != '' { if vstartup != '' {
mut lines := []string{} mut lines := []string{}
@ -101,15 +150,24 @@ fn (r &Repl) current_source_code(should_add_temp_lines bool, not_add_print bool)
return all_lines.join('\n') return all_lines.join('\n')
} }
fn repl_help() { // the new_line is probably a function call, but some function calls
println(version.full_v_version(false)) // do not return anything, while others return results.
println(' // This function checks which one we have:
|help Displays this information. fn (r &Repl) check_fn_type_kind(new_line string) FnType {
|list Show the program so far. source_code := r.current_source_code(true, false) + '\nprintln($new_line)'
|reset Clears the accumulated program, so you can start a fresh. check_file := os.join_path(os.temp_dir(), '${rand.ulid()}.vrepl.check.v')
|Ctrl-C, Ctrl-D, exit Exits the REPL. os.write_file(check_file, source_code) or { panic(err) }
|clear Clears the screen. defer {
'.strip_margin()) os.rm(check_file) or {}
}
// -w suppresses the unused import warnings
// -check just does syntax and checker analysis without generating/running code
os_response := os.execute('${os.quoted_path(vexe)} -w -check ${os.quoted_path(check_file)}')
str_response := convert_output(os_response)
if os_response.exit_code != 0 && str_response.contains('can not print void expressions') {
return FnType.void
}
return FnType.fn_type
} }
fn run_repl(workdir string, vrepl_prefix string) { fn run_repl(workdir string, vrepl_prefix string) {
@ -215,7 +273,7 @@ fn run_repl(workdir string, vrepl_prefix string) {
} else { } else {
mut temp_line := r.line mut temp_line := r.line
mut temp_flag := false mut temp_flag := false
func_call := r.function_call(r.line) func_call, fntype := r.function_call(r.line)
filter_line := r.line.replace(r.line.find_between("'", "'"), '').replace(r.line.find_between('"', filter_line := r.line.replace(r.line.find_between("'", "'"), '').replace(r.line.find_between('"',
'"'), '') '"'), '')
possible_statement_patterns := [ possible_statement_patterns := [
@ -257,7 +315,7 @@ fn run_repl(workdir string, vrepl_prefix string) {
if oline.starts_with(' ') { if oline.starts_with(' ') {
is_statement = true is_statement = true
} }
if !is_statement && !func_call && r.line != '' { if !is_statement && (!func_call || fntype == FnType.fn_type) && r.line != '' {
temp_line = 'println($r.line)' temp_line = 'println($r.line)'
temp_flag = true temp_flag = true
} }
@ -307,26 +365,33 @@ fn run_repl(workdir string, vrepl_prefix string) {
} }
} }
fn print_output(s os.Result) { fn convert_output(os_result os.Result) string {
lines := s.output.trim_right('\n\r').split_into_lines() lines := os_result.output.trim_right('\n\r').split_into_lines()
mut content := ''
for line in lines { for line in lines {
if line.contains('.vrepl_temp.v:') { if line.contains('.vrepl_temp.v:') {
// Hide the temporary file name // Hide the temporary file name
sline := line.all_after('.vrepl_temp.v:') sline := line.all_after('.vrepl_temp.v:')
idx := sline.index(' ') or { idx := sline.index(' ') or {
println(sline) content += endline_if_missed(sline)
return return content
} }
println(sline[idx + 1..]) content += endline_if_missed(sline[idx + 1..])
} else if line.contains('.vrepl.v:') { } else if line.contains('.vrepl.v:') {
// Ensure that .vrepl.v: is at the start, ignore the path // Ensure that .vrepl.v: is at the start, ignore the path
// This is needed to have stable .repl tests. // This is needed to have stable .repl tests.
idx := line.index('.vrepl.v:') or { return } idx := line.index('.vrepl.v:') or { panic(err) }
println(line[idx..]) content += endline_if_missed(line[idx..])
} else { } else {
println(line) content += endline_if_missed(line)
} }
} }
return content
}
fn print_output(os_result os.Result) {
content := convert_output(os_result)
print(content)
} }
fn main() { fn main() {

View File

@ -0,0 +1,10 @@
math.sinf(50.0)
println(1+math.sinf(50.0))
fn test() { println('foo') } fn test2(a int) { println(a) }
test()
test2(123)
===output===
-0.2623749
0.7376251
foo
123

View File

@ -0,0 +1,3 @@
os.write_file('hi','hi')?
os.rm('hi')?
===output===