Skip to content

Commit afb19ce

Browse files
committed
ssa: split optimizer.v into a separate module; v2: parser fixes, make v2 hello.v work
1 parent 8c75b07 commit afb19ce

21 files changed

Lines changed: 1541 additions & 1380 deletions

File tree

‎cmd/v2/test.v‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ fn print_str(s string) {
236236
C.puts(s.str)
237237
}
238238

239+
// C function with keyword name (tests parser allowing keywords after C.)
240+
fn C.select(ndfs int, readfds voidptr, writefds voidptr, exceptfds voidptr, timeout voidptr) int
241+
239242
fn nested_return(x int) int {
240243
if x < 10 {
241244
return 100
@@ -288,6 +291,18 @@ fn defer_order_test() {
288291
}
289292
}
290293

294+
// Helper function to test defer(fn) - function-level defer
295+
fn defer_fn_test() int {
296+
mut x := 0
297+
for i := 0; i < 3; i++ {
298+
defer(fn) {
299+
x += 100
300+
}
301+
x += 1
302+
}
303+
return x // returns 3, but defer(fn) adds 300 at function end
304+
}
305+
291306
// ===================== IF-GUARD HELPERS =====================
292307

293308
// Returns the value if positive, none otherwise
@@ -1980,6 +1995,9 @@ fn main() {
19801995
// 38.5 Test defer order in function
19811996
defer_order_test() // Should print: Third, Second, First
19821997

1998+
// 38.6 Test defer(fn) - function-level defer
1999+
print_int(defer_fn_test()) // 303 (3 from loop + 300 from function-level defers)
2000+
19832001
// ==================== 39. ENUMS ====================
19842002
print_str('--- 39. Enums ---')
19852003

‎cmd/v2/test_ssa_backends.v‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import v2.parser
99
import v2.token
1010
import v2.pref
1111
import v2.ssa
12+
import v2.ssa.optimize
1213
import v2.transform
1314
import v2.gen.x64
1415
import v2.gen.arm64
@@ -112,7 +113,7 @@ fn main() {
112113
builder.build_all(all_files)
113114
// Optimize
114115
println('[*] Optimizing SSA...')
115-
mod.optimize()
116+
optimize.optimize(mut mod)
116117
// Backend selection: default to native, use 'cleanc' or 'c' arg to switch
117118
use_cleanc := os.args.contains('cleanc')
118119
use_ssa_c := os.args.contains('c') && !use_cleanc

‎vlib/os/filepath.v‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,7 @@ fn is_normal_path(path string) bool {
297297
if plen == 0 {
298298
return false
299299
}
300-
return (plen == 1 && is_slash(path[0])) || (plen >= 2 && is_slash(path[0])
301-
&& !is_slash(path[1]))
300+
return (plen == 1 && is_slash(path[0])) || (plen >= 2 && is_slash(path[0]) && !is_slash(path[1]))
302301
}
303302

304303
// is_curr_dir_ref returns `true` if the 3 given integer construct

‎vlib/os/os.c.v‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -951,8 +951,7 @@ fn normalize_drive_letter(path string) {
951951
// a path like c:\nv\.bin (note the small `c`) in %PATH,
952952
// is NOT recognized by cmd.exe (and probably other programs too)...
953953
// Capital drive letters do work fine.
954-
if path.len > 2 && path[0] >= `a` && path[0] <= `z` && path[1] == `:`
955-
&& path[2] == path_separator[0] {
954+
if path.len > 2 && path[0] >= `a` && path[0] <= `z` && path[1] == `:` && path[2] == path_separator[0] {
956955
unsafe {
957956
x := &path.str[0]
958957
(*x) = *x - 32

‎vlib/v2/ast/ast.v‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ pub fn (lang Language) str() string {
226226
}
227227
}
228228

229+
pub enum DeferMode {
230+
scoped // default: defer to end of current scope
231+
function // defer(fn): defer to end of function
232+
}
233+
229234
// Expressions
230235
pub struct ArrayInitExpr {
231236
pub:
@@ -675,6 +680,7 @@ pub:
675680

676681
pub struct DeferStmt {
677682
pub:
683+
mode DeferMode
678684
stmts []Stmt
679685
}
680686

‎vlib/v2/builder/builder.v‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@ import v2.gen.v as gen_v
1212
import v2.gen.x64
1313
import v2.pref
1414
import v2.ssa
15+
import v2.ssa.optimize
1516
import v2.token
1617
import v2.transform
1718
import time
1819

1920
struct Builder {
2021
pref &pref.Preferences
2122
mut:
22-
files []ast.File
23-
file_set &token.FileSet = token.FileSet.new()
23+
files []ast.File
24+
user_files []string // original user-provided files (for output name)
25+
file_set &token.FileSet = token.FileSet.new()
2426
}
2527

2628
pub fn new_builder(prefs &pref.Preferences) &Builder {
@@ -32,6 +34,7 @@ pub fn new_builder(prefs &pref.Preferences) &Builder {
3234
}
3335

3436
pub fn (mut b Builder) build(files []string) {
37+
b.user_files = files
3538
mut sw := time.new_stopwatch()
3639
b.files = if b.pref.no_parallel {
3740
b.parse_files(files)
@@ -117,7 +120,7 @@ fn (mut b Builder) gen_ssa_c() {
117120
}
118121
// Build all files together with proper multi-file ordering
119122
ssa_builder.build_all(transformed_files)
120-
mod.optimize()
123+
optimize.optimize(mut mod)
121124

122125
mut gen := c.Gen.new(mod)
123126
c_source := gen.gen()
@@ -132,8 +135,8 @@ fn (mut b Builder) gen_ssa_c() {
132135
// Compile C to binary
133136
output_binary := if b.pref.output_file != '' {
134137
b.pref.output_file
135-
} else if b.files.len > 0 {
136-
os.file_name(b.files.last().name).all_before_last('.v')
138+
} else if b.user_files.len > 0 {
139+
os.file_name(b.user_files.last()).all_before_last('.v')
137140
} else {
138141
'out'
139142
}
@@ -165,13 +168,13 @@ fn (mut b Builder) gen_native(backend_arch pref.Arch) {
165168
}
166169
// Build all files together with proper multi-file ordering
167170
ssa_builder.build_all(transformed_files)
168-
mod.optimize()
171+
optimize.optimize(mut mod)
169172

170173
// Determine output binary name from the last user file
171174
output_binary := if b.pref.output_file != '' {
172175
b.pref.output_file
173-
} else if b.files.len > 0 {
174-
os.file_name(b.files.last().name).all_before_last('.v')
176+
} else if b.user_files.len > 0 {
177+
os.file_name(b.user_files.last()).all_before_last('.v')
175178
} else {
176179
'out'
177180
}

‎vlib/v2/builder/parse.v‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,21 @@ fn (mut b Builder) parse_files(files []string) []ast.File {
1111
mut ast_files := []ast.File{}
1212
skip_builtin := b.pref.skip_builtin
1313
if !skip_builtin {
14+
// Parse builtin
1415
ast_files << parser_reused.parse_files(get_v_files_from_dir(b.pref.get_vlib_module_path('builtin')), mut
1516
b.file_set)
16-
// ast_files << parser_reused.parse_files(get_v_files_from_dir(b.pref.get_vlib_module_path('sync')), mut b.file_set)
17+
// Parse strconv (used by builtin for string formatting)
18+
ast_files << parser_reused.parse_files(get_v_files_from_dir(b.pref.get_vlib_module_path('strconv')), mut
19+
b.file_set)
20+
// Parse strings (used by builtin for string building)
21+
ast_files << parser_reused.parse_files(get_v_files_from_dir(b.pref.get_vlib_module_path('strings')), mut
22+
b.file_set)
23+
// Parse hash (used by maps for wyhash)
24+
ast_files << parser_reused.parse_files(get_v_files_from_dir(b.pref.get_vlib_module_path('hash')), mut
25+
b.file_set)
26+
// Parse math.bits (used by strconv for bit operations)
27+
ast_files << parser_reused.parse_files(get_v_files_from_dir(b.pref.get_vlib_module_path('math.bits')), mut
28+
b.file_set)
1729
}
1830
// parse user files
1931
ast_files << parser_reused.parse_files(files, mut b.file_set)

‎vlib/v2/builder/parse_parallel.v‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,18 @@ fn (mut b Builder) parse_files_parallel(files []string) []ast.File {
6565
}
6666
skip_builtin := b.pref.skip_builtin
6767
if !skip_builtin {
68+
// Parse builtin and its dependencies
69+
// Mark them as parsed first to prevent re-parsing via imports
70+
pstate.mark_module_as_parsed('builtin')
71+
pstate.mark_module_as_parsed('strconv')
72+
pstate.mark_module_as_parsed('strings')
73+
pstate.mark_module_as_parsed('hash')
74+
pstate.mark_module_as_parsed('math.bits')
6875
worker_pool.queue_jobs(get_v_files_from_dir(b.pref.get_vlib_module_path('builtin')))
76+
worker_pool.queue_jobs(get_v_files_from_dir(b.pref.get_vlib_module_path('strconv')))
77+
worker_pool.queue_jobs(get_v_files_from_dir(b.pref.get_vlib_module_path('strings')))
78+
worker_pool.queue_jobs(get_v_files_from_dir(b.pref.get_vlib_module_path('hash')))
79+
worker_pool.queue_jobs(get_v_files_from_dir(b.pref.get_vlib_module_path('math.bits')))
6980
}
7081
// parse user files
7182
worker_pool.queue_jobs(files)

‎vlib/v2/builder/util.v‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ pub fn get_v_files_from_dir(dir string) []string {
99
mod_files := os.ls(dir) or { panic('error getting ls from ${dir}') }
1010
mut v_files := []string{}
1111
for file in mod_files {
12-
if !file.ends_with('.v') || file.ends_with('.js.v') || file.ends_with('.c.v')
13-
|| file.contains('_test.') {
12+
// Include .v files (including .c.v), exclude .js.v and test files
13+
if !file.ends_with('.v') || file.ends_with('.js.v') || file.contains('_test.') {
1414
continue
1515
}
1616
// skip platform-specific files
1717
if file.contains('.arm64.') || file.contains('.arm32.') || file.contains('.amd64.') {
1818
continue
1919
}
2020
// skip OS-specific files for other platforms
21+
// Note: _nix files are for Unix-like systems including macOS and Linux
2122
$if macos {
22-
if file.contains('_windows.') || file.contains('_linux.') || file.contains('_nix.') {
23+
if file.contains('_windows.') || file.contains('_linux.') {
2324
continue
2425
}
2526
} $else $if linux {

‎vlib/v2/parser/parser.v‎

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,22 @@ fn (mut p Parser) stmt() ast.Stmt {
248248
}
249249
.key_defer {
250250
p.next()
251+
mut defer_mode := ast.DeferMode.scoped
252+
if p.tok == .lpar {
253+
p.next()
254+
if p.tok == .key_fn {
255+
defer_mode = .function
256+
p.next()
257+
} else {
258+
mode := p.expect_name()
259+
p.error('unknown `defer` mode: `${mode}`')
260+
}
261+
p.expect(.rpar)
262+
}
251263
stmts := p.block()
252264
p.expect(.semicolon)
253265
return ast.DeferStmt{
266+
mode: defer_mode
254267
stmts: stmts
255268
}
256269
}
@@ -1250,6 +1263,18 @@ fn (mut p Parser) expect_name() string {
12501263
return name
12511264
}
12521265

1266+
// expect `.name` or keyword & return `p.lit` & go to next token
1267+
// used for C/JS function names where keywords are allowed (e.g. `C.select`)
1268+
@[inline]
1269+
fn (mut p Parser) expect_name_or_keyword() string {
1270+
if p.tok != .name && !p.tok.is_keyword() {
1271+
p.error_expected(.name, p.tok)
1272+
}
1273+
name := p.lit
1274+
p.next()
1275+
return name
1276+
}
1277+
12531278
// return `p.lit` & go to next token
12541279
@[inline]
12551280
fn (mut p Parser) lit() string {
@@ -1778,7 +1803,8 @@ fn (mut p Parser) fn_decl(is_public bool, attributes []ast.Attribute) ast.FnDecl
17781803
}
17791804
}
17801805
language := p.decl_language()
1781-
name_ident := p.ident()
1806+
// use ident_or_keyword() for C/JS functions to allow keywords as names (e.g. `C.select`)
1807+
name_ident := if language == .v { p.ident() } else { p.ident_or_keyword() }
17821808
mut name := name_ident.name
17831809
mut is_static := false
17841810
if p.tok == .dot {
@@ -1793,11 +1819,12 @@ fn (mut p Parser) fn_decl(is_public bool, attributes []ast.Attribute) ast.FnDecl
17931819
}
17941820
}
17951821
// eg. `Promise.resolve` in `JS.Promise.resolve`
1822+
// use expect_name_or_keyword() to allow keywords as names (e.g. `C.select`)
17961823
else {
1797-
name += '.' + p.expect_name()
1824+
name += '.' + p.expect_name_or_keyword()
17981825
for p.tok == .dot {
17991826
p.next()
1800-
name += '.' + p.expect_name()
1827+
name += '.' + p.expect_name_or_keyword()
18011828
}
18021829
}
18031830
}
@@ -2390,6 +2417,15 @@ fn (mut p Parser) ident() ast.Ident {
23902417
}
23912418
}
23922419

2420+
// like ident() but allows keywords as names (for C/JS function names like `C.select`)
2421+
@[inline]
2422+
fn (mut p Parser) ident_or_keyword() ast.Ident {
2423+
return ast.Ident{
2424+
pos: p.pos
2425+
name: p.expect_name_or_keyword()
2426+
}
2427+
}
2428+
23932429
@[inline]
23942430
fn (mut p Parser) ident_or_selector_expr() ast.Expr {
23952431
ident := p.ident()
@@ -2407,9 +2443,15 @@ fn (mut p Parser) ident_or_selector_expr() ast.Expr {
24072443
pos: p.pos
24082444
}
24092445
}
2446+
// for C/JS calls, allow keywords as names (e.g. `C.select`)
2447+
rhs := if ident.name == 'C' || ident.name == 'JS' {
2448+
p.ident_or_keyword()
2449+
} else {
2450+
p.ident()
2451+
}
24102452
return ast.SelectorExpr{
24112453
lhs: ident
2412-
rhs: p.ident()
2454+
rhs: rhs
24132455
pos: p.pos
24142456
}
24152457
}

0 commit comments

Comments
 (0)