all: make comment expressions + fix vfmt array init (#5851)

pull/5868/head
Enzo 2020-07-17 19:13:22 +02:00 committed by GitHub
parent 39f90e25f3
commit 90d9040e6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 397 additions and 291 deletions

View File

@ -9,11 +9,10 @@ const (
'vlib/net/http/http_httpbin_test.v',
]
skip_on_musl = []string{}
skip_on_ubuntu_musl =
[
skip_on_ubuntu_musl = [
'vlib/net/http/cookie_test.v',
'vlib/net/http/http_test.v',
'vlib/net/websocket/ws_test.v'
'vlib/net/websocket/ws_test.v',
'vlib/sqlite/sqlite_test.v',
'vlib/orm/orm_test.v',
'vlib/clipboard/clipboard_test.v',
@ -21,7 +20,7 @@ const (
]
skip_on_linux = []string{}
skip_on_non_linux = [
'vlib/net/websocket/ws_test.v'
'vlib/net/websocket/ws_test.v',
]
skip_on_windows = [
'vlib/orm/orm_test.v',
@ -75,6 +74,6 @@ fn main() {
tsession.test()
eprintln(tsession.benchmark.total_message(title))
if tsession.benchmark.nfail > 0 {
panic('\nWARNING: failed ${tsession.benchmark.nfail} times.\n')
panic('\nWARNING: failed $tsession.benchmark.nfail times.\n')
}
}

View File

@ -10,15 +10,15 @@ import v.errors
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr |
CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr |
IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None |
OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr |
StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr |
None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf |
SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompFor |
CompIf | ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt |
ForStmt | GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl |
Module | Return | SqlStmt | StructDecl | TypeDecl | UnsafeStmt
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | CompFor | CompIf |
ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt |
GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module |
Return | SqlStmt | StructDecl | TypeDecl | UnsafeStmt
pub type ScopeObject = ConstField | GlobalDecl | Var
@ -44,6 +44,7 @@ pub struct ExprStmt {
pub:
expr Expr
pos token.Position
comments []Comment
is_expr bool
pub mut:
typ table.Type
@ -284,6 +285,7 @@ pub struct Return {
pub:
pos token.Position
exprs []Expr
comments []Comment
pub mut:
types []table.Type
}
@ -579,6 +581,7 @@ pub:
right []Expr
op token.Kind
pos token.Position
comments []Comment
pub mut:
left []Expr
left_types []table.Type
@ -921,22 +924,25 @@ pub fn (expr Expr) position() token.Position {
AsCast {
return expr.pos
}
// ast.Ident { }
CastExpr {
return expr.pos
}
Assoc {
return expr.pos
}
BoolLiteral {
return expr.pos
}
// ast.Ident { }
CallExpr {
return expr.pos
}
CastExpr {
return expr.pos
}
CharLiteral {
return expr.pos
}
Comment {
return expr.pos
}
EnumVal {
return expr.pos
}
@ -1023,7 +1029,6 @@ pub fn (stmt Stmt) position() token.Position {
// BranchStmt {
// }
*/
Comment { return stmt.pos }
CompIf { return stmt.pos }
ConstDecl { return stmt.pos }
/*

View File

@ -1865,10 +1865,10 @@ fn (mut c Checker) stmt(node ast.Stmt) {
node.pos)
}
}
// ast.Attr {}
ast.AssignStmt {
c.assign_stmt(mut node)
}
ast.Attr {}
ast.Block {
c.stmts(node.stmts)
}
@ -2044,7 +2044,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
}
}
}
// ast.HashStmt {}
ast.GotoLabel {}
ast.GotoStmt {}
ast.HashStmt {}
ast.Import {}
ast.InterfaceDecl {
c.interface_decl(node)
@ -2074,10 +2076,6 @@ fn (mut c Checker) stmt(node ast.Stmt) {
c.stmts(node.stmts)
c.inside_unsafe = false
}
else {
// println('checker.stmt(): unhandled node')
// println('checker.stmt(): unhandled node (${typeof(node)})')
}
}
}
@ -2210,6 +2208,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
ast.CharLiteral {
return table.byte_type
}
ast.Comment {
return table.void_type
}
ast.ComptimeCall {
node.sym = c.table.get_type_symbol(c.unwrap_generic(c.expr(node.left)))
if node.is_vweb {

View File

@ -41,28 +41,22 @@ pub mut:
attrs map[string]string
}
pub fn merge_comments(stmts []ast.Stmt) string {
pub fn merge_comments(comments []ast.Comment) string {
mut res := []string{}
for s in stmts {
if s is ast.Comment {
res << s.text.trim_left('|')
}
for comment in comments {
res << comment.text.trim_left('|')
}
return res.join('\n')
}
pub fn get_comment_block_right_before(stmts []ast.Stmt) string {
if stmts.len == 0 {
pub fn get_comment_block_right_before(comments []ast.Comment) string {
if comments.len == 0 {
return ''
}
mut comment := ''
mut last_comment_line_nr := 0
for i := stmts.len-1; i >= 0; i-- {
stmt := stmts[i]
if stmt !is ast.Comment {
panic('Not a comment')
}
cmt := stmt as ast.Comment
for i := comments.len - 1; i >= 0; i-- {
cmt := comments[i]
if last_comment_line_nr != 0 && cmt.pos.line_nr < last_comment_line_nr - 1 {
// skip comments that are not part of a continuous block,
// located right above the top level statement.
@ -198,7 +192,9 @@ fn compare_nodes_by_category(a, b &DocNode) int {
pub fn (nodes []DocNode) index_by_name(node_name string) int {
for i, node in nodes {
if node.name != node_name { continue }
if node.name != node_name {
continue
}
return i
}
return -1
@ -208,7 +204,7 @@ pub fn (nodes []DocNode) find_children_of(parent string) []DocNode {
return nodes.find_nodes_with_attr('parent', parent)
}
pub fn (nodes []DocNode) find_nodes_with_attr(attr_name string, value string) []DocNode {
pub fn (nodes []DocNode) find_nodes_with_attr(attr_name, value string) []DocNode {
mut subgroup := []DocNode{}
if attr_name.len == 0 {
return subgroup
@ -226,9 +222,13 @@ pub fn (nodes []DocNode) find_nodes_with_attr(attr_name string, value string) []
fn get_parent_mod(dir string) ?string {
$if windows {
// windows root path is C: or D:
if dir.len <= 2 { return error('root folder reached') }
if dir.len <= 2 {
return error('root folder reached')
}
} $else {
if dir.len == 0 { return error('root folder reached') }
if dir.len == 0 {
return error('root folder reached')
}
}
base_dir := os.base_dir(dir)
if os.file_name(base_dir) in ['encoding', 'v'] && 'vlib' in base_dir {
@ -249,7 +249,9 @@ fn get_parent_mod(dir string) ?string {
return error('No V files found.')
}
tbl := table.new_table()
scope := &ast.Scope{ parent: 0 }
scope := &ast.Scope{
parent: 0
}
file_ast := parser.parse_file(v_files[0], tbl, .skip_comments, prefs, scope)
if file_ast.mod.name == 'main' {
return ''
@ -315,14 +317,16 @@ fn (mut d Doc) generate() ?Doc {
last_import_stmt_idx = sidx
}
}
mut prev_comments := []ast.Stmt{}
mut prev_comments := []ast.Comment{}
mut imports_section := true
for sidx, stmt in stmts {
// eprintln('stmt typeof: ' + typeof(stmt))
if stmt is ast.Comment {
prev_comments << stmt
if stmt is ast.ExprStmt {
if stmt.expr is ast.Comment as cmt {
prev_comments << cmt
continue
}
}
// TODO: Fetch head comment once
if stmt is ast.Module {
// the previous comments were probably a copyright/license one
@ -385,7 +389,9 @@ fn (mut d Doc) generate() ?Doc {
name: node.attrs['parent']
content: ''
comment: ''
attrs: {'category': 'Structs'}
attrs: {
'category': 'Structs'
}
}
}
}
@ -420,7 +426,6 @@ fn (mut d Doc) generate() ?Doc {
}
prev_comments = []
}
d.fmt.mod2alias = map[string]string{}
}
d.time_generated = time.now()

View File

@ -17,11 +17,6 @@ const (
max_len = [0, 35, 85, 93, 100]
)
enum CommentsLevel {
keep
indent
}
pub struct Fmt {
pub mut:
table &table.Table
@ -251,6 +246,9 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
}
match node {
ast.AssignStmt {
f.comments(node.comments, {
inline: false
})
for i, left in node.left {
if left is ast.Ident {
var_info := left.var_info()
@ -302,9 +300,6 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
else {}
}
}
ast.Comment {
f.comment(it)
}
ast.CompFor {}
ast.CompIf {
inversion := if it.is_not { '!' } else { '' }
@ -331,19 +326,27 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
}
name := it.name.after('.')
f.writeln('enum $name {')
f.comments(it.comments, false, .indent)
f.comments(it.comments, {
level: .indent
})
for field in it.fields {
f.write('\t$field.name')
if field.has_expr {
f.write(' = ')
f.expr(field.expr)
}
f.comments(field.comments, true, .indent)
f.comments(field.comments, {
has_nl: false
level: .indent
})
f.writeln('')
}
f.writeln('}\n')
}
ast.ExprStmt {
f.comments(it.comments, {
inline: false
})
f.expr(it.expr)
if !f.single_line_if {
f.writeln('')
@ -434,6 +437,9 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
f.mod(it)
}
ast.Return {
f.comments(it.comments, {
inline: false
})
f.write('return')
if it.exprs.len > 1 {
// multiple returns
@ -618,7 +624,8 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl) {
for j < comments.len && comments[j].pos.pos < field.pos.pos {
f.indent++
f.empty_line = true
f.comment(comments[j])
f.comment(comments[j], {})
f.writeln('')
f.indent--
j++
}
@ -651,7 +658,8 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl) {
for comment in node.end_comments {
f.indent++
f.empty_line = true
f.comment(comment)
f.comment(comment, {})
f.writeln('')
f.indent--
}
f.writeln('}\n')
@ -765,6 +773,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
ast.CharLiteral {
f.write('`$node.val`')
}
ast.Comment {
f.comment(node, {})
}
ast.ComptimeCall {
if node.is_vweb {
f.write('$' + 'vweb.html()')
@ -1120,10 +1131,21 @@ pub fn (mut f Fmt) or_expr(or_block ast.OrExpr) {
}
}
pub fn (mut f Fmt) comment(node ast.Comment) {
enum CommentsLevel {
keep
indent
}
struct CommentsOptions {
has_nl bool = true
inline bool = true
level CommentsLevel = .keep
}
pub fn (mut f Fmt) comment(node ast.Comment, options CommentsOptions) {
if !node.text.contains('\n') {
is_separate_line := node.text.starts_with('|')
mut s := if is_separate_line { node.text[1..] } else { node.text }
is_separate_line := !options.inline || node.text.starts_with('|')
mut s := if node.text.starts_with('|') { node.text[1..] } else { node.text }
if s == '' {
s = '//'
} else {
@ -1133,7 +1155,7 @@ pub fn (mut f Fmt) comment(node ast.Comment) {
f.remove_new_line() // delete the generated \n
f.write(' ')
}
f.writeln(s)
f.write(s)
return
}
lines := node.text.split_into_lines()
@ -1143,25 +1165,25 @@ pub fn (mut f Fmt) comment(node ast.Comment) {
f.empty_line = false
}
f.empty_line = true
f.writeln('*/')
f.write('*/')
}
pub fn (mut f Fmt) comments(some_comments []ast.Comment, remove_last_new_line bool, level CommentsLevel) {
for c in some_comments {
pub fn (mut f Fmt) comments(comments []ast.Comment, options CommentsOptions) {
for i, c in comments {
if !f.out.last_n(1)[0].is_space() {
f.write('\t')
}
if level == .indent {
if options.level == .indent {
f.indent++
}
f.comment(c)
if level == .indent {
f.comment(c, options)
if i < comments.len - 1 || options.has_nl {
f.writeln('')
}
if options.level == .indent {
f.indent--
}
}
if remove_last_new_line {
f.remove_new_line()
}
}
pub fn (mut f Fmt) fn_decl(node ast.FnDecl) {
@ -1232,7 +1254,7 @@ pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
f.single_line_if = single_line
for i, branch in it.branches {
if branch.comments.len > 0 {
f.comments(branch.comments, true, .keep)
f.comments(branch.comments, {})
}
if i == 0 {
f.write('if ')
@ -1373,7 +1395,8 @@ pub fn (mut f Fmt) match_expr(it ast.MatchExpr) {
}
for branch in it.branches {
if branch.comment.text != '' {
f.comment(branch.comment)
f.comment(branch.comment, {})
f.writeln('')
}
if !branch.is_else {
// normal branch
@ -1404,7 +1427,7 @@ pub fn (mut f Fmt) match_expr(it ast.MatchExpr) {
}
}
if branch.post_comments.len > 0 {
f.comments(branch.post_comments, false, .keep)
f.comments(branch.post_comments, {})
}
}
f.indent--
@ -1456,6 +1479,7 @@ fn (mut f Fmt) write_language_prefix(lang table.Language) {
fn expr_is_single_line(expr ast.Expr) bool {
match expr {
ast.IfExpr { return false }
ast.Comment { return false }
else {}
}
return true
@ -1553,9 +1577,12 @@ pub fn (mut f Fmt) array_init(it ast.ArrayInit) {
f.expr(expr)
if i == it.exprs.len - 1 {
if is_new_line {
if expr !is ast.Comment {
f.write(',')
}
f.writeln('')
}
} else {
} else if expr !is ast.Comment {
f.write(',')
}
last_line_nr = line_nr
@ -1634,7 +1661,8 @@ pub fn (mut f Fmt) const_decl(it ast.ConstDecl) {
comments := field.comments
mut j := 0
for j < comments.len && comments[j].pos.pos < field.pos.pos {
f.comment(comments[j])
f.comment(comments[j], {})
f.writeln('')
j++
}
name := field.name.after('.')

View File

@ -0,0 +1,9 @@
fn main() {
arr := [
// test 0
1,
// test 1
2,
// test 2
]
}

View File

@ -7,7 +7,7 @@ fn main() {
expected_flags := [
make_flag('solaris', '-L', '/opt/local/lib'),
make_flag('darwin', '-framework', 'Cocoa'),
make_flag('windows', '-l', 'gdi32')
make_flag('windows', '-l', 'gdi32'),
]
x := []int{len: 10, cap: 100, init: 1}
_ := expected_flags

View File

@ -0,0 +1,19 @@
fn fun() int {
// comment zero
return 0
}
fn mr_fun() (int, int) {
// one comment
// another comment
return 1, 2
}
fn main() {
// this is a comment
a := 1
// and another comment
// just to make it worse
b, c := a, 2
d := c // and an extra one
}

View File

@ -0,0 +1,13 @@
fn fun() int {
return /* comment zero */ 0
}
fn mr_fun() (int, int) {
return /* one comment */ 1, /* another comment */ 2
}
fn main() {
a := /* this is a comment */ 1
b, c := /* and another comment */ a, /* just to make it worse */ 2
d := c // and an extra one
}

View File

@ -17,13 +17,13 @@ const (
'first line',
'second line',
'third line',
'fourth line'
'fourth line',
]
)
const (
i_am_a_very_long_constant_name_so_i_stand_alone_and_my_length_is_over_90_characters = [
'testforit'
'testforit',
]
)
@ -34,27 +34,27 @@ pub const (
fn main() {
a := [
[3, 5, 6],
[7, 9, 2]
[7, 9, 2],
]
b := [[
[2, 5, 8],
[5, 1, 3],
[2, 6, 0]
[2, 6, 0],
], [
[9, 4, 5],
[7, 2, 3],
[1, 2, 3]
[1, 2, 3],
]]
c := [
[
[2, 5, 8],
[5, 1, 3],
[2, 6, 0]
[2, 6, 0],
],
[
[9, 4, 5],
[7, 2, 3],
[1, 2, 3]
]
[1, 2, 3],
],
]
}

View File

@ -676,7 +676,6 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.const_decl(node)
// }
}
ast.Comment {}
ast.CompFor {
g.comp_for(node)
}
@ -1679,6 +1678,7 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.ComptimeCall {
g.comptime_call(node)
}
ast.Comment {}
ast.ConcatExpr {
g.concat_expr(node)
}

View File

@ -62,9 +62,7 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
g.enable_doc = false
}
g.init()
mut graph := depgraph.new_dep_graph()
// Get class methods
for file in files {
g.file = file
@ -73,27 +71,22 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
g.find_class_methods(file.stmts)
g.escape_namespace()
}
for file in files {
g.file = file
g.enter_namespace(g.file.mod.name)
g.is_test = g.file.path.ends_with('_test.v')
// store imports
mut imports := []string{}
for imp in g.file.imports {
imports << imp.mod
}
graph.add(g.file.mod.name, imports)
g.stmts(file.stmts)
// store the current namespace
g.escape_namespace()
}
// resolve imports
deps_resolved := graph.resolve()
mut out := g.hashes() + g.definitions.str()
for node in deps_resolved.nodes {
name := g.js_name(node.name).replace('.', '_')
@ -103,7 +96,9 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
out += 'const $name = (function ('
imports := g.namespace_imports[node.name]
for i, key in imports.keys() {
if i > 0 { out += ', ' }
if i > 0 {
out += ', '
}
out += imports[key]
}
out += ') {\n\t'
@ -117,13 +112,19 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
out += '\n\treturn {'
for i, pub_var in g.namespaces_pub[node.name] {
out += '\n\t\t$pub_var'
if i < g.namespaces_pub[node.name].len - 1 { out += ',' }
if i < g.namespaces_pub[node.name].len - 1 {
out += ','
}
}
if g.namespaces_pub[node.name].len > 0 {
out += '\n\t'
}
if g.namespaces_pub[node.name].len > 0 { out += '\n\t' }
out += '};'
out += '\n})('
for i, key in imports.keys() {
if i > 0 { out += ', ' }
if i > 0 {
out += ', '
}
out += key.replace('.', '_')
}
out += ');\n\n'
@ -157,9 +158,9 @@ pub fn (mut g JsGen) find_class_methods(stmts []ast.Stmt) {
for stmt in stmts {
match stmt {
ast.FnDecl {
if it.is_method {
if stmt.is_method {
// Found struct method, store it to be generated along with the class.
class_name := g.table.get_type_name(it.receiver.typ)
class_name := g.table.get_type_name(stmt.receiver.typ)
// Workaround until `map[key] << val` works.
mut arr := g.method_fn_decls[class_name]
arr << stmt
@ -178,7 +179,7 @@ pub fn (mut g JsGen) init() {
}
pub fn (g JsGen) hashes() string {
mut res := '// V_COMMIT_HASH ${util.vhash()}\n'
mut res := '// V_COMMIT_HASH $util.vhash()\n'
res += '// V_CURRENT_COMMIT_HASH ${util.githash(g.pref.building_v)}\n'
return res
}
@ -187,7 +188,6 @@ pub fn (g JsGen) hashes() string {
pub fn (mut g JsGen) typ(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
match sym.kind {
.placeholder {
// This should never happen: means checker bug
@ -282,10 +282,11 @@ fn (mut g JsGen) fn_typ(args []table.Arg, return_type table.Type) string {
mut res := '('
for i, arg in args {
res += '$arg.name: ${g.typ(arg.typ)}'
if i < args.len - 1 { res += ', ' }
if i < args.len - 1 {
res += ', '
}
}
return res + ') => ' + g.typ(return_type)
}
fn (mut g JsGen) struct_typ(s string) string {
@ -293,17 +294,21 @@ fn (mut g JsGen) struct_typ(s string) string {
mut name := if ns == g.namespace { s.split('.').last() } else { g.get_alias(s) }
mut styp := ''
for i, v in name.split('.') {
if i == 0 { styp = v }
else { styp += '["$v"]' }
if i == 0 {
styp = v
} else {
styp += '["$v"]'
}
}
if ns in ['', g.namespace] {
return styp
}
if ns in ['', g.namespace] { return styp }
return styp + '["prototype"]'
}
fn (mut g JsGen) to_js_typ_val(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
match sym.kind {
.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t {
styp = '0'
@ -366,18 +371,22 @@ pub fn (mut g JsGen) new_tmp_var() string {
// 'fn' => ''
[inline]
fn get_ns(s string) string {
idx := s.last_index('.') or { return '' }
idx := s.last_index('.') or {
return ''
}
return s.substr(0, idx)
}
fn (mut g JsGen) get_alias(name string) string {
ns := get_ns(name)
if ns == '' { return name }
if ns == '' {
return name
}
imports := g.namespace_imports[g.namespace]
alias := imports[ns]
if alias == '' { return name }
if alias == '' {
return name
}
return alias + '.' + name.split('.').last()
}
@ -386,7 +395,9 @@ fn (mut g JsGen) js_name(name_ string) string {
mut name := if ns == g.namespace { name_.split('.').last() } else { g.get_alias(name_) }
mut parts := name.split('.')
for i, p in parts {
if p in js_reserved { parts[i] = 'v_$p' }
if p in js_reserved {
parts[i] = 'v_$p'
}
}
return parts.join('.')
}
@ -401,82 +412,77 @@ fn (mut g JsGen) stmts(stmts []ast.Stmt) {
fn (mut g JsGen) stmt(node ast.Stmt) {
g.stmt_start_pos = g.out.len
match node {
ast.AssertStmt {
g.gen_assert_stmt(it)
g.gen_assert_stmt(node)
}
ast.AssignStmt {
g.gen_assign_stmt(it)
g.gen_assign_stmt(node)
}
ast.Attr {
g.gen_attr(it)
g.gen_attr(node)
}
ast.Block {
g.gen_block(it)
g.gen_block(node)
g.writeln('')
}
ast.BranchStmt {
g.gen_branch_stmt(it)
}
ast.Comment {
// Skip: don't generate comments
}
ast.CompFor {
g.gen_branch_stmt(node)
}
ast.CompFor {}
ast.CompIf {
// skip: JS has no compile time if
}
ast.ConstDecl {
g.gen_const_decl(it)
g.gen_const_decl(node)
}
ast.DeferStmt {
g.defer_stmts << *it
g.defer_stmts << *node
}
ast.EnumDecl {
g.gen_enum_decl(it)
g.gen_enum_decl(node)
g.writeln('')
}
ast.ExprStmt {
g.gen_expr_stmt(it)
g.gen_expr_stmt(node)
}
ast.FnDecl {
g.fn_decl = it
g.gen_fn_decl(it)
g.fn_decl = node
g.gen_fn_decl(node)
}
ast.ForCStmt {
g.gen_for_c_stmt(it)
g.gen_for_c_stmt(node)
g.writeln('')
}
ast.ForInStmt {
g.gen_for_in_stmt(it)
g.gen_for_in_stmt(node)
g.writeln('')
}
ast.ForStmt {
g.gen_for_stmt(it)
g.gen_for_stmt(node)
g.writeln('')
}
ast.GlobalDecl {
// TODO
}
ast.GoStmt {
g.gen_go_stmt(it)
g.gen_go_stmt(node)
g.writeln('')
}
ast.GotoLabel {
g.writeln('${g.js_name(it.name)}:')
g.writeln('${g.js_name(node.name)}:')
}
ast.GotoStmt {
// skip: JS has no goto
}
ast.HashStmt {
g.gen_hash_stmt(it)
g.gen_hash_stmt(node)
}
ast.Import {
g.gen_import_stmt(it)
g.gen_import_stmt(node)
}
ast.InterfaceDecl {
g.gen_interface_decl(it)
g.gen_interface_decl(node)
}
ast.Module {
// skip: namespacing implemented externally
@ -485,18 +491,17 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
if g.defer_stmts.len > 0 {
g.gen_defer_stmts()
}
g.gen_return_stmt(it)
}
ast.SqlStmt{
g.gen_return_stmt(node)
}
ast.SqlStmt {}
ast.StructDecl {
g.gen_struct_decl(it)
g.gen_struct_decl(node)
}
ast.TypeDecl {
// skip JS has no typedecl
}
ast.UnsafeStmt {
g.stmts(it.stmts)
g.stmts(node.stmts)
}
}
}
@ -504,10 +509,10 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
fn (mut g JsGen) expr(node ast.Expr) {
match node {
ast.AnonFn {
g.gen_fn_decl(it.decl)
g.gen_fn_decl(node.decl)
}
ast.ArrayInit {
g.gen_array_init_expr(it)
g.gen_array_init_expr(node)
}
ast.AsCast {
// skip: JS has no types, so no need to cast
@ -517,57 +522,58 @@ fn (mut g JsGen) expr(node ast.Expr) {
// TODO
}
ast.BoolLiteral {
if it.val == true {
if node.val == true {
g.write('true')
} else {
g.write('false')
}
}
ast.CallExpr {
g.gen_call_expr(it)
g.gen_call_expr(node)
}
ast.CastExpr {
// JS has no types, so no need to cast
// Just write the expression inside
g.expr(it.expr)
g.expr(node.expr)
}
ast.CharLiteral {
g.write("'$it.val'")
g.write("'$node.val'")
}
ast.Comment {}
ast.ConcatExpr {
// TODO
}
ast.EnumVal {
sym := g.table.get_type_symbol(it.typ)
sym := g.table.get_type_symbol(node.typ)
styp := g.js_name(sym.name)
g.write('${styp}.${it.val}')
g.write('${styp}.$node.val')
}
ast.FloatLiteral {
g.write(it.val)
g.write(node.val)
}
ast.Ident {
g.gen_ident(it)
g.gen_ident(node)
}
ast.IfExpr {
g.gen_if_expr(it)
g.gen_if_expr(node)
}
ast.IfGuardExpr {
// TODO no optionals yet
}
ast.IndexExpr {
g.gen_index_expr(it)
g.gen_index_expr(node)
}
ast.InfixExpr {
g.gen_infix_expr(it)
g.gen_infix_expr(node)
}
ast.IntegerLiteral {
g.write(it.val)
g.write(node.val)
}
ast.LockExpr {
g.gen_lock_expr(it)
g.gen_lock_expr(node)
}
ast.MapInit {
g.gen_map_init_expr(it)
g.gen_map_init_expr(node)
}
ast.MatchExpr {
// TODO
@ -580,26 +586,26 @@ fn (mut g JsGen) expr(node ast.Expr) {
}
ast.ParExpr {
g.write('(')
g.expr(it.expr)
g.expr(node.expr)
g.write(')')
}
ast.PostfixExpr {
g.expr(it.expr)
g.write(it.op.str())
g.expr(node.expr)
g.write(node.op.str())
}
ast.PrefixExpr {
if it.op in [.amp, .mul] {
if node.op in [.amp, .mul] {
// C pointers/references: ignore them
} else {
g.write(it.op.str())
g.write(node.op.str())
}
g.expr(it.right)
g.expr(node.right)
}
ast.RangeExpr {
// Only used in IndexExpr, requires index type info
}
ast.SelectorExpr {
g.gen_selector_expr(it)
g.gen_selector_expr(node)
}
ast.SizeOf {
// TODO
@ -608,14 +614,14 @@ fn (mut g JsGen) expr(node ast.Expr) {
// TODO
}
ast.StringInterLiteral {
g.gen_string_inter_literal(it)
g.gen_string_inter_literal(node)
}
ast.StringLiteral {
g.write('"$it.val"')
g.write('"$node.val"')
}
ast.StructInit {
// `user := User{name: 'Bob'}`
g.gen_struct_init(it)
g.gen_struct_init(node)
}
ast.Type {
// skip: JS has no types
@ -623,24 +629,23 @@ fn (mut g JsGen) expr(node ast.Expr) {
}
ast.Likely {
g.write('(')
g.expr(it.expr)
g.expr(node.expr)
g.write(')')
}
ast.TypeOf {
g.gen_typeof_expr(it)
g.gen_typeof_expr(node)
// TODO: Should this print the V type or the JS type?
}
ast.ComptimeCall {
// TODO
}
ast.UnsafeExpr {
es := it.stmts[0] as ast.ExprStmt
es := node.stmts[0] as ast.ExprStmt
g.expr(es.expr)
}
}
}
// TODO
fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) {
g.writeln('// assert')
@ -651,17 +656,17 @@ fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) {
mut mod_path := g.file.path.replace('\\', '\\\\')
if g.is_test {
g.writeln(' g_test_oks++;')
g.writeln(' cb_assertion_ok("${mod_path}", ${a.pos.line_nr+1}, "assert ${s_assertion}", "${g.fn_decl.name}()" );')
g.writeln(' cb_assertion_ok("$mod_path", ${a.pos.line_nr+1}, "assert $s_assertion", "${g.fn_decl.name}()" );')
g.writeln('} else {')
g.writeln(' g_test_fails++;')
g.writeln(' cb_assertion_failed("${mod_path}", ${a.pos.line_nr+1}, "assert ${s_assertion}", "${g.fn_decl.name}()" );')
g.writeln(' cb_assertion_failed("$mod_path", ${a.pos.line_nr+1}, "assert $s_assertion", "${g.fn_decl.name}()" );')
g.writeln(' exit(1);')
g.writeln('}')
return
}
g.writeln('} else {')
g.inc_indent()
g.writeln('builtin.eprintln("${mod_path}:${a.pos.line_nr+1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");')
g.writeln('builtin.eprintln("$mod_path:${a.pos.line_nr+1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");')
g.writeln('builtin.exit(1);')
g.dec_indent()
g.writeln('}')
@ -686,7 +691,9 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
// `a := 1` | `a,b := 1,2`
for i, left in stmt.left {
mut op := stmt.op
if stmt.op == .decl_assign { op = .assign }
if stmt.op == .decl_assign {
op = .assign
}
val := stmt.right[i]
mut is_mut := false
if left is ast.Ident {
@ -700,13 +707,10 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
continue
}
}
mut styp := g.typ(stmt.left_types[i])
if !g.inside_loop && styp.len > 0 {
g.doc.gen_typ(styp)
}
if stmt.op == .decl_assign {
if g.inside_loop || is_mut {
g.write('let ')
@ -724,7 +728,6 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
g.write(' $op ')
g.expr(val)
}
if g.inside_loop {
g.write('; ')
} else {
@ -753,9 +756,9 @@ fn (mut g JsGen) gen_branch_stmt(it ast.BranchStmt) {
fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) {
for field in it.fields {
g.doc.gen_const(g.typ(field.typ))
if field.is_pub { g.push_pub_var(field.name) }
if field.is_pub {
g.push_pub_var(field.name)
}
g.write('const ${g.js_name(field.name)} = ')
g.expr(field.expr)
g.writeln(';')
@ -794,7 +797,9 @@ fn (mut g JsGen) gen_enum_decl(it ast.EnumDecl) {
fn (mut g JsGen) gen_expr_stmt(it ast.ExprStmt) {
g.expr(it.expr)
if !it.is_expr && it.expr !is ast.IfExpr && !g.inside_ternary { g.writeln(';') }
if !it.is_expr && it.expr !is ast.IfExpr && !g.inside_ternary {
g.writeln(';')
}
}
fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
@ -811,7 +816,9 @@ fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
fn fn_has_go(it ast.FnDecl) bool {
mut has_go := false
for stmt in it.stmts {
if stmt is ast.GoStmt { has_go = true }
if stmt is ast.GoStmt {
has_go = true
}
}
return has_go
}
@ -836,12 +843,9 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
if c in [`+`, `-`, `*`, `/`] {
name = util.replace_op(name)
}
// type_name := g.typ(it.return_type)
// generate jsdoc for the function
g.doc.gen_fn(it)
if has_go {
g.write('async ')
}
@ -849,25 +853,21 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
g.write('function ')
}
g.write('${name}(')
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
}
mut args := it.args
if it.is_method {
args = args[1..]
}
g.fn_args(args, it.is_variadic)
g.writeln(') {')
if it.is_method {
g.inc_indent()
g.writeln('const ${it.args[0].name} = this;')
g.dec_indent()
}
g.stmts(it.stmts)
g.write('}')
if is_main {
@ -876,7 +876,6 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
if !it.is_anon && !it.is_method {
g.writeln('\n')
}
g.fn_decl = voidptr(0)
}
@ -922,8 +921,9 @@ fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) {
if it.is_range {
// `for x in 1..10 {`
mut i := it.val_var
if i in ['', '_'] { i = g.new_tmp_var() }
if i in ['', '_'] {
i = g.new_tmp_var()
}
g.inside_loop = true
g.write('for (let $i = ')
g.expr(it.cond)
@ -1013,7 +1013,6 @@ fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) {
// JS is dynamically typed, so we don't need any codegen at all
// We just need the JSDoc so TypeScript type checking works
g.doc.gen_interface(it)
// This is a hack to make the interface's type accessible outside its namespace
// TODO: interfaces are always `pub`?
name := g.js_name(it.name)
@ -1026,7 +1025,6 @@ fn (mut g JsGen) gen_return_stmt(it ast.Return) {
g.write('return;')
return
}
g.write('return ')
if it.exprs.len == 1 {
g.expr(it.exprs[0])
@ -1050,7 +1048,9 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
} else {
g.write('${g.to_js_typ_val(field.typ)}')
}
if i < node.fields.len - 1 { g.write(', ') }
if i < node.fields.len - 1 {
g.write(', ')
}
}
g.writeln(' }) {')
g.inc_indent()
@ -1059,22 +1059,26 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
}
g.dec_indent()
g.writeln('};')
g.writeln('${g.js_name(node.name)}.prototype = {')
g.inc_indent()
fns := g.method_fn_decls[node.name]
for i, field in node.fields {
typ := g.typ(field.typ)
g.doc.gen_typ(typ)
g.write('$field.name: ${g.to_js_typ_val(field.typ)}')
if i < node.fields.len - 1 || fns.len > 0 { g.writeln(',') } else { g.writeln('') }
if i < node.fields.len - 1 || fns.len > 0 {
g.writeln(',')
} else {
g.writeln('')
}
}
for i, cfn in fns {
g.gen_method_decl(cfn)
if i < fns.len - 1 { g.writeln(',') } else { g.writeln('') }
if i < fns.len - 1 {
g.writeln(',')
} else {
g.writeln('')
}
}
g.dec_indent()
g.writeln('};\n')
@ -1141,7 +1145,6 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
if it.is_method { // foo.bar.baz()
sym := g.table.get_type_symbol(it.receiver_type)
g.write('.')
if sym.kind == .array && it.name in ['map', 'filter'] {
// Prevent 'it' from getting shadowed inside the match
node := it
@ -1166,7 +1169,8 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
return
}
}
} else {}
}
else {}
}
g.write('it => ')
g.expr(node.args[0].expr)
@ -1204,7 +1208,6 @@ fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) {
fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
type_sym := g.table.get_type_symbol(node.typ)
// one line ?:
if node.is_expr && node.branches.len >= 2 && node.has_else && type_sym.kind != .void {
// `x := if a > b { } else if { } else { }`
@ -1286,7 +1289,9 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
g.write('.get(')
}
g.expr(expr.index)
if !expr.is_setter { g.write(')') }
if !expr.is_setter {
g.write(')')
}
} else if left_typ.kind == .string {
if expr.is_setter {
// TODO: What's the best way to do this?
@ -1309,35 +1314,46 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
l_sym := g.table.get_type_symbol(it.left_type)
r_sym := g.table.get_type_symbol(it.right_type)
if l_sym.kind == .array && it.op == .left_shift { // arr << 1
g.expr(it.left)
g.write('.push(')
if r_sym.kind == .array { g.write('...') } // arr << [1, 2]
if r_sym.kind == .array {
g.write('...')
} // arr << [1, 2]
g.expr(it.right)
g.write(')')
} else if r_sym.kind in [.array, .map, .string] && it.op in [.key_in, .not_in] {
if it.op == .not_in { g.write('!(') }
if it.op == .not_in {
g.write('!(')
}
g.expr(it.right)
g.write(if r_sym.kind == .map { '.has(' } else { '.includes(' })
g.write(if r_sym.kind == .map {
'.has('
} else {
'.includes('
})
g.expr(it.left)
g.write(')')
if it.op == .not_in { g.write(')') }
if it.op == .not_in {
g.write(')')
}
} else if it.op in [.key_is, .not_is] { // foo is Foo
if it.op == .not_is { g.write('!(') }
if it.op == .not_is {
g.write('!(')
}
g.expr(it.left)
g.write(' instanceof ')
g.write(g.typ(it.right_type))
if it.op == .not_is { g.write(')') }
if it.op == .not_is {
g.write(')')
}
} else {
both_are_int := int(it.left_type) in table.integer_type_idxs && int(it.right_type) in table.integer_type_idxs
both_are_int := int(it.left_type) in table.integer_type_idxs &&
int(it.right_type) in table.integer_type_idxs
if it.op == .div && both_are_int {
g.write('parseInt(')
}
g.expr(it.left)
// in js == is non-strict & === is strict, always do strict
if it.op == .eq {
g.write(' === ')
@ -1346,9 +1362,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
} else {
g.write(' $it.op ')
}
g.expr(it.right)
// Int division: 2.5 -> 2 by prepending |0
if it.op == .div && both_are_int {
g.write(',10)')
@ -1356,7 +1370,6 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
}
}
fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) {
// key_typ_sym := g.table.get_type_symbol(it.key_type)
// value_typ_sym := g.table.get_type_symbol(it.value_type)
@ -1407,7 +1420,6 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
g.expr(expr)
} else {
sym := g.table.get_type_symbol(it.expr_types[i])
g.expr(expr)
if sym.kind == .struct_ && sym.has_method('str') {
g.write('.str()')
@ -1446,7 +1458,7 @@ fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) {
} else if sym.kind == .array_fixed {
fixed_info := sym.info as table.ArrayFixed
typ_name := g.table.get_type_name(fixed_info.elem_type)
g.write('"[$fixed_info.size]${typ_name}"')
g.write('"[$fixed_info.size]$typ_name"')
} else if sym.kind == .function {
info := sym.info as table.FnType
fn_info := info.func
@ -1463,6 +1475,6 @@ fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) {
}
g.write('"$repr"')
} else {
g.write('"${sym.name}"')
g.write('"$sym.name"')
}
}

View File

@ -7,7 +7,8 @@ import v.ast
import v.table
fn (mut p Parser) assign_stmt() ast.Stmt {
return p.partial_assign_stmt(p.expr_list())
exprs, comments := p.expr_list()
return p.partial_assign_stmt(exprs, comments)
}
fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) {
@ -77,12 +78,15 @@ fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool {
return false
}
fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt {
fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comment) ast.Stmt {
p.is_stmt_ident = false
op := p.tok.kind
pos := p.tok.position()
p.next()
right := p.expr_list()
right, right_comments := p.expr_list()
mut comments := []ast.Comment{cap: left_comments.len + right_comments.len}
comments << left_comments
comments << right_comments
mut has_cross_var := false
if op == .decl_assign {
// a, b := a + 1, b
@ -155,6 +159,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt {
op: op
left: left
right: right
comments: comments
pos: pos
has_cross_var: has_cross_var
is_simple: p.inside_for && p.tok.kind == .lcbr

View File

@ -37,12 +37,10 @@ fn (mut p Parser) array_init() ast.ArrayInit {
} else {
// [1,2,3] or [const]byte
for i := 0; p.tok.kind != .rsbr; i++ {
expr := p.expr(0)
exprs << expr
exprs << p.expr(0)
if p.tok.kind == .comma {
p.next()
}
// p.check_comment()
}
line_nr := p.tok.line_nr
$if tinyc {

View File

@ -31,5 +31,3 @@ fn (mut p Parser) lock_expr() ast.LockExpr {
pos: pos
}
}

View File

@ -21,10 +21,8 @@ pub fn (mut p Parser) parse_array_type() table.Type {
p.check(.rsbr)
elem_type := p.parse_type()
mut nr_dims := 1
// detect attr
not_attr := p.peek_tok.kind != .name && p.peek_tok2.kind !in [.semicolon, .rsbr]
for p.tok.kind == .lsbr && not_attr {
p.next()
p.check(.rsbr)
@ -100,7 +98,6 @@ pub fn (mut p Parser) parse_type_with_mut(is_mut bool) table.Type {
// Parses any language indicators on a type.
pub fn (mut p Parser) parse_language() table.Language {
language := if p.tok.lit == 'C' {
table.Language.c
} else if p.tok.lit == 'JS' {
@ -108,12 +105,10 @@ pub fn (mut p Parser) parse_language() table.Language {
} else {
table.Language.v
}
if language != .v {
p.next()
p.check(.dot)
}
return language
}
@ -124,7 +119,6 @@ pub fn (mut p Parser) parse_type() table.Type {
line_nr := p.tok.line_nr
p.next()
is_optional = true
if p.tok.line_nr > line_nr {
mut typ := table.void_type
if is_optional {
@ -135,7 +129,6 @@ pub fn (mut p Parser) parse_type() table.Type {
}
is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic
mut nr_muls := 0
if p.tok.kind == .key_mut || is_shared || is_atomic {
nr_muls++

View File

@ -137,7 +137,7 @@ fn (mut p Parser) parse() ast.File {
p.read_first_token()
mut stmts := []ast.Stmt{}
for p.tok.kind == .comment {
stmts << p.comment()
stmts << p.comment_stmt()
}
// module
module_decl := p.module_decl()
@ -149,7 +149,7 @@ fn (mut p Parser) parse() ast.File {
continue
}
if p.tok.kind == .comment {
stmts << p.comment()
stmts << p.comment_stmt()
continue
}
break
@ -443,7 +443,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
return p.struct_decl()
}
.comment {
return p.comment()
return p.comment_stmt()
}
else {
if p.pref.is_script && !p.pref.is_test {
@ -488,13 +488,21 @@ pub fn (mut p Parser) comment() ast.Comment {
}
}
pub fn (mut p Parser) comment_stmt() ast.ExprStmt {
comment := p.comment()
return ast.ExprStmt{
expr: comment
pos: comment.pos
}
}
pub fn (mut p Parser) eat_comments() []ast.Comment {
mut comments := []ast.Comment{}
for {
if p.tok.kind != .comment {
break
}
comments << p.check_comment()
comments << p.comment()
}
return comments
}
@ -546,7 +554,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
return p.parse_multi_expr(is_top_level)
}
.comment {
return p.comment()
return p.comment_stmt()
}
.key_return {
return p.return_stmt()
@ -622,16 +630,22 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
}
}
fn (mut p Parser) expr_list() []ast.Expr {
fn (mut p Parser) expr_list() ([]ast.Expr, []ast.Comment) {
mut exprs := []ast.Expr{}
mut comments := []ast.Comment{}
for {
exprs << p.expr(0)
expr := p.expr(0)
if expr is ast.Comment {
comments << expr
} else {
exprs << expr
if p.tok.kind != .comma {
break
}
p.next()
}
return exprs
}
return exprs, comments
}
// when is_top_stmt is true attrs are added to p.attrs
@ -769,10 +783,10 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
// a, mut b ... :=/= // multi-assign
// collect things upto hard boundaries
tok := p.tok
left := p.expr_list()
left, left_comments := p.expr_list()
left0 := left[0]
if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
return p.partial_assign_stmt(left)
return p.partial_assign_stmt(left, left_comments)
} else if is_top_level && tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock] &&
left0 !is ast.CallExpr && left0 !is ast.PostfixExpr && !(left0 is ast.InfixExpr &&
(left0 as ast.InfixExpr).op == .left_shift) &&
@ -783,6 +797,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
return ast.ExprStmt{
expr: left0
pos: tok.position()
comments: left_comments
is_expr: p.inside_for
}
}
@ -791,6 +806,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
vals: left
}
pos: tok.position()
comments: left_comments
}
}
@ -1416,10 +1432,11 @@ fn (mut p Parser) return_stmt() ast.Return {
}
}
// return exprs
exprs := p.expr_list()
exprs, comments := p.expr_list()
end_pos := exprs.last().position()
return ast.Return{
exprs: exprs
comments: comments
pos: first_pos.extend(end_pos)
}
}

View File

@ -13,7 +13,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
mut node := ast.Expr{}
is_stmt_ident := p.is_stmt_ident
p.is_stmt_ident = false
if !p.pref.is_fmt {
p.eat_comments()
}
// Prefix
match p.tok.kind {
.key_mut, .key_shared, .key_atomic, .key_static {
@ -33,6 +35,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
.string {
node = p.string_expr()
}
.comment {
node = p.comment()
}
.dot {
// .enum_val
node = p.enum_val()

View File

@ -165,7 +165,6 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
} else if kind == .delete && n != 'from' {
p.error('expecting `from`')
}
mut table_type := table.Type(0)
mut where_expr := ast.Expr{}
if kind == .insert {