...
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
module code
|
||||
|
||||
pub type Value = string
|
||||
@@ -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.
|
||||
@@ -6,4 +6,4 @@ pub struct Example {
|
||||
result Value
|
||||
}
|
||||
|
||||
// pub type Value = string
|
||||
pub type Value = string
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user