move codemodel to core as code and fix
This commit is contained in:
@@ -1,148 +0,0 @@
|
|||||||
# Code Model
|
|
||||||
|
|
||||||
A set of models that represent code structures, such as structs, functions, imports, and constants. The motivation behind this module is to provide a more generic and lighter alternative to v.ast code models, that can be used for code parsing and code generation across multiple languages.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Struct Modeling**: Complete struct representation including:
|
|
||||||
- Fields with types, visibility, and mutability
|
|
||||||
- Embedded structs
|
|
||||||
- Generic type support
|
|
||||||
- Attributes
|
|
||||||
- Documentation comments
|
|
||||||
|
|
||||||
- **Function Modeling**: Comprehensive function support with:
|
|
||||||
- Parameters and return types
|
|
||||||
- Receiver methods
|
|
||||||
- Optional and result types
|
|
||||||
- Function body content
|
|
||||||
- Visibility modifiers
|
|
||||||
|
|
||||||
- **Type System**: Rich type representation including:
|
|
||||||
- Basic types
|
|
||||||
- Reference types
|
|
||||||
- Arrays and maps
|
|
||||||
- Optional and result types
|
|
||||||
- Mutable and shared types
|
|
||||||
|
|
||||||
- **Code Organization**:
|
|
||||||
- Import statements with module and type specifications
|
|
||||||
- Constants (both single and grouped)
|
|
||||||
- Custom code blocks for specialized content
|
|
||||||
- Documentation through single and multi-line comments
|
|
||||||
|
|
||||||
## Using Codemodel
|
|
||||||
|
|
||||||
The codemodel module provides a set of types and utilities for working with code structures. Here are some examples of how to use the module:
|
|
||||||
|
|
||||||
### Working with Functions
|
|
||||||
|
|
||||||
```v
|
|
||||||
// Parse a function definition
|
|
||||||
fn_def := 'pub fn (mut app App) process() !string'
|
|
||||||
function := codemodel.parse_function(fn_def)!
|
|
||||||
println(function.name) // prints: process
|
|
||||||
println(function.receiver.name) // prints: app
|
|
||||||
println(function.result.typ.symbol) // prints: string
|
|
||||||
|
|
||||||
// Create a function model
|
|
||||||
my_fn := Function{
|
|
||||||
name: 'add'
|
|
||||||
is_pub: true
|
|
||||||
params: [
|
|
||||||
Param{
|
|
||||||
name: 'x'
|
|
||||||
typ: Type{symbol: 'int'}
|
|
||||||
},
|
|
||||||
Param{
|
|
||||||
name: 'y'
|
|
||||||
typ: Type{symbol: 'int'}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
result: Result{
|
|
||||||
typ: Type{symbol: 'int'}
|
|
||||||
}
|
|
||||||
body: 'return x + y'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Working with Imports
|
|
||||||
|
|
||||||
```v
|
|
||||||
// Parse an import statement
|
|
||||||
import_def := 'import os { exists }'
|
|
||||||
imp := codemodel.parse_import(import_def)
|
|
||||||
println(imp.mod) // prints: os
|
|
||||||
println(imp.types) // prints: ['exists']
|
|
||||||
|
|
||||||
// Create an import model
|
|
||||||
my_import := Import{
|
|
||||||
mod: 'json'
|
|
||||||
types: ['encode', 'decode']
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Working with Constants
|
|
||||||
|
|
||||||
```v
|
|
||||||
// Parse constant definitions
|
|
||||||
const_def := 'const max_size = 1000'
|
|
||||||
constant := codemodel.parse_const(const_def)!
|
|
||||||
println(constant.name) // prints: max_size
|
|
||||||
println(constant.value) // prints: 1000
|
|
||||||
|
|
||||||
// Parse grouped constants
|
|
||||||
const_block := 'const (
|
|
||||||
pi = 3.14
|
|
||||||
e = 2.718
|
|
||||||
)'
|
|
||||||
constants := codemodel.parse_consts(const_block)!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Working with Types
|
|
||||||
|
|
||||||
The module provides rich type modeling capabilities:
|
|
||||||
|
|
||||||
```v
|
|
||||||
// Basic type
|
|
||||||
basic := Type{
|
|
||||||
symbol: 'string'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array type
|
|
||||||
array := Type{
|
|
||||||
symbol: 'string'
|
|
||||||
is_array: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional type
|
|
||||||
optional := Type{
|
|
||||||
symbol: 'int'
|
|
||||||
is_optional: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result type
|
|
||||||
result := Type{
|
|
||||||
symbol: 'string'
|
|
||||||
is_result: true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Generation
|
|
||||||
|
|
||||||
The codemodel types can be used as intermediate structures for code generation. For example, generating documentation:
|
|
||||||
|
|
||||||
```v
|
|
||||||
mut doc := ''
|
|
||||||
|
|
||||||
// Document a struct
|
|
||||||
for field in my_struct.fields {
|
|
||||||
doc += '- ${field.name}: ${field.typ.symbol}'
|
|
||||||
if field.description != '' {
|
|
||||||
doc += ' // ${field.description}'
|
|
||||||
}
|
|
||||||
doc += '\n'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The codemodel module provides a foundation for building tools that need to work with code structures, whether for analysis, transformation, or generation purposes.
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
module codemodel
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
// Code is a list of statements
|
|
||||||
// pub type Code = []CodeItem
|
|
||||||
|
|
||||||
pub type CodeItem = Alias | Comment | CustomCode | Function | Import | Struct | Sumtype
|
|
||||||
|
|
||||||
// item for adding custom code in
|
|
||||||
pub struct CustomCode {
|
|
||||||
pub:
|
|
||||||
text string
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Comment {
|
|
||||||
pub:
|
|
||||||
text string
|
|
||||||
is_multi bool
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Struct {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
mod string
|
|
||||||
is_pub bool
|
|
||||||
embeds []Struct @[str: skip]
|
|
||||||
generics map[string]string @[str: skip]
|
|
||||||
attrs []Attribute
|
|
||||||
fields []StructField
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Sumtype {
|
|
||||||
pub:
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
types []Type
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StructField {
|
|
||||||
pub mut:
|
|
||||||
comments []Comment
|
|
||||||
attrs []Attribute
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
default string
|
|
||||||
is_pub bool
|
|
||||||
is_mut bool
|
|
||||||
is_ref bool
|
|
||||||
anon_struct Struct @[str: skip] // sometimes fields may hold anonymous structs
|
|
||||||
typ Type
|
|
||||||
structure Struct @[str: skip]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Attribute {
|
|
||||||
pub:
|
|
||||||
name string // [name]
|
|
||||||
has_arg bool
|
|
||||||
arg string // [name: arg]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Function {
|
|
||||||
pub:
|
|
||||||
name string
|
|
||||||
receiver Param
|
|
||||||
is_pub bool
|
|
||||||
mod string
|
|
||||||
pub mut:
|
|
||||||
description string
|
|
||||||
params []Param
|
|
||||||
body string
|
|
||||||
result Result
|
|
||||||
has_return bool
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_function(code_ string) !Function {
|
|
||||||
mut code := code_.trim_space()
|
|
||||||
is_pub := code.starts_with('pub ')
|
|
||||||
if is_pub {
|
|
||||||
code = code.trim_string_left('pub ').trim_space()
|
|
||||||
}
|
|
||||||
|
|
||||||
is_fn := code.starts_with('fn ')
|
|
||||||
if !is_fn {
|
|
||||||
return error('invalid function format')
|
|
||||||
}
|
|
||||||
code = code.trim_string_left('fn ').trim_space()
|
|
||||||
|
|
||||||
receiver := if code.starts_with('(') {
|
|
||||||
param_str := code.all_after('(').all_before(')').trim_space()
|
|
||||||
code = code.all_after(')').trim_space()
|
|
||||||
parse_param(param_str)!
|
|
||||||
} else {
|
|
||||||
Param{}
|
|
||||||
}
|
|
||||||
|
|
||||||
name := code.all_before('(').trim_space()
|
|
||||||
code = code.trim_string_left(name).trim_space()
|
|
||||||
|
|
||||||
params_str := code.all_after('(').all_before(')')
|
|
||||||
params := if params_str.trim_space() != '' {
|
|
||||||
params_str_lst := params_str.split(',')
|
|
||||||
params_str_lst.map(parse_param(it)!)
|
|
||||||
} else {
|
|
||||||
[]Param{}
|
|
||||||
}
|
|
||||||
result := parse_result(code.all_after(')').all_before('{').replace(' ', ''))!
|
|
||||||
|
|
||||||
body := if code.contains('{') { code.all_after('{').all_before_last('}') } else { '' }
|
|
||||||
return Function{
|
|
||||||
name: name
|
|
||||||
receiver: receiver
|
|
||||||
params: params
|
|
||||||
result: result
|
|
||||||
body: body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_param(code_ string) !Param {
|
|
||||||
mut code := code_.trim_space()
|
|
||||||
is_mut := code.starts_with('mut ')
|
|
||||||
if is_mut {
|
|
||||||
code = code.trim_string_left('mut ').trim_space()
|
|
||||||
}
|
|
||||||
split := code.split(' ').filter(it != '')
|
|
||||||
if split.len != 2 {
|
|
||||||
return error('invalid param format: ${code_}')
|
|
||||||
}
|
|
||||||
return Param{
|
|
||||||
name: split[0]
|
|
||||||
typ: Type{
|
|
||||||
symbol: split[1]
|
|
||||||
}
|
|
||||||
mutable: is_mut
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_result(code_ string) !Result {
|
|
||||||
code := code_.replace(' ', '').trim_space()
|
|
||||||
|
|
||||||
return Result{
|
|
||||||
result: code_.starts_with('!')
|
|
||||||
optional: code_.starts_with('?')
|
|
||||||
typ: Type{
|
|
||||||
symbol: code.trim('!?')
|
|
||||||
is_optional: code.starts_with('?')
|
|
||||||
is_result: code.starts_with('!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Param {
|
|
||||||
pub:
|
|
||||||
required bool
|
|
||||||
mutable bool
|
|
||||||
is_shared bool
|
|
||||||
is_optional bool
|
|
||||||
description string
|
|
||||||
name string
|
|
||||||
typ Type
|
|
||||||
struct_ Struct
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Result {
|
|
||||||
pub mut:
|
|
||||||
typ Type
|
|
||||||
description string
|
|
||||||
name string
|
|
||||||
result bool // whether is result type
|
|
||||||
optional bool // whether is result type
|
|
||||||
structure Struct
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: maybe make 'is_' fields methods?
|
|
||||||
pub struct Type {
|
|
||||||
pub mut:
|
|
||||||
is_reference bool @[str: skip]
|
|
||||||
is_map bool @[str: skip]
|
|
||||||
is_array bool
|
|
||||||
is_mutable bool @[str: skip]
|
|
||||||
is_shared bool @[str: skip]
|
|
||||||
is_optional bool @[str: skip]
|
|
||||||
is_result bool @[str: skip]
|
|
||||||
symbol string
|
|
||||||
mod string @[str: skip]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct File {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
extension string
|
|
||||||
content string
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (f File) write(path string) ! {
|
|
||||||
mut fd_file := pathlib.get_file(path: '${path}/${f.name}.${f.extension}')!
|
|
||||||
fd_file.write(f.content)!
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Alias {
|
|
||||||
pub:
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
typ Type
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
module codemodel
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
import os
|
|
||||||
|
|
||||||
pub struct Module {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
files []CodeFile
|
|
||||||
misc_files []File
|
|
||||||
// model CodeFile
|
|
||||||
// methods CodeFile
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mod Module) write_v(path string, options WriteOptions) ! {
|
|
||||||
mut module_dir := pathlib.get_dir(
|
|
||||||
path: '${path}/${mod.name}'
|
|
||||||
empty: options.overwrite
|
|
||||||
)!
|
|
||||||
|
|
||||||
if !options.overwrite && module_dir.exists() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for file in mod.files {
|
|
||||||
file.write_v(module_dir.path, options)!
|
|
||||||
}
|
|
||||||
for file in mod.misc_files {
|
|
||||||
file.write(module_dir.path)!
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.format {
|
|
||||||
os.execute('v fmt -w ${module_dir.path}')
|
|
||||||
}
|
|
||||||
if options.document {
|
|
||||||
os.execute('v doc -f html -o ${module_dir.path}/docs ${module_dir.path}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
module imagemagick
|
module imagemagick
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
import freeflowuniverse.herolib.core.pathlib
|
||||||
import freeflowuniverse.herolib.data.paramsparser
|
|
||||||
import freeflowuniverse.herolib.osal
|
import freeflowuniverse.herolib.osal
|
||||||
import freeflowuniverse.herolib.ui.console
|
import freeflowuniverse.herolib.ui.console
|
||||||
|
|
||||||
@@ -64,7 +63,10 @@ pub fn installed() bool {
|
|||||||
return installed1
|
return installed1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_imagemagic(mut path pathlib.Path, mut params_ paramsparser.Params) !bool {
|
pub struct FilterImageMagic {}
|
||||||
|
|
||||||
|
fn (f FilterImageMagic) filter (path_ pathlib.Path) !bool {
|
||||||
|
mut path := path_
|
||||||
// console.print_debug(" - check $path.path")
|
// console.print_debug(" - check $path.path")
|
||||||
// console.print_debug(" ===== "+path.name_no_ext())
|
// console.print_debug(" ===== "+path.name_no_ext())
|
||||||
if path.name().starts_with('.') {
|
if path.name().starts_with('.') {
|
||||||
@@ -100,28 +102,22 @@ fn filter_imagemagic(mut path pathlib.Path, mut params_ paramsparser.Params) !bo
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executor_imagemagic(mut path pathlib.Path, mut params_ paramsparser.Params) !paramsparser.Params {
|
pub struct ExecutorImageMagic {
|
||||||
|
pub mut:
|
||||||
|
backupdir string
|
||||||
|
redo bool // if you want to check the file again, even if processed
|
||||||
|
convertpng bool // if yes will go from png to jpg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (e ExecutorImageMagic) execute(path_ pathlib.Path) ! {
|
||||||
|
mut path := path_
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
return params_
|
return params_
|
||||||
}
|
}
|
||||||
// console.print_debug(' image check ${path.path}')
|
|
||||||
mut backupdir := ''
|
|
||||||
if params_.exists('backupdir') {
|
|
||||||
backupdir = params_.get('backupdir') or { panic(error) }
|
|
||||||
}
|
|
||||||
mut image := image_new(mut path)
|
mut image := image_new(mut path)
|
||||||
mut redo := false
|
if e.backupdir.len > 0 {
|
||||||
if params_.exists('redo') {
|
image.downsize(backup: true, backup_dest: e.backupdir, e.redo: redo, convertpng: e.convertpng)!
|
||||||
redo = true
|
|
||||||
}
|
|
||||||
mut convertpng := false
|
|
||||||
if params_.exists('convertpng') {
|
|
||||||
convertpng = true
|
|
||||||
}
|
|
||||||
if backupdir.len > 0 {
|
|
||||||
image.downsize(backup: true, backup_dest: backupdir, redo: redo, convertpng: convertpng)!
|
|
||||||
} else {
|
} else {
|
||||||
image.downsize(redo: redo, convertpng: convertpng)!
|
image.downsize(redo: redo, convertpng: convertpng)!
|
||||||
}
|
}
|
||||||
return params_
|
}
|
||||||
}
|
|
||||||
38
lib/core/code/README.md
Normal file
38
lib/core/code/README.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Code Model
|
||||||
|
|
||||||
|
A set of models that represent code, such as structs and functions. The motivation behind this module is to provide a more generic, and lighter alternative to v.ast code models, that can be used for code parsing and code generation across multiple languages.
|
||||||
|
|
||||||
|
## Using Codemodel
|
||||||
|
|
||||||
|
While the models in this module can be used in any domain, the models here are used extensively in the modules [codeparser](../codeparser/) and codegen (under development). Below are examples on how codemodel can be used for parsing and generating code.
|
||||||
|
## Code parsing with codemodel
|
||||||
|
|
||||||
|
As shown in the example below, the codemodels returned by the parser can be used to infer information about the code written
|
||||||
|
|
||||||
|
```js
|
||||||
|
code := codeparser.parse("somedir") // code is a list of code models
|
||||||
|
|
||||||
|
num_functions := code.filter(it is Function).len
|
||||||
|
structs := code.filter(it is Struct)
|
||||||
|
println("This directory has ${num_functions} functions")
|
||||||
|
println('The directory has the structs: ${structs.map(it.name)}')
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
or can be used as intermediate structures to serialize code into some other format:
|
||||||
|
|
||||||
|
```js
|
||||||
|
code_md := ''
|
||||||
|
|
||||||
|
// describes the struct in markdown format
|
||||||
|
for struct in structs {
|
||||||
|
code_md += '# ${struct.name}'
|
||||||
|
code_md += 'Type: ${struct.typ.symbol}'
|
||||||
|
code_md += '## Fields:'
|
||||||
|
for field in struct.fields {
|
||||||
|
code_md += '- ${field.name}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The [openrpc/docgen](../openrpc/docgen/) module demonstrates a good use case, where codemodels are serialized into JSON schema's, to generate an OpenRPC description document from a client in v.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
module codemodel
|
module code
|
||||||
|
|
||||||
pub struct Example {
|
pub struct Example {
|
||||||
function Function
|
function Function
|
||||||
19
lib/core/code/file.v
Normal file
19
lib/core/code/file.v
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module code
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.core.pathlib
|
||||||
|
|
||||||
|
pub interface IFile {
|
||||||
|
write(string, WriteOptions) !
|
||||||
|
}
|
||||||
|
|
||||||
|
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)!
|
||||||
|
}
|
||||||
30
lib/core/code/folder.v
Normal file
30
lib/core/code/folder.v
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
module code
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.core.pathlib
|
||||||
|
|
||||||
|
pub interface IFolder {
|
||||||
|
name string
|
||||||
|
files []IFile
|
||||||
|
write(string, WriteOptions) !
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Folder {
|
||||||
|
pub:
|
||||||
|
name string
|
||||||
|
files []IFile
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (f Folder) write(path string, options WriteOptions) ! {
|
||||||
|
mut dir := pathlib.get_dir(
|
||||||
|
path: '${path}/${f.name}'
|
||||||
|
empty: options.overwrite
|
||||||
|
)!
|
||||||
|
|
||||||
|
if !options.overwrite && dir.exists() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in f.files {
|
||||||
|
file.write(dir.path, options)!
|
||||||
|
}
|
||||||
|
}
|
||||||
100
lib/core/code/function.v
Normal file
100
lib/core/code/function.v
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
module code
|
||||||
|
|
||||||
|
pub struct Function {
|
||||||
|
pub:
|
||||||
|
name string @[omitempty]
|
||||||
|
receiver Param @[omitempty]
|
||||||
|
is_pub bool @[omitempty]
|
||||||
|
mod string @[omitempty]
|
||||||
|
pub mut:
|
||||||
|
summary string @[omitempty]
|
||||||
|
description string @[omitempty]
|
||||||
|
params []Param @[omitempty]
|
||||||
|
body string @[omitempty]
|
||||||
|
result Param @[omitempty]
|
||||||
|
has_return bool @[omitempty]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Param {
|
||||||
|
pub mut:
|
||||||
|
required bool @[omitempty]
|
||||||
|
mutable bool @[omitempty]
|
||||||
|
is_shared bool @[omitempty]
|
||||||
|
is_optional bool @[omitempty]
|
||||||
|
is_result bool @[omitempty]
|
||||||
|
description string @[omitempty]
|
||||||
|
name string @[omitempty]
|
||||||
|
typ Type @[omitempty]
|
||||||
|
struct_ Struct @[omitempty]
|
||||||
|
}
|
||||||
|
|
||||||
|
@[params]
|
||||||
|
pub struct Params{
|
||||||
|
pub:
|
||||||
|
v string
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_param(params Params) !Param {
|
||||||
|
// TODO: implement function from file line
|
||||||
|
return parse_param(params.v)!
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Result {
|
||||||
|
pub mut:
|
||||||
|
typ Type @[omitempty]
|
||||||
|
description string @[omitempty]
|
||||||
|
name string @[omitempty]
|
||||||
|
result bool @[omitempty] // whether is result type
|
||||||
|
optional bool @[omitempty] // whether is result type
|
||||||
|
structure Struct @[omitempty]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_function(code string) !Function {
|
||||||
|
// TODO: implement function from file line
|
||||||
|
return parse_function(code)!
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_function(code_ string) !Function {
|
||||||
|
mut code := code_.trim_space()
|
||||||
|
is_pub := code.starts_with('pub ')
|
||||||
|
if is_pub {
|
||||||
|
code = code.trim_string_left('pub ').trim_space()
|
||||||
|
}
|
||||||
|
|
||||||
|
is_fn := code.starts_with('fn ')
|
||||||
|
if !is_fn {
|
||||||
|
return error('invalid function format')
|
||||||
|
}
|
||||||
|
code = code.trim_string_left('fn ').trim_space()
|
||||||
|
|
||||||
|
receiver := if code.starts_with('(') {
|
||||||
|
param_str := code.all_after('(').all_before(')').trim_space()
|
||||||
|
code = code.all_after(')').trim_space()
|
||||||
|
parse_param(param_str)!
|
||||||
|
} else {
|
||||||
|
Param{}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := code.all_before('(').trim_space()
|
||||||
|
code = code.trim_string_left(name).trim_space()
|
||||||
|
|
||||||
|
params_str := code.all_after('(').all_before(')')
|
||||||
|
params := if params_str.trim_space() != '' {
|
||||||
|
params_str_lst := params_str.split(',')
|
||||||
|
params_str_lst.map(parse_param(it)!)
|
||||||
|
} else {
|
||||||
|
[]Param{}
|
||||||
|
}
|
||||||
|
result := new_param(
|
||||||
|
v: code.all_after(')').all_before('{').replace(' ', '')
|
||||||
|
)!
|
||||||
|
|
||||||
|
body := if code.contains('{') { code.all_after('{').all_before_last('}') } else { '' }
|
||||||
|
return Function{
|
||||||
|
name: name
|
||||||
|
receiver: receiver
|
||||||
|
params: params
|
||||||
|
result: result
|
||||||
|
body: body
|
||||||
|
}
|
||||||
|
}
|
||||||
130
lib/core/code/model.v
Normal file
130
lib/core/code/model.v
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
module code
|
||||||
|
|
||||||
|
// Code is a list of statements
|
||||||
|
// pub type Code = []CodeItem
|
||||||
|
|
||||||
|
pub type CodeItem = Alias | Comment | CustomCode | Function | Import | Struct | Sumtype
|
||||||
|
|
||||||
|
// item for adding custom code in
|
||||||
|
pub struct CustomCode {
|
||||||
|
pub:
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Comment {
|
||||||
|
pub:
|
||||||
|
text string
|
||||||
|
is_multi bool
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Struct {
|
||||||
|
pub mut:
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
mod string
|
||||||
|
is_pub bool
|
||||||
|
embeds []Struct @[str: skip]
|
||||||
|
generics map[string]string @[str: skip]
|
||||||
|
attrs []Attribute
|
||||||
|
fields []StructField
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sumtype {
|
||||||
|
pub:
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
types []Type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StructField {
|
||||||
|
pub mut:
|
||||||
|
comments []Comment
|
||||||
|
attrs []Attribute
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
default string
|
||||||
|
is_pub bool
|
||||||
|
is_mut bool
|
||||||
|
is_ref bool
|
||||||
|
anon_struct Struct @[str: skip] // sometimes fields may hold anonymous structs
|
||||||
|
typ Type
|
||||||
|
structure Struct @[str: skip]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Attribute {
|
||||||
|
pub:
|
||||||
|
name string // [name]
|
||||||
|
has_arg bool
|
||||||
|
arg string // [name: arg]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_param(code_ string) !Param {
|
||||||
|
mut code := code_.trim_space()
|
||||||
|
|
||||||
|
if code == '!' {
|
||||||
|
return Param{is_result: true}
|
||||||
|
} else if code == '?' {
|
||||||
|
return Param{is_optional: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_mut := code.starts_with('mut ')
|
||||||
|
if is_mut {
|
||||||
|
code = code.trim_string_left('mut ').trim_space()
|
||||||
|
}
|
||||||
|
split := code.split(' ').filter(it != '')
|
||||||
|
|
||||||
|
if split.len == 1 {
|
||||||
|
// means anonymous param
|
||||||
|
return Param{
|
||||||
|
typ: Type{
|
||||||
|
symbol: split[0]
|
||||||
|
}
|
||||||
|
mutable: is_mut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if split.len != 2 {
|
||||||
|
return error('invalid param format: ${code_}')
|
||||||
|
}
|
||||||
|
return Param{
|
||||||
|
name: split[0]
|
||||||
|
typ: Type{
|
||||||
|
symbol: split[1]
|
||||||
|
}
|
||||||
|
mutable: is_mut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_result(code_ string) !Result {
|
||||||
|
code := code_.replace(' ', '').trim_space()
|
||||||
|
|
||||||
|
return Result{
|
||||||
|
result: code_.starts_with('!')
|
||||||
|
optional: code_.starts_with('?')
|
||||||
|
typ: Type{
|
||||||
|
symbol: code.trim('!?')
|
||||||
|
is_optional: code.starts_with('?')
|
||||||
|
is_result: code.starts_with('!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: maybe make 'is_' fields methods?
|
||||||
|
pub struct Type {
|
||||||
|
pub mut:
|
||||||
|
is_reference bool @[str: skip]
|
||||||
|
is_map bool @[str: skip]
|
||||||
|
is_array bool
|
||||||
|
is_mutable bool @[str: skip]
|
||||||
|
is_shared bool @[str: skip]
|
||||||
|
is_optional bool @[str: skip]
|
||||||
|
is_result bool @[str: skip]
|
||||||
|
symbol string
|
||||||
|
mod string @[str: skip]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Alias {
|
||||||
|
pub:
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
typ Type
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
module codemodel
|
module code
|
||||||
|
|
||||||
pub struct Const {
|
pub struct Const {
|
||||||
name string
|
name string
|
||||||
@@ -11,7 +11,7 @@ pub fn parse_const(code_ string) !Const {
|
|||||||
return error('code <${code_}> is not of const')
|
return error('code <${code_}> is not of const')
|
||||||
}
|
}
|
||||||
return Const{
|
return Const{
|
||||||
name: code.split('=')[0].trim_space()
|
name: code.split('=')[0].trim_space()
|
||||||
value: code.split('=')[1].trim_space()
|
value: code.split('=')[1].trim_space()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
module codemodel
|
module code
|
||||||
|
|
||||||
pub struct Import {
|
pub struct Import {
|
||||||
pub mut:
|
pub mut:
|
||||||
@@ -14,7 +14,7 @@ pub fn parse_import(code_ string) Import {
|
|||||||
code := code_.trim_space().trim_string_left('import').trim_space()
|
code := code_.trim_space().trim_string_left('import').trim_space()
|
||||||
types_str := if code.contains(' ') { code.all_after(' ').trim('{}') } else { '' }
|
types_str := if code.contains(' ') { code.all_after(' ').trim('{}') } else { '' }
|
||||||
return Import{
|
return Import{
|
||||||
mod: code.all_before(' ')
|
mod: code.all_before(' ')
|
||||||
types: if types_str != '' {
|
types: if types_str != '' {
|
||||||
types_str.split(',').map(it.trim_space())
|
types_str.split(',').map(it.trim_space())
|
||||||
} else {
|
} else {
|
||||||
50
lib/core/code/module.v
Normal file
50
lib/core/code/module.v
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
module code
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.core.pathlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
pub struct Module {
|
||||||
|
pub mut:
|
||||||
|
name string
|
||||||
|
files []IFile
|
||||||
|
folders []IFolder
|
||||||
|
// model VFile
|
||||||
|
// methods VFile
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_module(mod Module) Module {
|
||||||
|
return Module {
|
||||||
|
...mod
|
||||||
|
files: mod.files.map(
|
||||||
|
if it is VFile {
|
||||||
|
IFile(VFile{...it, mod: mod.name})
|
||||||
|
} else {it}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mod Module) write(path string, options WriteOptions) ! {
|
||||||
|
mut module_dir := pathlib.get_dir(
|
||||||
|
path: '${path}/${mod.name}'
|
||||||
|
empty: options.overwrite
|
||||||
|
)!
|
||||||
|
|
||||||
|
if !options.overwrite && module_dir.exists() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in mod.files {
|
||||||
|
file.write(module_dir.path, options)!
|
||||||
|
}
|
||||||
|
|
||||||
|
for folder in mod.folders {
|
||||||
|
folder.write(module_dir.path, options)!
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.format {
|
||||||
|
os.execute('v fmt -w ${module_dir.path}')
|
||||||
|
}
|
||||||
|
if options.document {
|
||||||
|
os.execute('v doc -f html -o ${module_dir.path}/docs ${module_dir.path}')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
@if function.description != ''
|
@if function.description != ''
|
||||||
// @{function.description}
|
// @{function.description}
|
||||||
@endif
|
@endif
|
||||||
pub fn @receiver @{function.name}(@{params}) @{function.result.vgen()} {
|
pub fn @receiver @{function.name}(@{params}) @{result} {
|
||||||
@{function.body.trim_space().replace('\t', '')}
|
@{function.body.trim_space().replace('\t', '')}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module codemodel
|
module code
|
||||||
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
import log
|
||||||
import rand
|
import rand
|
||||||
|
|
||||||
pub struct GetStruct {
|
pub struct GetStruct {
|
||||||
@@ -68,7 +68,7 @@ pub fn (func Function) generate_call(params GenerateCallParams) !string {
|
|||||||
'()'
|
'()'
|
||||||
}
|
}
|
||||||
|
|
||||||
if func.result.result {
|
if func.result.is_result {
|
||||||
call += '!'
|
call += '!'
|
||||||
}
|
}
|
||||||
return call
|
return call
|
||||||
@@ -86,7 +86,7 @@ pub fn (param Param) generate_value() !string {
|
|||||||
} else if param.typ.symbol[0].is_capital() {
|
} else if param.typ.symbol[0].is_capital() {
|
||||||
return '${param.typ.symbol}{}'
|
return '${param.typ.symbol}{}'
|
||||||
} else {
|
} else {
|
||||||
console.print_debug('mock values for types other than strings and ints are not yet supported')
|
log.debug('mock values for types other than strings and ints are not yet supported')
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
module codemodel
|
module code
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
import freeflowuniverse.herolib.core.pathlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
pub struct CodeFile {
|
|
||||||
|
pub struct VFile {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
mod string
|
mod string
|
||||||
@@ -14,15 +15,15 @@ pub mut:
|
|||||||
content string
|
content string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_file(config CodeFile) CodeFile {
|
pub fn new_file(config VFile) VFile {
|
||||||
return CodeFile{
|
return VFile{
|
||||||
...config
|
...config
|
||||||
mod: texttools.name_fix(config.mod)
|
mod: texttools.name_fix(config.mod)
|
||||||
items: config.items
|
items: config.items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut file CodeFile) add_import(import_ Import) ! {
|
pub fn (mut file VFile) add_import(import_ Import) ! {
|
||||||
for mut i in file.imports {
|
for mut i in file.imports {
|
||||||
if i.mod == import_.mod {
|
if i.mod == import_.mod {
|
||||||
i.add_types(import_.types)
|
i.add_types(import_.types)
|
||||||
@@ -32,7 +33,7 @@ pub fn (mut file CodeFile) add_import(import_ Import) ! {
|
|||||||
file.imports << import_
|
file.imports << import_
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (code CodeFile) write_v(path string, options WriteOptions) ! {
|
pub fn (code VFile) write(path string, options WriteOptions) ! {
|
||||||
filename := '${options.prefix}${texttools.name_fix(code.name)}.v'
|
filename := '${options.prefix}${texttools.name_fix(code.name)}.v'
|
||||||
mut filepath := pathlib.get('${path}/${filename}')
|
mut filepath := pathlib.get('${path}/${filename}')
|
||||||
|
|
||||||
@@ -58,16 +59,21 @@ pub fn (code CodeFile) write_v(path string, options WriteOptions) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mut file := pathlib.get_file(
|
mut file := pathlib.get_file(
|
||||||
path: filepath.path
|
path: filepath.path
|
||||||
create: true
|
create: true
|
||||||
)!
|
)!
|
||||||
file.write('module ${code.mod}\n${imports_str}\n${consts_str}\n${code_str}')!
|
|
||||||
|
mod_stmt := if code.mod == '' {''} else {
|
||||||
|
'module ${code.mod}'
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write('${mod_stmt}\n${imports_str}\n${consts_str}${code_str}')!
|
||||||
if options.format {
|
if options.format {
|
||||||
os.execute('v fmt -w ${file.path}')
|
os.execute('v fmt -w ${file.path}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (file CodeFile) get_function(name string) ?Function {
|
pub fn (file VFile) get_function(name string) ?Function {
|
||||||
functions := file.items.filter(it is Function).map(it as Function)
|
functions := file.items.filter(it is Function).map(it as Function)
|
||||||
target_lst := functions.filter(it.name == name)
|
target_lst := functions.filter(it.name == name)
|
||||||
|
|
||||||
@@ -80,7 +86,7 @@ pub fn (file CodeFile) get_function(name string) ?Function {
|
|||||||
return target_lst[0]
|
return target_lst[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut file CodeFile) set_function(function Function) ! {
|
pub fn (mut file VFile) set_function(function Function) ! {
|
||||||
function_names := file.items.map(if it is Function { it.name } else { '' })
|
function_names := file.items.map(if it is Function { it.name } else { '' })
|
||||||
|
|
||||||
index := function_names.index(function.name)
|
index := function_names.index(function.name)
|
||||||
@@ -90,10 +96,10 @@ pub fn (mut file CodeFile) set_function(function Function) ! {
|
|||||||
file.items[index] = function
|
file.items[index] = function
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (file CodeFile) functions() []Function {
|
pub fn (file VFile) functions() []Function {
|
||||||
return file.items.filter(it is Function).map(it as Function)
|
return file.items.filter(it is Function).map(it as Function)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (file CodeFile) structs() []Struct {
|
pub fn (file VFile) structs() []Struct {
|
||||||
return file.items.filter(it is Struct).map(it as Struct)
|
return file.items.filter(it is Struct).map(it as Struct)
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
module codemodel
|
module code
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import log
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
|
||||||
|
|
||||||
pub struct WriteCode {
|
pub struct WriteCode {
|
||||||
destination string
|
destination string
|
||||||
@@ -123,14 +122,14 @@ pub fn (function Function) vgen(options WriteOptions) string {
|
|||||||
|
|
||||||
optionals := params_.filter(it.is_optional)
|
optionals := params_.filter(it.is_optional)
|
||||||
options_struct := Struct{
|
options_struct := Struct{
|
||||||
name: '${texttools.name_fix_snake_to_pascal(function.name)}Options'
|
name: '${texttools.name_fix_snake_to_pascal(function.name)}Options'
|
||||||
attrs: [Attribute{
|
attrs: [Attribute{
|
||||||
name: 'params'
|
name: 'params'
|
||||||
}]
|
}]
|
||||||
fields: optionals.map(StructField{
|
fields: optionals.map(StructField{
|
||||||
name: it.name
|
name: it.name
|
||||||
description: it.description
|
description: it.description
|
||||||
typ: Type{
|
typ: Type{
|
||||||
symbol: it.typ.symbol
|
symbol: it.typ.symbol
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -138,7 +137,7 @@ pub fn (function Function) vgen(options WriteOptions) string {
|
|||||||
if optionals.len > 0 {
|
if optionals.len > 0 {
|
||||||
params_ << Param{
|
params_ << Param{
|
||||||
name: 'options'
|
name: 'options'
|
||||||
typ: Type{
|
typ: Type{
|
||||||
symbol: options_struct.name
|
symbol: options_struct.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,10 +145,18 @@ pub fn (function Function) vgen(options WriteOptions) string {
|
|||||||
|
|
||||||
params := params_.filter(!it.is_optional).map('${it.name} ${it.typ.symbol}').join(', ')
|
params := params_.filter(!it.is_optional).map('${it.name} ${it.typ.symbol}').join(', ')
|
||||||
|
|
||||||
receiver := function.receiver.vgen()
|
receiver := if function.receiver.vgen() != '' {
|
||||||
|
'(${function.receiver.vgen()})'
|
||||||
|
} else {''}
|
||||||
|
|
||||||
|
// generate anon result param
|
||||||
|
result := Param{...function.result,
|
||||||
|
name: ''
|
||||||
|
}.vgen()
|
||||||
|
println('debugzo ${result}')
|
||||||
|
|
||||||
mut function_str := $tmpl('templates/function/function.v.template')
|
mut function_str := $tmpl('templates/function/function.v.template')
|
||||||
|
|
||||||
// if options.format {
|
// if options.format {
|
||||||
// result := os.execute_opt('echo "${function_str.replace('$', '\\$')}" | v fmt') or {
|
// result := os.execute_opt('echo "${function_str.replace('$', '\\$')}" | v fmt') or {
|
||||||
// panic('${function_str}\n${err}')
|
// panic('${function_str}\n${err}')
|
||||||
@@ -166,9 +173,9 @@ pub fn (function Function) vgen(options WriteOptions) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (param Param) vgen() string {
|
pub fn (param Param) vgen() string {
|
||||||
if param.name == '' {
|
// if param.name == '' {
|
||||||
return ''
|
// return ''
|
||||||
}
|
// }
|
||||||
sym := if param.struct_.name != '' {
|
sym := if param.struct_.name != '' {
|
||||||
param.struct_.get_type_symbol()
|
param.struct_.get_type_symbol()
|
||||||
} else {
|
} else {
|
||||||
@@ -182,7 +189,7 @@ pub fn (param Param) vgen() string {
|
|||||||
if param.mutable {
|
if param.mutable {
|
||||||
vstr = 'mut ${vstr}'
|
vstr = 'mut ${vstr}'
|
||||||
}
|
}
|
||||||
return '(${vstr})'
|
return '${vstr}'
|
||||||
}
|
}
|
||||||
|
|
||||||
// vgen_function generates a function statement for a function
|
// vgen_function generates a function statement for a function
|
||||||
@@ -220,7 +227,7 @@ pub fn (gen VGenerator) generate_struct(struct_ Struct) !string {
|
|||||||
mut struct_str := $tmpl('templates/struct/struct.v.template')
|
mut struct_str := $tmpl('templates/struct/struct.v.template')
|
||||||
if gen.format {
|
if gen.format {
|
||||||
result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {
|
result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {
|
||||||
console.print_debug(struct_str)
|
log.debug(struct_str)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return result.output
|
return result.output
|
||||||
Reference in New Issue
Block a user