From f3aec200001f1a012919d89d02ee6bcbe21fe914 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 15 Sep 2023 00:44:15 +0300 Subject: [PATCH] all: support `;` statements, allowing for oneliners like `./v -e 'import os; println( os.ls(os.args[1])!.sorted(a > b) )' vlib/math` (#19345) --- cmd/tools/vast/vast.v | 7 +++++++ vlib/v/ast/ast.v | 6 ++++++ vlib/v/checker/checker.v | 1 + vlib/v/fmt/fmt.v | 13 +++++++++---- vlib/v/fmt/tests/semicolons_expected.vv | 4 ++++ vlib/v/fmt/tests/semicolons_input.vv | 4 ++++ vlib/v/gen/c/cgen.v | 3 +++ vlib/v/gen/golang/golang.v | 3 +++ vlib/v/gen/js/js.v | 6 ++++++ vlib/v/markused/walker.v | 1 + vlib/v/parser/parser.v | 13 ++++++++++++- vlib/v/transformer/transformer.v | 1 + 12 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 vlib/v/fmt/tests/semicolons_expected.vv create mode 100644 vlib/v/fmt/tests/semicolons_input.vv diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 5100b4041703b5..c7e5174b84ca33 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -435,6 +435,7 @@ fn (t Tree) stmt(node ast.Stmt) &Node { ast.AssertStmt { return t.assert_stmt(node) } ast.ExprStmt { return t.expr_stmt(node) } ast.Block { return t.block(node) } + ast.SemicolonStmt { return t.semicolon_stmt(node) } ast.SqlStmt { return t.sql_stmt(node) } ast.AsmStmt { return t.asm_stmt(node) } ast.NodeError { return t.node_error(node) } @@ -1747,6 +1748,12 @@ fn (t Tree) sql_expr(node ast.SqlExpr) &Node { return obj } +fn (t Tree) semicolon_stmt(node ast.SemicolonStmt) &Node { + mut obj := new_object() + obj.add('pos', t.pos(node.pos)) + return obj +} + fn (t Tree) sql_stmt(node ast.SqlStmt) &Node { mut obj := new_object() obj.add_terse('ast_type', t.string_node('SqlStmt')) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index c64a84fc7c1963..2fddcef480d21e 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -87,6 +87,7 @@ pub type Stmt = AsmStmt | Module | NodeError | Return + | SemicolonStmt | SqlStmt | StructDecl | TypeDecl @@ -300,6 +301,11 @@ pub: is_skipped bool // module main can be skipped in single file programs } +pub struct SemicolonStmt { +pub: + pos token.Pos +} + [minify] pub struct StructField { pub: diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 153514fcade957..7ab7606e081626 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2099,6 +2099,7 @@ fn (mut c Checker) stmt(mut node ast.Stmt) { c.return_stmt(mut node) c.scope_returns = true } + ast.SemicolonStmt {} ast.SqlStmt { c.sql_stmt(mut node) } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index f85181ffab824d..4b15f48fa3270d 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -85,12 +85,12 @@ pub fn fmt(file ast.File, table &ast.Table, pref_ &pref.Preferences, is_debug bo imp_str := f.out_imports.str().trim_space() if imp_str.len > 0 { return res + '\n' + imp_str + '\n' - } else { - return res } - } else { - return res[..f.import_pos] + f.out_imports.str() + res[f.import_pos..] + return res } + source_for_imports := res[..f.import_pos] + f.out_imports.str() + source_after_imports := res[f.import_pos..] + return source_for_imports + source_after_imports } pub fn (mut f Fmt) process_file_imports(file &ast.File) { @@ -405,6 +405,9 @@ fn (f Fmt) should_insert_newline_before_node(node ast.Node, prev_node ast.Node) return true } } + ast.SemicolonStmt { + return false + } // Force a newline after struct declarations ast.StructDecl { return true @@ -552,6 +555,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { ast.Return { f.return_stmt(node) } + ast.SemicolonStmt {} ast.SqlStmt { f.sql_stmt(node) } @@ -568,6 +572,7 @@ fn stmt_is_single_line(stmt ast.Stmt) bool { return match stmt { ast.ExprStmt, ast.AssertStmt { expr_is_single_line(stmt.expr) } ast.Return, ast.AssignStmt, ast.BranchStmt { true } + ast.SemicolonStmt { true } else { false } } } diff --git a/vlib/v/fmt/tests/semicolons_expected.vv b/vlib/v/fmt/tests/semicolons_expected.vv new file mode 100644 index 00000000000000..ad5bc934eabe9d --- /dev/null +++ b/vlib/v/fmt/tests/semicolons_expected.vv @@ -0,0 +1,4 @@ +import os + +println(os.args) +println('hello') diff --git a/vlib/v/fmt/tests/semicolons_input.vv b/vlib/v/fmt/tests/semicolons_input.vv new file mode 100644 index 00000000000000..dfd77e9c81dcd9 --- /dev/null +++ b/vlib/v/fmt/tests/semicolons_input.vv @@ -0,0 +1,4 @@ +import os; + +println(os.args); +println('hello'); diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index db3eac6985585f..ff9c37e9dc9436 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2179,6 +2179,9 @@ fn (mut g Gen) stmt(node ast.Stmt) { ast.Return { g.return_stmt(node) } + ast.SemicolonStmt { + g.writeln(';') + } ast.SqlStmt { g.sql_stmt(node) } diff --git a/vlib/v/gen/golang/golang.v b/vlib/v/gen/golang/golang.v index 46c47dd299fe63..dfac3aaf961804 100644 --- a/vlib/v/gen/golang/golang.v +++ b/vlib/v/gen/golang/golang.v @@ -472,6 +472,9 @@ pub fn (mut f Gen) stmt(node ast.Stmt) { ast.Return { f.return_stmt(node) } + ast.SemicolonStmt { + f.writeln(';') + } ast.SqlStmt { f.sql_stmt(node) } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index fdd55757a74fc0..3654f30f99ddb8 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -739,6 +739,9 @@ fn (mut g JsGen) stmt_no_semi(node_ ast.Stmt) { } g.gen_return_stmt(node) } + ast.SemicolonStmt { + g.writeln(';') + } ast.SqlStmt {} ast.StructDecl { g.write_v_source_line_info(node.pos) @@ -842,6 +845,9 @@ fn (mut g JsGen) stmt(node_ ast.Stmt) { } g.gen_return_stmt(node) } + ast.SemicolonStmt { + g.writeln(';') + } ast.SqlStmt {} ast.StructDecl { g.write_v_source_line_info(node.pos) diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 5bbf513c0f614f..363c8478380154 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -192,6 +192,7 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) { ast.HashStmt {} ast.Import {} ast.InterfaceDecl {} + ast.SemicolonStmt {} ast.Module {} ast.TypeDecl {} ast.NodeError {} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 31e374aff3100c..5f7ce9e7f0334f 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1122,6 +1122,9 @@ fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { .key_asm { return p.asm_stmt(false) } + .semicolon { + return p.semicolon_stmt() + } // literals, 'if', etc. in here else { return p.parse_multi_expr(is_top_level) @@ -1129,6 +1132,14 @@ fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { } } +fn (mut p Parser) semicolon_stmt() ast.SemicolonStmt { + pos := p.tok.pos() + p.check(.semicolon) + return ast.SemicolonStmt{ + pos: pos + } +} + fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { p.inside_asm = true p.inside_asm_template = true @@ -3647,7 +3658,7 @@ fn (mut p Parser) import_stmt() ast.Import { } pos_t := p.tok.pos() if import_pos.line_nr == pos_t.line_nr { - if p.tok.kind !in [.lcbr, .eof, .comment] { + if p.tok.kind !in [.lcbr, .eof, .comment, .semicolon] { p.error_with_pos('cannot import multiple modules at a time', pos_t) return import_node } diff --git a/vlib/v/transformer/transformer.v b/vlib/v/transformer/transformer.v index 24574c70a559d2..c1e4c7957151ba 100644 --- a/vlib/v/transformer/transformer.v +++ b/vlib/v/transformer/transformer.v @@ -281,6 +281,7 @@ pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt { expr = t.expr(mut expr) } } + ast.SemicolonStmt {} ast.SqlStmt {} ast.StructDecl { for mut field in node.fields {