2019-10-04 14:48:09 +02:00
|
|
|
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
2019-10-13 15:37:43 +02:00
|
|
|
module compiler
|
2019-10-04 14:48:09 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
strings
|
|
|
|
os
|
|
|
|
)
|
|
|
|
|
2019-10-07 00:31:01 +02:00
|
|
|
/*
|
|
|
|
.vh generation logic.
|
2019-10-10 01:59:33 +02:00
|
|
|
.vh files contain only function signatures, consts, and types.
|
2019-10-07 00:31:01 +02:00
|
|
|
They are used together with pre-compiled modules.
|
2019-10-04 14:48:09 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
// "fn foo(a int) string"
|
|
|
|
fn (f &Fn) v_definition() string {
|
|
|
|
//t :=time.ticks()
|
|
|
|
mut sb := strings.new_builder(100)
|
|
|
|
if f.is_public {
|
|
|
|
sb.write('pub ')
|
|
|
|
}
|
|
|
|
sb.write('fn ')
|
|
|
|
if f.is_c {
|
|
|
|
sb.write('C.')
|
|
|
|
}
|
|
|
|
if f.is_method {
|
|
|
|
recv := f.args[0]
|
2019-10-07 00:31:01 +02:00
|
|
|
typ := v_type_str(recv.typ).replace('*', '')
|
2019-10-04 14:48:09 +02:00
|
|
|
mut mu := if recv.is_mut { 'mut' } else { '' }
|
|
|
|
if recv.ref {
|
|
|
|
mu = '&'
|
|
|
|
}
|
|
|
|
sb.write('($recv.name $mu $typ) ')
|
|
|
|
}
|
|
|
|
if f.name.contains('__') {
|
|
|
|
sb.write(f.name.all_after('__') + '(')
|
|
|
|
} else {
|
|
|
|
sb.write('$f.name(')
|
|
|
|
}
|
|
|
|
for i, arg in f.args {
|
2019-10-07 00:31:01 +02:00
|
|
|
if i == 0 && f.is_method { // skip the receiver
|
|
|
|
continue
|
|
|
|
}
|
2019-10-10 19:02:32 +02:00
|
|
|
typ := v_type_str(arg.typ).replace('*', '&')
|
2019-10-04 14:48:09 +02:00
|
|
|
if arg.name == '' {
|
|
|
|
sb.write(typ)
|
|
|
|
} else {
|
|
|
|
sb.write('$arg.name $typ')
|
|
|
|
}
|
|
|
|
if i != f.args.len - 1 {
|
|
|
|
sb.write(', ')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sb.write(')')
|
|
|
|
if f.typ != 'void' {
|
2019-10-10 19:02:32 +02:00
|
|
|
typ := v_type_str(f.typ).replace('*', '&')
|
2019-10-04 14:48:09 +02:00
|
|
|
sb.write(' ')
|
|
|
|
sb.write(typ)
|
|
|
|
sb.writeln(' ')
|
|
|
|
}
|
|
|
|
//println('ms: ${time.ticks() - t}')
|
|
|
|
return sb.str()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn v_type_str(typ_ string) string {
|
2019-10-07 00:31:01 +02:00
|
|
|
mut typ := if typ_.ends_with('*') {
|
2019-10-04 14:48:09 +02:00
|
|
|
'*' + typ_.left(typ_.len - 1)
|
|
|
|
} else {
|
|
|
|
typ_
|
|
|
|
}
|
2019-10-07 00:31:01 +02:00
|
|
|
typ = typ.replace('Option_', '?')
|
2019-10-13 02:05:11 +02:00
|
|
|
// fn parent/alias?
|
|
|
|
if typ.starts_with('fn ') {
|
|
|
|
mut types := []string
|
|
|
|
fi_lpar := typ.index_byte(`(`)
|
|
|
|
li_rpar := typ.last_index_byte(`)`)
|
|
|
|
ret_type := typ.right(li_rpar+1)
|
|
|
|
for t in typ.substr(fi_lpar+1, li_rpar).split(',') {
|
|
|
|
types << v_type_str(t)
|
|
|
|
}
|
|
|
|
return 'fn (' + types.join(', ') + ')$ret_type'
|
|
|
|
}
|
|
|
|
typ = typ.replace('Option_', '?')
|
|
|
|
// multiple return
|
2019-10-09 22:38:33 +02:00
|
|
|
if typ.contains('_V_MulRet') {
|
2019-10-13 02:05:11 +02:00
|
|
|
words := typ.replace('_V_MulRet_', '').replace('_PTR_', '*').split('_V_')
|
2019-10-09 22:38:33 +02:00
|
|
|
typ = '('
|
|
|
|
for i in 0 .. words.len {
|
2019-10-13 02:05:11 +02:00
|
|
|
typ += v_type_str(words[i])
|
2019-10-09 22:38:33 +02:00
|
|
|
if i != words.len - 1 {
|
|
|
|
typ += ','
|
|
|
|
}
|
|
|
|
}
|
|
|
|
typ += ')'
|
|
|
|
return typ
|
2019-10-13 02:05:11 +02:00
|
|
|
}
|
2019-10-04 14:48:09 +02:00
|
|
|
//println('"$typ"')
|
|
|
|
if typ == '*void' {
|
|
|
|
return 'voidptr'
|
|
|
|
}
|
|
|
|
if typ == '*byte' {
|
|
|
|
return 'byteptr'
|
|
|
|
}
|
|
|
|
if typ.starts_with('array_') {
|
|
|
|
return '[]' + typ.right(6)
|
|
|
|
}
|
|
|
|
if typ.contains('__') {
|
2019-10-10 01:59:33 +02:00
|
|
|
opt := typ.starts_with('?')
|
|
|
|
typ = typ.all_after('__')
|
|
|
|
if opt {
|
|
|
|
typ = '?' + typ
|
|
|
|
}
|
2019-10-04 14:48:09 +02:00
|
|
|
}
|
2019-10-07 00:31:01 +02:00
|
|
|
return typ
|
2019-10-04 14:48:09 +02:00
|
|
|
}
|
|
|
|
|
2019-10-21 13:21:30 +02:00
|
|
|
// `mod` == "vlib/os"
|
|
|
|
fn generate_vh(mod string) {
|
|
|
|
println('\n\n\n\nGenerating a V header file for module `$mod`')
|
|
|
|
vexe := os.executable()
|
|
|
|
full_mod_path := os.dir(vexe) + '/' + mod
|
|
|
|
|
|
|
|
mod_path := mod.replace('.', os.path_separator)
|
|
|
|
dir := if mod.starts_with('vlib') {
|
|
|
|
'$compiler.v_modules_path${os.path_separator}$mod'
|
|
|
|
} else {
|
|
|
|
'$compiler.v_modules_path${os.path_separator}$mod'
|
|
|
|
}
|
|
|
|
path := dir + '.vh'
|
|
|
|
pdir := dir.all_before_last(os.path_separator)
|
|
|
|
if !os.dir_exists(pdir) {
|
|
|
|
os.mkdir_all(pdir)
|
|
|
|
// os.mkdir(os.realpath(dir))
|
|
|
|
}
|
|
|
|
out := os.create(path) or { panic(err) }
|
|
|
|
// Consts
|
|
|
|
println(full_mod_path)
|
|
|
|
mut vfiles := os.walk_ext(full_mod_path, '.v')
|
|
|
|
filtered := vfiles.filter(!it.ends_with('test.v') && !it.ends_with('_win.v')) // TODO merge once filter allows it
|
|
|
|
println(filtered)
|
|
|
|
mut v := new_v(['foo.v'])
|
|
|
|
//v.pref.generating_vh = true
|
|
|
|
for file in filtered {
|
|
|
|
mut p := v.new_parser_from_file(file)
|
|
|
|
p.parse(.decl)
|
|
|
|
for i, tok in p.tokens {
|
|
|
|
if !p.tok.is_decl() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
match tok.tok {
|
2019-10-23 00:00:52 +02:00
|
|
|
TokenKind.key_fn => { generate_fn(out, p.tokens, i) }
|
|
|
|
TokenKind.key_const => { generate_const(out, p.tokens, i) }
|
2019-10-21 13:21:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generate_fn(file os.File, tokens []Token, i int) {
|
|
|
|
mut out := strings.new_builder(100)
|
|
|
|
mut next := tokens[i+1]
|
|
|
|
if tokens[i-1].tok != .key_pub {
|
|
|
|
// Skip private fns
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if next.tok == .name && next.lit == 'C' {
|
|
|
|
println('skipping C')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
//out.write('pub ')
|
|
|
|
mut tok := tokens[i]
|
|
|
|
for i < tokens.len && tok.tok != .lcbr {
|
|
|
|
next = tokens[i+1]
|
|
|
|
|
|
|
|
out.write(tok.str())
|
|
|
|
if tok.tok != .lpar && !(next.tok in [.comma, .rpar]) {
|
|
|
|
// No space after (), [], etc
|
|
|
|
out.write(' ')
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
tok = tokens[i]
|
|
|
|
}
|
|
|
|
file.writeln(out.str())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generate_const(file os.File, tokens []Token, i int) {
|
|
|
|
//mut out := strings.new_builder(100)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (v &V) generate_vh_old() {
|
2019-10-07 00:31:01 +02:00
|
|
|
println('\n\n\n\nGenerating a V header file for module `$v.mod`')
|
2019-10-12 21:31:05 +02:00
|
|
|
mod_path := v.mod.replace('.', os.path_separator)
|
2019-10-12 07:41:41 +02:00
|
|
|
dir := if v.dir.starts_with('vlib') {
|
2019-10-12 21:31:05 +02:00
|
|
|
'$v_modules_path${os.path_separator}$v.dir'
|
2019-10-12 07:41:41 +02:00
|
|
|
} else {
|
2019-10-12 21:31:05 +02:00
|
|
|
'$v_modules_path${os.path_separator}$mod_path'
|
2019-10-12 07:41:41 +02:00
|
|
|
}
|
2019-10-04 14:48:09 +02:00
|
|
|
path := dir + '.vh'
|
2019-10-12 21:31:05 +02:00
|
|
|
pdir := dir.all_before_last(os.path_separator)
|
2019-10-12 07:41:41 +02:00
|
|
|
if !os.dir_exists(pdir) {
|
|
|
|
os.mkdir_all(pdir)
|
2019-10-12 00:17:37 +02:00
|
|
|
// os.mkdir(os.realpath(dir))
|
2019-10-04 14:48:09 +02:00
|
|
|
}
|
|
|
|
file := os.create(path) or { panic(err) }
|
|
|
|
// Consts
|
2019-10-12 00:17:37 +02:00
|
|
|
mod_def := if v.mod.contains('.') { v.mod.all_after('.') } else { v.mod }
|
2019-10-04 14:48:09 +02:00
|
|
|
file.writeln('// $v.mod module header \n')
|
2019-10-12 00:17:37 +02:00
|
|
|
file.writeln('module $mod_def')
|
2019-10-04 14:48:09 +02:00
|
|
|
file.writeln('// Consts')
|
|
|
|
if v.table.consts.len > 0 {
|
|
|
|
file.writeln('const (')
|
|
|
|
for i, c in v.table.consts {
|
|
|
|
if c.mod != v.mod {
|
|
|
|
continue
|
|
|
|
}
|
2019-10-13 02:05:11 +02:00
|
|
|
// println('$i $c.name')
|
2019-10-04 14:48:09 +02:00
|
|
|
//if !c.name.contains('__') {
|
|
|
|
//continue
|
|
|
|
//}
|
|
|
|
name := c.name.all_after('__')
|
|
|
|
typ := v_type_str(c.typ)
|
|
|
|
file.writeln('\t$name $typ')
|
|
|
|
}
|
|
|
|
file.writeln(')\n')
|
|
|
|
// Globals
|
|
|
|
for var in v.table.consts {
|
|
|
|
if var.mod != v.mod {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !var.is_global {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
name := var.name.all_after('__')
|
|
|
|
typ := v_type_str(var.typ)
|
|
|
|
file.writeln('__global $name $typ')
|
|
|
|
}
|
|
|
|
file.writeln('\n')
|
|
|
|
}
|
|
|
|
// Types
|
2019-10-09 22:38:33 +02:00
|
|
|
file.writeln('// Types')
|
2019-10-04 14:48:09 +02:00
|
|
|
for _, typ in v.table.typesmap {
|
2019-10-07 00:31:01 +02:00
|
|
|
//println(typ.name)
|
|
|
|
if typ.mod != v.mod && typ.mod != ''{ // int, string etc mod == ''
|
2019-10-13 02:05:11 +02:00
|
|
|
// println('skipping type "$typ.name"')
|
2019-10-04 14:48:09 +02:00
|
|
|
continue
|
|
|
|
}
|
2019-10-09 22:38:33 +02:00
|
|
|
if typ.name.contains('_V_MulRet') {
|
|
|
|
continue
|
|
|
|
}
|
2019-10-07 00:31:01 +02:00
|
|
|
mut name := typ.name
|
2019-10-04 14:48:09 +02:00
|
|
|
if typ.name.contains('__') {
|
2019-10-07 00:31:01 +02:00
|
|
|
name = typ.name.all_after('__')
|
2019-10-04 14:48:09 +02:00
|
|
|
}
|
2019-10-12 03:09:37 +02:00
|
|
|
// type alias
|
|
|
|
if typ.parent != '' && typ.cat == .alias {
|
2019-10-12 03:11:07 +02:00
|
|
|
parent := v_type_str(typ.parent)
|
|
|
|
file.writeln('type $typ.name $parent')
|
2019-10-12 03:09:37 +02:00
|
|
|
}
|
2019-10-07 00:31:01 +02:00
|
|
|
if typ.cat in [TypeCategory.struct_, .c_struct] {
|
|
|
|
c := if typ.is_c { 'C.' } else { '' }
|
|
|
|
file.writeln('struct ${c}$name {')
|
2019-10-04 14:48:09 +02:00
|
|
|
// Private fields
|
|
|
|
for field in typ.fields {
|
|
|
|
if field.access_mod == .public {
|
|
|
|
continue
|
|
|
|
}
|
2019-10-10 19:02:32 +02:00
|
|
|
field_type := v_type_str(field.typ).replace('*', '&')
|
2019-10-04 14:48:09 +02:00
|
|
|
file.writeln('\t$field.name $field_type')
|
|
|
|
}
|
2019-10-07 00:31:01 +02:00
|
|
|
//file.writeln('pub:')
|
|
|
|
mut public_str := ''
|
2019-10-04 14:48:09 +02:00
|
|
|
for field in typ.fields {
|
|
|
|
if field.access_mod == .private {
|
|
|
|
continue
|
|
|
|
}
|
2019-10-10 19:02:32 +02:00
|
|
|
field_type := v_type_str(field.typ).replace('*', '&')
|
2019-10-07 00:31:01 +02:00
|
|
|
public_str += '\t$field.name $field_type\n'
|
|
|
|
//file.writeln('\t$field.name $field_type')
|
|
|
|
}
|
|
|
|
if public_str != '' {
|
|
|
|
file.writeln('pub:' + public_str)
|
2019-10-04 14:48:09 +02:00
|
|
|
}
|
|
|
|
file.writeln('}\n')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Functions & methods
|
|
|
|
file.writeln('// Functions')
|
|
|
|
// Public first
|
|
|
|
mut fns := []Fn
|
|
|
|
// TODO fns := v.table.fns.filter(.mod == v.mod)
|
|
|
|
for _, f in v.table.fns {
|
2019-10-07 00:31:01 +02:00
|
|
|
if f.mod == v.mod || f.mod == ''{
|
2019-10-04 14:48:09 +02:00
|
|
|
fns << f
|
2019-10-07 00:31:01 +02:00
|
|
|
} else {
|
2019-10-09 22:38:33 +02:00
|
|
|
//println('skipping fn $f.name mod=$f.mod')
|
2019-10-04 14:48:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, f in fns {
|
|
|
|
if !f.is_public {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
file.writeln(f.v_definition())
|
|
|
|
}
|
|
|
|
// Private
|
|
|
|
for _, f in fns {
|
|
|
|
if f.is_public {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
file.writeln(f.v_definition())
|
|
|
|
}
|
|
|
|
// Methods
|
|
|
|
file.writeln('\n// Methods //////////////////')
|
|
|
|
for _, typ in v.table.typesmap {
|
2019-10-10 01:59:33 +02:00
|
|
|
if typ.mod != v.mod && !(v.mod == 'builtin' && typ.mod == '') {
|
2019-10-13 02:05:11 +02:00
|
|
|
// println('skipping method typ $typ.name mod=$typ.mod')
|
2019-10-04 14:48:09 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
for method in typ.methods {
|
|
|
|
file.writeln(method.v_definition())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
file.close()
|
|
|
|
|
|
|
|
/*
|
|
|
|
for i, p in v.parsers {
|
|
|
|
if v.parsers[i].vh_lines.len > 0 {
|
|
|
|
os.write_file(p.file_name +'.vh', v.parsers[i].vh_lines.join('\n'))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|