This commit is contained in:
2025-11-23 04:43:08 +01:00
parent 61a3677883
commit 1d4770aca5
9 changed files with 490 additions and 274 deletions

View File

@@ -1,3 +0,0 @@
module code
pub type Value = string

View File

@@ -1,247 +0,0 @@
# Code Review and Improvement Plan for HeroLib Code Module
## Overview
The HeroLib `code` module provides utilities for parsing and generating V language code. It's designed to be a lightweight alternative to `v.ast` for code analysis and generation across multiple languages. While the module has good foundational structure, there are several areas that need improvement.
## Issues Identified
### 1. Incomplete TypeScript Generation Support
- The `typescript()` method exists in some models but lacks comprehensive implementation
- Missing TypeScript generation for complex types (arrays, maps, results)
- No TypeScript interface generation for structs
### 2. Template System Issues
- Some templates are empty (e.g., `templates/function/method.py`, `templates/comment/comment.py`)
- Template usage is inconsistent across the codebase
- No clear separation between V and other language templates
### 3. Missing Parser Documentation Examples
- README.md mentions codeparser but doesn't show how to use the parser from this module
- No clear examples of parsing V files or modules
### 4. Incomplete Type Handling
- The `parse_type` function doesn't handle all V language types comprehensively
- Missing support for function types, sum types, and complex generics
- No handling of optional types (`?Type`)
### 5. Code Structure and Consistency
- Some functions lack proper error handling
- Inconsistent naming conventions in test files
- Missing documentation for several key functions
## Improvement Plan
### 1. Complete TypeScript Generation Implementation
**What needs to be done:**
- Implement comprehensive TypeScript generation in `model_types.v`
- Add TypeScript generation for all type variants
- Create proper TypeScript interface generation in `model_struct.v`
**Specific fixes:**
```v
// In model_types.v, improve the typescript() method:
pub fn (t Type) typescript() string {
return match t {
Map { 'Record<string, ${t.typ.typescript()}>' }
Array { '${t.typ.typescript()}[]' }
Object { t.name }
Result { 'Promise<${t.typ.typescript()}>' } // Better representation for async operations
Boolean { 'boolean' }
Integer { 'number' }
Alias { t.name }
String { 'string' }
Function { '(...args: any[]) => any' } // More appropriate for function types
Void { 'void' }
}
}
// In model_struct.v, improve the typescript() method:
pub fn (s Struct) typescript() string {
name := texttools.pascal_case(s.name)
fields := s.fields.map(it.typescript()).join('\n ')
return 'export interface ${name} {\n ${fields}\n}'
}
```
### 2. Fix Template System
**What needs to be done:**
- Remove empty Python template files
- Ensure all templates are properly implemented
- Add template support for other languages
**Specific fixes:**
- Delete `templates/function/method.py` and `templates/comment/comment.py` if they're not needed
- Add proper TypeScript templates for struct and interface generation
- Create consistent template naming conventions
### 3. Improve Parser Documentation
**What needs to be done:**
- Add clear examples in README.md showing how to use the parser
- Document the parsing functions with practical examples
**Specific fixes:**
Add to README.md:
```markdown
## Parsing V Code
The code module provides utilities to parse V code into structured models:
```v
import incubaid.herolib.core.code
// Parse a V file
content := os.read_file('example.v') or { panic(err) }
vfile := code.parse_vfile(content) or { panic(err) }
// Access parsed information
println('Module: ${vfile.mod}')
println('Number of functions: ${vfile.functions().len}')
println('Number of structs: ${vfile.structs().len}')
// Parse individual components
function := code.parse_function(fn_code_string) or { panic(err) }
struct_ := code.parse_struct(struct_code_string) or { panic(err) }
```
### 4. Complete Type Handling
**What needs to be done:**
- Extend `parse_type` to handle more complex V types
- Add support for optional types (`?Type`)
- Improve generic type parsing
**Specific fixes:**
```v
// In model_types.v, enhance parse_type function:
pub fn parse_type(type_str string) Type {
mut type_str_trimmed := type_str.trim_space()
// Handle optional types
if type_str_trimmed.starts_with('?') {
return Optional{parse_type(type_str_trimmed.all_after('?'))}
}
// Handle function types
if type_str_trimmed.starts_with('fn ') {
// Parse function signature
return Function{}
}
// Handle sum types
if type_str_trimmed.contains('|') {
types := type_str_trimmed.split('|').map(parse_type(it.trim_space()))
return Sum{types}
}
// Existing parsing logic...
}
```
### 5. Code Structure Improvements
**What needs to be done:**
- Add proper error handling to all parsing functions
- Standardize naming conventions
- Improve documentation consistency
**Specific fixes:**
- Add error checking in `parse_function`, `parse_struct`, and other parsing functions
- Ensure all public functions have clear documentation comments
- Standardize test function names
## Module Generation to Other Languages
### Current Implementation
The current code shows basic TypeScript generation support, but it's incomplete. The generation should:
1. **Support multiple languages**: The code structure allows for multi-language generation, but only TypeScript has partial implementation
2. **Use templates consistently**: All language generation should use the template system
3. **Separate language-specific code**: Each language should have its own generation module
### What Needs to Move to Other Modules
**TypeScript Generation Module:**
- Move all TypeScript-specific generation code to a new `typescript` module
- Create TypeScript templates for structs, interfaces, and functions
- Add proper TypeScript formatting support
**Example Structure:**
```
lib/core/code/
├── model_types.v # Core type models (language agnostic)
├── model_struct.v # Core struct/function models (language agnostic)
└── typescript/ # TypeScript-specific generation
├── generator.v # TypeScript generation logic
└── templates/ # TypeScript templates
```
### Parser Usage Examples (to add to README.md)
```v
// Parse a V file into a structured representation
content := os.read_file('mymodule/example.v') or { panic(err) }
vfile := code.parse_vfile(content)!
// Extract all functions
functions := vfile.functions()
println('Found ${functions.len} functions')
// Extract all structs
structs := vfile.structs()
for s in structs {
println('Struct: ${s.name}')
for field in s.fields {
println(' Field: ${field.name} (${field.typ.symbol()})')
}
}
// Find a specific function
if greet_fn := vfile.get_function('greet') {
println('Found function: ${greet_fn.name}')
println('Parameters: ${greet_fn.params.map(it.name)}')
println('Returns: ${greet_fn.result.typ.symbol()}')
}
// Parse a function from string
fn_code := '
pub fn add(a int, b int) int {
return a + b
}
'
function := code.parse_function(fn_code)!
println('Parsed function: ${function.name}')
```
## Summary of Required Actions
1. **Implement complete TypeScript generation** across all model types
2. **Remove empty template files** and organize templates properly
3. **Enhance type parsing** to handle optional types, function types, and sum types
4. **Add comprehensive parser documentation** with practical examples to README.md
5. **Create language-specific generation modules** to separate concerns
6. **Improve error handling** in all parsing functions
7. **Standardize documentation and naming** conventions across the module
These improvements will make the code module more robust, easier to use, and better prepared for multi-language code generation.

View File

@@ -6,4 +6,4 @@ pub struct Example {
result Value
}
// pub type Value = string
pub type Value = string

View File

@@ -19,17 +19,30 @@ pub mut:
current_step string // links to steps dict
steps map[string]&Step
logger logger.Logger
ai aiclient.AIClient
ai ?aiclient.AIClient
redis ?&redisclient.Redis
}
pub fn new() !Coordinator {
@[params]
pub struct CoordinatorArgs {
pub mut:
name string @[required]
redis ?&redisclient.Redis
ai ?aiclient.AIClient = none
}
pub fn new(args CoordinatorArgs) !Coordinator {
ai := args.ai
return Coordinator{
name: args.name
logger: logger.new(path: '/tmp/flowlogger')!
ai: aiclient.new()!
ai: ai
redis: args.redis
}
}
@[params]
pub struct StepNewArgs {
pub mut:
@@ -37,8 +50,8 @@ pub mut:
description string
f fn (mut s Step) ! @[required]
context map[string]string
error_steps []Step
next_steps []Step
error_steps []string
next_steps []string
error string
params paramsparser.Params
}

View File

@@ -9,7 +9,7 @@ pub fn (mut c Coordinator) run() ! {
}
// Run a single step, including error and next steps
pub fn (mut c Coordinator) run_step(mut step Step) ! {
pub fn (mut c Coordinator) run_step(mut step &Step) ! {
// Initialize step
step.status = .running
step.started_at = ostime.now().unix_milli()
@@ -17,8 +17,8 @@ pub fn (mut c Coordinator) run_step(mut step Step) ! {
// Log step start
step.log(
level: .info
message: 'Step "${step.name}" started'
logtype: .stdout
log: 'Step "${step.name}" started'
)!
// Execute main step function
@@ -30,13 +30,16 @@ pub fn (mut c Coordinator) run_step(mut step Step) ! {
step.store_redis()!
step.log(
level: .error
message: 'Step "${step.name}" failed: ${err.msg()}'
logtype: .error
log: 'Step "${step.name}" failed: ${err.msg()}'
)!
// Run error steps if any
if step.error_steps.len > 0 {
for mut error_step in step.error_steps {
for error_step_name in step.error_steps {
mut error_step := c.steps[error_step_name] or {
return error('Error step "${error_step_name}" not found in coordinator "${c.name}"')
}
c.run_step(mut error_step)!
}
}
@@ -50,13 +53,16 @@ pub fn (mut c Coordinator) run_step(mut step Step) ! {
step.store_redis()!
step.log(
level: .info
message: 'Step "${step.name}" completed successfully'
logtype: .stdout
log: 'Step "${step.name}" completed successfully'
)!
// Run next steps if any
if step.next_steps.len > 0 {
for mut next_step in step.next_steps {
for next_step_name in step.next_steps {
mut next_step := c.steps[next_step_name] or {
return error('Next step "${next_step_name}" not found in coordinator "${c.name}"')
}
c.run_step(mut next_step)!
}
}
@@ -64,7 +70,7 @@ pub fn (mut c Coordinator) run_step(mut step Step) ! {
// Get step state from redis
pub fn (c Coordinator) get_step_state(step_name string) !map[string]string {
if redis := c.redis {
if mut redis := c.redis {
return redis.hgetall('flow:${c.name}:${step_name}')!
}
return error('Redis not configured')
@@ -73,7 +79,7 @@ pub fn (c Coordinator) get_step_state(step_name string) !map[string]string {
// Get all steps state from redis (for UI dashboard)
pub fn (c Coordinator) get_all_steps_state() ![]map[string]string {
mut states := []map[string]string{}
if redis := c.redis {
if mut redis := c.redis {
pattern := 'flow:${c.name}:*'
keys := redis.keys(pattern)!
for key in keys {
@@ -85,7 +91,7 @@ pub fn (c Coordinator) get_all_steps_state() ![]map[string]string {
}
pub fn (c Coordinator) clear_redis() ! {
if redis := c.redis {
if mut redis := c.redis {
pattern := 'flow:${c.name}:*'
keys := redis.keys(pattern)!
for key in keys {

View File

@@ -2,6 +2,8 @@ module flows
import incubaid.herolib.data.paramsparser
import incubaid.herolib.core.logger
import time as ostime
import json
pub enum StepStatus {
pending
@@ -21,23 +23,71 @@ pub mut:
description string
main_step fn (mut s Step) ! @[required]
context map[string]string
error_steps []Step
next_steps []Step
error_steps []string
next_steps []string
error string
logs []logger.LogItem
params paramsparser.Params
coordinator &Coordinator
}
pub fn (mut s Step) error_step_add(s2 Step) {
s.error_steps << s2
pub fn (mut s Step) error_step_add(s2 &Step) {
s.error_steps << s2.name
}
pub fn (mut s Step) next_step_add(s2 Step) {
s.next_steps << s2
pub fn (mut s Step) next_step_add(s2 &Step) {
s.next_steps << s2.name
}
pub fn (mut s Step) log(l logger.LogItemArgs) ! {
mut l2 := s.coordinator.logger.log(l)!
s.logs << l2
}
pub fn (mut s Step) store_redis() ! {
if mut redis := s.coordinator.redis {
key := 'flow:${s.coordinator.name}:${s.name}'
redis.hset(key, 'name', s.name)!
redis.hset(key, 'description', s.description)!
redis.hset(key, 'status', s.status.str())!
redis.hset(key, 'error', s.error_msg)!
redis.hset(key, 'logs_count', s.logs.len.str())!
redis.hset(key, 'started_at', s.started_at.str())!
redis.hset(key, 'finished_at', s.finished_at.str())!
redis.hset(key, 'json', s.to_json()!)!
// Set expiration to 24 hours
redis.expire(key, 86400)!
}
}
@[json: id]
pub struct StepJSON {
pub:
name string
description string
status string
error string
logs_count int
started_at i64
finished_at i64
duration i64 // milliseconds
}
pub fn (s Step) to_json() !string {
duration := s.finished_at - s.started_at
step_json := StepJSON{
name: s.name
description: s.description
status: s.status.str()
error: s.error_msg
logs_count: s.logs.len
started_at: s.started_at
finished_at: s.finished_at
duration: duration
}
return json.encode(step_json)
}