...
This commit is contained in:
@@ -6,9 +6,9 @@ import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
pub interface IFile {
|
||||
name string
|
||||
write(string, WriteOptions) !
|
||||
write_str(WriteOptions) !string
|
||||
name string
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
@@ -124,7 +124,9 @@ pub fn (code VFile) write_str(options WriteOptions) !string {
|
||||
''
|
||||
}
|
||||
|
||||
mod_stmt := if code.mod == '' {''} else {
|
||||
mod_stmt := if code.mod == '' {
|
||||
''
|
||||
} else {
|
||||
'module ${code.mod}'
|
||||
}
|
||||
|
||||
@@ -169,9 +171,9 @@ 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()
|
||||
@@ -180,7 +182,7 @@ pub fn parse_vfile(code string) !VFile {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Extract imports
|
||||
for line in lines {
|
||||
trimmed := line.trim_space()
|
||||
@@ -189,29 +191,29 @@ pub fn parse_vfile(code string) !VFile {
|
||||
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 {
|
||||
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 != '' {
|
||||
@@ -219,14 +221,14 @@ pub fn parse_vfile(code string) !VFile {
|
||||
}
|
||||
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
|
||||
@@ -235,18 +237,18 @@ pub fn parse_vfile(code string) !VFile {
|
||||
}
|
||||
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
|
||||
@@ -255,11 +257,11 @@ pub fn parse_vfile(code string) !VFile {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 {
|
||||
@@ -276,6 +278,6 @@ pub fn parse_vfile(code string) !VFile {
|
||||
vfile.items << fn_obj
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return vfile
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module code
|
||||
|
||||
fn test_parse_vfile() {
|
||||
code := '
|
||||
code := "
|
||||
module test
|
||||
|
||||
import os
|
||||
@@ -9,7 +9,7 @@ import strings
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
const (
|
||||
VERSION = \'1.0.0\'
|
||||
VERSION = '1.0.0'
|
||||
DEBUG = true
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ pub mut:
|
||||
|
||||
// greet returns a greeting message
|
||||
pub fn (p Person) greet() string {
|
||||
return \'Hello, my name is \${p.name} and I am \${p.age} years old\'
|
||||
return 'Hello, my name is \${p.name} and I am \${p.age} years old'
|
||||
}
|
||||
|
||||
// create_person creates a new Person instance
|
||||
@@ -31,7 +31,7 @@ pub fn create_person(name string, age int) Person {
|
||||
age: age
|
||||
}
|
||||
}
|
||||
'
|
||||
"
|
||||
|
||||
vfile := parse_vfile(code) or {
|
||||
assert false, 'Failed to parse VFile: ${err}'
|
||||
@@ -50,7 +50,7 @@ pub fn create_person(name string, age int) Person {
|
||||
// Test constants
|
||||
assert vfile.consts.len == 2
|
||||
assert vfile.consts[0].name == 'VERSION'
|
||||
assert vfile.consts[0].value == '\'1.0.0\''
|
||||
assert vfile.consts[0].value == "'1.0.0'"
|
||||
assert vfile.consts[1].name == 'DEBUG'
|
||||
assert vfile.consts[1].value == 'true'
|
||||
|
||||
@@ -68,13 +68,13 @@ pub fn create_person(name string, age int) Person {
|
||||
// Test functions
|
||||
functions := vfile.functions()
|
||||
assert functions.len == 2
|
||||
|
||||
|
||||
// Test method
|
||||
assert functions[0].name == 'greet'
|
||||
assert functions[0].is_pub == true
|
||||
assert functions[0].receiver.typ.vgen() == 'Person'
|
||||
assert functions[0].result.typ.vgen() == 'string'
|
||||
|
||||
|
||||
// Test standalone function
|
||||
assert functions[1].name == 'create_person'
|
||||
assert functions[1].is_pub == true
|
||||
|
||||
@@ -133,30 +133,30 @@ pub fn parse_function(code_ string) !Function {
|
||||
// Extract the result type, handling the ! for result types
|
||||
mut result_type := code.all_after(')').all_before('{').replace(' ', '')
|
||||
mut has_return := false
|
||||
|
||||
|
||||
// Check if the result type contains !
|
||||
if result_type.contains('!') {
|
||||
has_return = true
|
||||
result_type = result_type.replace('!', '')
|
||||
}
|
||||
|
||||
|
||||
result := new_param(
|
||||
v: result_type
|
||||
)!
|
||||
|
||||
body := if code.contains('{') { code.all_after('{').all_before_last('}') } else { '' }
|
||||
|
||||
|
||||
// Process the comments into a description
|
||||
description := comment_lines.join('\n')
|
||||
|
||||
|
||||
return Function{
|
||||
name: name
|
||||
receiver: receiver
|
||||
params: params
|
||||
result: result
|
||||
body: body
|
||||
name: name
|
||||
receiver: receiver
|
||||
params: params
|
||||
result: result
|
||||
body: body
|
||||
description: description
|
||||
is_pub: is_pub
|
||||
has_return: has_return
|
||||
is_pub: is_pub
|
||||
has_return: has_return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,20 @@ module code
|
||||
|
||||
fn test_parse_function_with_comments() {
|
||||
// Test function string with comments
|
||||
function_str := '// test_function is a simple function for testing the MCP tool code generation
|
||||
function_str := "// test_function is a simple function for testing the MCP tool code generation
|
||||
// It takes a config and returns a result
|
||||
pub fn test_function(config TestConfig) !TestResult {
|
||||
// This is just a mock implementation for testing purposes
|
||||
if config.name == \'\' {
|
||||
return error(\'Name cannot be empty\')
|
||||
if config.name == '' {
|
||||
return error('Name cannot be empty')
|
||||
}
|
||||
|
||||
return TestResult{
|
||||
success: config.enabled
|
||||
message: \'Test completed for \${config.name}\'
|
||||
message: 'Test completed for \${config.name}'
|
||||
code: if config.enabled { 0 } else { 1 }
|
||||
}
|
||||
}'
|
||||
}"
|
||||
|
||||
// Parse the function
|
||||
function := parse_function(function_str) or {
|
||||
@@ -30,7 +30,7 @@ pub fn test_function(config TestConfig) !TestResult {
|
||||
assert function.params[0].name == 'config'
|
||||
assert function.params[0].typ.symbol() == 'TestConfig'
|
||||
assert function.result.typ.symbol() == 'TestResult'
|
||||
|
||||
|
||||
// Verify that the comments were correctly parsed into the description
|
||||
expected_description := 'test_function is a simple function for testing the MCP tool code generation
|
||||
It takes a config and returns a result'
|
||||
@@ -41,9 +41,9 @@ It takes a config and returns a result'
|
||||
|
||||
fn test_parse_function_without_comments() {
|
||||
// Test function string without comments
|
||||
function_str := 'fn simple_function(name string, count int) string {
|
||||
return \'\${name} count: \${count}\'
|
||||
}'
|
||||
function_str := "fn simple_function(name string, count int) string {
|
||||
return '\${name} count: \${count}'
|
||||
}"
|
||||
|
||||
// Parse the function
|
||||
function := parse_function(function_str) or {
|
||||
@@ -60,7 +60,7 @@ fn test_parse_function_without_comments() {
|
||||
assert function.params[1].name == 'count'
|
||||
assert function.params[1].typ.symbol() == 'int'
|
||||
assert function.result.typ.symbol() == 'string'
|
||||
|
||||
|
||||
// Verify that there is no description
|
||||
assert function.description == ''
|
||||
|
||||
|
||||
@@ -79,4 +79,4 @@ pub fn (mod Module) write_str() !string {
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,10 +69,11 @@ pub fn parse_struct(code_ string) !Struct {
|
||||
trimmed := line.trim_space()
|
||||
if !in_struct && trimmed.starts_with('//') {
|
||||
comment_lines << trimmed.trim_string_left('//').trim_space()
|
||||
} else if !in_struct && (trimmed.starts_with('struct ') || trimmed.starts_with('pub struct ')) {
|
||||
} else if !in_struct && (trimmed.starts_with('struct ')
|
||||
|| trimmed.starts_with('pub struct ')) {
|
||||
in_struct = true
|
||||
struct_lines << line
|
||||
|
||||
|
||||
// Extract struct name
|
||||
is_pub = trimmed.starts_with('pub ')
|
||||
mut name_part := if is_pub {
|
||||
@@ -80,7 +81,7 @@ pub fn parse_struct(code_ string) !Struct {
|
||||
} else {
|
||||
trimmed.trim_string_left('struct ').trim_space()
|
||||
}
|
||||
|
||||
|
||||
// Handle generics in struct name
|
||||
if name_part.contains('<') {
|
||||
struct_name = name_part.all_before('<').trim_space()
|
||||
@@ -91,72 +92,71 @@ pub fn parse_struct(code_ string) !Struct {
|
||||
}
|
||||
} else if in_struct {
|
||||
struct_lines << line
|
||||
|
||||
|
||||
// Check if we've reached the end of the struct
|
||||
if trimmed.starts_with('}') {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if struct_name == '' {
|
||||
return error('Invalid struct format: could not extract struct name')
|
||||
}
|
||||
|
||||
|
||||
// Process the struct fields
|
||||
mut fields := []StructField{}
|
||||
mut current_section := ''
|
||||
|
||||
|
||||
for i := 1; i < struct_lines.len - 1; i++ { // Skip the first and last lines (struct declaration and closing brace)
|
||||
line := struct_lines[i].trim_space()
|
||||
|
||||
|
||||
// Skip empty lines and comments
|
||||
if line == '' || line.starts_with('//') {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// Check for section markers (pub:, mut:, pub mut:)
|
||||
if line.ends_with(':') {
|
||||
current_section = line
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// Parse field
|
||||
parts := line.split_any(' \t')
|
||||
if parts.len < 2 {
|
||||
continue // Skip invalid lines
|
||||
}
|
||||
|
||||
|
||||
field_name := parts[0]
|
||||
field_type_str := parts[1..].join(' ')
|
||||
|
||||
|
||||
// Parse the type string into a Type object
|
||||
field_type := parse_type(field_type_str)
|
||||
|
||||
|
||||
// Determine field visibility based on section
|
||||
is_pub_field := current_section.contains('pub')
|
||||
is_mut_field := current_section.contains('mut')
|
||||
|
||||
|
||||
fields << StructField{
|
||||
name: field_name
|
||||
typ: field_type
|
||||
name: field_name
|
||||
typ: field_type
|
||||
is_pub: is_pub_field
|
||||
is_mut: is_mut_field
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process the comments into a description
|
||||
description := comment_lines.join('\n')
|
||||
|
||||
|
||||
return Struct{
|
||||
name: struct_name
|
||||
name: struct_name
|
||||
description: description
|
||||
is_pub: is_pub
|
||||
fields: fields
|
||||
is_pub: is_pub
|
||||
fields: fields
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Interface {
|
||||
pub mut:
|
||||
name string
|
||||
|
||||
@@ -21,17 +21,17 @@ pub:
|
||||
It contains information about test execution'
|
||||
assert result.is_pub == true
|
||||
assert result.fields.len == 3
|
||||
|
||||
|
||||
assert result.fields[0].name == 'success'
|
||||
assert result.fields[0].typ.symbol() == 'bool'
|
||||
assert result.fields[0].is_pub == true
|
||||
assert result.fields[0].is_mut == false
|
||||
|
||||
|
||||
assert result.fields[1].name == 'message'
|
||||
assert result.fields[1].typ.symbol() == 'string'
|
||||
assert result.fields[1].is_pub == true
|
||||
assert result.fields[1].is_mut == false
|
||||
|
||||
|
||||
assert result.fields[2].name == 'code'
|
||||
assert result.fields[2].typ.symbol() == 'int'
|
||||
assert result.fields[2].is_pub == true
|
||||
@@ -55,17 +55,17 @@ mut:
|
||||
assert result2.description == ''
|
||||
assert result2.is_pub == false
|
||||
assert result2.fields.len == 3
|
||||
|
||||
|
||||
assert result2.fields[0].name == 'name'
|
||||
assert result2.fields[0].typ.symbol() == 'string'
|
||||
assert result2.fields[0].is_pub == true
|
||||
assert result2.fields[0].is_mut == false
|
||||
|
||||
|
||||
assert result2.fields[1].name == 'count'
|
||||
assert result2.fields[1].typ.symbol() == 'int'
|
||||
assert result2.fields[1].is_pub == false
|
||||
assert result2.fields[1].is_mut == true
|
||||
|
||||
|
||||
assert result2.fields[2].name == 'active'
|
||||
assert result2.fields[2].typ.symbol() == 'bool'
|
||||
assert result2.fields[2].is_pub == false
|
||||
|
||||
@@ -239,7 +239,7 @@ pub fn (t Type) empty_value() string {
|
||||
pub fn parse_type(type_str string) Type {
|
||||
println('Parsing type string: "${type_str}"')
|
||||
mut type_str_trimmed := type_str.trim_space()
|
||||
|
||||
|
||||
// Handle struct definitions by extracting just the struct name
|
||||
if type_str_trimmed.contains('struct ') {
|
||||
lines := type_str_trimmed.split_into_lines()
|
||||
@@ -257,7 +257,7 @@ pub fn parse_type(type_str string) Type {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check for simple types first
|
||||
if type_str_trimmed == 'string' {
|
||||
return String{}
|
||||
@@ -266,41 +266,61 @@ pub fn parse_type(type_str string) Type {
|
||||
} else if type_str_trimmed == 'int' {
|
||||
return Integer{}
|
||||
} else if type_str_trimmed == 'u8' {
|
||||
return Integer{bytes: 8, signed: false}
|
||||
return Integer{
|
||||
bytes: 8
|
||||
signed: false
|
||||
}
|
||||
} else if type_str_trimmed == 'u16' {
|
||||
return Integer{bytes: 16, signed: false}
|
||||
return Integer{
|
||||
bytes: 16
|
||||
signed: false
|
||||
}
|
||||
} else if type_str_trimmed == 'u32' {
|
||||
return Integer{bytes: 32, signed: false}
|
||||
return Integer{
|
||||
bytes: 32
|
||||
signed: false
|
||||
}
|
||||
} else if type_str_trimmed == 'u64' {
|
||||
return Integer{bytes: 64, signed: false}
|
||||
return Integer{
|
||||
bytes: 64
|
||||
signed: false
|
||||
}
|
||||
} else if type_str_trimmed == 'i8' {
|
||||
return Integer{bytes: 8}
|
||||
return Integer{
|
||||
bytes: 8
|
||||
}
|
||||
} else if type_str_trimmed == 'i16' {
|
||||
return Integer{bytes: 16}
|
||||
return Integer{
|
||||
bytes: 16
|
||||
}
|
||||
} else if type_str_trimmed == 'i32' {
|
||||
return Integer{bytes: 32}
|
||||
return Integer{
|
||||
bytes: 32
|
||||
}
|
||||
} else if type_str_trimmed == 'i64' {
|
||||
return Integer{bytes: 64}
|
||||
return Integer{
|
||||
bytes: 64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check for array types
|
||||
if type_str_trimmed.starts_with('[]') {
|
||||
elem_type := type_str_trimmed.all_after('[]')
|
||||
return Array{parse_type(elem_type)}
|
||||
}
|
||||
|
||||
|
||||
// Check for map types
|
||||
if type_str_trimmed.starts_with('map[') && type_str_trimmed.contains(']') {
|
||||
value_type := type_str_trimmed.all_after(']')
|
||||
return Map{parse_type(value_type)}
|
||||
}
|
||||
|
||||
|
||||
// Check for result types
|
||||
if type_str_trimmed.starts_with('!') {
|
||||
result_type := type_str_trimmed.all_after('!')
|
||||
return Result{parse_type(result_type)}
|
||||
}
|
||||
|
||||
|
||||
// If no other type matches, treat as an object/struct type
|
||||
println('Treating as object type: "${type_str_trimmed}"')
|
||||
return Object{type_str_trimmed}
|
||||
|
||||
@@ -66,15 +66,17 @@ fn find_closing_brace(content string, start_i int) ?int {
|
||||
// RETURNS:
|
||||
// string - the function block including comments, or error if not found
|
||||
pub fn get_function_from_file(file_path string, function_name string) !Function {
|
||||
content := os.read_file(file_path) or { return error('Failed to read file ${file_path}: ${err}') }
|
||||
|
||||
content := os.read_file(file_path) or {
|
||||
return error('Failed to read file ${file_path}: ${err}')
|
||||
}
|
||||
|
||||
vfile := parse_vfile(content) or { return error('Failed to parse file ${file_path}: ${err}') }
|
||||
|
||||
|
||||
if fn_obj := vfile.get_function(function_name) {
|
||||
return fn_obj
|
||||
}
|
||||
|
||||
return error('function ${function_name} not found in file ${file_path}')
|
||||
}
|
||||
|
||||
return error('function ${function_name} not found in file ${file_path}')
|
||||
}
|
||||
|
||||
// get_function_from_module searches for a function in all V files within a module
|
||||
@@ -91,15 +93,11 @@ pub fn get_function_from_module(module_path string, function_name string) !Funct
|
||||
log.error('Found ${v_files} V files in ${module_path}')
|
||||
for v_file in v_files {
|
||||
// Read the file content
|
||||
content := os.read_file(v_file) or {
|
||||
continue
|
||||
}
|
||||
|
||||
content := os.read_file(v_file) or { continue }
|
||||
|
||||
// Parse the file
|
||||
vfile := parse_vfile(content) or {
|
||||
continue
|
||||
}
|
||||
|
||||
vfile := parse_vfile(content) or { continue }
|
||||
|
||||
// Look for the function
|
||||
if fn_obj := vfile.get_function(function_name) {
|
||||
return fn_obj
|
||||
@@ -139,7 +137,7 @@ pub fn get_type_from_module(module_path string, type_name string) !string {
|
||||
|
||||
if i == -1 {
|
||||
type_import := content.split_into_lines().filter(it.contains('import')
|
||||
&& it.contains(type_name))
|
||||
&& it.contains(type_name))
|
||||
if type_import.len > 0 {
|
||||
log.debug('debugzoooo')
|
||||
mod := type_import[0].trim_space().trim_string_left('import ').all_before(' ')
|
||||
|
||||
Reference in New Issue
Block a user