Skip to content

Commit 75fc8be

Browse files
committed
v2: move more logic to transformer; -printfn
1 parent 91c7649 commit 75fc8be

7 files changed

Lines changed: 1087 additions & 63 deletions

File tree

‎cmd/v2/v2.v‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn main() {
2727

2828
// get_files extracts source files from args, excluding options and their values
2929
fn get_files(args []string) []string {
30-
options_with_values := ['-backend', '-o', '-output', '-arch']
30+
options_with_values := ['-backend', '-o', '-output', '-arch', '-printfn']
3131
mut files := []string{}
3232
mut skip_next := false
3333
for arg in args {

‎vlib/v2/gen/cleanc/cleanc.v‎

Lines changed: 137 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ mut:
7575
cur_sumtype_match_selector_lhs string // For SelectorExpr: LHS ident (e.g., "se" for se.lhs)
7676
cur_sumtype_match_selector_rhs string // For SelectorExpr: RHS field (e.g., "lhs" for se.lhs)
7777
cur_lambda_elem_type string // Element type for lambda `it` variable in filter/map/any
78+
// For -printfn support
79+
last_fn_c_name string // Current function's C name for -printfn
80+
fn_start_pos int // Start position of current function in output buffer
7881
}
7982

8083
pub fn Gen.new(files []ast.File) &Gen {
@@ -408,6 +411,34 @@ fn (g &Gen) get_struct_field_type_from_env(struct_name string, field_name string
408411
return none
409412
}
410413

414+
// lookup_method_return_type_from_env looks up a method's return type from the environment
415+
fn (g &Gen) lookup_method_return_type_from_env(type_name string, method_name string) ?string {
416+
if g.env == unsafe { nil } {
417+
return none
418+
}
419+
if fn_type := g.env.lookup_method(type_name, method_name) {
420+
if ret := fn_type.get_return_type() {
421+
return g.types_type_to_c(ret)
422+
}
423+
return 'void'
424+
}
425+
return none
426+
}
427+
428+
// lookup_fn_return_type_from_env looks up a function's return type from the environment
429+
fn (g &Gen) lookup_fn_return_type_from_env(module_name string, fn_name string) ?string {
430+
if g.env == unsafe { nil } {
431+
return none
432+
}
433+
if fn_type := g.env.lookup_fn(module_name, fn_name) {
434+
if ret := fn_type.get_return_type() {
435+
return g.types_type_to_c(ret)
436+
}
437+
return 'void'
438+
}
439+
return none
440+
}
441+
411442
// types_type_to_c converts a types.Type to a C type string
412443
fn (g &Gen) types_type_to_c(t types.Type) string {
413444
match t {
@@ -442,7 +473,8 @@ fn (g &Gen) types_type_to_c(t types.Type) string {
442473
return '${base}*'
443474
}
444475
types.Array {
445-
return 'Array' // Arrays use generic Array struct
476+
elem := g.types_type_to_c(t.elem_type)
477+
return 'Array_${elem}'
446478
}
447479
types.Struct {
448480
return t.name
@@ -1195,11 +1227,37 @@ pub fn (mut g Gen) gen() string {
11951227
// strings__Builder__free - Builder is a type alias for []u8, delegate to array__free
11961228
g.sb.writeln('static inline void strings__Builder__free(strings__Builder* b) { array__free((array*)b); }')
11971229
// array__contains_u8 - check if array contains element (for 'in' operator)
1198-
g.sb.writeln('static inline bool array__contains_u8(array* a, u8 v) { for (int i = 0; i < a->len; i++) { if (((u8*)a->data)[i] == v) return true; } return false; }')
1230+
g.sb.writeln('static inline bool array__contains_u8(array a, u8 v) { for (int i = 0; i < a.len; i++) { if (((u8*)a.data)[i] == v) return true; } return false; }')
11991231
// Forward declaration for string__eq (used by array__contains_string)
12001232
g.sb.writeln('bool string__eq(string s, string a);')
12011233
// array__contains_string - check if array contains string element
1202-
g.sb.writeln('static inline bool array__contains_string(array* a, string v) { for (int i = 0; i < a->len; i++) { if (string__eq(((string*)a->data)[i], v)) return true; } return false; }')
1234+
g.sb.writeln('static inline bool array__contains_string(array a, string v) { for (int i = 0; i < a.len; i++) { if (string__eq(((string*)a.data)[i], v)) return true; } return false; }')
1235+
// builtin__new_array_from_c_array_noscan - create array from C array literal
1236+
g.sb.writeln('static inline array builtin__new_array_from_c_array_noscan(int len, int cap, int elem_size, void* c_array) {')
1237+
g.sb.writeln(' array a = {0}; a.len = len; a.cap = cap; a.element_size = elem_size;')
1238+
g.sb.writeln(' if (len > 0) { a.data = malloc(cap * elem_size); if (a.data) memcpy(a.data, c_array, len * elem_size); }')
1239+
g.sb.writeln(' return a;')
1240+
g.sb.writeln('}')
1241+
// builtin__array_push_noscan - push element to array
1242+
g.sb.writeln('static inline void builtin__array_push_noscan(array* a, void* elem) {')
1243+
g.sb.writeln(' if (a->len >= a->cap) { a->cap = a->cap == 0 ? 8 : a->cap * 2; a->data = realloc(a->data, a->cap * a->element_size); }')
1244+
g.sb.writeln(' memcpy((char*)a->data + a->len * a->element_size, elem, a->element_size);')
1245+
g.sb.writeln(' a->len++;')
1246+
g.sb.writeln('}')
1247+
// builtin__array_push_many - push multiple elements from another array
1248+
g.sb.writeln('static inline void builtin__array_push_many(array* a, array b) {')
1249+
g.sb.writeln(' if (b.len == 0) return;')
1250+
g.sb.writeln(' int new_len = a->len + b.len;')
1251+
g.sb.writeln(' while (new_len > a->cap) { a->cap = a->cap == 0 ? 8 : a->cap * 2; }')
1252+
g.sb.writeln(' a->data = realloc(a->data, a->cap * a->element_size);')
1253+
g.sb.writeln(' memcpy((char*)a->data + a->len * a->element_size, b.data, b.len * a->element_size);')
1254+
g.sb.writeln(' a->len = new_len;')
1255+
g.sb.writeln('}')
1256+
// array__contains_* functions for common types (takes array by value)
1257+
g.sb.writeln('static inline bool array__contains_int(array a, int v) { for (int i = 0; i < a.len; i++) { if (((int*)a.data)[i] == v) return true; } return false; }')
1258+
g.sb.writeln('static inline bool array__contains_i64(array a, i64 v) { for (int i = 0; i < a.len; i++) { if (((i64*)a.data)[i] == v) return true; } return false; }')
1259+
g.sb.writeln('static inline bool array__contains_u64(array a, u64 v) { for (int i = 0; i < a.len; i++) { if (((u64*)a.data)[i] == v) return true; } return false; }')
1260+
g.sb.writeln('static inline bool array__contains_rune(array a, rune v) { for (int i = 0; i < a.len; i++) { if (((rune*)a.data)[i] == v) return true; } return false; }')
12031261
// strings__Builder__trim - trim builder to specified length
12041262
g.sb.writeln('static inline void strings__Builder__trim(strings__Builder b, int n) { if (n < b.len) b.len = n; }')
12051263
// ArrayFlags__has - check if flag is set
@@ -1208,13 +1266,6 @@ pub fn (mut g Gen) gen() string {
12081266
g.sb.writeln('static inline bool array__eq(array a, array b) { if (a.len != b.len) return false; return memcmp(a.data, b.data, a.len * a.element_size) == 0; }')
12091267
// array__clone - clone an array (returns a copy with same elements)
12101268
g.sb.writeln('static inline array array__clone(array* a) { array c = {0}; if (!a || a->len == 0) return c; c.len = a->len; c.cap = a->len; c.element_size = a->element_size; c.data = malloc(a->len * a->element_size); if (c.data) memcpy(c.data, a->data, a->len * a->element_size); return c; }')
1211-
// Register builtin array method return types
1212-
g.fn_types['array__clone'] = 'array'
1213-
g.fn_types['array__reverse'] = 'array'
1214-
g.fn_types['array__sorted'] = 'array'
1215-
g.fn_types['array__eq'] = 'bool'
1216-
g.fn_types['array__contains_u8'] = 'bool'
1217-
g.fn_types['array__contains_string'] = 'bool'
12181269
// Register Map getter return types
12191270
g.fn_types['__Map_int_int_get'] = 'int'
12201271
g.fn_types['__Map_string_int_get'] = 'int'
@@ -1228,6 +1279,11 @@ pub fn (mut g Gen) gen() string {
12281279
g.fn_types['__Map_string_Array_string_get'] = 'array'
12291280
g.fn_types['__Map_string_Map_string_bool_get'] = 'map'
12301281
g.fn_types['__Map_string_Map_string_string_get'] = 'map'
1282+
// Register transformer-synthesized function return types (not in type system)
1283+
g.fn_types['string__plus'] = 'string'
1284+
g.fn_types['string__plus_two'] = 'string'
1285+
g.fn_types['string__eq'] = 'bool'
1286+
g.fn_types['string__lt'] = 'bool'
12311287
// Map function stubs
12321288
g.sb.writeln('static inline Map_int_int __new_Map_int_int() { return (Map_int_int){0}; }')
12331289
g.sb.writeln('static inline int __Map_int_int_get(Map_int_int* m, int key) { return 0; }')
@@ -1789,6 +1845,10 @@ fn (mut g Gen) infer_type(node ast.Expr) string {
17891845
if t := g.fn_types[mangled] {
17901846
return t
17911847
}
1848+
// Look up method return type from environment
1849+
if ret := g.lookup_method_return_type_from_env(clean_type, name) {
1850+
return ret
1851+
}
17921852
// Array methods that return the element type
17931853
if clean_type.starts_with('Array_') && name in ['first', 'last', 'pop'] {
17941854
return clean_type['Array_'.len..]
@@ -1823,6 +1883,22 @@ fn (mut g Gen) infer_type(node ast.Expr) string {
18231883
return t
18241884
}
18251885
}
1886+
// Look up function return type from environment
1887+
if ret := g.lookup_fn_return_type_from_env(g.cur_module, name) {
1888+
return ret
1889+
}
1890+
if ret := g.lookup_fn_return_type_from_env('builtin', name) {
1891+
return ret
1892+
}
1893+
// For calls with ArrayInitExpr argument, infer array type from that argument
1894+
for arg in node.args {
1895+
if arg is ast.ArrayInitExpr {
1896+
arr_type := g.infer_type(arg)
1897+
if arr_type.starts_with('Array_') {
1898+
return arr_type
1899+
}
1900+
}
1901+
}
18261902
return 'int'
18271903
}
18281904
ast.CallOrCastExpr {
@@ -3265,6 +3341,10 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl) {
32653341
g.mut_params = map[string]bool{}
32663342
g.defer_stmts.clear()
32673343

3344+
// Record start position and function name for -printfn support
3345+
g.fn_start_pos = g.sb.len
3346+
g.last_fn_c_name = g.get_fn_name(node)
3347+
32683348
// Set the current function's return type for proper return statement generation
32693349
ret_expr := node.typ.return_type
32703350
g.cur_fn_returns_result = false
@@ -3646,6 +3726,12 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl) {
36463726
}
36473727
g.indent--
36483728
g.sb.writeln('}')
3729+
// Print function source if in -printfn list and exit
3730+
if g.pref != unsafe { nil } && g.pref.printfn_list.len > 0
3731+
&& g.last_fn_c_name in g.pref.printfn_list {
3732+
println(g.sb.after(g.fn_start_pos))
3733+
exit(0)
3734+
}
36493735
}
36503736

36513737
fn (mut g Gen) gen_stmts(stmts []ast.Stmt) {
@@ -5142,13 +5228,13 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
51425228
g.gen_expr(node.lhs)
51435229
g.sb.write_string(')')
51445230
} else if rhs_type.starts_with('Array_') {
5145-
// x in arr => array__contains_u8(&arr, x) (for u8 arrays)
5146-
// x !in arr => !array__contains_u8(&arr, x)
5231+
// x in arr => array__contains_T(arr, x) (for arrays)
5232+
// x !in arr => !array__contains_T(arr, x)
51475233
elem_type := rhs_type['Array_'.len..]
51485234
if node.op == .not_in {
51495235
g.sb.write_string('!')
51505236
}
5151-
g.sb.write_string('array__contains_${elem_type}(&')
5237+
g.sb.write_string('array__contains_${elem_type}(')
51525238
g.gen_expr(node.rhs)
51535239
g.sb.write_string(', ')
51545240
g.gen_expr(node.lhs)
@@ -6916,6 +7002,22 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
69167002
return
69177003
}
69187004
}
7005+
// Check for known enum values from stdlib
7006+
// ProcessState enum from os module
7007+
if node.rhs.name in ['not_started', 'running', 'stopped', 'exited', 'closed'] {
7008+
g.sb.write_string('os__ProcessState__${node.rhs.name}')
7009+
return
7010+
}
7011+
// TrimMode enum from strings module
7012+
if node.rhs.name in ['trim_left', 'trim_right', 'trim_both'] {
7013+
g.sb.write_string('TrimMode__${node.rhs.name}')
7014+
return
7015+
}
7016+
// CaseMode enum from unicode module
7017+
if node.rhs.name in ['to_upper', 'to_lower', 'to_title'] {
7018+
g.sb.write_string('CaseMode__${node.rhs.name}')
7019+
return
7020+
}
69197021
// Last fallback: just output the field name (might not be valid C)
69207022
g.sb.write_string('${node.rhs.name}')
69217023
return
@@ -7084,30 +7186,21 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
70847186
g.gen_expr(node.expr)
70857187
}
70867188
ast.ArrayInitExpr {
7087-
// Check if this is a fixed-size array (typ is ArrayFixedType)
7189+
// After transformer, ArrayInitExpr is either:
7190+
// 1. Fixed-size array: generate {val1, val2, ...}
7191+
// 2. Dynamic array as arg to builtin call: generate _MOV((elem_type[len]){values})
70887192
mut is_fixed_array := false
7089-
mut fixed_size := ''
7090-
mut fixed_elem_type := ''
70917193
match node.typ {
70927194
ast.Type {
70937195
if node.typ is ast.ArrayFixedType {
70947196
is_fixed_array = true
7095-
fixed_elem_type = g.expr_type_to_c(node.typ.elem_type)
7096-
fixed_size = if node.typ.len is ast.BasicLiteral {
7097-
node.typ.len.value
7098-
} else if node.typ.len is ast.Ident {
7099-
node.typ.len.name
7100-
} else {
7101-
'0'
7102-
}
71037197
}
71047198
}
71057199
else {}
71067200
}
71077201
if is_fixed_array {
7108-
// Generate C fixed-size array initializer: {val1, val2, ...}
7202+
// Fixed-size array: generate C array initializer
71097203
g.sb.write_string('{')
7110-
// If no explicit elements, generate zeros
71117204
if node.exprs.len == 0 {
71127205
g.sb.write_string('0')
71137206
} else {
@@ -7120,36 +7213,30 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
71207213
}
71217214
g.sb.write_string('}')
71227215
} else {
7123-
// Generate array using new_array_from_c_array builtin
7216+
// Dynamic array values: generate _MOV((elem_type[len]){values})
71247217
arr_len := node.exprs.len
7125-
mut c_elem_type := 'int' // Actual C type for sizeof and array literals
7126-
if node.typ !is ast.EmptyExpr {
7127-
// node.typ is ArrayType{elem_type: T} for []T{} syntax
7128-
// Get the actual C type for sizeof and array literals
7129-
match node.typ {
7130-
ast.Type {
7131-
if node.typ is ast.ArrayType {
7132-
c_elem_type = g.expr_type_to_c(node.typ.elem_type)
7133-
} else {
7134-
c_elem_type = g.expr_type_to_c(node.typ)
7135-
}
7136-
}
7137-
else {
7138-
c_elem_type = g.expr_type_to_c(node.typ)
7218+
mut c_elem_type := 'int'
7219+
match node.typ {
7220+
ast.Type {
7221+
if node.typ is ast.ArrayType {
7222+
c_elem_type = g.expr_type_to_c(node.typ.elem_type)
71397223
}
71407224
}
7141-
} else if arr_len > 0 {
7142-
c_elem_type = g.infer_type(node.exprs[0])
7225+
else {}
71437226
}
7144-
// new_array_from_c_array(len, len, sizeof(elem), (elem_type[len]){values})
7145-
g.sb.write_string('new_array_from_c_array(${arr_len}, ${arr_len}, sizeof(${c_elem_type}), (${c_elem_type}[${arr_len}]){')
7146-
for i, expr in node.exprs {
7147-
if i > 0 {
7148-
g.sb.write_string(', ')
7227+
// Handle empty arrays specially - generate empty struct initialization
7228+
if arr_len == 0 {
7229+
g.sb.write_string('(array){0}')
7230+
} else {
7231+
g.sb.write_string('_MOV((${c_elem_type}[${arr_len}]){')
7232+
for i, expr in node.exprs {
7233+
if i > 0 {
7234+
g.sb.write_string(', ')
7235+
}
7236+
g.gen_expr(expr)
71497237
}
7150-
g.gen_expr(expr)
7238+
g.sb.write_string('})')
71517239
}
7152-
g.sb.write_string('})')
71537240
}
71547241
}
71557242
ast.MapInitExpr {

‎vlib/v2/pref/pref.v‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub mut:
3333
backend Backend
3434
arch Arch = .auto
3535
output_file string
36+
printfn_list []string // List of function names whose generated C source should be printed
3637
pub:
3738
vroot string = os.dir(@VEXE)
3839
vmodules_path string = os.vmodules_dir()
@@ -68,6 +69,10 @@ pub fn new_preferences_from_args(args []string) Preferences {
6869

6970
output_file := cmdline.option(args, '-o', cmdline.option(args, '-output', ''))
7071

72+
// Parse -printfn option (comma-separated list of function names to print)
73+
printfn_str := cmdline.option(args, '-printfn', '')
74+
printfn_list := if printfn_str != '' { printfn_str.split(',') } else { []string{} }
75+
7176
options := cmdline.only_options(args)
7277
// Default to sequential parsing (no_parallel=true) unless --parallel is specified
7378
use_parallel := '--parallel' in options
@@ -84,6 +89,7 @@ pub fn new_preferences_from_args(args []string) Preferences {
8489
backend: backend
8590
arch: arch
8691
output_file: output_file
92+
printfn_list: printfn_list
8793
// Explicitly set defaults since cleanc doesn't handle struct default values
8894
vroot: os.dir(@VEXE)
8995
vmodules_path: os.vmodules_dir()

0 commit comments

Comments
 (0)