...
This commit is contained in:
58
examples/core/code/code_parser.vsh
Executable file
58
examples/core/code/code_parser.vsh
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import incubaid.herolib.core.code
|
||||||
|
import incubaid.herolib.ui.console
|
||||||
|
import os
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
console.print_header('Code Parser Example - lib/core/pathlib Analysis')
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
pathlib_dir := os.home_dir() + '/code/github/incubaid/herolib/lib/core/pathlib'
|
||||||
|
|
||||||
|
// Step 1: List all V files
|
||||||
|
console.print_header('1. Listing V Files')
|
||||||
|
v_files := code.list_v_files(pathlib_dir)!
|
||||||
|
for file in v_files {
|
||||||
|
console.print_item(os.base(file))
|
||||||
|
}
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Step 2: Parse and analyze each file
|
||||||
|
console.print_header('2. Parsing Files - Summary')
|
||||||
|
for v_file_path in v_files {
|
||||||
|
content := os.read_file(v_file_path)!
|
||||||
|
vfile := code.parse_vfile(content)!
|
||||||
|
|
||||||
|
console.print_item('${os.base(v_file_path)}')
|
||||||
|
console.print_item(' Module: ${vfile.mod}')
|
||||||
|
console.print_item(' Imports: ${vfile.imports.len}')
|
||||||
|
console.print_item(' Structs: ${vfile.structs().len}')
|
||||||
|
console.print_item(' Functions: ${vfile.functions().len}')
|
||||||
|
}
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Step 3: Find Path struct
|
||||||
|
console.print_header('3. Analyzing Path Struct')
|
||||||
|
path_code := code.get_type_from_module(pathlib_dir, 'Path')!
|
||||||
|
console.print_stdout(path_code)
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Step 4: List all public functions
|
||||||
|
console.print_header('4. Public Functions in pathlib')
|
||||||
|
for v_file_path in v_files {
|
||||||
|
content := os.read_file(v_file_path)!
|
||||||
|
vfile := code.parse_vfile(content)!
|
||||||
|
|
||||||
|
pub_functions := vfile.functions().filter(it.is_pub)
|
||||||
|
if pub_functions.len > 0 {
|
||||||
|
console.print_item('From ${os.base(v_file_path)}:')
|
||||||
|
for f in pub_functions {
|
||||||
|
console.print_item(' ${f.name}() -> ${f.result.typ.symbol()}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
console.print_green('✓ Analysis completed!')
|
||||||
|
}
|
||||||
339
examples/core/flows/runner_test.vsh
Executable file
339
examples/core/flows/runner_test.vsh
Executable file
@@ -0,0 +1,339 @@
|
|||||||
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import incubaid.herolib.core.flows
|
||||||
|
import incubaid.herolib.core.redisclient
|
||||||
|
import incubaid.herolib.ui.console
|
||||||
|
import incubaid.herolib.data.ourtime
|
||||||
|
import time
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut cons := console.new()
|
||||||
|
|
||||||
|
console.print_header('Flow Runner Test Suite')
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Test 1: Basic Flow Execution
|
||||||
|
console.print_item('Test 1: Basic Flow with Successful Steps')
|
||||||
|
test_basic_flow()!
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Test 2: Error Handling
|
||||||
|
console.print_item('Test 2: Error Handling with Error Steps')
|
||||||
|
test_error_handling()!
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Test 3: Multiple Next Steps
|
||||||
|
console.print_item('Test 3: Multiple Next Steps')
|
||||||
|
test_multiple_next_steps()!
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Test 4: Redis State Retrieval
|
||||||
|
console.print_item('Test 4: Redis State Retrieval and JSON')
|
||||||
|
test_redis_state()!
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
// Test 5: Complex Flow Chain
|
||||||
|
console.print_item('Test 5: Complex Flow Chain')
|
||||||
|
test_complex_flow()!
|
||||||
|
console.print_lf(1)
|
||||||
|
|
||||||
|
console.print_header('All Tests Completed Successfully!')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_basic_flow() ! {
|
||||||
|
mut redis := redisclient.core_get()!
|
||||||
|
redis.flushdb()!
|
||||||
|
|
||||||
|
mut coordinator := flows.new(
|
||||||
|
name: 'test_basic_flow',
|
||||||
|
redis: redis,
|
||||||
|
ai: none
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 1: Initialize
|
||||||
|
mut step1 := coordinator.step_new(
|
||||||
|
name: 'initialize'
|
||||||
|
description: 'Initialize test environment'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Step 1: Initializing...')
|
||||||
|
s.context['init_time'] = ourtime.now().str()
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 2: Process
|
||||||
|
mut step2 := coordinator.step_new(
|
||||||
|
name: 'process'
|
||||||
|
description: 'Process data'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Step 2: Processing...')
|
||||||
|
s.context['processed'] = 'true'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 3: Finalize
|
||||||
|
mut step3 := coordinator.step_new(
|
||||||
|
name: 'finalize'
|
||||||
|
description: 'Finalize results'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Step 3: Finalizing...')
|
||||||
|
s.context['status'] = 'completed'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
step1.next_step_add(step2)
|
||||||
|
step2.next_step_add(step3)
|
||||||
|
|
||||||
|
coordinator.run()!
|
||||||
|
|
||||||
|
// Verify Redis state
|
||||||
|
state := coordinator.get_all_steps_state()!
|
||||||
|
assert state.len >= 3, 'Expected at least 3 steps in Redis'
|
||||||
|
|
||||||
|
for step_state in state {
|
||||||
|
assert step_state['status'] == 'success', 'Expected all steps to be successful'
|
||||||
|
}
|
||||||
|
|
||||||
|
println(' ✓ Test 1 PASSED: All steps executed successfully')
|
||||||
|
coordinator.clear_redis()!
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_error_handling() ! {
|
||||||
|
mut redis := redisclient.core_get()!
|
||||||
|
redis.flushdb()!
|
||||||
|
|
||||||
|
mut coordinator := flows.new(
|
||||||
|
name: 'test_error_flow',
|
||||||
|
redis: redis,
|
||||||
|
ai: none
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Error step
|
||||||
|
mut error_recovery := coordinator.step_new(
|
||||||
|
name: 'error_recovery'
|
||||||
|
description: 'Recover from error'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Error Step: Executing recovery...')
|
||||||
|
s.context['recovered'] = 'true'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Main step that fails
|
||||||
|
mut main_step := coordinator.step_new(
|
||||||
|
name: 'failing_step'
|
||||||
|
description: 'This step will fail'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✗ Main Step: Intentionally failing...')
|
||||||
|
return error('Simulated error for testing')
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
main_step.error_step_add(error_recovery)
|
||||||
|
|
||||||
|
// Run and expect error
|
||||||
|
coordinator.run() or {
|
||||||
|
println(' ✓ Error caught as expected: ${err.msg()}')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify error state in Redis
|
||||||
|
error_state := coordinator.get_step_state('failing_step')!
|
||||||
|
assert error_state['status'] == 'error', 'Expected step to be in error state'
|
||||||
|
|
||||||
|
recovery_state := coordinator.get_step_state('error_recovery')!
|
||||||
|
assert recovery_state['status'] == 'success', 'Expected error step to execute'
|
||||||
|
|
||||||
|
println(' ✓ Test 2 PASSED: Error handling works correctly')
|
||||||
|
coordinator.clear_redis()!
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_multiple_next_steps() ! {
|
||||||
|
mut redis := redisclient.core_get()!
|
||||||
|
redis.flushdb()!
|
||||||
|
|
||||||
|
mut coordinator := flows.new(
|
||||||
|
name: 'test_parallel_steps',
|
||||||
|
redis: redis,
|
||||||
|
ai: none
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Parent step
|
||||||
|
mut parent := coordinator.step_new(
|
||||||
|
name: 'parent_step'
|
||||||
|
description: 'Parent step with multiple children'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Parent Step: Executing...')
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Child steps
|
||||||
|
mut child1 := coordinator.step_new(
|
||||||
|
name: 'child_step_1'
|
||||||
|
description: 'First child'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Child Step 1: Executing...')
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
mut child2 := coordinator.step_new(
|
||||||
|
name: 'child_step_2'
|
||||||
|
description: 'Second child'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Child Step 2: Executing...')
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
mut child3 := coordinator.step_new(
|
||||||
|
name: 'child_step_3'
|
||||||
|
description: 'Third child'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Child Step 3: Executing...')
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Add multiple next steps
|
||||||
|
parent.next_step_add(child1)
|
||||||
|
parent.next_step_add(child2)
|
||||||
|
parent.next_step_add(child3)
|
||||||
|
|
||||||
|
coordinator.run()!
|
||||||
|
|
||||||
|
// Verify all steps executed
|
||||||
|
all_states := coordinator.get_all_steps_state()!
|
||||||
|
assert all_states.len >= 4, 'Expected 4 steps to execute'
|
||||||
|
|
||||||
|
println(' ✓ Test 3 PASSED: Multiple next steps executed sequentially')
|
||||||
|
coordinator.clear_redis()!
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_redis_state() ! {
|
||||||
|
mut redis := redisclient.core_get()!
|
||||||
|
redis.flushdb()!
|
||||||
|
|
||||||
|
mut coordinator := flows.new(
|
||||||
|
name: 'test_redis_state',
|
||||||
|
redis: redis,
|
||||||
|
ai: none
|
||||||
|
)!
|
||||||
|
|
||||||
|
mut step1 := coordinator.step_new(
|
||||||
|
name: 'redis_test_step'
|
||||||
|
description: 'Test Redis state storage'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Executing step with context...')
|
||||||
|
s.context['user'] = 'test_user'
|
||||||
|
s.context['action'] = 'test_action'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
coordinator.run()!
|
||||||
|
|
||||||
|
// Retrieve state from Redis
|
||||||
|
step_state := coordinator.get_step_state('redis_test_step')!
|
||||||
|
|
||||||
|
println(' Step state in Redis:')
|
||||||
|
for key, value in step_state {
|
||||||
|
println(' ${key}: ${value}')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify fields
|
||||||
|
assert step_state['name'] == 'redis_test_step', 'Step name mismatch'
|
||||||
|
assert step_state['status'] == 'success', 'Step status should be success'
|
||||||
|
assert step_state['description'] == 'Test Redis state storage', 'Description mismatch'
|
||||||
|
|
||||||
|
// Verify JSON is stored
|
||||||
|
if json_data := step_state['json'] {
|
||||||
|
println(' ✓ JSON data stored in Redis: ${json_data[0..50]}...')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify log count
|
||||||
|
logs_count := step_state['logs_count'] or { '0' }
|
||||||
|
println(' ✓ Logs count: ${logs_count}')
|
||||||
|
|
||||||
|
println(' ✓ Test 4 PASSED: Redis state correctly stored and retrieved')
|
||||||
|
coordinator.clear_redis()!
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_complex_flow() ! {
|
||||||
|
mut redis := redisclient.core_get()!
|
||||||
|
redis.flushdb()!
|
||||||
|
|
||||||
|
mut coordinator := flows.new(
|
||||||
|
name: 'test_complex_flow',
|
||||||
|
redis: redis,
|
||||||
|
ai: none
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 1: Validate
|
||||||
|
mut validate := coordinator.step_new(
|
||||||
|
name: 'validate_input'
|
||||||
|
description: 'Validate input parameters'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Validating input...')
|
||||||
|
s.context['validated'] = 'true'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 2: Transform (next step after validate)
|
||||||
|
mut transform := coordinator.step_new(
|
||||||
|
name: 'transform_data'
|
||||||
|
description: 'Transform input data'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Transforming data...')
|
||||||
|
s.context['transformed'] = 'true'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 3a: Save to DB (next step after transform)
|
||||||
|
mut save_db := coordinator.step_new(
|
||||||
|
name: 'save_to_database'
|
||||||
|
description: 'Save data to database'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Saving to database...')
|
||||||
|
s.context['saved'] = 'true'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 3b: Send notification (next step after transform)
|
||||||
|
mut notify := coordinator.step_new(
|
||||||
|
name: 'send_notification'
|
||||||
|
description: 'Send notification'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Sending notification...')
|
||||||
|
s.context['notified'] = 'true'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Step 4: Cleanup (final step)
|
||||||
|
mut cleanup := coordinator.step_new(
|
||||||
|
name: 'cleanup'
|
||||||
|
description: 'Cleanup resources'
|
||||||
|
f: fn (mut s flows.Step) ! {
|
||||||
|
println(' ✓ Cleaning up...')
|
||||||
|
s.context['cleaned'] = 'true'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Build the flow chain
|
||||||
|
validate.next_step_add(transform)
|
||||||
|
transform.next_step_add(save_db)
|
||||||
|
transform.next_step_add(notify)
|
||||||
|
save_db.next_step_add(cleanup)
|
||||||
|
notify.next_step_add(cleanup)
|
||||||
|
|
||||||
|
coordinator.run()!
|
||||||
|
|
||||||
|
// Verify all steps executed
|
||||||
|
all_states := coordinator.get_all_steps_state()!
|
||||||
|
println(' Total steps executed: ${all_states.len}')
|
||||||
|
|
||||||
|
for state in all_states {
|
||||||
|
name := state['name'] or { 'unknown' }
|
||||||
|
status := state['status'] or { 'unknown' }
|
||||||
|
duration := state['duration'] or { '0' }
|
||||||
|
println(' - ${name}: ${status} (${duration}ms)')
|
||||||
|
}
|
||||||
|
|
||||||
|
assert all_states.len >= 5, 'Expected at least 5 steps'
|
||||||
|
|
||||||
|
println(' ✓ Test 5 PASSED: Complex flow executed successfully')
|
||||||
|
coordinator.clear_redis()!
|
||||||
|
}
|
||||||
@@ -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
|
result Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub type Value = string
|
pub type Value = string
|
||||||
|
|||||||
@@ -19,17 +19,30 @@ pub mut:
|
|||||||
current_step string // links to steps dict
|
current_step string // links to steps dict
|
||||||
steps map[string]&Step
|
steps map[string]&Step
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
ai aiclient.AIClient
|
ai ?aiclient.AIClient
|
||||||
redis ?&redisclient.Redis
|
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{
|
return Coordinator{
|
||||||
|
name: args.name
|
||||||
logger: logger.new(path: '/tmp/flowlogger')!
|
logger: logger.new(path: '/tmp/flowlogger')!
|
||||||
ai: aiclient.new()!
|
ai: ai
|
||||||
|
redis: args.redis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@[params]
|
@[params]
|
||||||
pub struct StepNewArgs {
|
pub struct StepNewArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
@@ -37,8 +50,8 @@ pub mut:
|
|||||||
description string
|
description string
|
||||||
f fn (mut s Step) ! @[required]
|
f fn (mut s Step) ! @[required]
|
||||||
context map[string]string
|
context map[string]string
|
||||||
error_steps []Step
|
error_steps []string
|
||||||
next_steps []Step
|
next_steps []string
|
||||||
error string
|
error string
|
||||||
params paramsparser.Params
|
params paramsparser.Params
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub fn (mut c Coordinator) run() ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run a single step, including error and next steps
|
// 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
|
// Initialize step
|
||||||
step.status = .running
|
step.status = .running
|
||||||
step.started_at = ostime.now().unix_milli()
|
step.started_at = ostime.now().unix_milli()
|
||||||
@@ -17,8 +17,8 @@ pub fn (mut c Coordinator) run_step(mut step Step) ! {
|
|||||||
|
|
||||||
// Log step start
|
// Log step start
|
||||||
step.log(
|
step.log(
|
||||||
level: .info
|
logtype: .stdout
|
||||||
message: 'Step "${step.name}" started'
|
log: 'Step "${step.name}" started'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Execute main step function
|
// Execute main step function
|
||||||
@@ -30,13 +30,16 @@ pub fn (mut c Coordinator) run_step(mut step Step) ! {
|
|||||||
step.store_redis()!
|
step.store_redis()!
|
||||||
|
|
||||||
step.log(
|
step.log(
|
||||||
level: .error
|
logtype: .error
|
||||||
message: 'Step "${step.name}" failed: ${err.msg()}'
|
log: 'Step "${step.name}" failed: ${err.msg()}'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Run error steps if any
|
// Run error steps if any
|
||||||
if step.error_steps.len > 0 {
|
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)!
|
c.run_step(mut error_step)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,13 +53,16 @@ pub fn (mut c Coordinator) run_step(mut step Step) ! {
|
|||||||
step.store_redis()!
|
step.store_redis()!
|
||||||
|
|
||||||
step.log(
|
step.log(
|
||||||
level: .info
|
logtype: .stdout
|
||||||
message: 'Step "${step.name}" completed successfully'
|
log: 'Step "${step.name}" completed successfully'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Run next steps if any
|
// Run next steps if any
|
||||||
if step.next_steps.len > 0 {
|
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)!
|
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
|
// Get step state from redis
|
||||||
pub fn (c Coordinator) get_step_state(step_name string) !map[string]string {
|
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 redis.hgetall('flow:${c.name}:${step_name}')!
|
||||||
}
|
}
|
||||||
return error('Redis not configured')
|
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)
|
// Get all steps state from redis (for UI dashboard)
|
||||||
pub fn (c Coordinator) get_all_steps_state() ![]map[string]string {
|
pub fn (c Coordinator) get_all_steps_state() ![]map[string]string {
|
||||||
mut states := []map[string]string{}
|
mut states := []map[string]string{}
|
||||||
if redis := c.redis {
|
if mut redis := c.redis {
|
||||||
pattern := 'flow:${c.name}:*'
|
pattern := 'flow:${c.name}:*'
|
||||||
keys := redis.keys(pattern)!
|
keys := redis.keys(pattern)!
|
||||||
for key in keys {
|
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() ! {
|
pub fn (c Coordinator) clear_redis() ! {
|
||||||
if redis := c.redis {
|
if mut redis := c.redis {
|
||||||
pattern := 'flow:${c.name}:*'
|
pattern := 'flow:${c.name}:*'
|
||||||
keys := redis.keys(pattern)!
|
keys := redis.keys(pattern)!
|
||||||
for key in keys {
|
for key in keys {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ module flows
|
|||||||
|
|
||||||
import incubaid.herolib.data.paramsparser
|
import incubaid.herolib.data.paramsparser
|
||||||
import incubaid.herolib.core.logger
|
import incubaid.herolib.core.logger
|
||||||
|
import time as ostime
|
||||||
|
import json
|
||||||
|
|
||||||
pub enum StepStatus {
|
pub enum StepStatus {
|
||||||
pending
|
pending
|
||||||
@@ -21,23 +23,71 @@ pub mut:
|
|||||||
description string
|
description string
|
||||||
main_step fn (mut s Step) ! @[required]
|
main_step fn (mut s Step) ! @[required]
|
||||||
context map[string]string
|
context map[string]string
|
||||||
error_steps []Step
|
error_steps []string
|
||||||
next_steps []Step
|
next_steps []string
|
||||||
error string
|
error string
|
||||||
logs []logger.LogItem
|
logs []logger.LogItem
|
||||||
params paramsparser.Params
|
params paramsparser.Params
|
||||||
coordinator &Coordinator
|
coordinator &Coordinator
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut s Step) error_step_add(s2 Step) {
|
pub fn (mut s Step) error_step_add(s2 &Step) {
|
||||||
s.error_steps << s2
|
s.error_steps << s2.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut s Step) next_step_add(s2 Step) {
|
pub fn (mut s Step) next_step_add(s2 &Step) {
|
||||||
s.next_steps << s2
|
s.next_steps << s2.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut s Step) log(l logger.LogItemArgs) ! {
|
pub fn (mut s Step) log(l logger.LogItemArgs) ! {
|
||||||
mut l2 := s.coordinator.logger.log(l)!
|
mut l2 := s.coordinator.logger.log(l)!
|
||||||
s.logs << l2
|
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