mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
generics, vweb, comptime codegen, etc
This commit is contained in:
parent
f1373874ef
commit
207bab5f79
@ -20,11 +20,11 @@ struct CGen {
|
|||||||
fns []string
|
fns []string
|
||||||
so_fns []string
|
so_fns []string
|
||||||
consts_init []string
|
consts_init []string
|
||||||
lines []string
|
|
||||||
//buf strings.Builder
|
//buf strings.Builder
|
||||||
is_user bool
|
is_user bool
|
||||||
mut:
|
mut:
|
||||||
run Pass
|
lines []string
|
||||||
|
pass Pass
|
||||||
nogen bool
|
nogen bool
|
||||||
tmp_line string
|
tmp_line string
|
||||||
cur_line string
|
cur_line string
|
||||||
@ -53,7 +53,7 @@ fn new_cgen(out_name_c string) *CGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (g mut CGen) genln(s string) {
|
fn (g mut CGen) genln(s string) {
|
||||||
if g.nogen || g.run != .main {
|
if g.nogen || g.pass != .main {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.is_tmp {
|
if g.is_tmp {
|
||||||
@ -72,7 +72,7 @@ fn (g mut CGen) genln(s string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (g mut CGen) gen(s string) {
|
fn (g mut CGen) gen(s string) {
|
||||||
if g.nogen || g.run != .main {
|
if g.nogen || g.pass != .main {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.is_tmp {
|
if g.is_tmp {
|
||||||
@ -84,7 +84,7 @@ fn (g mut CGen) gen(s string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (g mut CGen) resetln(s string) {
|
fn (g mut CGen) resetln(s string) {
|
||||||
if g.nogen || g.run != .main {
|
if g.nogen || g.pass != .main {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.is_tmp {
|
if g.is_tmp {
|
||||||
@ -127,7 +127,7 @@ fn (g mut CGen) add_placeholder() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (g mut CGen) set_placeholder(pos int, val string) {
|
fn (g mut CGen) set_placeholder(pos int, val string) {
|
||||||
if g.nogen || g.run != .main {
|
if g.nogen || g.pass != .main {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// g.lines.set(pos, val)
|
// g.lines.set(pos, val)
|
||||||
@ -153,7 +153,7 @@ fn (g mut CGen) add_placeholder2() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (g mut CGen) set_placeholder2(pos int, val string) {
|
fn (g mut CGen) set_placeholder2(pos int, val string) {
|
||||||
if g.nogen || g.run != .main {
|
if g.nogen || g.pass != .main {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.is_tmp {
|
if g.is_tmp {
|
||||||
@ -219,21 +219,21 @@ fn (p mut Parser) print_prof_counters() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) gen_type(s string) {
|
fn (p mut Parser) gen_type(s string) {
|
||||||
if !p.first_run() {
|
if !p.first_pass() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.cgen.types << s
|
p.cgen.types << s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) gen_typedef(s string) {
|
fn (p mut Parser) gen_typedef(s string) {
|
||||||
if !p.first_run() {
|
if !p.first_pass() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.cgen.typedefs << s
|
p.cgen.typedefs << s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) gen_type_alias(s string) {
|
fn (p mut Parser) gen_type_alias(s string) {
|
||||||
if !p.first_run() {
|
if !p.first_pass() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.cgen.type_aliases << s
|
p.cgen.type_aliases << s
|
||||||
|
203
compiler/comptime.v
Normal file
203
compiler/comptime.v
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
module main
|
||||||
|
|
||||||
|
import (
|
||||||
|
vweb.tmpl // for `$vweb_html()`
|
||||||
|
os
|
||||||
|
)
|
||||||
|
|
||||||
|
fn (p mut Parser) comp_time() {
|
||||||
|
p.check(.dollar)
|
||||||
|
if p.tok == .key_if {
|
||||||
|
p.check(.key_if)
|
||||||
|
p.fspace()
|
||||||
|
not := p.tok == .not
|
||||||
|
if not {
|
||||||
|
p.check(.not)
|
||||||
|
}
|
||||||
|
name := p.check_name()
|
||||||
|
p.fspace()
|
||||||
|
if name in SupportedPlatforms {
|
||||||
|
ifdef_name := os_name_to_ifdef(name)
|
||||||
|
if not {
|
||||||
|
p.genln('#ifndef $ifdef_name')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.genln('#ifdef $ifdef_name')
|
||||||
|
}
|
||||||
|
p.check(.lcbr)
|
||||||
|
p.statements_no_rcbr()
|
||||||
|
if ! (p.tok == .dollar && p.peek() == .key_else) {
|
||||||
|
p.genln('#endif')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println('Supported platforms:')
|
||||||
|
println(SupportedPlatforms)
|
||||||
|
p.error('unknown platform `$name`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if p.tok == .key_for {
|
||||||
|
p.next()
|
||||||
|
name := p.check_name()
|
||||||
|
if name != 'field' {
|
||||||
|
p.error('for field only')
|
||||||
|
}
|
||||||
|
p.check(.key_in)
|
||||||
|
p.check_name()
|
||||||
|
p.check(.dot)
|
||||||
|
p.check_name()// fields
|
||||||
|
p.check(.lcbr)
|
||||||
|
// for p.tok != .rcbr && p.tok != .eof {
|
||||||
|
res_name := p.check_name()
|
||||||
|
println(res_name)
|
||||||
|
p.check(.dot)
|
||||||
|
p.check(.dollar)
|
||||||
|
p.check(.name)
|
||||||
|
p.check(.assign)
|
||||||
|
p.cgen.start_tmp()
|
||||||
|
p.bool_expression()
|
||||||
|
val := p.cgen.end_tmp()
|
||||||
|
println(val)
|
||||||
|
p.check(.rcbr)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
else if p.tok == .key_else {
|
||||||
|
p.next()
|
||||||
|
p.check(.lcbr)
|
||||||
|
p.genln('#else')
|
||||||
|
p.statements_no_rcbr()
|
||||||
|
p.genln('#endif')
|
||||||
|
}
|
||||||
|
// $vweb.html()
|
||||||
|
// Compile vweb html template to V code, parse that V code and embed the resulting V functions
|
||||||
|
// that returns an html string
|
||||||
|
else if p.tok == .name && p.lit == 'vweb' {
|
||||||
|
path := p.cur_fn.name + '.html'
|
||||||
|
if !os.file_exists(path) {
|
||||||
|
p.error('vweb HTML template "$path" not found')
|
||||||
|
}
|
||||||
|
p.check(.name) // skip `vweb.html()` TODO
|
||||||
|
p.check(.dot)
|
||||||
|
p.check(.name)
|
||||||
|
p.check(.lpar)
|
||||||
|
p.check(.rpar)
|
||||||
|
v_code := tmpl.compile_template(path)
|
||||||
|
os.write_file('.vwebtmpl.v', v_code.clone()) // TODO don't need clone, compiler bug
|
||||||
|
p.genln('')
|
||||||
|
// Parse the function and embed resulting C code in current function so that
|
||||||
|
// all variables are available.
|
||||||
|
pos := p.cgen.lines.len - 1
|
||||||
|
mut pp := p.v.new_parser('.vwebtmpl.v', Pass.main)
|
||||||
|
os.rm('.vwebtmpl.v')
|
||||||
|
pp.is_vweb = true
|
||||||
|
pp.cur_fn = p.cur_fn // give access too all variables in current function
|
||||||
|
pp.parse()
|
||||||
|
tmpl_fn_body := p.cgen.lines.slice(pos + 2, p.cgen.lines.len).join('\n').clone()
|
||||||
|
end_pos := tmpl_fn_body.last_index('Builder_str( sb )') + 19 // TODO
|
||||||
|
p.cgen.lines = p.cgen.lines.left(pos)
|
||||||
|
p.genln('/////////////////// tmpl start')
|
||||||
|
p.genln(tmpl_fn_body.left(end_pos))
|
||||||
|
p.genln('/////////////////// tmpl end')
|
||||||
|
// `app.vweb.html(index_view())`
|
||||||
|
receiver := p.cur_fn.args[0]
|
||||||
|
dot := if receiver.is_mut { '->' } else { '.' }
|
||||||
|
p.genln('vweb__Context_html($receiver.name $dot vweb, tmpl_res)')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.error('bad comptime expr')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #include, #flag, #v
|
||||||
|
fn (p mut Parser) chash() {
|
||||||
|
hash := p.lit.trim_space()
|
||||||
|
// println('chsh() file=$p.file is_sig=${p.is_sig()} hash="$hash"')
|
||||||
|
p.next()
|
||||||
|
is_sig := p.is_sig()
|
||||||
|
if hash.starts_with('flag ') {
|
||||||
|
mut flag := hash.right(5)
|
||||||
|
// No the right os? Skip!
|
||||||
|
// mut ok := true
|
||||||
|
if hash.contains('linux') && p.os != .linux {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if hash.contains('darwin') && p.os != .mac {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if hash.contains('windows') && (p.os != .windows && p.os != .msvc) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Remove "linux" etc from flag
|
||||||
|
if flag.contains('linux') || flag.contains('darwin') || flag.contains('windows') {
|
||||||
|
pos := flag.index(' ')
|
||||||
|
flag = flag.right(pos)
|
||||||
|
}
|
||||||
|
has_vroot := flag.contains('@VROOT')
|
||||||
|
flag = flag.trim_space().replace('@VROOT', p.vroot)
|
||||||
|
if p.table.flags.contains(flag) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.log('adding flag "$flag"')
|
||||||
|
// `@VROOT/thirdparty/glad/glad.o`, make sure it exists, otherwise build it
|
||||||
|
if has_vroot && flag.contains('.o') {
|
||||||
|
if p.os == .msvc {
|
||||||
|
build_thirdparty_obj_file_with_msvc(flag)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
build_thirdparty_obj_file(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.table.flags << flag
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hash.starts_with('include') {
|
||||||
|
if p.first_pass() && !is_sig {
|
||||||
|
p.cgen.includes << '#$hash'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO remove after ui_mac.m is removed
|
||||||
|
else if hash.contains('embed') {
|
||||||
|
pos := hash.index('embed') + 5
|
||||||
|
file := hash.right(pos)
|
||||||
|
if p.pref.build_mode != BuildMode.default_mode {
|
||||||
|
p.genln('#include $file')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if hash.contains('define') {
|
||||||
|
// Move defines on top
|
||||||
|
p.cgen.includes << '#$hash'
|
||||||
|
}
|
||||||
|
else if hash == 'v' {
|
||||||
|
println('v script')
|
||||||
|
//p.v_script = true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if !p.can_chash {
|
||||||
|
p.error('bad token `#` (embedding C code is no longer supported)')
|
||||||
|
}
|
||||||
|
p.genln(hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `user.$method()` (`method` is a string)
|
||||||
|
fn (p mut Parser) comptime_method_call(typ Type) {
|
||||||
|
p.cgen.cur_line = ''
|
||||||
|
p.check(.dollar)
|
||||||
|
var := p.check_name()
|
||||||
|
for method in typ.methods {
|
||||||
|
if method.typ != 'void' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
receiver := method.args[0]
|
||||||
|
amp := if receiver.is_mut { '&' } else { '' }
|
||||||
|
p.gen('if ( string_eq($var, _STR("$method.name")) ) ${typ.name}_$method.name($amp $p.expr_var.name);')
|
||||||
|
}
|
||||||
|
p.check(.lpar)
|
||||||
|
p.check(.rpar)
|
||||||
|
}
|
||||||
|
|
150
compiler/fn.v
150
compiler/fn.v
@ -30,6 +30,7 @@ mut:
|
|||||||
returns_error bool
|
returns_error bool
|
||||||
is_decl bool // type myfn fn(int, int)
|
is_decl bool // type myfn fn(int, int)
|
||||||
defer_text string
|
defer_text string
|
||||||
|
//gen_types []string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (f &Fn) find_var(name string) Var {
|
fn (f &Fn) find_var(name string) Var {
|
||||||
@ -110,7 +111,7 @@ fn (p mut Parser) fn_decl() {
|
|||||||
defer { p.fgenln('\n') }
|
defer { p.fgenln('\n') }
|
||||||
is_pub := p.tok == .key_pub
|
is_pub := p.tok == .key_pub
|
||||||
is_live := p.attr == 'live' && !p.pref.is_so && p.pref.is_live
|
is_live := p.attr == 'live' && !p.pref.is_so && p.pref.is_live
|
||||||
if p.attr == 'live' && p.first_run() && !p.pref.is_live && !p.pref.is_so {
|
if p.attr == 'live' && p.first_pass() && !p.pref.is_live && !p.pref.is_so {
|
||||||
println('INFO: run `v -live program.v` if you want to use [live] functions')
|
println('INFO: run `v -live program.v` if you want to use [live] functions')
|
||||||
}
|
}
|
||||||
if is_pub {
|
if is_pub {
|
||||||
@ -136,7 +137,7 @@ fn (p mut Parser) fn_decl() {
|
|||||||
p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)')
|
p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)')
|
||||||
}
|
}
|
||||||
// Don't allow modifying types from a different module
|
// Don't allow modifying types from a different module
|
||||||
if !p.first_run() && !p.builtin_pkg && T.mod != p.mod {
|
if !p.first_pass() && !p.builtin_pkg && T.mod != p.mod {
|
||||||
println('T.mod=$T.mod')
|
println('T.mod=$T.mod')
|
||||||
println('p.mod=$p.mod')
|
println('p.mod=$p.mod')
|
||||||
p.error('cannot define new methods on non-local type `$receiver_typ`')
|
p.error('cannot define new methods on non-local type `$receiver_typ`')
|
||||||
@ -203,7 +204,7 @@ fn (p mut Parser) fn_decl() {
|
|||||||
if !is_c && !p.builtin_pkg && p.mod != 'main' && receiver_typ.len == 0 {
|
if !is_c && !p.builtin_pkg && p.mod != 'main' && receiver_typ.len == 0 {
|
||||||
f.name = p.prepend_pkg(f.name)
|
f.name = p.prepend_pkg(f.name)
|
||||||
}
|
}
|
||||||
if p.first_run() && p.table.known_fn(f.name) && receiver_typ.len == 0 {
|
if p.first_pass() && p.table.known_fn(f.name) && receiver_typ.len == 0 {
|
||||||
existing_fn := p.table.find_fn(f.name)
|
existing_fn := p.table.find_fn(f.name)
|
||||||
// This existing function could be defined as C decl before (no body), then we don't need to throw an erro
|
// This existing function could be defined as C decl before (no body), then we don't need to throw an erro
|
||||||
if !existing_fn.is_decl {
|
if !existing_fn.is_decl {
|
||||||
@ -213,13 +214,19 @@ fn (p mut Parser) fn_decl() {
|
|||||||
// Generic?
|
// Generic?
|
||||||
mut is_generic := false
|
mut is_generic := false
|
||||||
if p.tok == .lt {
|
if p.tok == .lt {
|
||||||
|
is_generic = true
|
||||||
p.next()
|
p.next()
|
||||||
gen_type := p.check_name()
|
gen_type := p.check_name()
|
||||||
if gen_type != 'T' {
|
if gen_type != 'T' {
|
||||||
p.error('only `T` is allowed as a generic type for now')
|
p.error('only `T` is allowed as a generic type for now')
|
||||||
}
|
}
|
||||||
p.check(.gt)
|
p.check(.gt)
|
||||||
is_generic = true
|
if p.first_pass() {
|
||||||
|
p.table.register_generic_fn(f.name)
|
||||||
|
} else {
|
||||||
|
//gen_types := p.table.fn_gen_types(f.name)
|
||||||
|
//println(gen_types)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Args (...)
|
// Args (...)
|
||||||
p.fn_args(mut f)
|
p.fn_args(mut f)
|
||||||
@ -247,14 +254,13 @@ fn (p mut Parser) fn_decl() {
|
|||||||
p.fgen(' ')
|
p.fgen(' ')
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
}
|
}
|
||||||
// Register option ? type
|
// Register ?option type
|
||||||
if typ.starts_with('Option_') {
|
if typ.starts_with('Option_') {
|
||||||
p.cgen.typedefs << 'typedef Option $typ;'
|
p.cgen.typedefs << 'typedef Option $typ;'
|
||||||
}
|
}
|
||||||
// Register function
|
// Register function
|
||||||
f.typ = typ
|
f.typ = typ
|
||||||
mut str_args := f.str_args(p.table)
|
mut str_args := f.str_args(p.table)
|
||||||
// println('FN .decL $f.name typ=$f.typ str_args="$str_args"')
|
|
||||||
// Special case for main() args
|
// Special case for main() args
|
||||||
if f.name == 'main' && !has_receiver {
|
if f.name == 'main' && !has_receiver {
|
||||||
if str_args != '' || typ != 'void' {
|
if str_args != '' || typ != 'void' {
|
||||||
@ -263,21 +269,19 @@ fn (p mut Parser) fn_decl() {
|
|||||||
typ = 'int'
|
typ = 'int'
|
||||||
str_args = 'int argc, char** argv'
|
str_args = 'int argc, char** argv'
|
||||||
}
|
}
|
||||||
|
dll_export_linkage := if p.os == .msvc && p.attr == 'live' && p.pref.is_so {
|
||||||
mut dll_export_linkage := ''
|
'__declspec(dllexport) '
|
||||||
|
} else {
|
||||||
if p.os == .msvc && p.attr == 'live' && p.pref.is_so {
|
''
|
||||||
dll_export_linkage = '__declspec(dllexport) '
|
|
||||||
}
|
}
|
||||||
|
// if p.file_name != '.vwebtmpl.v' {
|
||||||
// Only in C code generate User_register() instead of register()
|
// oif !p.name.ends_with('_vwebview') {
|
||||||
|
if !p.is_vweb {
|
||||||
|
//println('SETTING CUR FN TO "$f.name" "$p.file_name"')
|
||||||
|
p.cur_fn = f
|
||||||
|
}
|
||||||
|
// Generate `User_register()` instead of `register()`
|
||||||
// Internally it's still stored as "register" in type User
|
// Internally it's still stored as "register" in type User
|
||||||
// mut fn_name_cgen := f.name
|
|
||||||
// if receiver_typ != '' {
|
|
||||||
// fn_name_cgen = '${receiver_typ}_$f.name'
|
|
||||||
// fn_name_cgen = fn_name_cgen.replace(' ', '')
|
|
||||||
// fn_name_cgen = fn_name_cgen.replace('*', '')
|
|
||||||
// }
|
|
||||||
mut fn_name_cgen := p.table.cgen_name(f)
|
mut fn_name_cgen := p.table.cgen_name(f)
|
||||||
// Start generation of the function body
|
// Start generation of the function body
|
||||||
skip_main_in_test := f.name == 'main' && p.pref.is_test
|
skip_main_in_test := f.name == 'main' && p.pref.is_test
|
||||||
@ -285,7 +289,27 @@ fn (p mut Parser) fn_decl() {
|
|||||||
if p.pref.obfuscate {
|
if p.pref.obfuscate {
|
||||||
p.genln('; // $f.name')
|
p.genln('; // $f.name')
|
||||||
}
|
}
|
||||||
p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {')
|
// Generate this function's body for all generic types
|
||||||
|
if is_generic {
|
||||||
|
gen_types := p.table.fn_gen_types(f.name)
|
||||||
|
// Remember current scanner position, go back here for each type
|
||||||
|
// TODO remove this once tokens are cached in `new_parser()`
|
||||||
|
cur_pos := p.scanner.pos
|
||||||
|
cur_tok := p.tok
|
||||||
|
cur_lit := p.lit
|
||||||
|
for gen_type in gen_types {
|
||||||
|
p.genln('$dll_export_linkage$typ ${fn_name_cgen}_$gen_type($str_args) {')
|
||||||
|
p.genln('// T start $p.pass ${p.strtok()}')
|
||||||
|
p.cur_gen_type = gen_type // TODO support more than T
|
||||||
|
p.statements()
|
||||||
|
p.scanner.pos = cur_pos
|
||||||
|
p.tok = cur_tok
|
||||||
|
p.lit = cur_lit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if is_fn_header {
|
if is_fn_header {
|
||||||
p.genln('$typ $fn_name_cgen($str_args);')
|
p.genln('$typ $fn_name_cgen($str_args);')
|
||||||
@ -294,13 +318,12 @@ fn (p mut Parser) fn_decl() {
|
|||||||
if is_c {
|
if is_c {
|
||||||
p.fgenln('\n')
|
p.fgenln('\n')
|
||||||
}
|
}
|
||||||
p.cur_fn = f
|
|
||||||
// Register the method
|
// Register the method
|
||||||
if receiver_typ != '' {
|
if receiver_typ != '' {
|
||||||
mut receiver_t := p.table.find_type(receiver_typ)
|
mut receiver_t := p.table.find_type(receiver_typ)
|
||||||
// No such type yet? It could be defined later. Create a new type.
|
// No such type yet? It could be defined later. Create a new type.
|
||||||
// struct declaration later will modify it instead of creating a new one.
|
// struct declaration later will modify it instead of creating a new one.
|
||||||
if p.first_run() && receiver_t.name == '' {
|
if p.first_pass() && receiver_t.name == '' {
|
||||||
// println('fn decl !!!!!!! REG PH $receiver_typ')
|
// println('fn decl !!!!!!! REG PH $receiver_typ')
|
||||||
p.table.register_type2(Type {
|
p.table.register_type2(Type {
|
||||||
name: receiver_typ.replace('*', '')
|
name: receiver_typ.replace('*', '')
|
||||||
@ -315,8 +338,9 @@ fn (p mut Parser) fn_decl() {
|
|||||||
// println('register_fn typ=$typ isg=$is_generic')
|
// println('register_fn typ=$typ isg=$is_generic')
|
||||||
p.table.register_fn(f)
|
p.table.register_fn(f)
|
||||||
}
|
}
|
||||||
if is_sig || p.first_run() || is_live || is_fn_header || skip_main_in_test {
|
if is_sig || p.first_pass() || is_live || is_fn_header || skip_main_in_test {
|
||||||
// First pass? Skip the body for now [BIG]
|
// First pass? Skip the body for now
|
||||||
|
// Look for generic calls.
|
||||||
if !is_sig && !is_fn_header {
|
if !is_sig && !is_fn_header {
|
||||||
mut opened_scopes := 0
|
mut opened_scopes := 0
|
||||||
mut closed_scopes := 0
|
mut closed_scopes := 0
|
||||||
@ -328,10 +352,24 @@ fn (p mut Parser) fn_decl() {
|
|||||||
closed_scopes++
|
closed_scopes++
|
||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
|
// find `foo<Bar>()` in function bodies and register generic types
|
||||||
|
// TODO remove this once tokens are cached
|
||||||
|
if p.tok == .gt && p.prev_tok == .name && p.prev_tok2 == .lt &&
|
||||||
|
p.scanner.text[p.scanner.pos-1] != `T` {
|
||||||
|
p.scanner.pos -= 3
|
||||||
|
for p.scanner.pos > 0 && is_name_char(p.scanner.text[p.scanner.pos]) || p.scanner.text[p.scanner.pos] == `.` ||
|
||||||
|
p.scanner.text[p.scanner.pos] == `<` {
|
||||||
|
p.scanner.pos--
|
||||||
|
}
|
||||||
|
p.scanner.pos--
|
||||||
|
p.next()
|
||||||
|
// Run the function in the firt pass to register the generic type
|
||||||
|
p.name_expr()
|
||||||
|
}
|
||||||
if p.tok.is_decl() && !(p.prev_tok == .dot && p.tok == .key_type) {
|
if p.tok.is_decl() && !(p.prev_tok == .dot && p.tok == .key_type) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
//fn body ended, and a new fn attribute declaration like [live] is starting?
|
// fn body ended, and a new fn attribute declaration like [live] is starting?
|
||||||
if closed_scopes > opened_scopes && p.prev_tok == .rcbr {
|
if closed_scopes > opened_scopes && p.prev_tok == .rcbr {
|
||||||
if p.tok == .lsbr {
|
if p.tok == .lsbr {
|
||||||
break
|
break
|
||||||
@ -340,18 +378,18 @@ fn (p mut Parser) fn_decl() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Live code reloading? Load all fns from .so
|
// Live code reloading? Load all fns from .so
|
||||||
if is_live && p.first_run() && p.mod == 'main' {
|
if is_live && p.first_pass() && p.mod == 'main' {
|
||||||
//println('ADDING SO FN $fn_name_cgen')
|
//println('ADDING SO FN $fn_name_cgen')
|
||||||
p.cgen.so_fns << fn_name_cgen
|
p.cgen.so_fns << fn_name_cgen
|
||||||
fn_name_cgen = '(* $fn_name_cgen )'
|
fn_name_cgen = '(* $fn_name_cgen )'
|
||||||
}
|
}
|
||||||
// Actual fn declaration!
|
// Function definition that goes to the top of the C file.
|
||||||
mut fn_decl := '$dll_export_linkage$typ $fn_name_cgen($str_args)'
|
mut fn_decl := '$dll_export_linkage$typ $fn_name_cgen($str_args)'
|
||||||
if p.pref.obfuscate {
|
if p.pref.obfuscate {
|
||||||
fn_decl += '; // ${f.name}'
|
fn_decl += '; // $f.name'
|
||||||
}
|
}
|
||||||
// Add function definition to the top
|
// Add function definition to the top
|
||||||
if !is_c && f.name != 'main' && p.first_run() {
|
if !is_c && f.name != 'main' && p.first_pass() {
|
||||||
// TODO hack to make Volt compile without -embed_vlib
|
// TODO hack to make Volt compile without -embed_vlib
|
||||||
if f.name == 'darwin__nsstring' && p.pref.build_mode == .default_mode {
|
if f.name == 'darwin__nsstring' && p.pref.build_mode == .default_mode {
|
||||||
return
|
return
|
||||||
@ -360,12 +398,10 @@ fn (p mut Parser) fn_decl() {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.attr == 'live' && p.pref.is_so {
|
if p.attr == 'live' && p.pref.is_so {
|
||||||
//p.genln('// live_function body start')
|
//p.genln('// live_function body start')
|
||||||
p.genln('pthread_mutex_lock(&live_fn_mutex);')
|
p.genln('pthread_mutex_lock(&live_fn_mutex);')
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.name == 'main' || f.name == 'WinMain' {
|
if f.name == 'main' || f.name == 'WinMain' {
|
||||||
p.genln('init_consts();')
|
p.genln('init_consts();')
|
||||||
if p.table.imports.contains('os') {
|
if p.table.imports.contains('os') {
|
||||||
@ -404,13 +440,18 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
|||||||
// println('IS SIG .key_returnING tok=${p.strtok()}')
|
// println('IS SIG .key_returnING tok=${p.strtok()}')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// We are in profile mode? Start counting at the beginning of the function (save current time).
|
// Profiling mode? Start counting at the beginning of the function (save current time).
|
||||||
if p.pref.is_prof && f.name != 'main' && f.name != 'time__ticks' {
|
if p.pref.is_prof && f.name != 'main' && f.name != 'time__ticks' {
|
||||||
p.genln('double _PROF_START = time__ticks();//$f.name')
|
p.genln('double _PROF_START = time__ticks();//$f.name')
|
||||||
cgen_name := p.table.cgen_name(f)
|
cgen_name := p.table.cgen_name(f)
|
||||||
f.defer_text = ' ${cgen_name}_time += time__ticks() - _PROF_START;'
|
f.defer_text = ' ${cgen_name}_time += time__ticks() - _PROF_START;'
|
||||||
}
|
}
|
||||||
|
if is_generic {
|
||||||
|
// Don't need to generate body for the actual generic definition
|
||||||
|
p.cgen.nogen = true
|
||||||
|
}
|
||||||
p.statements_no_rcbr()
|
p.statements_no_rcbr()
|
||||||
|
p.cgen.nogen = false
|
||||||
// Print counting result after all statements in main
|
// Print counting result after all statements in main
|
||||||
if p.pref.is_prof && f.name == 'main' {
|
if p.pref.is_prof && f.name == 'main' {
|
||||||
p.genln(p.print_prof_counters())
|
p.genln(p.print_prof_counters())
|
||||||
@ -420,12 +461,10 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
|||||||
if typ != 'void' && !p.returns && f.name != 'main' && f.name != 'WinMain' {
|
if typ != 'void' && !p.returns && f.name != 'main' && f.name != 'WinMain' {
|
||||||
p.error('$f.name must return "$typ"')
|
p.error('$f.name must return "$typ"')
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.attr == 'live' && p.pref.is_so {
|
if p.attr == 'live' && p.pref.is_so {
|
||||||
//p.genln('// live_function body end')
|
//p.genln('// live_function body end')
|
||||||
p.genln('pthread_mutex_unlock(&live_fn_mutex);')
|
p.genln('pthread_mutex_unlock(&live_fn_mutex);')
|
||||||
}
|
}
|
||||||
|
|
||||||
// {} closed correctly? scope_level should be 0
|
// {} closed correctly? scope_level should be 0
|
||||||
if p.mod == 'main' {
|
if p.mod == 'main' {
|
||||||
// println(p.cur_fn.scope_level)
|
// println(p.cur_fn.scope_level)
|
||||||
@ -436,12 +475,16 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
|||||||
// Make sure all vars in this function are used (only in main for now)
|
// Make sure all vars in this function are used (only in main for now)
|
||||||
// if p.builtin_pkg || p.mod == 'os' ||p.mod=='http'{
|
// if p.builtin_pkg || p.mod == 'os' ||p.mod=='http'{
|
||||||
if p.mod != 'main' {
|
if p.mod != 'main' {
|
||||||
p.genln('}')
|
if !is_generic {
|
||||||
|
p.genln('}')
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.check_unused_variables()
|
p.check_unused_variables()
|
||||||
p.cur_fn = EmptyFn
|
p.cur_fn = EmptyFn
|
||||||
p.genln('}')
|
if !is_generic {
|
||||||
|
p.genln('}')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) check_unused_variables() {
|
fn (p mut Parser) check_unused_variables() {
|
||||||
@ -539,6 +582,9 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type string) {
|
fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type string) {
|
||||||
|
if p.fileis('vtalk2.v') {
|
||||||
|
//println('FN CALL k $f.name')
|
||||||
|
}
|
||||||
if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.pkg != p.mod {
|
if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.pkg != p.mod {
|
||||||
p.error('function `$f.name` is private')
|
p.error('function `$f.name` is private')
|
||||||
}
|
}
|
||||||
@ -550,7 +596,25 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
|||||||
p.error('use `malloc()` instead of `C.malloc()`')
|
p.error('use `malloc()` instead of `C.malloc()`')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cgen_name := p.table.cgen_name(f)
|
mut cgen_name := p.table.cgen_name(f)
|
||||||
|
p.next()
|
||||||
|
mut gen_type := ''
|
||||||
|
if p.tok == .lt {
|
||||||
|
p.check(.lt)
|
||||||
|
gen_type = p.check_name()
|
||||||
|
// `foo<Bar>()`
|
||||||
|
// If we are in the first pass, we need to add `Bar` type to the generic function `foo`,
|
||||||
|
// so that generic `foo`s body can be generated for each type in the second pass.
|
||||||
|
if p.first_pass() {
|
||||||
|
println('reging $gen_type in $f.name')
|
||||||
|
p.table.register_generic_fn_type(f.name, gen_type)
|
||||||
|
// Function bodies are skipped in the first passed, we only need to register the generic type here.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cgen_name += '_' + gen_type
|
||||||
|
|
||||||
|
p.check(.gt)
|
||||||
|
}
|
||||||
// if p.pref.is_prof {
|
// if p.pref.is_prof {
|
||||||
// p.cur_fn.called_fns << cgen_name
|
// p.cur_fn.called_fns << cgen_name
|
||||||
// }
|
// }
|
||||||
@ -590,8 +654,8 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
|||||||
}
|
}
|
||||||
p.cgen.set_placeholder(method_ph, '$cast $method_call')
|
p.cgen.set_placeholder(method_ph, '$cast $method_call')
|
||||||
}
|
}
|
||||||
p.next()
|
// foo<Bar>()
|
||||||
p.fn_call_args(f)
|
p.fn_call_args(mut f)
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
p.calling_c = false
|
p.calling_c = false
|
||||||
// println('end of fn call typ=$f.typ')
|
// println('end of fn call typ=$f.typ')
|
||||||
@ -648,7 +712,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|||||||
'\nreturn values instead: `foo(n mut int)` => `foo(n int) int`')
|
'\nreturn values instead: `foo(n mut int)` => `foo(n int) int`')
|
||||||
}
|
}
|
||||||
for name in names {
|
for name in names {
|
||||||
if !p.first_run() && !p.table.known_type(typ) {
|
if !p.first_pass() && !p.table.known_type(typ) {
|
||||||
p.error('fn_args: unknown type $typ')
|
p.error('fn_args: unknown type $typ')
|
||||||
}
|
}
|
||||||
if is_mut {
|
if is_mut {
|
||||||
@ -678,7 +742,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// foo *(1, 2, 3, mut bar)*
|
// foo *(1, 2, 3, mut bar)*
|
||||||
fn (p mut Parser) fn_call_args(f *Fn) *Fn {
|
fn (p mut Parser) fn_call_args(f mut Fn) *Fn {
|
||||||
// p.gen('(')
|
// p.gen('(')
|
||||||
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
||||||
// C func. # of args is not known
|
// C func. # of args is not known
|
||||||
@ -762,10 +826,10 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn {
|
|||||||
p.error(error_msg)
|
p.error(error_msg)
|
||||||
}
|
}
|
||||||
p.cgen.resetln(p.cgen.cur_line.left(index))
|
p.cgen.resetln(p.cgen.cur_line.left(index))
|
||||||
p.create_type_string(T, name)
|
p.scanner.create_type_string(T, name)
|
||||||
p.cgen.cur_line.replace(typ, '')
|
p.cgen.cur_line.replace(typ, '')
|
||||||
p.next()
|
p.next()
|
||||||
return p.fn_call_args(f)
|
return p.fn_call_args(mut f)
|
||||||
}
|
}
|
||||||
p.error(error_msg)
|
p.error(error_msg)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ fn (p mut Parser) gen_json_for_type(typ Type) {
|
|||||||
if t == 'int' || t == 'string' || t == 'bool' {
|
if t == 'int' || t == 'string' || t == 'bool' {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if p.first_run() {
|
if p.first_pass() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// println('gen_json_for_type( $typ.name )')
|
// println('gen_json_for_type( $typ.name )')
|
||||||
|
@ -179,7 +179,7 @@ fn (v mut V) compile() {
|
|||||||
p.parse()
|
p.parse()
|
||||||
}
|
}
|
||||||
// Main pass
|
// Main pass
|
||||||
cgen.run = Pass.main
|
cgen.pass = Pass.main
|
||||||
if v.pref.is_play {
|
if v.pref.is_play {
|
||||||
cgen.genln('#define VPLAY (1) ')
|
cgen.genln('#define VPLAY (1) ')
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
module main
|
module main
|
||||||
|
|
||||||
import os
|
import (
|
||||||
import rand
|
os
|
||||||
import strings
|
rand
|
||||||
|
strings
|
||||||
|
)
|
||||||
|
|
||||||
struct Var {
|
struct Var {
|
||||||
mut:
|
mut:
|
||||||
@ -35,6 +37,7 @@ struct Parser {
|
|||||||
file_path string // "/home/user/hello.v"
|
file_path string // "/home/user/hello.v"
|
||||||
file_name string // "hello.v"
|
file_name string // "hello.v"
|
||||||
mut:
|
mut:
|
||||||
|
v *V
|
||||||
scanner *Scanner
|
scanner *Scanner
|
||||||
// tokens []Token // TODO cache all tokens, right now they have to be scanned twice
|
// tokens []Token // TODO cache all tokens, right now they have to be scanned twice
|
||||||
token_idx int
|
token_idx int
|
||||||
@ -45,7 +48,7 @@ mut:
|
|||||||
cgen *CGen
|
cgen *CGen
|
||||||
table *Table
|
table *Table
|
||||||
import_table *FileImportTable // Holds imports for just the file being parsed
|
import_table *FileImportTable // Holds imports for just the file being parsed
|
||||||
run Pass // TODO rename `run` to `pass`
|
pass Pass
|
||||||
os OS
|
os OS
|
||||||
mod string
|
mod string
|
||||||
inside_const bool
|
inside_const bool
|
||||||
@ -73,6 +76,8 @@ mut:
|
|||||||
var_decl_name string // To allow declaring the variable so that it can be used in the struct initialization
|
var_decl_name string // To allow declaring the variable so that it can be used in the struct initialization
|
||||||
building_v bool
|
building_v bool
|
||||||
is_alloc bool // Whether current expression resulted in an allocation
|
is_alloc bool // Whether current expression resulted in an allocation
|
||||||
|
cur_gen_type string // "App" to replace "T" in current generic function
|
||||||
|
is_vweb bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -84,29 +89,30 @@ const (
|
|||||||
MaxModuleDepth = 4
|
MaxModuleDepth = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
fn (c mut V) new_parser(path string, run Pass) Parser {
|
fn (v mut V) new_parser(path string, pass Pass) Parser {
|
||||||
c.log('new_parser("$path")')
|
v.log('new_parser("$path")')
|
||||||
c.cgen.run = run
|
v.cgen.pass = pass
|
||||||
mut p := Parser {
|
mut p := Parser {
|
||||||
|
v: v
|
||||||
file_path: path
|
file_path: path
|
||||||
file_name: path.all_after('/')
|
file_name: path.all_after('/')
|
||||||
scanner: new_scanner(path)
|
scanner: new_scanner(path)
|
||||||
table: c.table
|
table: v.table
|
||||||
import_table: new_file_import_table(path)
|
import_table: new_file_import_table(path)
|
||||||
cur_fn: EmptyFn
|
cur_fn: EmptyFn
|
||||||
cgen: c.cgen
|
cgen: v.cgen
|
||||||
is_script: (c.pref.is_script && path == c.dir)
|
is_script: (v.pref.is_script && path == v.dir)
|
||||||
pref: c.pref
|
pref: v.pref
|
||||||
os: c.os
|
os: v.os
|
||||||
run: run
|
pass: pass
|
||||||
vroot: c.vroot
|
vroot: v.vroot
|
||||||
building_v: !c.pref.is_repl && (path.contains('compiler/') ||
|
building_v: !v.pref.is_repl && (path.contains('compiler/') ||
|
||||||
path.contains('v/vlib'))
|
path.contains('v/vlib'))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.cgen.line_directives = c.pref.is_debuggable
|
v.cgen.line_directives = v.pref.is_debuggable
|
||||||
c.cgen.file = path
|
v.cgen.file = path
|
||||||
|
|
||||||
p.next()
|
p.next()
|
||||||
// p.scanner.debug_tokens()
|
// p.scanner.debug_tokens()
|
||||||
@ -132,7 +138,7 @@ fn (p &Parser) log(s string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) parse() {
|
fn (p mut Parser) parse() {
|
||||||
p.log('\nparse() run=$p.run file=$p.file_name tok=${p.strtok()}')// , "script_file=", script_file)
|
p.log('\nparse() run=$p.pass file=$p.file_name tok=${p.strtok()}')// , "script_file=", script_file)
|
||||||
// `module main` is not required if it's a single file program
|
// `module main` is not required if it's a single file program
|
||||||
if p.is_script || p.pref.is_test {
|
if p.is_script || p.pref.is_test {
|
||||||
p.mod = 'main'
|
p.mod = 'main'
|
||||||
@ -158,7 +164,7 @@ fn (p mut Parser) parse() {
|
|||||||
p.table.register_package(fq_mod)
|
p.table.register_package(fq_mod)
|
||||||
// replace "." with "_dot_" in module name for C variable names
|
// replace "." with "_dot_" in module name for C variable names
|
||||||
p.mod = fq_mod.replace('.', '_dot_')
|
p.mod = fq_mod.replace('.', '_dot_')
|
||||||
if p.run == .imports {
|
if p.pass == .imports {
|
||||||
for p.tok == .key_import && p.peek() != .key_const {
|
for p.tok == .key_import && p.peek() != .key_const {
|
||||||
p.imports()
|
p.imports()
|
||||||
}
|
}
|
||||||
@ -256,7 +262,7 @@ fn (p mut Parser) parse() {
|
|||||||
p.cur_fn = MainFn
|
p.cur_fn = MainFn
|
||||||
p.check_unused_variables()
|
p.check_unused_variables()
|
||||||
}
|
}
|
||||||
if false && !p.first_run() && p.fileis('main.v') {
|
if false && !p.first_pass() && p.fileis('main.v') {
|
||||||
out := os.create('/var/tmp/fmt.v') or {
|
out := os.create('/var/tmp/fmt.v') or {
|
||||||
panic('failed to create fmt.v')
|
panic('failed to create fmt.v')
|
||||||
return
|
return
|
||||||
@ -270,7 +276,7 @@ fn (p mut Parser) parse() {
|
|||||||
if p.is_script && !p.pref.is_test {
|
if p.is_script && !p.pref.is_test {
|
||||||
// cur_fn is empty since there was no fn main declared
|
// cur_fn is empty since there was no fn main declared
|
||||||
// we need to set it to save and find variables
|
// we need to set it to save and find variables
|
||||||
if p.first_run() {
|
if p.first_pass() {
|
||||||
if p.cur_fn.name == '' {
|
if p.cur_fn.name == '' {
|
||||||
p.cur_fn = MainFn
|
p.cur_fn = MainFn
|
||||||
}
|
}
|
||||||
@ -320,43 +326,6 @@ fn (p mut Parser) imports() {
|
|||||||
p.register_import()
|
p.register_import()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) register_import() {
|
|
||||||
if p.tok != .name {
|
|
||||||
p.error('bad import format')
|
|
||||||
}
|
|
||||||
mut pkg := p.lit.trim_space()
|
|
||||||
mut mod_alias := pkg
|
|
||||||
// submodule support
|
|
||||||
mut depth := 1
|
|
||||||
p.next()
|
|
||||||
for p.tok == .dot {
|
|
||||||
p.check(.dot)
|
|
||||||
submodule := p.check_name()
|
|
||||||
mod_alias = submodule
|
|
||||||
pkg += '.' + submodule
|
|
||||||
depth++
|
|
||||||
if depth > MaxModuleDepth {
|
|
||||||
p.error('module depth of $MaxModuleDepth exceeded: $pkg')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// aliasing (import encoding.base64 as b64)
|
|
||||||
if p.tok == .key_as && p.peek() == .name {
|
|
||||||
p.check(.key_as)
|
|
||||||
mod_alias = p.check_name()
|
|
||||||
}
|
|
||||||
// add import to file scope import table
|
|
||||||
p.import_table.register_alias(mod_alias, pkg)
|
|
||||||
// Make sure there are no duplicate imports
|
|
||||||
if p.table.imports.contains(pkg) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.log('adding import $pkg')
|
|
||||||
p.table.imports << pkg
|
|
||||||
p.table.register_package(pkg)
|
|
||||||
|
|
||||||
p.fgenln(' ' + pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) const_decl() {
|
fn (p mut Parser) const_decl() {
|
||||||
is_import := p.tok == .key_import
|
is_import := p.tok == .key_import
|
||||||
p.inside_const = true
|
p.inside_const = true
|
||||||
@ -371,9 +340,9 @@ fn (p mut Parser) const_decl() {
|
|||||||
for p.tok == .name {
|
for p.tok == .name {
|
||||||
// `Age = 20`
|
// `Age = 20`
|
||||||
mut name := p.check_name()
|
mut name := p.check_name()
|
||||||
if p.pref.is_play && ! (name[0] >= `A` && name[0] <= `Z`) {
|
//if ! (name[0] >= `A` && name[0] <= `Z`) {
|
||||||
p.error('const name must be capitalized')
|
//p.error('const name must be capitalized')
|
||||||
}
|
//}
|
||||||
// Imported consts (like GL_TRIANG.leS) dont need pkg prepended (gl__GL_TRIANG.leS)
|
// Imported consts (like GL_TRIANG.leS) dont need pkg prepended (gl__GL_TRIANG.leS)
|
||||||
if !is_import {
|
if !is_import {
|
||||||
name = p.prepend_pkg(name)
|
name = p.prepend_pkg(name)
|
||||||
@ -383,11 +352,11 @@ fn (p mut Parser) const_decl() {
|
|||||||
p.check_space(.assign)
|
p.check_space(.assign)
|
||||||
typ = p.expression()
|
typ = p.expression()
|
||||||
}
|
}
|
||||||
if p.first_run() && !is_import && p.table.known_const(name) {
|
if p.first_pass() && !is_import && p.table.known_const(name) {
|
||||||
p.error('redefinition of `$name`')
|
p.error('redefinition of `$name`')
|
||||||
}
|
}
|
||||||
p.table.register_const(name, typ, p.mod, is_import)
|
p.table.register_const(name, typ, p.mod, is_import)
|
||||||
if p.run == .main && !is_import {
|
if p.pass == .main && !is_import {
|
||||||
// TODO hack
|
// TODO hack
|
||||||
// cur_line has const's value right now. if it's just a number, then optimize generation:
|
// cur_line has const's value right now. if it's just a number, then optimize generation:
|
||||||
// output a #define so that we don't pollute the binary with unnecessary global vars
|
// output a #define so that we don't pollute the binary with unnecessary global vars
|
||||||
@ -437,7 +406,7 @@ fn (p mut Parser) interface_method(field_name, receiver string) &Fn {
|
|||||||
is_method: true
|
is_method: true
|
||||||
receiver_typ: receiver
|
receiver_typ: receiver
|
||||||
}
|
}
|
||||||
p.log('is interface. field=$field_name run=$p.run')
|
p.log('is interface. field=$field_name run=$p.pass')
|
||||||
p.fn_args(mut method)
|
p.fn_args(mut method)
|
||||||
if p.scanner.has_gone_over_line_end() {
|
if p.scanner.has_gone_over_line_end() {
|
||||||
method.typ = 'void'
|
method.typ = 'void'
|
||||||
@ -488,7 +457,7 @@ fn (p mut Parser) struct_decl() {
|
|||||||
if !is_c && !p.builtin_pkg && p.mod != 'main' {
|
if !is_c && !p.builtin_pkg && p.mod != 'main' {
|
||||||
name = p.prepend_pkg(name)
|
name = p.prepend_pkg(name)
|
||||||
}
|
}
|
||||||
if p.run == .decl && p.table.known_type(name) {
|
if p.pass == .decl && p.table.known_type(name) {
|
||||||
p.error('`$name` redeclared')
|
p.error('`$name` redeclared')
|
||||||
}
|
}
|
||||||
// Generate type definitions
|
// Generate type definitions
|
||||||
@ -543,7 +512,7 @@ fn (p mut Parser) struct_decl() {
|
|||||||
fmt_max_len = field.name.len
|
fmt_max_len = field.name.len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.run')
|
println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
|
||||||
*/
|
*/
|
||||||
mut did_gen_something := false
|
mut did_gen_something := false
|
||||||
for p.tok != .rcbr {
|
for p.tok != .rcbr {
|
||||||
@ -616,7 +585,7 @@ fn (p mut Parser) struct_decl() {
|
|||||||
p.fgenln('')
|
p.fgenln('')
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_ph && p.first_run() {
|
if !is_ph && p.first_pass() {
|
||||||
p.table.register_type2(typ)
|
p.table.register_type2(typ)
|
||||||
//println('registering 1 nrfields=$typ.fields.len')
|
//println('registering 1 nrfields=$typ.fields.len')
|
||||||
}
|
}
|
||||||
@ -654,7 +623,7 @@ fn (p mut Parser) enum_decl(_enum_name string) {
|
|||||||
fields << field
|
fields << field
|
||||||
p.fgenln('')
|
p.fgenln('')
|
||||||
name := '${p.mod}__${enum_name}_$field'
|
name := '${p.mod}__${enum_name}_$field'
|
||||||
if p.run == .main {
|
if p.pass == .main {
|
||||||
p.cgen.consts << '#define $name $val'
|
p.cgen.consts << '#define $name $val'
|
||||||
}
|
}
|
||||||
if p.tok == .comma {
|
if p.tok == .comma {
|
||||||
@ -738,19 +707,14 @@ if p.scanner.line_comment != '' {
|
|||||||
|
|
||||||
fn (p mut Parser) error(s string) {
|
fn (p mut Parser) error(s string) {
|
||||||
// Dump all vars and types for debugging
|
// Dump all vars and types for debugging
|
||||||
if false {
|
if p.pref.is_debug {
|
||||||
//file_types := os.create('$TmpPath/types')
|
|
||||||
//file_vars := os.create('$TmpPath/vars')
|
|
||||||
// ////debug("ALL T", q.J(p.table.types))
|
|
||||||
// os.write_to_file('/var/tmp/lang.types', '')//pes(p.table.types))
|
// os.write_to_file('/var/tmp/lang.types', '')//pes(p.table.types))
|
||||||
// //debug("ALL V", q.J(p.table.vars))
|
|
||||||
// os.write_to_file('/var/tmp/lang.vars', q.J(p.table.vars))
|
// os.write_to_file('/var/tmp/lang.vars', q.J(p.table.vars))
|
||||||
//file_types.close()
|
os.write_file('fns.txt', p.table.debug_fns())
|
||||||
//file_vars.close()
|
|
||||||
}
|
|
||||||
if p.pref.is_verbose {
|
|
||||||
println('pass=$p.run fn=`$p.cur_fn.name`')
|
|
||||||
}
|
}
|
||||||
|
//if p.pref.is_verbose {
|
||||||
|
println('pass=$p.pass fn=`$p.cur_fn.name`')
|
||||||
|
//}
|
||||||
p.cgen.save()
|
p.cgen.save()
|
||||||
// V git pull hint
|
// V git pull hint
|
||||||
cur_path := os.getwd()
|
cur_path := os.getwd()
|
||||||
@ -769,8 +733,8 @@ fn (p mut Parser) error(s string) {
|
|||||||
p.scanner.error(s.replace('array_', '[]').replace('__', '.').replace('Option_', '?'))
|
p.scanner.error(s.replace('array_', '[]').replace('__', '.').replace('Option_', '?'))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p &Parser) first_run() bool {
|
fn (p &Parser) first_pass() bool {
|
||||||
return p.run == .decl
|
return p.pass == .decl
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return Type instead of string?
|
// TODO return Type instead of string?
|
||||||
@ -890,13 +854,13 @@ fn (p mut Parser) get_type() string {
|
|||||||
mut t := p.table.find_type(typ)
|
mut t := p.table.find_type(typ)
|
||||||
// "typ" not found? try "pkg__typ"
|
// "typ" not found? try "pkg__typ"
|
||||||
if t.name == '' && !p.builtin_pkg {
|
if t.name == '' && !p.builtin_pkg {
|
||||||
// && !p.first_run() {
|
// && !p.first_pass() {
|
||||||
if !typ.contains('array_') && p.mod != 'main' && !typ.contains('__') &&
|
if !typ.contains('array_') && p.mod != 'main' && !typ.contains('__') &&
|
||||||
!typ.starts_with('[') {
|
!typ.starts_with('[') {
|
||||||
typ = p.prepend_pkg(typ)
|
typ = p.prepend_pkg(typ)
|
||||||
}
|
}
|
||||||
t = p.table.find_type(typ)
|
t = p.table.find_type(typ)
|
||||||
if t.name == '' && !p.pref.translated && !p.first_run() && !typ.starts_with('[') {
|
if t.name == '' && !p.pref.translated && !p.first_pass() && !typ.starts_with('[') {
|
||||||
println('get_type() bad type')
|
println('get_type() bad type')
|
||||||
// println('all registered types:')
|
// println('all registered types:')
|
||||||
// for q in p.table.types {
|
// for q in p.table.types {
|
||||||
@ -919,7 +883,7 @@ fn (p mut Parser) get_type() string {
|
|||||||
}
|
}
|
||||||
else if is_arr {
|
else if is_arr {
|
||||||
typ = 'array_$typ'
|
typ = 'array_$typ'
|
||||||
// p.log('ARR TYPE="$typ" run=$p.run')
|
// p.log('ARR TYPE="$typ" run=$p.pass')
|
||||||
// We come across "[]User" etc ?
|
// We come across "[]User" etc ?
|
||||||
p.register_array(typ)
|
p.register_array(typ)
|
||||||
}
|
}
|
||||||
@ -1050,14 +1014,6 @@ fn (p mut Parser) vh_genln(s string) {
|
|||||||
p.vh_lines << s
|
p.vh_lines << s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) fmt_inc() {
|
|
||||||
p.scanner.fmt_indent++
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) fmt_dec() {
|
|
||||||
p.scanner.fmt_indent--
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) statement(add_semi bool) string {
|
fn (p mut Parser) statement(add_semi bool) string {
|
||||||
p.cgen.is_tmp = false
|
p.cgen.is_tmp = false
|
||||||
tok := p.tok
|
tok := p.tok
|
||||||
@ -1342,13 +1298,17 @@ fn (p mut Parser) bterm() string {
|
|||||||
|
|
||||||
// also called on *, &, @, . (enum)
|
// also called on *, &, @, . (enum)
|
||||||
fn (p mut Parser) name_expr() string {
|
fn (p mut Parser) name_expr() string {
|
||||||
p.log('\nname expr() pass=$p.run tok=${p.tok.str()} $p.lit')
|
if p.fileis('vtalk') {
|
||||||
|
//println('\nname expr() pass=$p.pass tok=${p.tok.str()} lit="$p.lit" ${p.scanner.line_nr+1}')
|
||||||
|
}
|
||||||
// print('known type:')
|
// print('known type:')
|
||||||
// println(p.table.known_type(p.lit))
|
// println(p.table.known_type(p.lit))
|
||||||
// hack for struct_init TODO
|
// hack for struct_init TODO
|
||||||
|
/*
|
||||||
hack_pos := p.scanner.pos
|
hack_pos := p.scanner.pos
|
||||||
hack_tok := p.tok
|
hack_tok := p.tok
|
||||||
hack_lit := p.lit
|
hack_lit := p.lit
|
||||||
|
*/
|
||||||
ph := p.cgen.add_placeholder()
|
ph := p.cgen.add_placeholder()
|
||||||
// amp
|
// amp
|
||||||
ptr := p.tok == .amp
|
ptr := p.tok == .amp
|
||||||
@ -1379,7 +1339,7 @@ fn (p mut Parser) name_expr() string {
|
|||||||
}
|
}
|
||||||
// enum value? (`color == .green`)
|
// enum value? (`color == .green`)
|
||||||
if p.tok == .dot {
|
if p.tok == .dot {
|
||||||
//println('got enum dot val $p.left_type pass=$p.run $p.scanner.line_nr left=$p.left_type')
|
//println('got enum dot val $p.left_type pass=$p.pass $p.scanner.line_nr left=$p.left_type')
|
||||||
T := p.find_type(p.expected_type)
|
T := p.find_type(p.expected_type)
|
||||||
if T.is_enum {
|
if T.is_enum {
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
@ -1445,7 +1405,7 @@ fn (p mut Parser) name_expr() string {
|
|||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
// if known_type || is_c_struct_init || (p.first_run() && p.peek() == .lcbr) {
|
// if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) {
|
||||||
// known type? int(4.5) or Color.green (enum)
|
// known type? int(4.5) or Color.green (enum)
|
||||||
if p.table.known_type(name) {
|
if p.table.known_type(name) {
|
||||||
// float(5), byte(0), (*int)(ptr) etc
|
// float(5), byte(0), (*int)(ptr) etc
|
||||||
@ -1480,16 +1440,24 @@ fn (p mut Parser) name_expr() string {
|
|||||||
}
|
}
|
||||||
else if p.peek() == .lcbr {
|
else if p.peek() == .lcbr {
|
||||||
// go back to name start (pkg.name)
|
// go back to name start (pkg.name)
|
||||||
|
/*
|
||||||
p.scanner.pos = hack_pos
|
p.scanner.pos = hack_pos
|
||||||
p.tok = hack_tok
|
p.tok = hack_tok
|
||||||
p.lit = hack_lit
|
p.lit = hack_lit
|
||||||
|
*/
|
||||||
// TODO hack. If it's a C type, we may need to add struct before declaration:
|
// TODO hack. If it's a C type, we may need to add struct before declaration:
|
||||||
// a := &C.A{} ==> struct A* a = malloc(sizeof(struct A));
|
// a := &C.A{} ==> struct A* a = malloc(sizeof(struct A));
|
||||||
if is_c_struct_init {
|
if is_c_struct_init {
|
||||||
p.is_c_struct_init = true
|
p.is_c_struct_init = true
|
||||||
p.cgen.insert_before('struct /*c struct init*/')
|
p.cgen.insert_before('struct /*c struct init*/')
|
||||||
}
|
}
|
||||||
return p.struct_init(is_c_struct_init)
|
if ptr {
|
||||||
|
name += '*' // `&User{}` => type `User*`
|
||||||
|
}
|
||||||
|
if name == 'T' {
|
||||||
|
name = p.cur_gen_type
|
||||||
|
}
|
||||||
|
return p.struct_init(name, is_c_struct_init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// C fn
|
// C fn
|
||||||
@ -1529,7 +1497,7 @@ fn (p mut Parser) name_expr() string {
|
|||||||
mut f := p.table.find_fn(name)
|
mut f := p.table.find_fn(name)
|
||||||
if f.name == '' {
|
if f.name == '' {
|
||||||
// We are in a second pass, that means this function was not defined, throw an error.
|
// We are in a second pass, that means this function was not defined, throw an error.
|
||||||
if !p.first_run() {
|
if !p.first_pass() {
|
||||||
// V script? Try os module.
|
// V script? Try os module.
|
||||||
if p.v_script {
|
if p.v_script {
|
||||||
name = name.replace('main__', 'os__')
|
name = name.replace('main__', 'os__')
|
||||||
@ -1554,7 +1522,8 @@ fn (p mut Parser) name_expr() string {
|
|||||||
}
|
}
|
||||||
// no () after func, so func is an argument, just gen its name
|
// no () after func, so func is an argument, just gen its name
|
||||||
// TODO verify this and handle errors
|
// TODO verify this and handle errors
|
||||||
if p.peek() != .lpar {
|
peek := p.peek()
|
||||||
|
if peek != .lpar && peek != .lt {
|
||||||
p.gen(p.table.cgen_name(f))
|
p.gen(p.table.cgen_name(f))
|
||||||
p.next()
|
p.next()
|
||||||
return 'void*'
|
return 'void*'
|
||||||
@ -1564,6 +1533,9 @@ fn (p mut Parser) name_expr() string {
|
|||||||
// p.error('`$f.name` used as value')
|
// p.error('`$f.name` used as value')
|
||||||
}
|
}
|
||||||
p.log('calling function')
|
p.log('calling function')
|
||||||
|
if p.fileis('vtalk') {
|
||||||
|
//println('calling fn $f.name')
|
||||||
|
}
|
||||||
p.fn_call(f, 0, '', '')
|
p.fn_call(f, 0, '', '')
|
||||||
// dot after a function call: `get_user().age`
|
// dot after a function call: `get_user().age`
|
||||||
if p.tok == .dot {
|
if p.tok == .dot {
|
||||||
@ -1593,7 +1565,7 @@ fn (p mut Parser) var_expr(v Var) string {
|
|||||||
//p.print_tok()
|
//p.print_tok()
|
||||||
T := p.table.find_type(typ)
|
T := p.table.find_type(typ)
|
||||||
p.gen('(')
|
p.gen('(')
|
||||||
p.fn_call_args(T.func)
|
p.fn_call_args(mut T.func)
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
typ = T.func.typ
|
typ = T.func.typ
|
||||||
}
|
}
|
||||||
@ -1601,7 +1573,6 @@ fn (p mut Parser) var_expr(v Var) string {
|
|||||||
// users[0].name
|
// users[0].name
|
||||||
if p.tok == .lsbr {
|
if p.tok == .lsbr {
|
||||||
typ = p.index_expr(typ, fn_ph)
|
typ = p.index_expr(typ, fn_ph)
|
||||||
// ////println('QQQQ KEK $typ')
|
|
||||||
}
|
}
|
||||||
// a.b.c().d chain
|
// a.b.c().d chain
|
||||||
// mut dc := 0
|
// mut dc := 0
|
||||||
@ -1651,6 +1622,7 @@ fn (p mut Parser) var_expr(v Var) string {
|
|||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for debugging only
|
||||||
fn (p &Parser) fileis(s string) bool {
|
fn (p &Parser) fileis(s string) bool {
|
||||||
return p.scanner.file_path.contains(s)
|
return p.scanner.file_path.contains(s)
|
||||||
}
|
}
|
||||||
@ -1659,19 +1631,23 @@ fn (p &Parser) fileis(s string) bool {
|
|||||||
// user.company.name => `str_typ` is `Company`
|
// user.company.name => `str_typ` is `Company`
|
||||||
fn (p mut Parser) dot(str_typ string, method_ph int) string {
|
fn (p mut Parser) dot(str_typ string, method_ph int) string {
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
|
typ := p.find_type(str_typ)
|
||||||
|
if typ.name.len == 0 {
|
||||||
|
p.error('dot(): cannot find type `$str_typ`')
|
||||||
|
}
|
||||||
|
if p.tok == .dollar {
|
||||||
|
p.comptime_method_call(typ)
|
||||||
|
return 'void'
|
||||||
|
}
|
||||||
field_name := p.lit
|
field_name := p.lit
|
||||||
p.fgen(field_name)
|
p.fgen(field_name)
|
||||||
p.log('dot() field_name=$field_name typ=$str_typ')
|
p.log('dot() field_name=$field_name typ=$str_typ')
|
||||||
//if p.fileis('main.v') {
|
//if p.fileis('main.v') {
|
||||||
//println('dot() field_name=$field_name typ=$str_typ prev_tok=${prev_tok.str()}')
|
//println('dot() field_name=$field_name typ=$str_typ prev_tok=${prev_tok.str()}')
|
||||||
//}
|
//}
|
||||||
typ := p.find_type(str_typ)
|
|
||||||
if typ.name.len == 0 {
|
|
||||||
p.error('dot(): cannot find type `$str_typ`')
|
|
||||||
}
|
|
||||||
has_field := p.table.type_has_field(typ, field_name)
|
has_field := p.table.type_has_field(typ, field_name)
|
||||||
has_method := p.table.type_has_method(typ, field_name)
|
has_method := p.table.type_has_method(typ, field_name)
|
||||||
if !typ.is_c && !has_field && !has_method && !p.first_run() {
|
if !typ.is_c && !has_field && !has_method && !p.first_pass() {
|
||||||
if typ.name.starts_with('Option_') {
|
if typ.name.starts_with('Option_') {
|
||||||
opt_type := typ.name.right(7)
|
opt_type := typ.name.right(7)
|
||||||
p.error('unhandled option type: $opt_type?')
|
p.error('unhandled option type: $opt_type?')
|
||||||
@ -1944,7 +1920,7 @@ fn (p mut Parser) index_expr(typ string, fn_ph int) string {
|
|||||||
// returns resulting type
|
// returns resulting type
|
||||||
fn (p mut Parser) expression() string {
|
fn (p mut Parser) expression() string {
|
||||||
if p.scanner.file_path.contains('test_test') {
|
if p.scanner.file_path.contains('test_test') {
|
||||||
println('expression() pass=$p.run tok=')
|
println('expression() pass=$p.pass tok=')
|
||||||
p.print_tok()
|
p.print_tok()
|
||||||
}
|
}
|
||||||
p.cgen('/* expr start*/')
|
p.cgen('/* expr start*/')
|
||||||
@ -2298,32 +2274,6 @@ fn (p mut Parser) char_expr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
|
||||||
t := p.table.find_type(typ)
|
|
||||||
if t.is_enum {
|
|
||||||
return '%d'
|
|
||||||
}
|
|
||||||
switch typ {
|
|
||||||
case 'string': return '%.*s'
|
|
||||||
case 'ustring': return '%.*s'
|
|
||||||
case 'byte', 'int', 'char', 'byte', 'bool', 'u32', 'i32', 'i16', 'u16', 'i8', 'u8': return '%d'
|
|
||||||
case 'f64', 'f32': return '%f'
|
|
||||||
case 'i64', 'u64': return '%lld'
|
|
||||||
case 'byte*', 'byteptr': return '%s'
|
|
||||||
// case 'array_string': return '%s'
|
|
||||||
// case 'array_int': return '%s'
|
|
||||||
case 'void': p.error('cannot interpolate this value')
|
|
||||||
default:
|
|
||||||
if typ.ends_with('*') {
|
|
||||||
return '%p'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t.parent != '' && level == 0 {
|
|
||||||
return p.typ_to_fmt(t.parent, level+1)
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_str(str string) string {
|
fn format_str(str string) string {
|
||||||
str = str.replace('"', '\\"')
|
str = str.replace('"', '\\"')
|
||||||
$if windows {
|
$if windows {
|
||||||
@ -2546,7 +2496,7 @@ fn (p mut Parser) array_init() string {
|
|||||||
if is_fixed_size {
|
if is_fixed_size {
|
||||||
p.next()
|
p.next()
|
||||||
p.gen(' }')
|
p.gen(' }')
|
||||||
if !p.first_run() {
|
if !p.first_pass() {
|
||||||
// If we are defining a const array, we don't need to specify the type:
|
// If we are defining a const array, we don't need to specify the type:
|
||||||
// `a = {1,2,3}`, not `a = (int[]) {1,2,3}`
|
// `a = {1,2,3}`, not `a = (int[]) {1,2,3}`
|
||||||
if p.inside_const {
|
if p.inside_const {
|
||||||
@ -2567,8 +2517,8 @@ fn (p mut Parser) array_init() string {
|
|||||||
}
|
}
|
||||||
p.gen(' })')
|
p.gen(' })')
|
||||||
// p.gen('$new_arr($vals.len, $vals.len, sizeof($typ), ($typ[]) $c_arr );')
|
// p.gen('$new_arr($vals.len, $vals.len, sizeof($typ), ($typ[]) $c_arr );')
|
||||||
// TODO why need !first_run()?? Otherwise it goes to the very top of the out.c file
|
// TODO why need !first_pass()?? Otherwise it goes to the very top of the out.c file
|
||||||
if !p.first_run() {
|
if !p.first_pass() {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
p.cgen.set_placeholder(new_arr_ph, '$new_arr($i, $i, sizeof($typ), ($typ[]) {EMPTY_STRUCT_INIT ')
|
p.cgen.set_placeholder(new_arr_ph, '$new_arr($i, $i, sizeof($typ), ($typ[]) {EMPTY_STRUCT_INIT ')
|
||||||
} else {
|
} else {
|
||||||
@ -2580,32 +2530,9 @@ fn (p mut Parser) array_init() string {
|
|||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) register_array(typ string) {
|
fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||||
if typ.contains('*') {
|
|
||||||
println('bad arr $typ')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !p.table.known_type(typ) {
|
|
||||||
p.register_type_with_parent(typ, 'array')
|
|
||||||
p.cgen.typedefs << 'typedef array $typ;'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn (p mut Parser) register_map(typ string) {
|
|
||||||
if typ.contains('*') {
|
|
||||||
println('bad map $typ')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !p.table.known_type(typ) {
|
|
||||||
p.register_type_with_parent(typ, 'map')
|
|
||||||
p.cgen.typedefs << 'typedef map $typ;'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) struct_init(is_c_struct_init bool) string {
|
|
||||||
p.is_struct_init = true
|
p.is_struct_init = true
|
||||||
mut typ := p.get_type()
|
p.next()
|
||||||
p.scanner.fmt_out.cut(typ.len)
|
p.scanner.fmt_out.cut(typ.len)
|
||||||
ptr := typ.contains('*')
|
ptr := typ.contains('*')
|
||||||
// TODO tm struct struct bug
|
// TODO tm struct struct bug
|
||||||
@ -2810,153 +2737,6 @@ fn os_name_to_ifdef(name string) string {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) comp_time() {
|
|
||||||
p.check(.dollar)
|
|
||||||
if p.tok == .key_if {
|
|
||||||
p.check(.key_if)
|
|
||||||
p.fspace()
|
|
||||||
not := p.tok == .not
|
|
||||||
if not {
|
|
||||||
p.check(.not)
|
|
||||||
}
|
|
||||||
name := p.check_name()
|
|
||||||
p.fspace()
|
|
||||||
if name in SupportedPlatforms {
|
|
||||||
ifdef_name := os_name_to_ifdef(name)
|
|
||||||
if not {
|
|
||||||
p.genln('#ifndef $ifdef_name')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p.genln('#ifdef $ifdef_name')
|
|
||||||
}
|
|
||||||
p.check(.lcbr)
|
|
||||||
p.statements_no_rcbr()
|
|
||||||
if ! (p.tok == .dollar && p.peek() == .key_else) {
|
|
||||||
p.genln('#endif')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
println('Supported platforms:')
|
|
||||||
println(SupportedPlatforms)
|
|
||||||
p.error('unknown platform `$name`')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if p.tok == .key_for {
|
|
||||||
p.next()
|
|
||||||
name := p.check_name()
|
|
||||||
if name != 'field' {
|
|
||||||
p.error('for field only')
|
|
||||||
}
|
|
||||||
p.check(.key_in)
|
|
||||||
p.check_name()
|
|
||||||
p.check(.dot)
|
|
||||||
p.check_name()// fields
|
|
||||||
p.check(.lcbr)
|
|
||||||
// for p.tok != .rcbr && p.tok != .eof {
|
|
||||||
res_name := p.check_name()
|
|
||||||
println(res_name)
|
|
||||||
p.check(.dot)
|
|
||||||
p.check(.dollar)
|
|
||||||
p.check(.name)
|
|
||||||
p.check(.assign)
|
|
||||||
p.cgen.start_tmp()
|
|
||||||
p.bool_expression()
|
|
||||||
val := p.cgen.end_tmp()
|
|
||||||
println(val)
|
|
||||||
p.check(.rcbr)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
else if p.tok == .key_else {
|
|
||||||
p.next()
|
|
||||||
p.check(.lcbr)
|
|
||||||
p.genln('#else')
|
|
||||||
p.statements_no_rcbr()
|
|
||||||
p.genln('#endif')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p.error('bad comptime expr')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) chash() {
|
|
||||||
hash := p.lit.trim_space()
|
|
||||||
// println('chsh() file=$p.file is_sig=${p.is_sig()} hash="$hash"')
|
|
||||||
p.next()
|
|
||||||
is_sig := p.is_sig()
|
|
||||||
if is_sig {
|
|
||||||
// p.cgen.nogen = true
|
|
||||||
}
|
|
||||||
if hash.starts_with('flag ') {
|
|
||||||
mut flag := hash.right(5)
|
|
||||||
// No the right os? Skip!
|
|
||||||
// mut ok := true
|
|
||||||
if hash.contains('linux') && p.os != .linux {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else if hash.contains('darwin') && p.os != .mac {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else if hash.contains('windows') && (p.os != .windows && p.os != .msvc) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Remove "linux" etc from flag
|
|
||||||
if flag.contains('linux') || flag.contains('darwin') || flag.contains('windows') {
|
|
||||||
pos := flag.index(' ')
|
|
||||||
flag = flag.right(pos)
|
|
||||||
}
|
|
||||||
has_vroot := flag.contains('@VROOT')
|
|
||||||
flag = flag.trim_space().replace('@VROOT', p.vroot)
|
|
||||||
if p.table.flags.contains(flag) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.log('adding flag "$flag"')
|
|
||||||
// `@VROOT/thirdparty/glad/glad.o`, make sure it exists, otherwise build it
|
|
||||||
if has_vroot && flag.contains('.o') {
|
|
||||||
if p.os == .msvc {
|
|
||||||
build_thirdparty_obj_file_with_msvc(flag)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
build_thirdparty_obj_file(flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.table.flags << flag
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if hash.starts_with('include') {
|
|
||||||
if p.first_run() && !is_sig {
|
|
||||||
p.cgen.includes << '#$hash'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if hash.starts_with('typedef') {
|
|
||||||
if p.first_run() {
|
|
||||||
p.cgen.typedefs << '$hash'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO remove after ui_mac.m is removed
|
|
||||||
else if hash.contains('embed') {
|
|
||||||
pos := hash.index('embed') + 5
|
|
||||||
file := hash.right(pos)
|
|
||||||
if p.pref.build_mode != BuildMode.default_mode {
|
|
||||||
p.genln('#include $file')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if hash.contains('define') {
|
|
||||||
// Move defines on top
|
|
||||||
p.cgen.includes << '#$hash'
|
|
||||||
}
|
|
||||||
else if hash == 'v' {
|
|
||||||
println('v script')
|
|
||||||
//p.v_script = true
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if !p.can_chash {
|
|
||||||
p.error('bad token `#` (embedding C code is no longer supported)')
|
|
||||||
}
|
|
||||||
p.genln(hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) if_st(is_expr bool, elif_depth int) string {
|
fn (p mut Parser) if_st(is_expr bool, elif_depth int) string {
|
||||||
if is_expr {
|
if is_expr {
|
||||||
if p.fileis('if_expr') {
|
if p.fileis('if_expr') {
|
||||||
@ -3272,7 +3052,7 @@ fn (p mut Parser) switch_statement() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) assert_statement() {
|
fn (p mut Parser) assert_statement() {
|
||||||
if p.first_run() {
|
if p.first_pass() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.check(.key_assert)
|
p.check(.key_assert)
|
||||||
@ -3442,22 +3222,6 @@ fn (p mut Parser) js_decode() string {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_compile_time_const(s string) bool {
|
|
||||||
s = s.trim_space()
|
|
||||||
if s == '' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.contains('\'') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for c in s {
|
|
||||||
if ! ((c >= `0` && c <= `9`) || c == `.`) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
fn (p &Parser) building_v() bool {
|
fn (p &Parser) building_v() bool {
|
||||||
cur_dir := os.getwd()
|
cur_dir := os.getwd()
|
||||||
@ -3495,48 +3259,3 @@ fn (p mut Parser) defer_st() {
|
|||||||
p.genln('*/')
|
p.genln('*/')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// fmt helpers
|
|
||||||
fn (scanner mut Scanner) fgen(s string) {
|
|
||||||
if scanner.fmt_line_empty {
|
|
||||||
s = strings.repeat(`\t`, scanner.fmt_indent) + s
|
|
||||||
}
|
|
||||||
scanner.fmt_out.write(s)
|
|
||||||
scanner.fmt_line_empty = false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (scanner mut Scanner) fgenln(s string) {
|
|
||||||
if scanner.fmt_line_empty {
|
|
||||||
s = strings.repeat(`\t`, scanner.fmt_indent) + s
|
|
||||||
}
|
|
||||||
scanner.fmt_out.writeln(s)
|
|
||||||
scanner.fmt_line_empty = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) fgen(s string) {
|
|
||||||
p.scanner.fgen(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) fspace() {
|
|
||||||
p.fgen(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) fgenln(s string) {
|
|
||||||
p.scanner.fgenln(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) peek() Token {
|
|
||||||
for {
|
|
||||||
p.cgen.line = p.scanner.line_nr + 1
|
|
||||||
tok := p.scanner.peek()
|
|
||||||
if tok != .nl {
|
|
||||||
return tok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) create_type_string(T Type, name string) {
|
|
||||||
p.scanner.create_type_string(T, name)
|
|
||||||
}
|
|
||||||
|
@ -758,3 +758,4 @@ fn (s mut Scanner) create_type_string(T Type, name string) {
|
|||||||
s.line_nr = line
|
s.line_nr = line
|
||||||
s.inside_string = inside_string
|
s.inside_string = inside_string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
155
compiler/table.v
155
compiler/table.v
@ -5,12 +5,14 @@
|
|||||||
module main
|
module main
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
import strings
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
mut:
|
mut:
|
||||||
types []Type
|
types []Type
|
||||||
consts []Var
|
consts []Var
|
||||||
fns map[string]Fn
|
fns map[string]Fn
|
||||||
|
generic_fns []GenTable //map[string]GenTable // generic_fns['listen_and_serve'] == ['Blog', 'Forum']
|
||||||
obf_ids map[string]int // obf_ids['myfunction'] == 23
|
obf_ids map[string]int // obf_ids['myfunction'] == 23
|
||||||
packages []string // List of all modules registered by the application
|
packages []string // List of all modules registered by the application
|
||||||
imports []string // List of all imports
|
imports []string // List of all imports
|
||||||
@ -19,6 +21,11 @@ mut:
|
|||||||
obfuscate bool
|
obfuscate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GenTable {
|
||||||
|
fn_name string
|
||||||
|
types []string
|
||||||
|
}
|
||||||
|
|
||||||
// Holds import information scoped to the parsed file
|
// Holds import information scoped to the parsed file
|
||||||
struct FileImportTable {
|
struct FileImportTable {
|
||||||
mut:
|
mut:
|
||||||
@ -28,11 +35,11 @@ mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum AccessMod {
|
enum AccessMod {
|
||||||
private // private imkey_mut
|
private // private immutable
|
||||||
private_mut // private key_mut
|
private_mut // private mutable
|
||||||
public // public immkey_mut (readonly)
|
public // public immutable (readonly)
|
||||||
public_mut // public, but key_mut only in this module
|
public_mut // public, but mutable only in this module
|
||||||
public_mut_mut // public and key_mut both inside and outside (not recommended to use, that's why it's so verbose)
|
public_mut_mut // public and mutable both inside and outside (not recommended to use, that's why it's so verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Type {
|
struct Type {
|
||||||
@ -47,6 +54,7 @@ mut:
|
|||||||
is_interface bool
|
is_interface bool
|
||||||
is_enum bool
|
is_enum bool
|
||||||
enum_vals []string
|
enum_vals []string
|
||||||
|
gen_types []string
|
||||||
// This field is used for types that are not defined yet but are known to exist.
|
// This field is used for types that are not defined yet but are known to exist.
|
||||||
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
|
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
|
||||||
// This information is needed in the first pass.
|
// This information is needed in the first pass.
|
||||||
@ -98,6 +106,14 @@ fn (f Fn) str() string {
|
|||||||
return '$f.name($str_args) $f.typ'
|
return '$f.name($str_args) $f.typ'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (t &Table) debug_fns() string {
|
||||||
|
mut s := strings.new_builder(1000)
|
||||||
|
for _, f in t.fns {
|
||||||
|
s.writeln(f.name)
|
||||||
|
}
|
||||||
|
return s.str()
|
||||||
|
}
|
||||||
|
|
||||||
// fn (types array_Type) print_to_file(f string) {
|
// fn (types array_Type) print_to_file(f string) {
|
||||||
// }
|
// }
|
||||||
const (
|
const (
|
||||||
@ -121,6 +137,8 @@ fn new_table(obfuscate bool) *Table {
|
|||||||
mut t := &Table {
|
mut t := &Table {
|
||||||
obf_ids: map[string]int{}
|
obf_ids: map[string]int{}
|
||||||
fns: map[string]Fn{}
|
fns: map[string]Fn{}
|
||||||
|
//generic_fns: map[string]GenTable{}
|
||||||
|
generic_fns: []GenTable
|
||||||
obfuscate: obfuscate
|
obfuscate: obfuscate
|
||||||
}
|
}
|
||||||
t.register_type('int')
|
t.register_type('int')
|
||||||
@ -142,6 +160,7 @@ fn new_table(obfuscate bool) *Table {
|
|||||||
t.register_type('bool')
|
t.register_type('bool')
|
||||||
t.register_type('void')
|
t.register_type('void')
|
||||||
t.register_type('voidptr')
|
t.register_type('voidptr')
|
||||||
|
t.register_type('T')
|
||||||
t.register_type('va_list')
|
t.register_type('va_list')
|
||||||
t.register_const('stdin', 'int', 'main', false)
|
t.register_const('stdin', 'int', 'main', false)
|
||||||
t.register_const('stdout', 'int', 'main', false)
|
t.register_const('stdout', 'int', 'main', false)
|
||||||
@ -169,6 +188,66 @@ fn (t mut Table) register_package(pkg string) {
|
|||||||
t.packages << pkg
|
t.packages << pkg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) register_import() {
|
||||||
|
if p.tok != .name {
|
||||||
|
p.error('bad import format')
|
||||||
|
}
|
||||||
|
mut pkg := p.lit.trim_space()
|
||||||
|
mut mod_alias := pkg
|
||||||
|
// submodule support
|
||||||
|
mut depth := 1
|
||||||
|
p.next()
|
||||||
|
for p.tok == .dot {
|
||||||
|
p.check(.dot)
|
||||||
|
submodule := p.check_name()
|
||||||
|
mod_alias = submodule
|
||||||
|
pkg += '.' + submodule
|
||||||
|
depth++
|
||||||
|
if depth > MaxModuleDepth {
|
||||||
|
p.error('module depth of $MaxModuleDepth exceeded: $pkg')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// aliasing (import encoding.base64 as b64)
|
||||||
|
if p.tok == .key_as && p.peek() == .name {
|
||||||
|
p.check(.key_as)
|
||||||
|
mod_alias = p.check_name()
|
||||||
|
}
|
||||||
|
// add import to file scope import table
|
||||||
|
p.import_table.register_alias(mod_alias, pkg)
|
||||||
|
// Make sure there are no duplicate imports
|
||||||
|
if p.table.imports.contains(pkg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.log('adding import $pkg')
|
||||||
|
p.table.imports << pkg
|
||||||
|
p.table.register_package(pkg)
|
||||||
|
|
||||||
|
p.fgenln(' ' + pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn (p mut Parser) register_array(typ string) {
|
||||||
|
if typ.contains('*') {
|
||||||
|
println('bad arr $typ')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !p.table.known_type(typ) {
|
||||||
|
p.register_type_with_parent(typ, 'array')
|
||||||
|
p.cgen.typedefs << 'typedef array $typ;'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) register_map(typ string) {
|
||||||
|
if typ.contains('*') {
|
||||||
|
println('bad map $typ')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !p.table.known_type(typ) {
|
||||||
|
p.register_type_with_parent(typ, 'map')
|
||||||
|
p.cgen.typedefs << 'typedef map $typ;'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (table &Table) known_pkg(pkg string) bool {
|
fn (table &Table) known_pkg(pkg string) bool {
|
||||||
return pkg in table.packages
|
return pkg in table.packages
|
||||||
}
|
}
|
||||||
@ -679,6 +758,72 @@ fn is_valid_int_const(val, typ string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (t mut Table) register_generic_fn(fn_name string) {
|
||||||
|
t.generic_fns << GenTable{fn_name, []string}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Table) fn_gen_types(fn_name string) []string {
|
||||||
|
for _, f in t.generic_fns {
|
||||||
|
if f.fn_name == fn_name {
|
||||||
|
return f.types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `foo<Bar>()`
|
||||||
|
// fn_name == 'foo'
|
||||||
|
// typ == 'Bar'
|
||||||
|
fn (t mut Table) register_generic_fn_type(fn_name, typ string) {
|
||||||
|
for i, f in t.generic_fns {
|
||||||
|
if f.fn_name == fn_name {
|
||||||
|
t.generic_fns[i].types << typ
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
||||||
|
t := p.table.find_type(typ)
|
||||||
|
if t.is_enum {
|
||||||
|
return '%d'
|
||||||
|
}
|
||||||
|
switch typ {
|
||||||
|
case 'string': return '%.*s'
|
||||||
|
case 'ustring': return '%.*s'
|
||||||
|
case 'byte', 'int', 'char', 'byte', 'bool', 'u32', 'i32', 'i16', 'u16', 'i8', 'u8': return '%d'
|
||||||
|
case 'f64', 'f32': return '%f'
|
||||||
|
case 'i64', 'u64': return '%lld'
|
||||||
|
case 'byte*', 'byteptr': return '%s'
|
||||||
|
// case 'array_string': return '%s'
|
||||||
|
// case 'array_int': return '%s'
|
||||||
|
case 'void': p.error('cannot interpolate this value')
|
||||||
|
default:
|
||||||
|
if typ.ends_with('*') {
|
||||||
|
return '%p'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.parent != '' && level == 0 {
|
||||||
|
return p.typ_to_fmt(t.parent, level+1)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_compile_time_const(s string) bool {
|
||||||
|
s = s.trim_space()
|
||||||
|
if s == '' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.contains('\'') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for c in s {
|
||||||
|
if ! ((c >= `0` && c <= `9`) || c == `.`) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Once we have a module format we can read from module file instead
|
// Once we have a module format we can read from module file instead
|
||||||
// this is not optimal
|
// this is not optimal
|
||||||
fn (table &Table) qualify_module(mod string, file_path string) string {
|
fn (table &Table) qualify_module(mod string, file_path string) string {
|
||||||
|
54
compiler/vfmt.v
Normal file
54
compiler/vfmt.v
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
module main
|
||||||
|
|
||||||
|
import strings
|
||||||
|
|
||||||
|
// fmt helpers
|
||||||
|
fn (scanner mut Scanner) fgen(s string) {
|
||||||
|
if scanner.fmt_line_empty {
|
||||||
|
s = strings.repeat(`\t`, scanner.fmt_indent) + s
|
||||||
|
}
|
||||||
|
scanner.fmt_out.write(s)
|
||||||
|
scanner.fmt_line_empty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (scanner mut Scanner) fgenln(s string) {
|
||||||
|
if scanner.fmt_line_empty {
|
||||||
|
s = strings.repeat(`\t`, scanner.fmt_indent) + s
|
||||||
|
}
|
||||||
|
scanner.fmt_out.writeln(s)
|
||||||
|
scanner.fmt_line_empty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fgen(s string) {
|
||||||
|
p.scanner.fgen(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fspace() {
|
||||||
|
p.fgen(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fgenln(s string) {
|
||||||
|
p.scanner.fgenln(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) peek() Token {
|
||||||
|
for {
|
||||||
|
tok := p.scanner.peek()
|
||||||
|
if tok != .nl {
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fmt_inc() {
|
||||||
|
p.scanner.fmt_indent++
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fmt_dec() {
|
||||||
|
p.scanner.fmt_indent--
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ struct Option {
|
|||||||
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
||||||
fn opt_ok(data voidptr, size int) Option {
|
fn opt_ok(data voidptr, size int) Option {
|
||||||
if size > 255 {
|
if size > 255 {
|
||||||
panic('option size too big: $size (max is 255)')
|
panic('option size too big: $size (max is 255), this is a temporary limit')
|
||||||
}
|
}
|
||||||
res := Option {
|
res := Option {
|
||||||
ok: true
|
ok: true
|
||||||
|
@ -189,11 +189,11 @@ pub fn (req &Request) do() Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unescape(s string) string {
|
pub fn unescape(s string) string {
|
||||||
return string(byteptr(C.curl_unescape(s.str, s.len)))
|
return string(byteptr(C.curl_unescape(s.str, s.len)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape(s string) string {
|
pub fn escape(s string) string {
|
||||||
return string(byteptr(C.curl_escape(s.str, s.len)))
|
return string(byteptr(C.curl_escape(s.str, s.len)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +79,10 @@ pub fn socket(family int, _type int, proto int) ?Socket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sockfd := C.socket(family, _type, proto)
|
sockfd := C.socket(family, _type, proto)
|
||||||
|
one:=1
|
||||||
|
// This is needed so that there are no problems with reusing the
|
||||||
|
// same port after the application exits.
|
||||||
|
C.setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int))
|
||||||
if sockfd == 0 {
|
if sockfd == 0 {
|
||||||
return error('socket: init failed')
|
return error('socket: init failed')
|
||||||
}
|
}
|
||||||
@ -117,11 +121,12 @@ pub fn (s Socket) bind(port int) ?int {
|
|||||||
// put socket into passive mode and wait to receive
|
// put socket into passive mode and wait to receive
|
||||||
pub fn (s Socket) listen() ?int {
|
pub fn (s Socket) listen() ?int {
|
||||||
backlog := 128
|
backlog := 128
|
||||||
res := C.listen(s.sockfd, backlog)
|
res := int(C.listen(s.sockfd, backlog))
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
return error('socket: listen failed')
|
return error('socket: listen failed')
|
||||||
}
|
}
|
||||||
return int(res)
|
println('liisten res = $res')
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// put socket into passive mode with user specified backlog and wait to receive
|
// put socket into passive mode with user specified backlog and wait to receive
|
||||||
@ -139,6 +144,7 @@ pub fn (s Socket) listen_backlog(backlog int) ?int {
|
|||||||
|
|
||||||
// helper method to create, bind, and listen given port number
|
// helper method to create, bind, and listen given port number
|
||||||
pub fn listen(port int) ?Socket {
|
pub fn listen(port int) ?Socket {
|
||||||
|
println('net.listen($port)')
|
||||||
s := socket(AF_INET, SOCK_STREAM, 0) or {
|
s := socket(AF_INET, SOCK_STREAM, 0) or {
|
||||||
return error(err)
|
return error(err)
|
||||||
}
|
}
|
||||||
@ -153,6 +159,7 @@ pub fn listen(port int) ?Socket {
|
|||||||
|
|
||||||
// accept first connection request from socket queue
|
// accept first connection request from socket queue
|
||||||
pub fn (s Socket) accept() ?Socket {
|
pub fn (s Socket) accept() ?Socket {
|
||||||
|
println('accept()')
|
||||||
addr := C.sockaddr_storage{}
|
addr := C.sockaddr_storage{}
|
||||||
size := 128 // sizeof(sockaddr_storage)
|
size := 128 // sizeof(sockaddr_storage)
|
||||||
sockfd := C.accept(s.sockfd, &addr, &size)
|
sockfd := C.accept(s.sockfd, &addr, &size)
|
||||||
@ -251,3 +258,47 @@ pub fn (s Socket) close() ?int {
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
MAX_READ = 400
|
||||||
|
)
|
||||||
|
pub fn (s Socket) write(str string) {
|
||||||
|
line := '$str\r\n'
|
||||||
|
C.write(s.sockfd, line.str, line.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (s Socket) read_line() string {
|
||||||
|
mut res := ''
|
||||||
|
for {
|
||||||
|
println('.')
|
||||||
|
mut buf := malloc(MAX_READ)
|
||||||
|
n := int(C.recv(s.sockfd, buf, MAX_READ-1, 0))
|
||||||
|
println('numbytes=$n')
|
||||||
|
if n == -1 {
|
||||||
|
println('recv failed')
|
||||||
|
// TODO
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// println('resp len=$numbytes')
|
||||||
|
buf[n] = `\0`
|
||||||
|
// C.printf('!!buf= "%s" n=%d\n', buf,n)
|
||||||
|
line := string(buf)
|
||||||
|
res += line
|
||||||
|
// Reached a newline. That's an end of an IRC message
|
||||||
|
// TODO dont need ends_with check ?
|
||||||
|
if line.ends_with('\n') || n < MAX_READ - 1 {
|
||||||
|
// println('NL')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if line.ends_with('\r\n') {
|
||||||
|
// println('RNL')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
108
vlib/time/time.v
108
vlib/time/time.v
@ -56,10 +56,110 @@ pub fn random() Time {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unix(u int) Time {
|
const (
|
||||||
mut t := &C.tm{!}
|
// The unsigned zero year for internal calculations.
|
||||||
t = C.localtime(&u)
|
// Must be 1 mod 400, and times before it will not compute correctly,
|
||||||
return convert_ctime(t)
|
// but otherwise can be changed at will.
|
||||||
|
absoluteZeroYear = i64(-292277022399)
|
||||||
|
|
||||||
|
secondsPerMinute = 60
|
||||||
|
secondsPerHour = 60 * secondsPerMinute
|
||||||
|
secondsPerDay = 24 * secondsPerHour
|
||||||
|
secondsPerWeek = 7 * secondsPerDay
|
||||||
|
daysPer400Years = 365*400 + 97
|
||||||
|
daysPer100Years = 365*100 + 24
|
||||||
|
daysPer4Years = 365*4 + 1
|
||||||
|
|
||||||
|
daysBefore = [
|
||||||
|
0,
|
||||||
|
31,
|
||||||
|
31 + 28,
|
||||||
|
31 + 28 + 31,
|
||||||
|
31 + 28 + 31 + 30,
|
||||||
|
31 + 28 + 31 + 30 + 31,
|
||||||
|
31 + 28 + 31 + 30 + 31 + 30,
|
||||||
|
31 + 28 + 31 + 30 + 31 + 30 + 31,
|
||||||
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
|
||||||
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
|
||||||
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
|
||||||
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
|
||||||
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
|
||||||
|
]
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Based on Go's time package.
|
||||||
|
// Copyright 2009 The Go Authors.
|
||||||
|
pub fn unix(abs int) Time {
|
||||||
|
// Split into time and day.
|
||||||
|
mut d := abs / secondsPerDay
|
||||||
|
|
||||||
|
// Account for 400 year cycles.
|
||||||
|
mut n := d / daysPer400Years
|
||||||
|
mut y := 400 * n
|
||||||
|
d -= daysPer400Years * n
|
||||||
|
|
||||||
|
// Cut off 100-year cycles.
|
||||||
|
// The last cycle has one extra leap year, so on the last day
|
||||||
|
// of that year, day / daysPer100Years will be 4 instead of 3.
|
||||||
|
// Cut it back down to 3 by subtracting n>>2.
|
||||||
|
n = d / daysPer100Years
|
||||||
|
n -= n >> 2
|
||||||
|
y += 100 * n
|
||||||
|
d -= daysPer100Years * n
|
||||||
|
|
||||||
|
// Cut off 4-year cycles.
|
||||||
|
// The last cycle has a missing leap year, which does not
|
||||||
|
// affect the computation.
|
||||||
|
n = d / daysPer4Years
|
||||||
|
y += 4 * n
|
||||||
|
d -= daysPer4Years * n
|
||||||
|
|
||||||
|
// Cut off years within a 4-year cycle.
|
||||||
|
// The last year is a leap year, so on the last day of that year,
|
||||||
|
// day / 365 will be 4 instead of 3. Cut it back down to 3
|
||||||
|
// by subtracting n>>2.
|
||||||
|
n = d / 365
|
||||||
|
n -= n >> 2
|
||||||
|
y += n
|
||||||
|
d -= 365 * n
|
||||||
|
|
||||||
|
yday := int(d)
|
||||||
|
mut day := yday
|
||||||
|
|
||||||
|
year := abs / int(3.154e+7) + 1970 //int(i64(y) + absoluteZeroYear)
|
||||||
|
hour := int(abs%secondsPerDay) / secondsPerHour
|
||||||
|
minute := int(abs % secondsPerHour) / secondsPerMinute
|
||||||
|
second := int(abs % secondsPerMinute)
|
||||||
|
|
||||||
|
if is_leap_year(year) {
|
||||||
|
// Leap year
|
||||||
|
if day > 31+29-1 {
|
||||||
|
// After leap day; pretend it wasn't there.
|
||||||
|
day--
|
||||||
|
} else if day == 31+29-1 {
|
||||||
|
// Leap day.
|
||||||
|
day = 29
|
||||||
|
return Time{year:year, month:2, day:day, hour:hour, minute: minute, second: second}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimate month on assumption that every month has 31 days.
|
||||||
|
// The estimate may be too low by at most one month, so adjust.
|
||||||
|
mut month := day / 31
|
||||||
|
mut begin := 0
|
||||||
|
end := int(daysBefore[month+1])
|
||||||
|
if day >= end {
|
||||||
|
month++
|
||||||
|
begin = end
|
||||||
|
} else {
|
||||||
|
begin = int(daysBefore[month])
|
||||||
|
}
|
||||||
|
|
||||||
|
month++ // because January is 1
|
||||||
|
day = day - begin + 1
|
||||||
|
return Time{year:year, month: month, day:day, hour:hour, minute: minute, second: second}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_ctime(t tm) Time {
|
pub fn convert_ctime(t tm) Time {
|
||||||
|
@ -38,3 +38,16 @@ fn test_days_in_month() {
|
|||||||
assert check(11, 2001, 30) // November
|
assert check(11, 2001, 30) // November
|
||||||
assert check(12, 2001, 31) // December
|
assert check(12, 2001, 31) // December
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_unix() {
|
||||||
|
t := time.unix(1564366499)
|
||||||
|
assert t.year == 2019
|
||||||
|
assert t.month == 7
|
||||||
|
assert t.day == 29
|
||||||
|
assert t.hour == 2
|
||||||
|
assert t.minute == 14
|
||||||
|
//assert t.second == 32 // TODO broken
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
69
vlib/vweb/tmpl/tmpl.v
Normal file
69
vlib/vweb/tmpl/tmpl.v
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
module tmpl
|
||||||
|
|
||||||
|
import os
|
||||||
|
import strings
|
||||||
|
|
||||||
|
const (
|
||||||
|
STR_START = 'sb.write(\''
|
||||||
|
STR_END = '\' ) '
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
pub fn compile_template(path string) string {
|
||||||
|
//lines := os.read_lines(path)
|
||||||
|
mut html := os.read_file(path) or {
|
||||||
|
panic('html failed')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
mut header := ''
|
||||||
|
if os.file_exists('header.html') {
|
||||||
|
h := os.read_file('header.html') or {
|
||||||
|
panic('html failed')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
header = h
|
||||||
|
}
|
||||||
|
lines := html.split_into_lines()
|
||||||
|
mut s := strings.new_builder(1000)
|
||||||
|
base := path.all_after('/').replace('.html', '')
|
||||||
|
s.writeln('module main import strings fn ${base}_view() string { // this line will get removed becase only function body is embedded
|
||||||
|
mut sb := strings.new_builder(${lines.len * 30})
|
||||||
|
header := \'$header\'
|
||||||
|
_ := header
|
||||||
|
//footer := \'footer\'
|
||||||
|
')
|
||||||
|
s.writeln(STR_START)
|
||||||
|
for line in lines {
|
||||||
|
if line.contains('@if ') {
|
||||||
|
s.writeln(STR_END)
|
||||||
|
pos := line.index('@if')
|
||||||
|
s.writeln('if ' + line.right(pos + 4) + '{')
|
||||||
|
s.writeln(STR_START)
|
||||||
|
}
|
||||||
|
else if line.contains('@end') {
|
||||||
|
s.writeln(STR_END)
|
||||||
|
s.writeln('}')
|
||||||
|
s.writeln(STR_START)
|
||||||
|
}
|
||||||
|
else if line.contains('@else') {
|
||||||
|
s.writeln(STR_END)
|
||||||
|
s.writeln(' } else { ')
|
||||||
|
s.writeln(STR_START)
|
||||||
|
}
|
||||||
|
else if line.contains('@for') {
|
||||||
|
s.writeln(STR_END)
|
||||||
|
pos := line.index('@for')
|
||||||
|
s.writeln('for ' + line.right(pos + 4) + '{')
|
||||||
|
s.writeln(STR_START)
|
||||||
|
}
|
||||||
|
// @name
|
||||||
|
else {
|
||||||
|
s.writeln(line.replace('@', '\x24').replace('\'', '"') )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.writeln(STR_END)
|
||||||
|
s.writeln('tmpl_res := sb.str() ')
|
||||||
|
s.writeln('return tmpl_res }')
|
||||||
|
return s.str()
|
||||||
|
}
|
||||||
|
|
121
vlib/vweb/vweb.v
Normal file
121
vlib/vweb/vweb.v
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
module vweb
|
||||||
|
|
||||||
|
import (
|
||||||
|
os
|
||||||
|
strings
|
||||||
|
net
|
||||||
|
http
|
||||||
|
)
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
pub:
|
||||||
|
req http.Request
|
||||||
|
conn net.Socket
|
||||||
|
post_form map[string]string
|
||||||
|
// TODO Response
|
||||||
|
headers []string
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx Context) write(s string) {
|
||||||
|
//ctx.conn.write(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx Context) redirect(url string) {
|
||||||
|
h := ctx.headers.join('\n')
|
||||||
|
ctx.conn.write('
|
||||||
|
HTTP/1.1 302 Found
|
||||||
|
Location: $url
|
||||||
|
$h
|
||||||
|
')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx mut Context) set_cookie(key, val string) {
|
||||||
|
ctx.set_header('Set-Cookie', '$key=$val')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx Context) get_cookie(key string) string {
|
||||||
|
cookie := ctx.req.headers['Cookie']
|
||||||
|
return cookie.find_between('$key=', ';')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (ctx mut Context) set_header(key, val string) {
|
||||||
|
// ctx.resp.headers[key] = val
|
||||||
|
ctx.headers << '$key: $val'
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx Context) html(html string) {
|
||||||
|
//tmpl := os.read_file(path) or {return}
|
||||||
|
ctx.conn.write('HTTP/1.1 200 OK
|
||||||
|
Content-Type: text/html
|
||||||
|
|
||||||
|
$html
|
||||||
|
')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<T>(port int) {
|
||||||
|
l := net.listen(port) or { panic('failed to listen') return }
|
||||||
|
for {
|
||||||
|
conn := l.accept() or {
|
||||||
|
panic('accept() failed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO move this to handle_conn<T>(conn, app)
|
||||||
|
s := conn.read_line()
|
||||||
|
// Parse the first line
|
||||||
|
// "GET / HTTP/1.1"
|
||||||
|
first_line := s.all_before('\n')
|
||||||
|
vals := first_line.split(' ')
|
||||||
|
mut action := vals[1].right(1).all_before('/')
|
||||||
|
if action == '' {
|
||||||
|
action = 'index'
|
||||||
|
}
|
||||||
|
req := http.Request{
|
||||||
|
headers: map[string]string{}
|
||||||
|
ws_func: 0
|
||||||
|
user_ptr: 0
|
||||||
|
method: vals[0]
|
||||||
|
url: vals[1]
|
||||||
|
}
|
||||||
|
mut app := T{
|
||||||
|
vweb: Context{
|
||||||
|
req: req
|
||||||
|
conn: conn
|
||||||
|
post_form: map[string]string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.init()
|
||||||
|
if req.method == 'POST' {
|
||||||
|
app.vweb.parse_form(s)
|
||||||
|
}
|
||||||
|
println('vweb action = "$action"')
|
||||||
|
if vals.len < 2 {
|
||||||
|
println('no vals for http')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
app.$action()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn (ctx mut Context) parse_form(s string) {
|
||||||
|
if ctx.req.method != 'POST' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pos := s.index('\r\n\r\n')
|
||||||
|
if pos > -1 {
|
||||||
|
mut str_form := s.substr(pos, s.len)
|
||||||
|
str_form = str_form.replace('+', ' ')
|
||||||
|
words := str_form.split('&')
|
||||||
|
for word in words {
|
||||||
|
keyval := word.split('=')
|
||||||
|
key := keyval[0]
|
||||||
|
val := keyval[1]
|
||||||
|
//println('http form $key => $val')
|
||||||
|
ctx.post_form[key] = http.unescape(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user