Files
herolib/lib/core/code/model_file.v
2025-05-04 08:19:47 +03:00

284 lines
6.2 KiB
V

module code
import log
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
import os
pub interface IFile {
name string
write(string, WriteOptions) !
write_str(WriteOptions) !string
}
pub struct File {
pub mut:
name string
extension string
content string
}
pub fn (f File) write(path string, params WriteOptions) ! {
mut fd_file := pathlib.get_file(path: '${path}/${f.name}.${f.extension}')!
fd_file.write(f.content)!
if f.extension == 'ts' {
return f.typescript(path, params)
}
}
pub fn (f File) write_str(params WriteOptions) !string {
return f.content
}
pub fn (f File) typescript(path string, params WriteOptions) ! {
if params.format {
os.execute('npx prettier --write ${path}')
}
}
pub struct VFile {
pub mut:
name string
mod string
imports []Import
consts []Const
items []CodeItem
content string
}
pub fn new_file(config VFile) VFile {
return VFile{
...config
mod: texttools.name_fix(config.mod)
items: config.items
}
}
pub fn (mut file VFile) add_import(import_ Import) ! {
for mut i in file.imports {
if i.mod == import_.mod {
i.add_types(import_.types)
return
}
}
file.imports << import_
}
pub fn (code VFile) write(path string, options WriteOptions) ! {
filename := '${options.prefix}${texttools.name_fix(code.name)}.v'
mut filepath := pathlib.get('${path}/${filename}')
if !options.overwrite && filepath.exists() {
return
}
imports_str := code.imports.map(it.vgen()).join_lines()
code_str := if code.content != '' {
code.content
} else {
vgen(code.items)
}
consts_str := if code.consts.len > 1 {
stmts := code.consts.map('${it.name} = ${it.value}')
'\nconst(\n${stmts.join('\n')}\n)\n'
} else if code.consts.len == 1 {
'\nconst ${code.consts[0].name} = ${code.consts[0].value}\n'
} else {
''
}
mut file := pathlib.get_file(
path: filepath.path
create: true
)!
mod_stmt := if code.mod == '' {
''
} else {
'module ${code.mod}'
}
file.write('${mod_stmt}\n${imports_str}\n${consts_str}${code_str}')!
if options.format {
os.execute('v fmt -w ${file.path}')
}
}
pub fn (code VFile) write_str(options WriteOptions) !string {
imports_str := code.imports.map(it.vgen()).join_lines()
code_str := if code.content != '' {
code.content
} else {
vgen(code.items)
}
consts_str := if code.consts.len > 1 {
stmts := code.consts.map('${it.name} = ${it.value}')
'\nconst(\n${stmts.join('\n')}\n)\n'
} else if code.consts.len == 1 {
'\nconst ${code.consts[0].name} = ${code.consts[0].value}\n'
} else {
''
}
mod_stmt := if code.mod == '' {
''
} else {
'module ${code.mod}'
}
return '${mod_stmt}\n${imports_str}\n${consts_str}${code_str}'
}
pub fn (file VFile) get_function(name string) ?Function {
log.error('Looking for function ${name} in file ${file.name}')
functions := file.items.filter(it is Function).map(it as Function)
target_lst := functions.filter(it.name == name)
if target_lst.len == 0 {
return none
}
if target_lst.len > 1 {
panic('This should never happen')
}
return target_lst[0]
}
pub fn (mut file VFile) set_function(function Function) ! {
function_names := file.items.map(if it is Function { it.name } else { '' })
index := function_names.index(function.name)
if index == -1 {
return error('function not found')
}
file.items[index] = function
}
pub fn (file VFile) functions() []Function {
return file.items.filter(it is Function).map(it as Function)
}
pub fn (file VFile) structs() []Struct {
return file.items.filter(it is Struct).map(it as Struct)
}
// parse_vfile parses V code into a VFile struct
// It extracts the module name, imports, constants, structs, and functions
pub fn parse_vfile(code string) !VFile {
mut vfile := VFile{
content: code
}
lines := code.split_into_lines()
// Extract module name
for line in lines {
trimmed := line.trim_space()
if trimmed.starts_with('module ') {
vfile.mod = trimmed.trim_string_left('module ').trim_space()
break
}
}
// Extract imports
for line in lines {
trimmed := line.trim_space()
if trimmed.starts_with('import ') {
import_obj := parse_import(trimmed)
vfile.imports << import_obj
}
}
// Extract constants
vfile.consts = parse_consts(code) or { []Const{} }
// Split code into chunks for parsing structs and functions
mut chunks := []string{}
mut current_chunk := ''
mut brace_count := 0
mut in_struct_or_fn := false
mut comment_block := []string{}
for line in lines {
trimmed := line.trim_space()
// Collect comments
if trimmed.starts_with('//') && !in_struct_or_fn {
comment_block << line
continue
}
// Check for struct or function start
if (trimmed.starts_with('struct ') || trimmed.starts_with('pub struct ')
|| trimmed.starts_with('fn ') || trimmed.starts_with('pub fn ')) && !in_struct_or_fn {
in_struct_or_fn = true
current_chunk = comment_block.join('\n')
if current_chunk != '' {
current_chunk += '\n'
}
current_chunk += line
comment_block = []string{}
if line.contains('{') {
brace_count += line.count('{')
}
if line.contains('}') {
brace_count -= line.count('}')
}
if brace_count == 0 {
// Single line definition
chunks << current_chunk
current_chunk = ''
in_struct_or_fn = false
}
continue
}
// Add line to current chunk if we're inside a struct or function
if in_struct_or_fn {
current_chunk += '\n' + line
if line.contains('{') {
brace_count += line.count('{')
}
if line.contains('}') {
brace_count -= line.count('}')
}
// Check if we've reached the end of the struct or function
if brace_count == 0 {
chunks << current_chunk
current_chunk = ''
in_struct_or_fn = false
}
}
}
// Parse each chunk and add to items
for chunk in chunks {
trimmed := chunk.trim_space()
if trimmed.contains('struct ') || trimmed.contains('pub struct ') {
// Parse struct
struct_obj := parse_struct(chunk) or {
// Skip invalid structs
continue
}
vfile.items << struct_obj
} else if trimmed.contains('fn ') || trimmed.contains('pub fn ') {
// Parse function
fn_obj := parse_function(chunk) or {
// Skip invalid functions
continue
}
vfile.items << fn_obj
}
}
return vfile
}