mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
JavaSript backend (early stage)
This commit is contained in:
parent
982a162fbf
commit
5cc81b91cb
@ -12,9 +12,30 @@ import (
|
||||
fn (v mut V) cc() {
|
||||
// build any thirdparty obj files
|
||||
v.build_thirdparty_obj_files()
|
||||
|
||||
// Just create a c file and exit
|
||||
if v.out_name.ends_with('.c') {
|
||||
// Just create a C/JavaScript file and exit
|
||||
if v.out_name.ends_with('.c') || v.out_name.ends_with('.js') {
|
||||
// Translating V code to JS by launching vjs
|
||||
$if !js {
|
||||
if v.out_name.ends_with('.js') {
|
||||
vexe := os.executable()
|
||||
vjs_path := vexe + 'js'
|
||||
dir := os.dir(vexe)
|
||||
if !os.file_exists(vjs_path) {
|
||||
println('V.js compiler not found, building...')
|
||||
ret := os.system('$vexe -o $vjs_path -os js $dir/compiler')
|
||||
if ret == 0 {
|
||||
println('Done.')
|
||||
} else {
|
||||
println('Failed.')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
ret := os.system('$vjs_path -o $v.out_name $v.dir')
|
||||
if ret == 0 {
|
||||
println('Done. Run it with `node $v.out_name`')
|
||||
}
|
||||
}
|
||||
}
|
||||
os.mv(v.out_name_c, v.out_name)
|
||||
exit(0)
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ module main
|
||||
|
||||
import os
|
||||
import strings
|
||||
import time
|
||||
|
||||
struct CGen {
|
||||
out os.File
|
||||
@ -275,6 +274,7 @@ fn os_name_to_ifdef(name string) string {
|
||||
case 'netbsd': return '__NetBSD__'
|
||||
case 'dragonfly': return '__DragonFly__'
|
||||
case 'msvc': return '_MSC_VER'
|
||||
case 'js': return '_VJS'
|
||||
}
|
||||
cerror('bad os ifdef name "$name"')
|
||||
return ''
|
||||
@ -295,7 +295,7 @@ fn platform_postfix_to_ifdefguard(name string) string {
|
||||
// C struct definitions, ordered
|
||||
// Sort the types, make sure types that are referenced by other types
|
||||
// are added before them.
|
||||
fn (v mut V) c_type_definitions() string {
|
||||
fn (v mut V) type_definitions() string {
|
||||
mut types := []Type // structs that need to be sorted
|
||||
mut builtin_types := []Type // builtin types
|
||||
// builtin types need to be on top
|
||||
@ -318,32 +318,6 @@ fn (v mut V) c_type_definitions() string {
|
||||
types_to_c(types_sorted, v.table)
|
||||
}
|
||||
|
||||
fn types_to_c(types []Type, table &Table) string {
|
||||
mut sb := strings.new_builder(10)
|
||||
for t in types {
|
||||
if t.cat != .union_ && t.cat != .struct_ {
|
||||
continue
|
||||
}
|
||||
//if is_objc {
|
||||
//sb.writeln('@interface $name : $objc_parent { @public')
|
||||
//}
|
||||
//if is_atomic {
|
||||
//sb.write('_Atomic ')
|
||||
//}
|
||||
kind := if t.cat == .union_ {'union'} else {'struct'}
|
||||
sb.writeln('$kind $t.name {')
|
||||
for field in t.fields {
|
||||
sb.writeln(table.cgen_name_type_pair(field.name,
|
||||
field.typ) + ';')
|
||||
}
|
||||
sb.writeln('};\n')
|
||||
//if is_objc {
|
||||
//sb.writeln('@end')
|
||||
//}
|
||||
}
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
// sort structs by dependant fields
|
||||
fn sort_structs(types []Type) []Type {
|
||||
mut dep_graph := new_dep_graph()
|
||||
|
@ -123,4 +123,33 @@ void init_consts();
|
||||
|
||||
'
|
||||
|
||||
js_headers = '
|
||||
|
||||
class array_string {}
|
||||
class array_byte {}
|
||||
class array_int {}
|
||||
class byte {}
|
||||
class double {}
|
||||
class int {}
|
||||
class f64 {}
|
||||
class f32 {}
|
||||
class i64 {}
|
||||
class i32 {}
|
||||
class i16 {}
|
||||
class u64 {}
|
||||
class u32 {}
|
||||
class u16 {}
|
||||
class i8 {}
|
||||
class u8 {}
|
||||
class bool {}
|
||||
class rune {}
|
||||
class map_string {}
|
||||
class map_int {}
|
||||
|
||||
function init_consts() {
|
||||
|
||||
}
|
||||
|
||||
'
|
||||
|
||||
)
|
||||
|
@ -175,9 +175,23 @@ fn (p mut Parser) chash() {
|
||||
println('v script')
|
||||
//p.v_script = true
|
||||
}
|
||||
// Don't parse a non-JS V file (`#-js` flag)
|
||||
else if hash == '-js' {
|
||||
$if js {
|
||||
for p.tok != .eof {
|
||||
p.next()
|
||||
}
|
||||
} $else {
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
if !p.can_chash {
|
||||
p.error('bad token `#` (embedding C code is no longer supported)')
|
||||
$if !js {
|
||||
if !p.can_chash {
|
||||
println('hash="$hash"')
|
||||
println(hash.starts_with('include'))
|
||||
p.error('bad token `#` (embedding C code is no longer supported)')
|
||||
}
|
||||
}
|
||||
p.genln(hash)
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ fn (p mut Parser) is_sig() bool {
|
||||
fn new_fn(mod string, is_public bool) Fn {
|
||||
return Fn {
|
||||
mod: mod
|
||||
local_vars: [Var{} ; MaxLocalVars]
|
||||
local_vars: [Var{}].repeat2(MaxLocalVars)
|
||||
is_public: is_public
|
||||
}
|
||||
}
|
||||
@ -286,7 +286,7 @@ fn (p mut Parser) fn_decl() {
|
||||
}
|
||||
// Generate `User_register()` instead of `register()`
|
||||
// Internally it's still stored as "register" in type User
|
||||
mut fn_name_cgen := p.table.cgen_name(f)
|
||||
mut fn_name_cgen := p.table.fn_gen_name(f)
|
||||
// Start generation of the function body
|
||||
skip_main_in_test := f.name == 'main' && p.pref.is_test
|
||||
if !is_c && !is_live && !is_sig && !is_fn_header && !skip_main_in_test {
|
||||
@ -312,7 +312,7 @@ fn (p mut Parser) fn_decl() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {')
|
||||
p.gen_fn_decl(f, typ, str_args)
|
||||
}
|
||||
}
|
||||
if is_fn_header {
|
||||
@ -451,9 +451,9 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
||||
// 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' {
|
||||
p.genln('double _PROF_START = time__ticks();//$f.name')
|
||||
cgen_name := p.table.cgen_name(f)
|
||||
cgen_name := p.table.fn_gen_name(f)
|
||||
if f.defer_text.len > f.scope_level {
|
||||
f.defer_text[f.scope_level] = ' ${cgen_name}_time += time__ticks() - _PROF_START;'
|
||||
f.defer_text[f.scope_level] = ' ${cgen_name}_time += time__ticks() - _PROF_START;'
|
||||
}
|
||||
}
|
||||
if is_generic {
|
||||
@ -544,16 +544,16 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
||||
mut did_gen_something := false
|
||||
for i, arg in f.args {
|
||||
arg_struct += '$arg.typ $arg.name ;'// Add another field (arg) to the tmp struct definition
|
||||
str_args += 'arg->$arg.name'
|
||||
str_args += 'arg $dot_ptr $arg.name'
|
||||
if i == 0 && f.is_method {
|
||||
p.genln('$tmp_struct -> $arg.name = $receiver_var ;')
|
||||
p.genln('$tmp_struct $dot_ptr $arg.name = $receiver_var ;')
|
||||
if i < f.args.len - 1 {
|
||||
str_args += ','
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Set the struct values (args)
|
||||
p.genln('$tmp_struct -> $arg.name = ')
|
||||
p.genln('$tmp_struct $dot_ptr $arg.name = ')
|
||||
p.expression()
|
||||
p.genln(';')
|
||||
if i < f.args.len - 1 {
|
||||
@ -570,7 +570,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
||||
|
||||
arg_struct += '} $arg_struct_name ;'
|
||||
// Also register the wrapper, so we can use the original function without modifying it
|
||||
fn_name = p.table.cgen_name(f)
|
||||
fn_name = p.table.fn_gen_name(f)
|
||||
wrapper_name := '${fn_name}_thread_wrapper'
|
||||
wrapper_text := 'void* $wrapper_name($arg_struct_name * arg) {$fn_name( /*f*/$str_args ); }'
|
||||
p.cgen.register_thread_fn(wrapper_name, wrapper_text, arg_struct)
|
||||
@ -595,6 +595,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
||||
p.check(.rpar)
|
||||
}
|
||||
|
||||
// p.tok == fn_name
|
||||
fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type string) {
|
||||
if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.mod != p.mod {
|
||||
if f.name == 'contains' {
|
||||
@ -610,7 +611,7 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
||||
p.error('use `malloc()` instead of `C.malloc()`')
|
||||
}
|
||||
}
|
||||
mut cgen_name := p.table.cgen_name(f)
|
||||
mut cgen_name := p.table.fn_gen_name(f)
|
||||
p.next()
|
||||
mut gen_type := ''
|
||||
if p.tok == .lt {
|
||||
@ -644,32 +645,16 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
||||
// If we have a method placeholder,
|
||||
// we need to preappend "method(receiver, ...)"
|
||||
else {
|
||||
mut method_call := '${cgen_name}('
|
||||
receiver := f.args.first()
|
||||
//println('r=$receiver.typ RT=$receiver_type')
|
||||
if receiver.is_mut && !p.expr_var.is_mut {
|
||||
println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut')
|
||||
//println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut')
|
||||
p.error('`$p.expr_var.name` is immutable, declare it with `mut`')
|
||||
}
|
||||
if !p.expr_var.is_changed {
|
||||
p.mark_var_changed(p.expr_var)
|
||||
}
|
||||
// if receiver is key_mut or a ref (&), generate & for the first arg
|
||||
if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) {
|
||||
method_call += '& /* ? */'
|
||||
}
|
||||
// generate deref (TODO copy pasta later in fn_call_args)
|
||||
if !receiver.is_mut && receiver_type.contains('*') {
|
||||
method_call += '*'
|
||||
}
|
||||
mut cast := ''
|
||||
// Method returns (void*) => cast it to int, string, user etc
|
||||
// number := *(int*)numbers.first()
|
||||
if f.typ == 'void*' {
|
||||
// array_int => int
|
||||
cast = receiver_type.all_after('_')
|
||||
cast = '*($cast*) '
|
||||
}
|
||||
p.cgen.set_placeholder(method_ph, '$cast $method_call')
|
||||
p.gen_method_call(receiver_type, f.typ, cgen_name, receiver, method_ph)
|
||||
}
|
||||
// foo<Bar>()
|
||||
p.fn_call_args(mut f)
|
||||
@ -778,7 +763,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||
return f
|
||||
}
|
||||
// add debug information to panic when -debug arg is passed
|
||||
if p.v.pref.is_debug && f.name == 'panic' {
|
||||
if p.v.pref.is_debug && f.name == 'panic' && !p.is_js {
|
||||
mod_name := p.mod.replace('_dot_', '.')
|
||||
fn_name := p.cur_fn.name.replace('${p.mod}__', '')
|
||||
file_path := p.file_path.replace('\\', '\\\\') // escape \
|
||||
@ -792,7 +777,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||
// println('$i) arg=$arg.name')
|
||||
// Skip receiver, because it was already generated in the expression
|
||||
if i == 0 && f.is_method {
|
||||
if f.args.len > 1 {
|
||||
if f.args.len > 1 && !p.is_js {
|
||||
p.gen(',')
|
||||
}
|
||||
continue
|
||||
@ -836,23 +821,27 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||
p.expected_type = arg.typ
|
||||
typ := p.bool_expression()
|
||||
// Optimize `println`: replace it with `printf` to avoid extra allocations and
|
||||
// function calls. `println(777)` => `printf("%d\n", 777)`
|
||||
// function calls.
|
||||
// `println(777)` => `printf("%d\n", 777)`
|
||||
// (If we don't check for void, then V will compile `println(func())`)
|
||||
if i == 0 && (f.name == 'println' || f.name == 'print') && typ != 'string' && typ != 'void' {
|
||||
T := p.table.find_type(typ)
|
||||
$if !windows {
|
||||
$if !js {
|
||||
fmt := p.typ_to_fmt(typ, 0)
|
||||
if fmt != '' {
|
||||
p.cgen.resetln(p.cgen.cur_line.replace(f.name + ' (', '/*opt*/printf ("' + fmt + '\\n", '))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if typ.ends_with('*') {
|
||||
p.cgen.set_placeholder(ph, 'ptr_str(')
|
||||
p.gen(')')
|
||||
continue
|
||||
}
|
||||
// Make sure this type has a `str()` method
|
||||
$if !js {
|
||||
if !T.has_method('str') {
|
||||
// Arrays have automatic `str()` methods
|
||||
if T.name.starts_with('array_') {
|
||||
@ -879,6 +868,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
||||
}
|
||||
p.cgen.set_placeholder(ph, '${typ}_str(')
|
||||
p.gen(')')
|
||||
}
|
||||
continue
|
||||
}
|
||||
got := typ
|
||||
|
462
compiler/gen_c.v
Normal file
462
compiler/gen_c.v
Normal file
@ -0,0 +1,462 @@
|
||||
module main
|
||||
|
||||
import strings
|
||||
|
||||
const (
|
||||
dot_ptr = '->'
|
||||
)
|
||||
|
||||
// returns the type of the new variable
|
||||
fn (p mut Parser) gen_var_decl(name string, is_static bool) string {
|
||||
// Generate expression to tmp because we need its type first
|
||||
// `[typ] [name] = bool_expression();`
|
||||
pos := p.cgen.add_placeholder()
|
||||
mut typ := p.bool_expression()
|
||||
//p.gen('/*after expr*/')
|
||||
// Option check ? or {
|
||||
or_else := p.tok == .key_orelse
|
||||
tmp := p.get_tmp()
|
||||
if or_else {
|
||||
// Option_User tmp = get_user(1);
|
||||
// if (!tmp.ok) { or_statement }
|
||||
// User user = *(User*)tmp.data;
|
||||
// p.assigned_var = ''
|
||||
p.cgen.set_placeholder(pos, '$typ $tmp = ')
|
||||
p.genln(';')
|
||||
typ = typ.replace('Option_', '')
|
||||
p.next()
|
||||
p.check(.lcbr)
|
||||
p.genln('if (!$tmp .ok) {')
|
||||
p.register_var(Var {
|
||||
name: 'err'
|
||||
typ: 'string'
|
||||
is_mut: false
|
||||
is_used: true
|
||||
})
|
||||
p.genln('string err = $tmp . error;')
|
||||
p.statements()
|
||||
p.genln('$typ $name = *($typ*) $tmp . data;')
|
||||
if !p.returns && p.prev_tok2 != .key_continue && p.prev_tok2 != .key_break {
|
||||
p.error('`or` block must return/continue/break/panic')
|
||||
}
|
||||
p.returns = false
|
||||
return typ
|
||||
}
|
||||
gen_name := p.table.var_cgen_name(name)
|
||||
mut nt_gen := p.table.cgen_name_type_pair(gen_name, typ)
|
||||
// `foo := C.Foo{}` => `Foo foo;`
|
||||
if !p.is_empty_c_struct_init && !typ.starts_with('['){
|
||||
nt_gen += '='
|
||||
}
|
||||
if is_static {
|
||||
nt_gen = 'static $nt_gen'
|
||||
}
|
||||
p.cgen.set_placeholder(pos, nt_gen)
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_fn_decl(f Fn, typ, str_args string) {
|
||||
dll_export_linkage := if p.os == .msvc && p.attr == 'live' && p.pref.is_so {
|
||||
'__declspec(dllexport) '
|
||||
} else if p.attr == 'inline' {
|
||||
'static inline '
|
||||
} else {
|
||||
''
|
||||
}
|
||||
fn_name_cgen := p.table.fn_gen_name(f)
|
||||
//str_args := f.str_args(p.table)
|
||||
p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {')
|
||||
}
|
||||
|
||||
fn types_to_c(types []Type, table &Table) string {
|
||||
mut sb := strings.new_builder(10)
|
||||
for t in types {
|
||||
if t.cat != .union_ && t.cat != .struct_ {
|
||||
continue
|
||||
}
|
||||
//if is_objc {
|
||||
//sb.writeln('@interface $name : $objc_parent { @public')
|
||||
//}
|
||||
//if is_atomic {
|
||||
//sb.write('_Atomic ')
|
||||
//}
|
||||
kind := if t.cat == .union_ {'union'} else {'struct'}
|
||||
sb.writeln('$kind $t.name {')
|
||||
for field in t.fields {
|
||||
sb.write('\t')
|
||||
sb.writeln(table.cgen_name_type_pair(field.name,
|
||||
field.typ) + ';')
|
||||
}
|
||||
sb.writeln('};\n')
|
||||
//if is_objc {
|
||||
//sb.writeln('@end')
|
||||
//}
|
||||
}
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
fn (p mut Parser) index_get(typ string, fn_ph int, cfg IndexCfg) {
|
||||
// Erase var name we generated earlier: "int a = m, 0"
|
||||
// "m, 0" gets killed since we need to start from scratch. It's messy.
|
||||
// "m, 0" is an index expression, save it before deleting and insert later in map_get()
|
||||
mut index_expr := ''
|
||||
if p.cgen.is_tmp {
|
||||
index_expr = p.cgen.tmp_line.right(fn_ph)
|
||||
p.cgen.resetln(p.cgen.tmp_line.left(fn_ph))
|
||||
} else {
|
||||
index_expr = p.cgen.cur_line.right(fn_ph)
|
||||
p.cgen.resetln(p.cgen.cur_line.left(fn_ph))
|
||||
}
|
||||
// Can't pass integer literal, because map_get() requires a void*
|
||||
tmp := p.get_tmp()
|
||||
tmp_ok := p.get_tmp()
|
||||
if cfg.is_map {
|
||||
p.gen('$tmp')
|
||||
def := type_default(typ)
|
||||
p.cgen.insert_before('$typ $tmp = $def; bool $tmp_ok = map_get($index_expr, & $tmp);')
|
||||
}
|
||||
else if cfg.is_arr {
|
||||
if p.pref.translated && !p.builtin_mod {
|
||||
p.gen('$index_expr ]')
|
||||
}
|
||||
else {
|
||||
if cfg.is_ptr {
|
||||
p.gen('( *($typ*) array__get(* $index_expr) )')
|
||||
} else {
|
||||
p.gen('( *($typ*) array__get($index_expr) )')
|
||||
}
|
||||
}
|
||||
}
|
||||
else if cfg.is_str && !p.builtin_mod {
|
||||
p.gen('string_at($index_expr)')
|
||||
}
|
||||
// Zero the string after map_get() if it's nil, numbers are automatically 0
|
||||
// This is ugly, but what can I do without generics?
|
||||
// TODO what about user types?
|
||||
if cfg.is_map && typ == 'string' {
|
||||
// p.cgen.insert_before('if (!${tmp}.str) $tmp = tos("", 0);')
|
||||
p.cgen.insert_before('if (!$tmp_ok) $tmp = tos((byte *)"", 0);')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn (table mut Table) fn_gen_name(f &Fn) string {
|
||||
mut name := f.name
|
||||
if f.is_method {
|
||||
name = '${f.receiver_typ}_$f.name'
|
||||
name = name.replace(' ', '')
|
||||
name = name.replace('*', '')
|
||||
name = name.replace('+', 'plus')
|
||||
name = name.replace('-', 'minus')
|
||||
}
|
||||
// Avoid name conflicts (with things like abs(), print() etc).
|
||||
// Generate b_abs(), b_print()
|
||||
// TODO duplicate functionality
|
||||
if f.mod == 'builtin' && f.name in CReserved {
|
||||
return 'v_$name'
|
||||
}
|
||||
// Obfuscate but skip certain names
|
||||
// TODO ugly, fix
|
||||
if table.obfuscate && f.name != 'main' && f.name != 'WinMain' && f.mod != 'builtin' && !f.is_c &&
|
||||
f.mod != 'darwin' && f.mod != 'os' && !f.name.contains('window_proc') && f.name != 'gg__vec2' &&
|
||||
f.name != 'build_token_str' && f.name != 'build_keys' && f.mod != 'json' &&
|
||||
!name.ends_with('_str') && !name.contains('contains') {
|
||||
mut idx := table.obf_ids[name]
|
||||
// No such function yet, register it
|
||||
if idx == 0 {
|
||||
table.fn_cnt++
|
||||
table.obf_ids[name] = table.fn_cnt
|
||||
idx = table.fn_cnt
|
||||
}
|
||||
old := name
|
||||
name = 'f_$idx'
|
||||
println('$old ==> $name')
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_method_call(receiver_type, ftyp string, cgen_name string, receiver Var,method_ph int) {
|
||||
//mut cgen_name := p.table.fn_gen_name(f)
|
||||
mut method_call := cgen_name + '('
|
||||
// if receiver is key_mut or a ref (&), generate & for the first arg
|
||||
if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) {
|
||||
method_call += '& /* ? */'
|
||||
}
|
||||
// generate deref (TODO copy pasta later in fn_call_args)
|
||||
if !receiver.is_mut && receiver_type.contains('*') {
|
||||
method_call += '*'
|
||||
}
|
||||
mut cast := ''
|
||||
// Method returns (void*) => cast it to int, string, user etc
|
||||
// number := *(int*)numbers.first()
|
||||
if ftyp == 'void*' {
|
||||
// array_int => int
|
||||
cast = receiver_type.all_after('_')
|
||||
cast = '*($cast*) '
|
||||
}
|
||||
p.cgen.set_placeholder(method_ph, '$cast $method_call')
|
||||
//return method_call
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_array_at(typ_ string, is_arr0 bool, fn_ph int) {
|
||||
mut typ := typ_
|
||||
//p.fgen('[')
|
||||
// array_int a; a[0]
|
||||
// type is "array_int", need "int"
|
||||
// typ = typ.replace('array_', '')
|
||||
if is_arr0 {
|
||||
typ = typ.right(6)
|
||||
}
|
||||
// array a; a.first() voidptr
|
||||
// type is "array", need "void*"
|
||||
if typ == 'array' {
|
||||
typ = 'void*'
|
||||
}
|
||||
// No bounds check in translated from C code
|
||||
if p.pref.translated && !p.builtin_mod {
|
||||
// Cast void* to typ*: add (typ*) to the beginning of the assignment :
|
||||
// ((int*)a.data = ...
|
||||
p.cgen.set_placeholder(fn_ph, '(($typ*)(')
|
||||
p.gen('.data))[')
|
||||
}
|
||||
else {
|
||||
p.gen(',')
|
||||
}
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_for_header(i, tmp, var_typ, val string) {
|
||||
p.genln('for (int $i = 0; $i < ${tmp}.len; $i++) {')
|
||||
p.genln('$var_typ $val = (($var_typ *) $tmp . data)[$i];')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_for_str_header(i, tmp, var_typ, val string) {
|
||||
p.genln('array_byte bytes_$tmp = string_bytes( $tmp );')
|
||||
p.genln(';\nfor (int $i = 0; $i < $tmp .len; $i ++) {')
|
||||
p.genln('$var_typ $val = (($var_typ *) bytes_$tmp . data)[$i];')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_for_map_header(i, tmp, var_typ, val, typ string) {
|
||||
def := type_default(typ)
|
||||
p.genln('array_string keys_$tmp = map_keys(& $tmp ); ')
|
||||
p.genln('for (int l = 0; l < keys_$tmp .len; l++) {')
|
||||
p.genln('string $i = ((string*)keys_$tmp .data)[l];')
|
||||
// TODO don't call map_get() for each key, fetch values while traversing
|
||||
// the tree (replace `map_keys()` above with `map_key_vals()`)
|
||||
p.genln('$var_typ $val = $def; map_get($tmp, $i, & $val);')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_array_init(typ string, no_alloc bool, new_arr_ph int, nr_elems int) {
|
||||
mut new_arr := 'new_array_from_c_array'
|
||||
if no_alloc {
|
||||
new_arr += '_no_alloc'
|
||||
}
|
||||
if nr_elems == 0 && p.pref.ccompiler != 'tcc' {
|
||||
p.gen(' 0 })')
|
||||
} else {
|
||||
p.gen(' })')
|
||||
}
|
||||
// Need to do this in the second pass, otherwise it goes to the very top of the out.c file
|
||||
if !p.first_pass() {
|
||||
// Due to a tcc bug, the length needs to be specified.
|
||||
// GCC crashes if it is.
|
||||
cast := if p.pref.ccompiler == 'tcc' { '($typ[$nr_elems])' } else { '($typ[])' }
|
||||
p.cgen.set_placeholder(new_arr_ph,
|
||||
'$new_arr($nr_elems, $nr_elems, sizeof($typ), $cast { ')
|
||||
}
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_array_set(typ string, is_ptr, is_map bool,fn_ph, assign_pos int, is_cao bool) {
|
||||
// `a[0] = 7`
|
||||
// curline right now: `a , 0 = 7`
|
||||
mut val := p.cgen.cur_line.right(assign_pos)
|
||||
p.cgen.resetln(p.cgen.cur_line.left(assign_pos))
|
||||
mut cao_tmp := p.cgen.cur_line
|
||||
mut func := ''
|
||||
if is_map {
|
||||
func = 'map__set(&'
|
||||
// CAO on map is a bit more complicated as it loads
|
||||
// the value inside a pointer instead of returning it.
|
||||
}
|
||||
else {
|
||||
if is_ptr {
|
||||
func = 'array_set('
|
||||
if is_cao {
|
||||
cao_tmp = '*($p.expected_type *) array__get(*$cao_tmp)'
|
||||
}
|
||||
}
|
||||
else {
|
||||
func = 'array_set(&/*q*/'
|
||||
if is_cao {
|
||||
cao_tmp = '*($p.expected_type *) array__get($cao_tmp)'
|
||||
}
|
||||
}
|
||||
}
|
||||
p.cgen.set_placeholder(fn_ph, func)
|
||||
if is_cao {
|
||||
val = cao_tmp + val.all_before('=') + val.all_after('=')
|
||||
}
|
||||
p.gen(', & ($typ []) { $val })')
|
||||
}
|
||||
|
||||
|
||||
// returns true in case of an early return
|
||||
fn (p mut Parser) gen_struct_init(typ string, t Type) bool {
|
||||
// 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));
|
||||
if p.is_c_struct_init {
|
||||
if t.cat != .c_typedef {
|
||||
p.cgen.insert_before('struct /*c struct init*/')
|
||||
}
|
||||
}
|
||||
// TODO tm struct struct bug
|
||||
if typ == 'tm' {
|
||||
p.cgen.lines[p.cgen.lines.len-1] = ''
|
||||
}
|
||||
p.next()
|
||||
p.check(.lcbr)
|
||||
ptr := typ.contains('*')
|
||||
// `user := User{foo:bar}` => `User user = (User){ .foo = bar}`
|
||||
if !ptr {
|
||||
if p.is_c_struct_init {
|
||||
// `face := C.FT_Face{}` => `FT_Face face;`
|
||||
if p.tok == .rcbr {
|
||||
p.is_empty_c_struct_init = true
|
||||
p.check(.rcbr)
|
||||
return true
|
||||
}
|
||||
p.gen('(struct $typ) {')
|
||||
p.is_c_struct_init = false
|
||||
}
|
||||
else {
|
||||
p.gen('($typ) {')
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO tmp hack for 0 pointers init
|
||||
// &User{!} ==> 0
|
||||
if p.tok == .not {
|
||||
p.next()
|
||||
p.gen('0')
|
||||
p.check(.rcbr)
|
||||
return true
|
||||
}
|
||||
p.gen('($t.name*)memdup(&($t.name) {')
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_struct_field_init(field string) {
|
||||
p.gen('.$field = ')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_empty_map(typ string) {
|
||||
p.gen('new_map(1, sizeof($typ))')
|
||||
}
|
||||
|
||||
fn (p mut Parser) cast(typ string) {
|
||||
p.next()
|
||||
pos := p.cgen.add_placeholder()
|
||||
if p.tok == .rpar {
|
||||
// skip `)` if it's `(*int)(ptr)`, not `int(a)`
|
||||
p.ptr_cast = true
|
||||
p.next()
|
||||
}
|
||||
p.check(.lpar)
|
||||
p.expected_type = typ
|
||||
expr_typ := p.bool_expression()
|
||||
// `face := FT_Face(cobj)` => `FT_Face face = *((FT_Face*)cobj);`
|
||||
casting_voidptr_to_value := expr_typ == 'void*' && typ != 'int' &&
|
||||
typ != 'byteptr' && !typ.ends_with('*')
|
||||
p.expected_type = ''
|
||||
// `string(buffer)` => `tos2(buffer)`
|
||||
// `string(buffer, len)` => `tos(buffer, len)`
|
||||
// `string(bytes_array, len)` => `tos(bytes_array.data, len)`
|
||||
is_byteptr := expr_typ == 'byte*' || expr_typ == 'byteptr'
|
||||
is_bytearr := expr_typ == 'array_byte'
|
||||
if typ == 'string' {
|
||||
if is_byteptr || is_bytearr {
|
||||
if p.tok == .comma {
|
||||
p.check(.comma)
|
||||
p.cgen.set_placeholder(pos, 'tos((byte *)')
|
||||
if is_bytearr {
|
||||
p.gen('.data')
|
||||
}
|
||||
p.gen(', ')
|
||||
p.check_types(p.expression(), 'int')
|
||||
} else {
|
||||
if is_bytearr {
|
||||
p.gen('.data')
|
||||
}
|
||||
p.cgen.set_placeholder(pos, 'tos2((byte *)')
|
||||
}
|
||||
}
|
||||
// `string(234)` => error
|
||||
else if expr_typ == 'int' {
|
||||
p.error('cannot cast `$expr_typ` to `$typ`, use `str()` method instead')
|
||||
}
|
||||
else {
|
||||
p.error('cannot cast `$expr_typ` to `$typ`')
|
||||
}
|
||||
}
|
||||
else if typ == 'byte' && expr_typ == 'string' {
|
||||
p.error('cannot cast `$expr_typ` to `$typ`, use backquotes `` to create a `$typ` or access the value of an index of `$expr_typ` using []')
|
||||
}
|
||||
else if casting_voidptr_to_value {
|
||||
p.cgen.set_placeholder(pos, '*($typ*)(')
|
||||
}
|
||||
else {
|
||||
p.cgen.set_placeholder(pos, '($typ)(')
|
||||
}
|
||||
p.check(.rpar)
|
||||
p.gen(')')
|
||||
}
|
||||
|
||||
fn type_default(typ string) string {
|
||||
if typ.starts_with('array_') {
|
||||
return 'new_array(0, 1, sizeof( ${typ.right(6)} ))'
|
||||
}
|
||||
// Always set pointers to 0
|
||||
if typ.ends_with('*') {
|
||||
return '0'
|
||||
}
|
||||
// User struct defined in another module.
|
||||
if typ.contains('__') {
|
||||
return '{0}'
|
||||
}
|
||||
// Default values for other types are not needed because of mandatory initialization
|
||||
switch typ {
|
||||
case 'bool': return '0'
|
||||
case 'string': return 'tos((byte *)"", 0)'
|
||||
case 'i8': return '0'
|
||||
case 'i16': return '0'
|
||||
case 'i64': return '0'
|
||||
case 'u16': return '0'
|
||||
case 'u32': return '0'
|
||||
case 'u64': return '0'
|
||||
case 'byte': return '0'
|
||||
case 'int': return '0'
|
||||
case 'rune': return '0'
|
||||
case 'f32': return '0.0'
|
||||
case 'f64': return '0.0'
|
||||
case 'byteptr': return '0'
|
||||
case 'voidptr': return '0'
|
||||
}
|
||||
return '{0}'
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_array_push(ph int, typ, expr_type, tmp, tmp_typ string) {
|
||||
push_array := typ == expr_type
|
||||
if push_array {
|
||||
p.cgen.set_placeholder(ph, '_PUSH_MANY(&' )
|
||||
p.gen('), $tmp, $typ)')
|
||||
} else {
|
||||
p.check_types(expr_type, tmp_typ)
|
||||
// Pass tmp var info to the _PUSH macro
|
||||
// Prepend tmp initialisation and push call
|
||||
// Don't dereference if it's already a mutable array argument (`fn foo(mut []int)`)
|
||||
push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'}
|
||||
p.cgen.set_placeholder(ph, push_call)
|
||||
p.gen('), $tmp, $tmp_typ)')
|
||||
}
|
||||
}
|
||||
|
215
compiler/gen_js.v
Normal file
215
compiler/gen_js.v
Normal file
@ -0,0 +1,215 @@
|
||||
module main
|
||||
|
||||
import strings
|
||||
|
||||
const (
|
||||
dot_ptr = '.'
|
||||
)
|
||||
|
||||
fn (p mut Parser) gen_var_decl(name string, is_static bool) string {
|
||||
p.gen('var $name /* typ */ = ')
|
||||
typ := p.bool_expression()
|
||||
or_else := p.tok == .key_orelse
|
||||
//tmp := p.get_tmp()
|
||||
if or_else {
|
||||
//panic('optionals todo')
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_fn_decl(f Fn, typ, _str_args string) {
|
||||
mut str_args := ''
|
||||
for i, arg in f.args {
|
||||
str_args += arg.name + ' /* $arg.typ */ '
|
||||
if i < f.args.len - 1 {
|
||||
str_args += ', '
|
||||
}
|
||||
}
|
||||
name := p.table.fn_gen_name(f)
|
||||
if f.is_method {
|
||||
p.genln('\n${f.receiver_typ}.prototype.${name} = function($str_args)/* $typ */ {')
|
||||
} else {
|
||||
p.genln('\nfunction $name($str_args) /* $typ */ {')
|
||||
}
|
||||
}
|
||||
|
||||
fn types_to_c(types []Type, table &Table) string {
|
||||
println('js typ to code ')
|
||||
mut sb := strings.new_builder(10)
|
||||
for t in types {
|
||||
if t.cat != .union_ && t.cat != .struct_ {
|
||||
continue
|
||||
}
|
||||
sb.writeln('class $t.name {')
|
||||
for field in t.fields {
|
||||
sb.write('\t')
|
||||
sb.write(field.name)
|
||||
sb.writeln('; // $field.typ')
|
||||
}
|
||||
sb.writeln('}\n')
|
||||
}
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
fn (p mut Parser) index_get(typ string, fn_ph int, cfg IndexCfg) {
|
||||
p.cgen.cur_line = p.cgen.cur_line.replace(',', '[') + ']'
|
||||
}
|
||||
|
||||
fn (table mut Table) fn_gen_name(f &Fn) string {
|
||||
mut name := f.name
|
||||
if f.is_method {
|
||||
name = name.replace(' ', '')
|
||||
name = name.replace('*', '')
|
||||
name = name.replace('+', 'plus')
|
||||
name = name.replace('-', 'minus')
|
||||
return name
|
||||
}
|
||||
// Avoid name conflicts (with things like abs(), print() etc).
|
||||
// Generate b_abs(), b_print()
|
||||
// TODO duplicate functionality
|
||||
if f.mod == 'builtin' && f.name in CReserved {
|
||||
return 'v_$name'
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_method_call(receiver_type, ftyp string, cgen_name string, receiver Var,method_ph int) {
|
||||
//mut cgen_name := p.table.fn_gen_name(f)
|
||||
//mut method_call := cgen_name + '('
|
||||
p.gen('.' + cgen_name.all_after('_') + '(')
|
||||
//p.cgen.set_placeholder(method_ph, '$cast kKE $method_call')
|
||||
//return method_call
|
||||
}
|
||||
|
||||
|
||||
fn (p mut Parser) gen_array_at(typ string, is_arr0 bool, fn_ph int) {
|
||||
p.gen('[')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_for_header(i, tmp, var_typ, val string) {
|
||||
p.genln('for (var $i = 0; $i < ${tmp}.length; $i++) {')
|
||||
p.genln('var $val = $tmp [$i];')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_for_str_header(i, tmp, var_typ, val string) {
|
||||
p.genln('for (var $i = 0; $i < $tmp .length; $i ++) {')
|
||||
p.genln('var $val = $tmp[$i];')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_for_map_header(i, tmp, var_typ, val, typ string) {
|
||||
p.genln('for (var $i in $tmp) {')
|
||||
p.genln('var $val = $tmp[$i];')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_array_init(typ string, no_alloc bool, new_arr_ph int, nr_elems int) {
|
||||
p.cgen.set_placeholder(new_arr_ph, '[')
|
||||
p.gen(']')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_array_set(typ string, is_ptr, is_map bool,fn_ph, assign_pos int, is_cao bool) {
|
||||
mut val := p.cgen.cur_line.right(assign_pos)
|
||||
p.cgen.resetln(p.cgen.cur_line.left(assign_pos))
|
||||
p.gen('] =')
|
||||
cao_tmp := p.cgen.cur_line
|
||||
if is_cao {
|
||||
val = cao_tmp + val.all_before('=') + val.all_after('=')
|
||||
}
|
||||
p.gen(val)
|
||||
}
|
||||
|
||||
// returns true in case of an early return
|
||||
fn (p mut Parser) gen_struct_init(typ string, t Type) bool {
|
||||
p.next()
|
||||
p.check(.lcbr)
|
||||
ptr := typ.contains('*')
|
||||
if !ptr {
|
||||
p.gen('{')
|
||||
}
|
||||
else {
|
||||
// TODO tmp hack for 0 pointers init
|
||||
// &User{!} ==> 0
|
||||
if p.tok == .not {
|
||||
p.next()
|
||||
p.gen('}')
|
||||
p.check(.rcbr)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_struct_field_init(field string) {
|
||||
p.gen('$field : ')
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_empty_map(typ string) {
|
||||
p.gen('{}')
|
||||
}
|
||||
|
||||
fn (p mut Parser) cast(typ string) string {
|
||||
p.next()
|
||||
pos := p.cgen.add_placeholder()
|
||||
if p.tok == .rpar {
|
||||
p.next()
|
||||
}
|
||||
p.check(.lpar)
|
||||
p.bool_expression()
|
||||
if typ == 'string' {
|
||||
if p.tok == .comma {
|
||||
p.check(.comma)
|
||||
p.cgen.set_placeholder(pos, 'tos(')
|
||||
//p.gen('tos(')
|
||||
p.gen(', ')
|
||||
p.expression()
|
||||
p.gen(')')
|
||||
}
|
||||
}
|
||||
p.check(.rpar)
|
||||
return typ
|
||||
}
|
||||
|
||||
fn type_default(typ string) string {
|
||||
if typ.starts_with('array_') {
|
||||
return '[]'
|
||||
}
|
||||
// Always set pointers to 0
|
||||
if typ.ends_with('*') {
|
||||
return '0'
|
||||
}
|
||||
// User struct defined in another module.
|
||||
if typ.contains('__') {
|
||||
return '{}'
|
||||
}
|
||||
// Default values for other types are not needed because of mandatory initialization
|
||||
switch typ {
|
||||
case 'bool': return '0'
|
||||
case 'string': return '""'
|
||||
case 'i8': return '0'
|
||||
case 'i16': return '0'
|
||||
case 'i64': return '0'
|
||||
case 'u16': return '0'
|
||||
case 'u32': return '0'
|
||||
case 'u64': return '0'
|
||||
case 'byte': return '0'
|
||||
case 'int': return '0'
|
||||
case 'rune': return '0'
|
||||
case 'f32': return '0.0'
|
||||
case 'f64': return '0.0'
|
||||
case 'byteptr': return '0'
|
||||
case 'voidptr': return '0'
|
||||
}
|
||||
return '{}'
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_array_push(ph int, typ, expr_type, tmp, tmp_typ string) {
|
||||
push_array := typ == expr_type
|
||||
if push_array {
|
||||
p.cgen.set_placeholder(ph, 'push(&' )
|
||||
p.gen('), $tmp, $typ)')
|
||||
} else {
|
||||
p.check_types(expr_type, tmp_typ)
|
||||
p.gen(')')
|
||||
p.cgen.cur_line = p.cgen.cur_line.replace(',', '.push')
|
||||
}
|
||||
}
|
||||
|
@ -35,9 +35,9 @@ fn (v mut V) generate_hotcode_reloading_declarations() {
|
||||
}
|
||||
}
|
||||
|
||||
fn (v mut V) generate_hotcode_reloading_code() {
|
||||
fn (v mut V) generate_hot_reload_code() {
|
||||
mut cgen := v.cgen
|
||||
|
||||
|
||||
// Hot code reloading
|
||||
if v.pref.is_live {
|
||||
file := v.dir
|
||||
|
@ -27,7 +27,8 @@ enum BuildMode {
|
||||
}
|
||||
|
||||
const (
|
||||
SupportedPlatforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'msvc']
|
||||
SupportedPlatforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd',
|
||||
'netbsd', 'dragonfly', 'msvc', 'js']
|
||||
ModPath = os.home_dir() + '/.vmodules/'
|
||||
)
|
||||
|
||||
@ -40,6 +41,7 @@ enum OS {
|
||||
netbsd
|
||||
dragonfly
|
||||
msvc
|
||||
js
|
||||
}
|
||||
|
||||
enum Pass {
|
||||
@ -208,7 +210,7 @@ fn (v mut V) compile() {
|
||||
println(v.files)
|
||||
}
|
||||
v.add_v_files_to_compile()
|
||||
if v.pref.is_verbose {
|
||||
if v.pref.is_verbose || v.pref.is_debug {
|
||||
println('all .v files:')
|
||||
println(v.files)
|
||||
}
|
||||
@ -220,7 +222,14 @@ fn (v mut V) compile() {
|
||||
// Main pass
|
||||
cgen.pass = Pass.main
|
||||
if v.pref.is_debug {
|
||||
cgen.genln('#define VDEBUG (1) ')
|
||||
$if js {
|
||||
cgen.genln('const VDEBUG = 1;\n')
|
||||
} $else {
|
||||
cgen.genln('#define VDEBUG (1)')
|
||||
}
|
||||
}
|
||||
if v.os == .js {
|
||||
cgen.genln('#define _VJS (1) ')
|
||||
}
|
||||
|
||||
if v.pref.building_v {
|
||||
@ -228,9 +237,13 @@ fn (v mut V) compile() {
|
||||
cgen.genln('#define V_COMMIT_HASH "' + vhash() + '"')
|
||||
cgen.genln('#endif')
|
||||
}
|
||||
|
||||
cgen.genln(CommonCHeaders)
|
||||
|
||||
$if js {
|
||||
cgen.genln(js_headers)
|
||||
} $else {
|
||||
cgen.genln(CommonCHeaders)
|
||||
}
|
||||
|
||||
v.generate_hotcode_reloading_declarations()
|
||||
|
||||
imports_json := 'json' in v.table.imports
|
||||
@ -251,7 +264,9 @@ fn (v mut V) compile() {
|
||||
// `/usr/bin/ld: multiple definition of 'total_m'`
|
||||
// TODO
|
||||
//cgen.genln('i64 total_m = 0; // For counting total RAM allocated')
|
||||
cgen.genln('int g_test_ok = 1; ')
|
||||
if v.pref.is_test {
|
||||
cgen.genln('int g_test_ok = 1; ')
|
||||
}
|
||||
if 'json' in v.table.imports {
|
||||
cgen.genln('
|
||||
#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key))
|
||||
@ -261,7 +276,7 @@ fn (v mut V) compile() {
|
||||
if '-debug_alloc' in os.args {
|
||||
cgen.genln('#define DEBUG_ALLOC 1')
|
||||
}
|
||||
cgen.genln('/*================================== FNS =================================*/')
|
||||
//cgen.genln('/*================================== FNS =================================*/')
|
||||
cgen.genln('this line will be replaced with definitions')
|
||||
defs_pos := cgen.lines.len - 1
|
||||
for file in v.files {
|
||||
@ -276,12 +291,16 @@ fn (v mut V) compile() {
|
||||
v.log('Done parsing.')
|
||||
// Write everything
|
||||
mut d := strings.new_builder(10000)// Avoid unnecessary allocations
|
||||
d.writeln(cgen.includes.join_lines())
|
||||
d.writeln(cgen.typedefs.join_lines())
|
||||
d.writeln(v.c_type_definitions())
|
||||
d.writeln('\nstring _STR(const char*, ...);\n')
|
||||
d.writeln('\nstring _STR_TMP(const char*, ...);\n')
|
||||
d.writeln(cgen.fns.join_lines())
|
||||
$if !js {
|
||||
d.writeln(cgen.includes.join_lines())
|
||||
d.writeln(cgen.typedefs.join_lines())
|
||||
d.writeln(v.type_definitions())
|
||||
d.writeln('\nstring _STR(const char*, ...);\n')
|
||||
d.writeln('\nstring _STR_TMP(const char*, ...);\n')
|
||||
d.writeln(cgen.fns.join_lines()) // fn definitions
|
||||
} $else {
|
||||
d.writeln(v.type_definitions())
|
||||
}
|
||||
d.writeln(cgen.consts.join_lines())
|
||||
d.writeln(cgen.thread_args.join_lines())
|
||||
if v.pref.is_prof {
|
||||
@ -290,23 +309,24 @@ fn (v mut V) compile() {
|
||||
}
|
||||
dd := d.str()
|
||||
cgen.lines[defs_pos] = dd// TODO `def.str()` doesn't compile
|
||||
|
||||
v.generate_main()
|
||||
|
||||
v.generate_hotcode_reloading_code()
|
||||
|
||||
cgen.save()
|
||||
v.generate_main()
|
||||
v.generate_hot_reload_code()
|
||||
if v.pref.is_verbose {
|
||||
v.log('flags=')
|
||||
for flag in v.get_os_cflags() {
|
||||
println(' * ' + flag.format())
|
||||
}
|
||||
}
|
||||
$if js {
|
||||
cgen.genln('main();')
|
||||
}
|
||||
cgen.save()
|
||||
v.cc()
|
||||
}
|
||||
|
||||
fn (v mut V) generate_main() {
|
||||
mut cgen := v.cgen
|
||||
$if js { return }
|
||||
|
||||
// if v.build_mode in [.default, .embed_vlib] {
|
||||
if v.pref.build_mode == .default_mode || v.pref.build_mode == .embed_vlib {
|
||||
@ -461,9 +481,18 @@ fn (v &V) v_files_from_dir(dir string) []string {
|
||||
if file.ends_with('_mac.v') && v.os != .mac {
|
||||
continue
|
||||
}
|
||||
if file.ends_with('_js.v') {
|
||||
continue
|
||||
}
|
||||
if file.ends_with('_nix.v') && (v.os == .windows || v.os == .msvc) {
|
||||
continue
|
||||
}
|
||||
if file.ends_with('_js.v') && v.os != .js {
|
||||
continue
|
||||
}
|
||||
if file.ends_with('_c.v') && v.os == .js {
|
||||
continue
|
||||
}
|
||||
res << '$dir/$file'
|
||||
}
|
||||
return res
|
||||
@ -491,7 +520,7 @@ fn (v mut V) add_v_files_to_compile() {
|
||||
dir = dir.all_before('/')
|
||||
}
|
||||
else {
|
||||
// Add .v files from the directory being compied
|
||||
// Add .v files from the directory being compiled
|
||||
files := v.v_files_from_dir(dir)
|
||||
for file in files {
|
||||
user_files << file
|
||||
@ -579,6 +608,7 @@ fn (v mut V) add_v_files_to_compile() {
|
||||
module_path = '$ModPath/vlib/$mod_p'
|
||||
}
|
||||
*/
|
||||
if mod == 'builtin' { continue } // builtin files were already added
|
||||
vfiles := v.v_files_from_dir(mod_path)
|
||||
for file in vfiles {
|
||||
if !(file in v.files) {
|
||||
@ -666,7 +696,7 @@ fn new_v(args[]string) &V {
|
||||
//if args.contains('-lib') {
|
||||
if joined_args.contains('build module ') {
|
||||
build_mode = .build_module
|
||||
// v -lib ~/v/os => os.o
|
||||
// v build module ~/v/os => os.o
|
||||
//mod = os.dir(dir)
|
||||
mod = if dir.contains(os.PathSeparator) {
|
||||
dir.all_after(os.PathSeparator)
|
||||
@ -741,8 +771,11 @@ fn new_v(args[]string) &V {
|
||||
case 'netbsd': _os = .netbsd
|
||||
case 'dragonfly': _os = .dragonfly
|
||||
case 'msvc': _os = .msvc
|
||||
case 'js': _os = .js
|
||||
}
|
||||
}
|
||||
//println('OS=$_os')
|
||||
builtin := 'builtin.v'
|
||||
builtins := [
|
||||
'array.v',
|
||||
'string.v',
|
||||
@ -752,6 +785,7 @@ fn new_v(args[]string) &V {
|
||||
'map.v',
|
||||
'option.v',
|
||||
]
|
||||
//println(builtins)
|
||||
// Location of all vlib files
|
||||
vroot := os.dir(os.executable())
|
||||
//println('VROOT=$vroot')
|
||||
@ -768,13 +802,16 @@ fn new_v(args[]string) &V {
|
||||
//if !out_name.contains('builtin.o') {
|
||||
for builtin in builtins {
|
||||
mut f := '$vroot/vlib/builtin/$builtin'
|
||||
__ := 1
|
||||
$if js {
|
||||
f = '$vroot/vlib/builtin/js/$builtin'
|
||||
}
|
||||
// In default mode we use precompiled vlib.o, point to .vh files with signatures
|
||||
if build_mode == .default_mode || build_mode == .build_module {
|
||||
//f = '$TmpPath/vlib/builtin/${builtin}h'
|
||||
}
|
||||
files << f
|
||||
}
|
||||
//}
|
||||
|
||||
mut cflags := ''
|
||||
for ci, cv in args {
|
||||
|
@ -65,6 +65,7 @@ mut:
|
||||
cur_gen_type string // "App" to replace "T" in current generic function
|
||||
is_vweb bool
|
||||
is_sql bool
|
||||
is_js bool
|
||||
sql_i int // $1 $2 $3
|
||||
sql_params []string // ("select * from users where id = $1", ***"100"***)
|
||||
sql_types []string // int, string and so on; see sql_params
|
||||
@ -108,6 +109,9 @@ fn (v mut V) new_parser(path string) Parser {
|
||||
vroot: v.vroot
|
||||
|
||||
}
|
||||
$if js {
|
||||
p.is_js = true
|
||||
}
|
||||
|
||||
if p.pref.is_repl {
|
||||
p.scanner.should_print_line_on_error = false
|
||||
@ -1245,7 +1249,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) {
|
||||
p.gen(' = ')
|
||||
}
|
||||
case Token.plus_assign:
|
||||
if is_str {
|
||||
if is_str && !p.is_js {
|
||||
p.gen('= string_add($v.name, ')// TODO can't do `foo.bar += '!'`
|
||||
}
|
||||
else {
|
||||
@ -1275,7 +1279,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) {
|
||||
p.scanner.line_nr--
|
||||
p.error('cannot use type `$expr_type` as type `$p.assigned_type` in assignment')
|
||||
}
|
||||
if is_str && tok == .plus_assign {
|
||||
if is_str && tok == .plus_assign && !p.is_js {
|
||||
p.gen(')')
|
||||
}
|
||||
// p.assigned_var = ''
|
||||
@ -1310,38 +1314,7 @@ fn (p mut Parser) var_decl() {
|
||||
p.error('variable names cannot contain uppercase letters, use snake_case instead')
|
||||
}
|
||||
p.check_space(.decl_assign) // :=
|
||||
// Generate expression to tmp because we need its type first
|
||||
// [TYP .name =] bool_expression()
|
||||
pos := p.cgen.add_placeholder()
|
||||
mut typ := p.bool_expression()
|
||||
// Option check ? or {
|
||||
or_else := p.tok == .key_orelse
|
||||
tmp := p.get_tmp()
|
||||
if or_else {
|
||||
// Option_User tmp = get_user(1);
|
||||
// if (!tmp.ok) { or_statement }
|
||||
// User user = *(User*)tmp.data;
|
||||
// p.assigned_var = ''
|
||||
p.cgen.set_placeholder(pos, '$typ $tmp = ')
|
||||
p.genln(';')
|
||||
typ = typ.replace('Option_', '')
|
||||
p.next()
|
||||
p.check(.lcbr)
|
||||
p.genln('if (!$tmp .ok) {')
|
||||
p.register_var(Var {
|
||||
name: 'err'
|
||||
typ: 'string'
|
||||
is_mut: false
|
||||
is_used: true
|
||||
})
|
||||
p.genln('string err = $tmp . error;')
|
||||
p.statements()
|
||||
p.genln('$typ $name = *($typ*) $tmp . data;')
|
||||
if !p.returns && p.prev_tok2 != .key_continue && p.prev_tok2 != .key_break {
|
||||
p.error('`or` block must return/continue/break/panic')
|
||||
}
|
||||
p.returns = false
|
||||
}
|
||||
typ := p.gen_var_decl(name, is_static)
|
||||
p.register_var(Var {
|
||||
name: name
|
||||
typ: typ
|
||||
@ -1349,18 +1322,6 @@ fn (p mut Parser) var_decl() {
|
||||
is_alloc: p.is_alloc
|
||||
})
|
||||
//if p.is_alloc { println('REG VAR IS ALLOC $name') }
|
||||
if !or_else {
|
||||
gen_name := p.table.var_cgen_name(name)
|
||||
mut nt_gen := p.table.cgen_name_type_pair(gen_name, typ)
|
||||
// `foo := C.Foo{}` => `Foo foo;`
|
||||
if !p.is_empty_c_struct_init {
|
||||
nt_gen += '='
|
||||
}
|
||||
if is_static {
|
||||
nt_gen = 'static $nt_gen'
|
||||
}
|
||||
p.cgen.set_placeholder(pos, nt_gen)
|
||||
}
|
||||
p.var_decl_name = ''
|
||||
p.is_empty_c_struct_init = false
|
||||
}
|
||||
@ -1415,7 +1376,7 @@ fn (p mut Parser) bterm() string {
|
||||
// if tok in [ .eq, .gt, .lt, .le, .ge, .ne] {
|
||||
if tok == .eq || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne {
|
||||
p.fgen(' ${p.tok.str()} ')
|
||||
if is_str {
|
||||
if is_str && !p.is_js {
|
||||
p.gen(',')
|
||||
}
|
||||
else if p.is_sql && tok == .eq {
|
||||
@ -1439,7 +1400,7 @@ fn (p mut Parser) bterm() string {
|
||||
p.check_types(p.expression(), typ)
|
||||
}
|
||||
typ = 'bool'
|
||||
if is_str { //&& !p.is_sql {
|
||||
if is_str && !p.is_js { //&& !p.is_sql {
|
||||
p.gen(')')
|
||||
switch tok {
|
||||
case Token.eq: p.cgen.set_placeholder(ph, 'string_eq(')
|
||||
@ -1575,7 +1536,8 @@ fn (p mut Parser) name_expr() string {
|
||||
name += '*'
|
||||
}
|
||||
p.gen('(')
|
||||
mut typ := p.cast(name)
|
||||
mut typ := name
|
||||
p.cast(name)
|
||||
p.gen(')')
|
||||
for p.tok == .dot {
|
||||
typ = p.dot(typ, ph)
|
||||
@ -1604,7 +1566,8 @@ fn (p mut Parser) name_expr() string {
|
||||
if name == 'T' {
|
||||
name = p.cur_gen_type
|
||||
}
|
||||
return p.struct_init(name, is_c_struct_init)
|
||||
p.is_c_struct_init = is_c_struct_init
|
||||
return p.struct_init(name)
|
||||
}
|
||||
}
|
||||
if is_c {
|
||||
@ -1698,7 +1661,7 @@ fn (p mut Parser) name_expr() string {
|
||||
func: f
|
||||
}
|
||||
p.table.register_type2(fn_typ)
|
||||
p.gen(p.table.cgen_name(f))
|
||||
p.gen(p.table.fn_gen_name(f))
|
||||
p.next()
|
||||
return f.typ_str() //'void*'
|
||||
}
|
||||
@ -1858,7 +1821,7 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
|
||||
}
|
||||
mut dot := '.'
|
||||
if str_typ.ends_with('*') || str_typ == 'FT_Face' { // TODO fix C ptr typedefs
|
||||
dot = '->'
|
||||
dot = dot_ptr
|
||||
}
|
||||
// field
|
||||
if has_field {
|
||||
@ -1917,6 +1880,27 @@ struct $f.parent_fn {
|
||||
return method.typ
|
||||
}
|
||||
|
||||
enum IndexType {
|
||||
none
|
||||
str
|
||||
map
|
||||
array
|
||||
array0
|
||||
fixed_array
|
||||
ptr
|
||||
}
|
||||
|
||||
fn get_index_type(typ string) IndexType {
|
||||
if typ.starts_with('map_') { return IndexType.map }
|
||||
if typ == 'string' { return IndexType.str }
|
||||
if typ.starts_with('array_') || typ == 'array' { return IndexType.array }
|
||||
if typ == 'byte*' || typ == 'byteptr' || typ.contains('*') {
|
||||
return IndexType.ptr
|
||||
}
|
||||
if typ[0] == `[` { return IndexType.fixed_array }
|
||||
return IndexType.none
|
||||
}
|
||||
|
||||
fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||
mut typ := typ_
|
||||
// a[0]
|
||||
@ -1974,34 +1958,10 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||
}
|
||||
}
|
||||
if is_arr {
|
||||
//p.fgen('[')
|
||||
// array_int a; a[0]
|
||||
// type is "array_int", need "int"
|
||||
// typ = typ.replace('array_', '')
|
||||
if is_arr0 {
|
||||
if p.fileis('int_test') {
|
||||
println('\nRRRR0 $typ')
|
||||
}
|
||||
typ = typ.right(6)
|
||||
if p.fileis('int_test') {
|
||||
println('RRRR $typ')
|
||||
}
|
||||
}
|
||||
// array a; a.first() voidptr
|
||||
// type is "array", need "void*"
|
||||
if typ == 'array' {
|
||||
typ = 'void*'
|
||||
}
|
||||
// No bounds check in translated from C code
|
||||
if p.pref.translated && !p.builtin_mod{
|
||||
// Cast void* to typ*: add (typ*) to the beginning of the assignment :
|
||||
// ((int*)a.data = ...
|
||||
p.cgen.set_placeholder(fn_ph, '(($typ*)(')
|
||||
p.gen('.data))[')
|
||||
}
|
||||
else {
|
||||
p.gen(',')
|
||||
}
|
||||
}
|
||||
p.gen_array_at(typ, is_arr0, fn_ph)
|
||||
}
|
||||
// map is tricky
|
||||
// need to replace "m[key] = val" with "tmp = val; map_set(&m, key, &tmp)"
|
||||
@ -2050,43 +2010,14 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||
if is_indexer && is_str && !p.builtin_mod {
|
||||
p.error('strings are immutable')
|
||||
}
|
||||
assign_pos := p.cgen.cur_line.len
|
||||
is_cao := p.tok ≠ .assign
|
||||
p.assigned_type = typ
|
||||
p.expected_type = typ
|
||||
assign_pos := p.cgen.cur_line.len
|
||||
is_cao := p.tok ≠ .assign
|
||||
p.assign_statement(v, fn_ph, is_indexer && (is_map || is_arr))
|
||||
// m[key] = val
|
||||
// `m[key] = val`
|
||||
if is_indexer && (is_map || is_arr) {
|
||||
// `a[0] = 7`
|
||||
// curline right now: `a , 0 = 7`
|
||||
mut val := p.cgen.cur_line.right(assign_pos)
|
||||
p.cgen.resetln(p.cgen.cur_line.left(assign_pos))
|
||||
mut cao_tmp := p.cgen.cur_line
|
||||
mut func := ''
|
||||
if is_map {
|
||||
func = 'map__set(&'
|
||||
// CAO on map is a bit more complicated as it loads
|
||||
// the value inside a pointer instead of returning it.
|
||||
}
|
||||
else {
|
||||
if is_ptr {
|
||||
func = 'array_set('
|
||||
if is_cao {
|
||||
cao_tmp = '*($p.expected_type *) array__get(*$cao_tmp)'
|
||||
}
|
||||
}
|
||||
else {
|
||||
func = 'array_set(&/*q*/'
|
||||
if is_cao {
|
||||
cao_tmp = '*($p.expected_type *) array__get($cao_tmp)'
|
||||
}
|
||||
}
|
||||
}
|
||||
p.cgen.set_placeholder(fn_ph, func)
|
||||
if is_cao {
|
||||
val = cao_tmp + val.all_before('=') + val.all_after('=')
|
||||
}
|
||||
p.gen(', & ($typ []) { $val })')
|
||||
p.gen_array_set(typ, is_ptr, is_map, fn_ph, assign_pos, is_cao)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
@ -2095,52 +2026,26 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||
// }
|
||||
// m[key]. no =, just a getter
|
||||
else if (is_map || is_arr || (is_str && !p.builtin_mod)) && is_indexer {
|
||||
// Erase var name we generated earlier: "int a = m, 0"
|
||||
// "m, 0" gets killed since we need to start from scratch. It's messy.
|
||||
// "m, 0" is an index expression, save it before deleting and insert later in map_get()
|
||||
mut index_expr := ''
|
||||
if p.cgen.is_tmp {
|
||||
index_expr = p.cgen.tmp_line.right(fn_ph)
|
||||
p.cgen.resetln(p.cgen.tmp_line.left(fn_ph))
|
||||
} else {
|
||||
index_expr = p.cgen.cur_line.right(fn_ph)
|
||||
p.cgen.resetln(p.cgen.cur_line.left(fn_ph))
|
||||
}
|
||||
// Can't pass integer literal, because map_get() requires a void*
|
||||
tmp := p.get_tmp()
|
||||
tmp_ok := p.get_tmp()
|
||||
if is_map {
|
||||
p.gen('$tmp')
|
||||
def := type_default(typ)
|
||||
p.cgen.insert_before('$typ $tmp = $def; bool $tmp_ok = map_get($index_expr, & $tmp);')
|
||||
}
|
||||
else if is_arr {
|
||||
if p.pref.translated && !p.builtin_mod {
|
||||
p.gen('$index_expr ]')
|
||||
}
|
||||
else {
|
||||
if is_ptr {
|
||||
p.gen('( *($typ*) array__get(* $index_expr) )')
|
||||
} else {
|
||||
p.gen('( *($typ*) array__get($index_expr) )')
|
||||
}
|
||||
}
|
||||
}
|
||||
else if is_str && !p.builtin_mod {
|
||||
p.gen('string_at($index_expr)')
|
||||
}
|
||||
// Zero the string after map_get() if it's nil, numbers are automatically 0
|
||||
// This is ugly, but what can I do without generics?
|
||||
// TODO what about user types?
|
||||
if is_map && typ == 'string' {
|
||||
// p.cgen.insert_before('if (!${tmp}.str) $tmp = tos("", 0);')
|
||||
p.cgen.insert_before('if (!$tmp_ok) $tmp = tos((byte *)"", 0);')
|
||||
}
|
||||
p.index_get(typ, fn_ph, IndexCfg{
|
||||
is_arr: is_arr
|
||||
is_map: is_map
|
||||
is_ptr: is_ptr
|
||||
is_str: is_str
|
||||
})
|
||||
}
|
||||
// else if is_arr && is_indexer{}
|
||||
return typ
|
||||
}
|
||||
|
||||
struct IndexCfg {
|
||||
is_map bool
|
||||
is_str bool
|
||||
is_ptr bool
|
||||
is_arr bool
|
||||
is_arr0 bool
|
||||
|
||||
}
|
||||
|
||||
// returns resulting type
|
||||
fn (p mut Parser) expression() string {
|
||||
if p.scanner.file_path.contains('test_test') {
|
||||
@ -2151,7 +2056,7 @@ fn (p mut Parser) expression() string {
|
||||
ph := p.cgen.add_placeholder()
|
||||
mut typ := p.term()
|
||||
is_str := typ=='string'
|
||||
// a << b ==> array2_push(&a, b)
|
||||
// `a << b` ==> `array_push(&a, b)`
|
||||
if p.tok == .left_shift {
|
||||
if typ.contains('array_') {
|
||||
// Can't pass integer literal, because push requires a void*
|
||||
@ -2171,19 +2076,7 @@ fn (p mut Parser) expression() string {
|
||||
}
|
||||
expr_type := p.expression()
|
||||
// Two arrays of the same type?
|
||||
push_array := typ == expr_type
|
||||
if push_array {
|
||||
p.cgen.set_placeholder(ph, '_PUSH_MANY(&' )
|
||||
p.gen('), $tmp, $typ)')
|
||||
} else {
|
||||
p.check_types(expr_type, tmp_typ)
|
||||
// Pass tmp var info to the _PUSH macro
|
||||
// Prepend tmp initialisation and push call
|
||||
// Don't dereference if it's already a mutable array argument (`fn foo(mut []int)`)
|
||||
push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'}
|
||||
p.cgen.set_placeholder(ph, push_call)
|
||||
p.gen('), $tmp, $tmp_typ)')
|
||||
}
|
||||
p.gen_array_push(ph, typ, expr_type, tmp, tmp_typ)
|
||||
return 'void'
|
||||
}
|
||||
else {
|
||||
@ -2236,12 +2129,12 @@ fn (p mut Parser) expression() string {
|
||||
tok_op := p.tok
|
||||
is_num := typ == 'void*' || typ == 'byte*' || is_number_type(typ)
|
||||
p.check_space(p.tok)
|
||||
if is_str && tok_op == .plus {
|
||||
if is_str && tok_op == .plus && !p.is_js {
|
||||
p.cgen.set_placeholder(ph, 'string_add(')
|
||||
p.gen(',')
|
||||
}
|
||||
// 3 + 4
|
||||
else if is_num {
|
||||
else if is_num || p.is_js {
|
||||
if typ == 'void*' {
|
||||
// Msvc errors on void* pointer arithmatic
|
||||
// ... So cast to byte* and then do the add
|
||||
@ -2259,7 +2152,7 @@ fn (p mut Parser) expression() string {
|
||||
}
|
||||
}
|
||||
p.check_types(p.term(), typ)
|
||||
if is_str && tok_op == .plus {
|
||||
if is_str && tok_op == .plus && !p.is_js {
|
||||
p.gen(')')
|
||||
}
|
||||
// Make sure operators are used with correct types
|
||||
@ -2535,12 +2428,18 @@ fn (p mut Parser) string_expr() {
|
||||
else if p.is_sql {
|
||||
p.gen('\'$str\'')
|
||||
}
|
||||
else if p.is_js {
|
||||
p.gen('"$f"')
|
||||
}
|
||||
else {
|
||||
p.gen('tos2((byte*)"$f")')
|
||||
}
|
||||
p.next()
|
||||
return
|
||||
}
|
||||
$if js {
|
||||
p.error('js backend does not support string formatting yet')
|
||||
}
|
||||
// tmp := p.get_tmp()
|
||||
p.is_alloc = true // $ interpolation means there's allocation
|
||||
mut args := '"'
|
||||
@ -2601,7 +2500,7 @@ fn (p mut Parser) string_expr() {
|
||||
if fspec == 's' {
|
||||
//println('custom str F=$cformat | format_specifier: "$fspec" | typ: $typ ')
|
||||
if typ != 'string' {
|
||||
p.error('only v strings can be formatted with a :${cformat} format, but you have given "${val}", which has type ${typ}.')
|
||||
p.error('only V strings can be formatted with a :${cformat} format, but you have given "${val}", which has type ${typ}.')
|
||||
}
|
||||
args = args.all_before_last('${val}.len, ${val}.str') + '${val}.str'
|
||||
}
|
||||
@ -2741,10 +2640,10 @@ fn (p mut Parser) array_init() string {
|
||||
mut typ := ''
|
||||
new_arr_ph := p.cgen.add_placeholder()
|
||||
mut i := 0
|
||||
pos := p.cgen.cur_line.len// remember cur line to fetch first number in cgen for [0; 10]
|
||||
pos := p.cgen.cur_line.len// remember cur line to fetch first number in cgen for [0; 10]
|
||||
for p.tok != .rsbr {
|
||||
val_typ := p.bool_expression()
|
||||
// Get type of the first expression
|
||||
// Get the type of the first expression
|
||||
if i == 0 {
|
||||
typ = val_typ
|
||||
// fixed width array initialization? (`arr := [20]byte`)
|
||||
@ -2755,18 +2654,16 @@ fn (p mut Parser) array_init() string {
|
||||
if !nextc.is_space() {
|
||||
p.check(.rsbr)
|
||||
array_elem_typ := p.get_type()
|
||||
if p.table.known_type(array_elem_typ) {
|
||||
p.cgen.resetln('')
|
||||
p.gen('{0}')
|
||||
p.is_alloc = false
|
||||
if is_const_len {
|
||||
return '[${p.mod}__$lit]$array_elem_typ'
|
||||
}
|
||||
return '[$lit]$array_elem_typ'
|
||||
}
|
||||
else {
|
||||
if !p.table.known_type(array_elem_typ) {
|
||||
p.error('bad type `$array_elem_typ`')
|
||||
}
|
||||
p.cgen.resetln('')
|
||||
//p.gen('{0}')
|
||||
p.is_alloc = false
|
||||
if is_const_len {
|
||||
return '[${p.mod}__$lit]$array_elem_typ'
|
||||
}
|
||||
return '[$lit]$array_elem_typ'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2783,10 +2680,11 @@ fn (p mut Parser) array_init() string {
|
||||
i++
|
||||
// Repeat (a = [0;5] )
|
||||
if i == 1 && p.tok == .semicolon {
|
||||
p.warn('`[0 ; len]` syntax was removed. Use `[0].repeat(len)` instead')
|
||||
p.check_space(.semicolon)
|
||||
val := p.cgen.cur_line.right(pos)
|
||||
p.cgen.resetln(p.cgen.cur_line.left(pos))
|
||||
p.gen('array_repeat(& ($typ[]){ $val }, ')
|
||||
p.gen('array_repeat_old(& ($typ[]){ $val }, ')
|
||||
p.check_types(p.bool_expression(), 'int')
|
||||
p.gen(', sizeof($typ) )')
|
||||
p.check(.rsbr)
|
||||
@ -2801,7 +2699,6 @@ fn (p mut Parser) array_init() string {
|
||||
if p.tok == .name && i == 0 {
|
||||
// vals.len == 0 {
|
||||
typ = p.get_type()
|
||||
// println('.key_goT TYP after [] $typ')
|
||||
}
|
||||
// ! after array => no malloc and no copy
|
||||
no_alloc := p.tok == .not
|
||||
@ -2829,56 +2726,20 @@ fn (p mut Parser) array_init() string {
|
||||
// if ptr {
|
||||
// typ += '_ptr"
|
||||
// }
|
||||
mut new_arr := 'new_array_from_c_array'
|
||||
if no_alloc {
|
||||
new_arr += '_no_alloc'
|
||||
}
|
||||
|
||||
if i == 0 && p.pref.ccompiler != 'tcc' {
|
||||
p.gen(' 0 })')
|
||||
} else {
|
||||
p.gen(' })')
|
||||
}
|
||||
|
||||
// p.gen('$new_arr($vals.len, $vals.len, sizeof($typ), ($typ[$vals.len]) $c_arr );')
|
||||
// Need to do this in the second pass, otherwise it goes to the very top of the out.c file
|
||||
if !p.first_pass() {
|
||||
//if i == 0 {
|
||||
//p.cgen.set_placeholder(new_arr_ph, '$new_arr($i, $i, sizeof($typ), ($typ[]) { 0 ')
|
||||
//} else {
|
||||
// Due to a tcc bug, the length needs to be specified.
|
||||
// GCC crashes if it is.
|
||||
cast := if p.pref.ccompiler == 'tcc' { '($typ[$i])' } else { '($typ[])' }
|
||||
p.cgen.set_placeholder(new_arr_ph,
|
||||
'$new_arr($i, $i, sizeof($typ), $cast { ')
|
||||
//}
|
||||
}
|
||||
p.gen_array_init(typ, no_alloc, new_arr_ph, i)
|
||||
typ = 'array_$typ'
|
||||
p.register_array(typ)
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||
fn (p mut Parser) struct_init(typ string) string {
|
||||
//p.gen('/* struct init */')
|
||||
p.is_struct_init = true
|
||||
t := p.table.find_type(typ)
|
||||
// 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));
|
||||
if is_c_struct_init {
|
||||
p.is_c_struct_init = true
|
||||
if t.cat != .c_typedef {
|
||||
p.cgen.insert_before('struct /*c struct init*/')
|
||||
}
|
||||
}
|
||||
p.next()
|
||||
if p.gen_struct_init(typ, t) { return typ }
|
||||
p.scanner.fmt_out.cut(typ.len)
|
||||
ptr := typ.contains('*')
|
||||
// TODO tm struct struct bug
|
||||
if typ == 'tm' {
|
||||
p.cgen.lines[p.cgen.lines.len-1] = ''
|
||||
}
|
||||
p.check(.lcbr)
|
||||
no_star := typ.replace('*', '')
|
||||
// `user := User{foo:bar}` => `User user = (User){ .foo = bar}`
|
||||
/*
|
||||
if !ptr {
|
||||
if p.is_c_struct_init {
|
||||
// `face := C.FT_Face{}` => `FT_Face face;`
|
||||
@ -2891,7 +2752,7 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||
p.is_c_struct_init = false
|
||||
}
|
||||
else {
|
||||
p.gen('($typ) {')
|
||||
p.gen('($typ /*str init */) {')
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -2903,10 +2764,11 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||
p.check(.rcbr)
|
||||
return typ
|
||||
}
|
||||
p.gen('($no_star*)memdup(&($no_star) {')
|
||||
p.is_alloc = true
|
||||
//println('setting is_alloc=true (ret $typ)')
|
||||
p.gen('($t.name*)memdup(&($t.name) {')
|
||||
}
|
||||
*/
|
||||
mut did_gen_something := false
|
||||
// Loop thru all struct init keys and assign values
|
||||
// u := User{age:20, name:'bob'}
|
||||
@ -2924,7 +2786,7 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||
}
|
||||
f := t.find_field(field)
|
||||
inited_fields << field
|
||||
p.gen('.$field = ')
|
||||
p.gen_struct_field_init(field)
|
||||
p.check(.colon)
|
||||
p.fspace()
|
||||
p.check_types(p.bool_expression(), f.typ)
|
||||
@ -2954,7 +2816,8 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||
}
|
||||
// init map fields
|
||||
if field_typ.starts_with('map_') {
|
||||
p.gen('.$field.name = new_map(1, sizeof( ${field_typ.right(4)} ))')
|
||||
p.gen_struct_field_init(field.name)
|
||||
p.gen_empty_map(field_typ.right(4))
|
||||
inited_fields << field.name
|
||||
if i != t.fields.len - 1 {
|
||||
p.gen(',')
|
||||
@ -2964,7 +2827,8 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||
}
|
||||
def_val := type_default(field_typ)
|
||||
if def_val != '' && def_val != '{0}' {
|
||||
p.gen('.$field.name = $def_val')
|
||||
p.gen_struct_field_init(field.name)
|
||||
p.gen(def_val)
|
||||
if i != t.fields.len - 1 {
|
||||
p.gen(',')
|
||||
}
|
||||
@ -3006,74 +2870,17 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
|
||||
p.gen('0')
|
||||
}
|
||||
p.gen('}')
|
||||
if ptr {
|
||||
p.gen(', sizeof($no_star))')
|
||||
if ptr && !p.is_js {
|
||||
p.gen(', sizeof($t.name))')
|
||||
}
|
||||
p.check(.rcbr)
|
||||
p.is_struct_init = false
|
||||
p.is_c_struct_init = false
|
||||
return typ
|
||||
}
|
||||
|
||||
// `f32(3)`
|
||||
// tok is `f32` or `)` if `(*int)(ptr)`
|
||||
fn (p mut Parser) cast(typ string) string {
|
||||
p.next()
|
||||
pos := p.cgen.add_placeholder()
|
||||
if p.tok == .rpar {
|
||||
// skip `)` if it's `(*int)(ptr)`, not `int(a)`
|
||||
p.ptr_cast = true
|
||||
p.next()
|
||||
}
|
||||
p.check(.lpar)
|
||||
p.expected_type = typ
|
||||
expr_typ := p.bool_expression()
|
||||
// `face := FT_Face(cobj)` => `FT_Face face = *((FT_Face*)cobj);`
|
||||
casting_voidptr_to_value := expr_typ == 'void*' && typ != 'int' &&
|
||||
typ != 'byteptr' && !typ.ends_with('*')
|
||||
p.expected_type = ''
|
||||
// `string(buffer)` => `tos2(buffer)`
|
||||
// `string(buffer, len)` => `tos(buffer, len)`
|
||||
// `string(bytes_array, len)` => `tos(bytes_array.data, len)`
|
||||
is_byteptr := expr_typ == 'byte*' || expr_typ == 'byteptr'
|
||||
is_bytearr := expr_typ == 'array_byte'
|
||||
if typ == 'string' {
|
||||
if is_byteptr || is_bytearr {
|
||||
if p.tok == .comma {
|
||||
p.check(.comma)
|
||||
p.cgen.set_placeholder(pos, 'tos((byte *)')
|
||||
if is_bytearr {
|
||||
p.gen('.data')
|
||||
}
|
||||
p.gen(', ')
|
||||
p.check_types(p.expression(), 'int')
|
||||
} else {
|
||||
if is_bytearr {
|
||||
p.gen('.data')
|
||||
}
|
||||
p.cgen.set_placeholder(pos, 'tos2((byte *)')
|
||||
}
|
||||
}
|
||||
// `string(234)` => error
|
||||
else if expr_typ == 'int' {
|
||||
p.error('cannot cast `$expr_typ` to `$typ`, use `str()` method instead')
|
||||
}
|
||||
else {
|
||||
p.error('cannot cast `$expr_typ` to `$typ`')
|
||||
}
|
||||
}
|
||||
else if typ == 'byte' && expr_typ == 'string' {
|
||||
p.error('cannot cast `$expr_typ` to `$typ`, use backquotes `` to create a `$typ` or access the value of an index of `$expr_typ` using []')
|
||||
}
|
||||
else if casting_voidptr_to_value {
|
||||
p.cgen.set_placeholder(pos, '*($typ*)(')
|
||||
}
|
||||
else {
|
||||
p.cgen.set_placeholder(pos, '($typ)(')
|
||||
}
|
||||
p.check(.rpar)
|
||||
p.gen(')')
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (p mut Parser) get_tmp() string {
|
||||
p.tmp_cnt++
|
||||
@ -3173,6 +2980,7 @@ fn (p mut Parser) for_st() {
|
||||
next_tok := p.peek()
|
||||
//debug := p.scanner.file_path.contains('r_draw')
|
||||
p.open_scope()
|
||||
i_type := if p.is_js { 'var' } else { 'int' }
|
||||
if p.tok == .lcbr {
|
||||
// Infinite loop
|
||||
p.gen('while (1) {')
|
||||
@ -3207,11 +3015,15 @@ fn (p mut Parser) for_st() {
|
||||
}
|
||||
// for i, val in array
|
||||
else if p.peek() == .comma {
|
||||
// for i, val in array { ==>
|
||||
//
|
||||
// array_int tmp = array;
|
||||
// for (int i = 0; i < tmp.len; i++) {
|
||||
// int val = tmp[i];
|
||||
/*
|
||||
`for i, val in array {`
|
||||
==>
|
||||
```
|
||||
array_int tmp = array;
|
||||
for (int i = 0; i < tmp.len; i++) {
|
||||
int val = tmp[i];
|
||||
```
|
||||
*/
|
||||
i := p.check_name()
|
||||
p.check(.comma)
|
||||
val := p.check_name()
|
||||
@ -3228,7 +3040,11 @@ fn (p mut Parser) for_st() {
|
||||
p.error('cannot range over type `$typ`')
|
||||
}
|
||||
expr := p.cgen.end_tmp()
|
||||
p.genln('$typ $tmp = $expr ;')
|
||||
if p.is_js {
|
||||
p.genln('var $tmp = $expr;')
|
||||
} else {
|
||||
p.genln('$typ $tmp = $expr;')
|
||||
}
|
||||
pad := if is_arr { 6 } else { 4 }
|
||||
var_typ := if is_str { 'byte' } else { typ.right(pad) }
|
||||
// typ = strings.Replace(typ, "_ptr", "*", -1)
|
||||
@ -3247,9 +3063,9 @@ fn (p mut Parser) for_st() {
|
||||
is_mut: true
|
||||
is_changed: true
|
||||
}
|
||||
//p.genln(';\nfor ($i_type $i = 0; $i < $tmp .len; $i ++) {')
|
||||
p.gen_for_header(i, tmp, var_typ, val)
|
||||
p.register_var(i_var)
|
||||
p.genln(';\nfor (int $i = 0; $i < $tmp .len; $i ++) {')
|
||||
p.genln('$var_typ $val = (($var_typ *) $tmp . data)[$i];')
|
||||
}
|
||||
else if is_map {
|
||||
i_var := Var {
|
||||
@ -3259,14 +3075,7 @@ fn (p mut Parser) for_st() {
|
||||
is_changed: true
|
||||
}
|
||||
p.register_var(i_var)
|
||||
p.genln('array_string keys_$tmp = map_keys(& $tmp ); ')
|
||||
p.genln('for (int l = 0; l < keys_$tmp .len; l++) {')
|
||||
p.genln(' string $i = ((string*)keys_$tmp .data)[l];')
|
||||
//p.genln(' string $i = *(string*) ( array__get(keys_$tmp, l) );')
|
||||
def := type_default(typ)
|
||||
// TODO don't call map_get() for each key, fetch values while traversing
|
||||
// the tree (replace `map_keys()` above with `map_key_vals()`)
|
||||
p.genln('$var_typ $val = $def; map_get($tmp, $i, & $val);')
|
||||
p.gen_for_map_header(i, tmp, var_typ, val, typ)
|
||||
}
|
||||
else if is_str {
|
||||
i_var := Var {
|
||||
@ -3276,9 +3085,7 @@ fn (p mut Parser) for_st() {
|
||||
is_changed: true
|
||||
}
|
||||
p.register_var(i_var)
|
||||
p.genln('array_byte bytes_$tmp = string_bytes( $tmp );')
|
||||
p.genln(';\nfor (int $i = 0; $i < $tmp .len; $i ++) {')
|
||||
p.genln('$var_typ $val = (($var_typ *) bytes_$tmp . data)[$i];')
|
||||
p.gen_for_str_header(i, tmp, var_typ, val)
|
||||
}
|
||||
}
|
||||
// `for val in vals`
|
||||
@ -3305,7 +3112,11 @@ fn (p mut Parser) for_st() {
|
||||
if !is_arr && !is_str && !is_range {
|
||||
p.error('cannot range over type `$typ`')
|
||||
}
|
||||
p.genln('$typ $tmp = $expr;')
|
||||
if p.is_js {
|
||||
p.genln('var $tmp = $expr;')
|
||||
} else {
|
||||
p.genln('$typ $tmp = $expr;')
|
||||
}
|
||||
// TODO var_type := if...
|
||||
mut var_type := ''
|
||||
if is_arr {
|
||||
@ -3327,23 +3138,17 @@ fn (p mut Parser) for_st() {
|
||||
}
|
||||
p.register_var(val_var)
|
||||
i := p.get_tmp()
|
||||
if is_range {
|
||||
p.genln(';\nfor (int $i = $tmp; $i < $range_end; $i++) {')
|
||||
}
|
||||
else {
|
||||
p.genln(';\nfor (int $i = 0; $i < $tmp .len; $i ++) {')
|
||||
}
|
||||
if is_arr {
|
||||
p.genln('$var_type $val = (($var_type *) ${tmp}.data)[$i];')
|
||||
p.gen_for_header(i, tmp, var_type, val)
|
||||
}
|
||||
else if is_str {
|
||||
p.genln('$var_type $val = (($var_type *) ${tmp}.str)[$i];')
|
||||
p.gen_for_str_header(i, tmp, var_type, val)
|
||||
}
|
||||
else if is_range {
|
||||
else if is_range && !p.is_js {
|
||||
p.genln(';\nfor ($i_type $i = $tmp; $i < $range_end; $i++) {')
|
||||
p.genln('$var_type $val = $i;')
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// `for a < b {`
|
||||
p.gen('while (')
|
||||
p.check_types(p.bool_expression(), 'bool')
|
||||
|
@ -402,7 +402,7 @@ fn (s mut Scanner) scan() ScanRes {
|
||||
// @LINE => will be substituted with the V line number where it appears (as a string).
|
||||
// @COLUMN => will be substituted with the column where it appears (as a string).
|
||||
// @VHASH => will be substituted with the shortened commit hash of the V compiler (as a string).
|
||||
// This allows things like this:
|
||||
// This allows things like this:
|
||||
// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @FN)
|
||||
// ... which is useful while debugging/tracing
|
||||
if name == 'FN' { return scan_res(.str, s.fn_name) }
|
||||
@ -624,7 +624,7 @@ fn (s &Scanner) error(msg string) {
|
||||
linestart := s.find_current_line_start_position()
|
||||
lineend := s.find_current_line_end_position()
|
||||
column := s.pos - linestart
|
||||
if s.should_print_line_on_error {
|
||||
if s.should_print_line_on_error && lineend > linestart {
|
||||
line := s.text.substr( linestart, lineend )
|
||||
// The pointerline should have the same spaces/tabs as the offending
|
||||
// line, so that it prints the ^ character exactly on the *same spot*
|
||||
|
@ -129,6 +129,7 @@ fn (t Type) str() string {
|
||||
|
||||
const (
|
||||
CReserved = [
|
||||
'delete',
|
||||
'exit',
|
||||
'unix',
|
||||
//'print',
|
||||
@ -141,32 +142,21 @@ const (
|
||||
|
||||
// Full list of C reserved words, from: https://en.cppreference.com/w/c/keyword
|
||||
'auto',
|
||||
'break',
|
||||
'case',
|
||||
'char',
|
||||
'const',
|
||||
'continue',
|
||||
'default',
|
||||
'do',
|
||||
'double',
|
||||
'else',
|
||||
'enum',
|
||||
'extern',
|
||||
'float',
|
||||
'for',
|
||||
'goto',
|
||||
'if',
|
||||
'inline',
|
||||
'int',
|
||||
'long',
|
||||
'register',
|
||||
'restrict',
|
||||
'return',
|
||||
'short',
|
||||
'signed',
|
||||
'sizeof',
|
||||
'static',
|
||||
'struct',
|
||||
'switch',
|
||||
'typedef',
|
||||
'union',
|
||||
@ -461,7 +451,6 @@ fn (table &Table) type_has_method(typ &Type, name string) bool {
|
||||
|
||||
// TODO use `?Fn`
|
||||
fn (table &Table) find_method(typ &Type, name string) Fn {
|
||||
// println('TYPE HAS METHOD $name')
|
||||
// method := typ.find_method(name)
|
||||
t := table.typesmap[typ.name]
|
||||
method := t.find_method(name)
|
||||
@ -638,38 +627,6 @@ fn (p mut Parser) satisfies_interface(interface_name, _typ string, throw bool) b
|
||||
return true
|
||||
}
|
||||
|
||||
fn type_default(typ string) string {
|
||||
if typ.starts_with('array_') {
|
||||
return 'new_array(0, 1, sizeof( ${typ.right(6)} ))'
|
||||
}
|
||||
// Always set pointers to 0
|
||||
if typ.ends_with('*') {
|
||||
return '0'
|
||||
}
|
||||
// User struct defined in another module.
|
||||
if typ.contains('__') {
|
||||
return '{0}'
|
||||
}
|
||||
// Default values for other types are not needed because of mandatory initialization
|
||||
switch typ {
|
||||
case 'bool': return '0'
|
||||
case 'string': return 'tos((byte *)"", 0)'
|
||||
case 'i8': return '0'
|
||||
case 'i16': return '0'
|
||||
case 'i64': return '0'
|
||||
case 'u16': return '0'
|
||||
case 'u32': return '0'
|
||||
case 'u64': return '0'
|
||||
case 'byte': return '0'
|
||||
case 'int': return '0'
|
||||
case 'rune': return '0'
|
||||
case 'f32': return '0.0'
|
||||
case 'f64': return '0.0'
|
||||
case 'byteptr': return '0'
|
||||
case 'voidptr': return '0'
|
||||
}
|
||||
return '{0}'
|
||||
}
|
||||
|
||||
fn (table &Table) is_interface(name string) bool {
|
||||
if !(name in table.typesmap) {
|
||||
@ -700,41 +657,6 @@ fn (t &Table) find_const(name string) Var {
|
||||
return Var{}
|
||||
}
|
||||
|
||||
fn (table mut Table) cgen_name(f &Fn) string {
|
||||
mut name := f.name
|
||||
if f.is_method {
|
||||
name = '${f.receiver_typ}_$f.name'
|
||||
name = name.replace(' ', '')
|
||||
name = name.replace('*', '')
|
||||
name = name.replace('+', 'plus')
|
||||
name = name.replace('-', 'minus')
|
||||
}
|
||||
// Avoid name conflicts (with things like abs(), print() etc).
|
||||
// Generate b_abs(), b_print()
|
||||
// TODO duplicate functionality
|
||||
if f.mod == 'builtin' && f.name in CReserved {
|
||||
return 'v_$name'
|
||||
}
|
||||
// Obfuscate but skip certain names
|
||||
// TODO ugly, fix
|
||||
if table.obfuscate && f.name != 'main' && f.name != 'WinMain' && f.mod != 'builtin' && !f.is_c &&
|
||||
f.mod != 'darwin' && f.mod != 'os' && !f.name.contains('window_proc') && f.name != 'gg__vec2' &&
|
||||
f.name != 'build_token_str' && f.name != 'build_keys' && f.mod != 'json' &&
|
||||
!name.ends_with('_str') && !name.contains('contains') {
|
||||
mut idx := table.obf_ids[name]
|
||||
// No such function yet, register it
|
||||
if idx == 0 {
|
||||
table.fn_cnt++
|
||||
table.obf_ids[name] = table.fn_cnt
|
||||
idx = table.fn_cnt
|
||||
}
|
||||
old := name
|
||||
name = 'f_$idx'
|
||||
println('$old ==> $name')
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// ('s', 'string') => 'string s'
|
||||
// ('nums', '[20]byte') => 'byte nums[20]'
|
||||
// ('myfn', 'fn(int) string') => 'string (*myfn)(int)'
|
||||
|
@ -124,7 +124,7 @@ fn build_keys() map[string]int {
|
||||
|
||||
// TODO remove once we have `enum Token { name('name') if('if') ... }`
|
||||
fn build_token_str() []string {
|
||||
mut s := [''; NrTokens]
|
||||
mut s := [''].repeat2(NrTokens)
|
||||
s[Token.keyword_beg] = ''
|
||||
s[Token.keyword_end] = ''
|
||||
s[Token.eof] = 'eof'
|
||||
|
@ -58,7 +58,7 @@ fn new_array_from_c_array_no_alloc(len, cap, elm_size int, c_array voidptr) arra
|
||||
}
|
||||
|
||||
// Private function, used by V (`[0; 100]`)
|
||||
fn array_repeat(val voidptr, nr_repeats, elm_size int) array {
|
||||
fn array_repeat_old(val voidptr, nr_repeats, elm_size int) array {
|
||||
arr := array {
|
||||
len: nr_repeats
|
||||
cap: nr_repeats
|
||||
@ -71,6 +71,35 @@ fn array_repeat(val voidptr, nr_repeats, elm_size int) array {
|
||||
return arr
|
||||
}
|
||||
|
||||
pub fn (a array) repeat(nr_repeats int) array {
|
||||
arr := array {
|
||||
len: nr_repeats
|
||||
cap: nr_repeats
|
||||
element_size: a.element_size
|
||||
data: malloc(nr_repeats * a.element_size)
|
||||
}
|
||||
val := a.data + 0 //nr_repeats * a.element_size
|
||||
for i := 0; i < nr_repeats; i++ {
|
||||
C.memcpy(arr.data + i * a.element_size, val, a.element_size)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
pub fn (a array) repeat2(nr_repeats int) array {
|
||||
arr := array {
|
||||
len: nr_repeats
|
||||
cap: nr_repeats
|
||||
element_size: a.element_size
|
||||
data: malloc(nr_repeats * a.element_size)
|
||||
}
|
||||
val := a.data + 0 //nr_repeats * a.element_size
|
||||
for i := 0; i < nr_repeats; i++ {
|
||||
C.memcpy(arr.data + i * a.element_size, val, a.element_size)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
pub fn (a mut array) sort_with_compare(compare voidptr) {
|
||||
C.qsort(a.data, a.len, a.element_size, compare)
|
||||
}
|
||||
@ -232,7 +261,9 @@ pub fn (a []string) str() string {
|
||||
sb.write('[')
|
||||
for i := 0; i < a.len; i++ {
|
||||
val := a[i]
|
||||
sb.write('"$val"')
|
||||
sb.write('"')
|
||||
sb.write(val)
|
||||
sb.write('"')
|
||||
if i < a.len - 1 {
|
||||
sb.write(', ')
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ fn test_slice() {
|
||||
fn test_push_many() {
|
||||
mut a := [1, 2, 3]
|
||||
b := [4, 5, 6]
|
||||
a << b
|
||||
a << b
|
||||
assert a.len == 6
|
||||
assert a[0] == 1
|
||||
assert a[3] == 4
|
||||
@ -163,42 +163,44 @@ fn test_reverse() {
|
||||
|
||||
const (
|
||||
N = 5
|
||||
)
|
||||
)
|
||||
|
||||
fn test_fixed() {
|
||||
mut nums := [4]int
|
||||
assert nums[0] == 0
|
||||
assert nums[1] == 0
|
||||
assert nums[2] == 0
|
||||
assert nums[3] == 0
|
||||
nums[1] = 7
|
||||
assert nums[1] == 7
|
||||
/*
|
||||
mut nums := [4]int
|
||||
assert nums[0] == 0
|
||||
assert nums[1] == 0
|
||||
assert nums[2] == 0
|
||||
assert nums[3] == 0
|
||||
nums[1] = 7
|
||||
assert nums[1] == 7
|
||||
///////
|
||||
nums2 := [N]int
|
||||
assert nums2[N - 1] == 0
|
||||
}
|
||||
nums2 := [N]int
|
||||
assert nums2[N - 1] == 0
|
||||
*/
|
||||
}
|
||||
|
||||
fn modify (numbers mut []int) {
|
||||
numbers[0] = 777
|
||||
}
|
||||
|
||||
fn test_mut_slice() {
|
||||
fn test_mut_slice() {
|
||||
mut n := [1,2,3]
|
||||
modify(mut n.left(2))
|
||||
assert n[0] == 777
|
||||
modify(mut n.right(2))
|
||||
assert n[2] == 777
|
||||
modify(mut n.left(2))
|
||||
assert n[0] == 777
|
||||
modify(mut n.right(2))
|
||||
assert n[2] == 777
|
||||
println(n)
|
||||
}
|
||||
|
||||
fn test_clone() {
|
||||
nums := [1, 2, 3, 4, 100]
|
||||
nums2 := nums.clone()
|
||||
assert nums2.len == 5
|
||||
assert nums2.str() == '[1, 2, 3, 4, 100]'
|
||||
assert nums.slice(1, 3).str() == '[2, 3]'
|
||||
}
|
||||
|
||||
nums := [1, 2, 3, 4, 100]
|
||||
nums2 := nums.clone()
|
||||
assert nums2.len == 5
|
||||
assert nums2.str() == '[1, 2, 3, 4, 100]'
|
||||
assert nums.slice(1, 3).str() == '[2, 3]'
|
||||
}
|
||||
|
||||
fn test_doubling() {
|
||||
mut nums := [1, 2, 3, 4, 5]
|
||||
for i := 0; i < nums.len; i++ {
|
||||
|
@ -213,7 +213,7 @@ pub fn (c byte) is_capital() bool {
|
||||
}
|
||||
|
||||
pub fn (b []byte) clone() []byte {
|
||||
mut res := [byte(0); b.len]
|
||||
mut res := [byte(0)].repeat2(b.len)
|
||||
for i := 0; i < b.len; i++ {
|
||||
res[i] = b[i]
|
||||
}
|
||||
|
130
vlib/builtin/js/array.v
Normal file
130
vlib/builtin/js/array.v
Normal file
@ -0,0 +1,130 @@
|
||||
// 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 builtin
|
||||
|
||||
import strings
|
||||
|
||||
struct array {
|
||||
pub:
|
||||
data voidptr
|
||||
len int
|
||||
cap int
|
||||
element_size int
|
||||
}
|
||||
|
||||
/*
|
||||
// Private function, used by V (`nums := []int`)
|
||||
fn new_array(mylen, cap, elm_size int) array {
|
||||
arr := array {
|
||||
len: mylen
|
||||
cap: cap
|
||||
element_size: elm_size
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
|
||||
// TODO
|
||||
pub fn _make(len, cap, elm_size int) array {
|
||||
return new_array(len, cap, elm_size)
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
fn array_repeat(val voidptr, nr_repeats, elm_size int) array {
|
||||
return val
|
||||
}
|
||||
|
||||
pub fn (a array) repeat2(nr_repeats int) array {
|
||||
#return Array(a[0]).fill(nr_repeats)
|
||||
return a
|
||||
}
|
||||
|
||||
pub fn (a mut array) sort_with_compare(compare voidptr) {
|
||||
}
|
||||
|
||||
pub fn (a mut array) insert(i int, val voidptr) {
|
||||
}
|
||||
|
||||
pub fn (a mut array) prepend(val voidptr) {
|
||||
a.insert(0, val)
|
||||
}
|
||||
|
||||
pub fn (a mut array) delete_elm(idx int) {
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn (a array) first() voidptr {
|
||||
if a.len == 0 {
|
||||
panic('array.first: empty array')
|
||||
}
|
||||
return a.data + 0
|
||||
}
|
||||
|
||||
pub fn (a array) last() voidptr {
|
||||
if a.len == 0 {
|
||||
panic('array.last: empty array')
|
||||
}
|
||||
return a.data + (a.len - 1) * a.element_size
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn (s array) left(n int) array {
|
||||
if n >= s.len {
|
||||
return s
|
||||
}
|
||||
return s.slice(0, n)
|
||||
}
|
||||
|
||||
pub fn (s array) right(n int) array {
|
||||
if n >= s.len {
|
||||
return s
|
||||
}
|
||||
return s.slice(n, s.len)
|
||||
}
|
||||
|
||||
pub fn (s array) slice(start, _end int) array {
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (a array) reverse() array {
|
||||
return a
|
||||
}
|
||||
|
||||
pub fn (a array) clone() array {
|
||||
return a
|
||||
}
|
||||
|
||||
pub fn (a array) free() {
|
||||
}
|
||||
|
||||
// "[ 'a', 'b', 'c' ]"
|
||||
pub fn (a []string) str() string {
|
||||
mut sb := strings.new_builder(a.len * 3)
|
||||
sb.write('[')
|
||||
for i := 0; i < a.len; i++ {
|
||||
val := a[i]
|
||||
sb.write('"')
|
||||
sb.write(val)
|
||||
sb.write('"')
|
||||
if i < a.len - 1 {
|
||||
sb.write(', ')
|
||||
}
|
||||
}
|
||||
sb.write(']')
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
pub fn (b []byte) hex() string {
|
||||
return 'sdf'
|
||||
}
|
||||
|
||||
pub fn (arr mut array) _push_many(val voidptr, size int) {
|
||||
}
|
||||
|
||||
pub fn free(voidptr) {
|
||||
|
||||
}
|
||||
|
35
vlib/builtin/js/builtin.v
Normal file
35
vlib/builtin/js/builtin.v
Normal file
@ -0,0 +1,35 @@
|
||||
// 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 builtin
|
||||
|
||||
pub fn exit(code int) {
|
||||
println('js.exit()')
|
||||
}
|
||||
|
||||
// isnil returns true if an object is nil (only for C objects).
|
||||
pub fn isnil(v voidptr) bool {
|
||||
return v == 0
|
||||
}
|
||||
|
||||
pub fn panic(s string) {
|
||||
println('V panic: ' + s)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
pub fn println(s string) {
|
||||
#console.log(s)
|
||||
}
|
||||
|
||||
pub fn eprintln(s string) {
|
||||
#console.log(s)
|
||||
}
|
||||
|
||||
pub fn print(s string) {
|
||||
#console.log(s)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
99
vlib/builtin/js/int.v
Normal file
99
vlib/builtin/js/int.v
Normal file
@ -0,0 +1,99 @@
|
||||
// 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 builtin
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
pub fn (d double) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (d f64) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (d f32) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn ptr_str(ptr voidptr) string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
// compare floats using C epsilon
|
||||
pub fn (a f64) eq(b f64) bool {
|
||||
//return C.fabs(a - b) <= C.DBL_EPSILON
|
||||
return (a - b) <= 0.01
|
||||
}
|
||||
|
||||
// fn (nn i32) str() string {
|
||||
// return i
|
||||
// }
|
||||
pub fn (nn int) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (nn u32) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (nn u8) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (nn i64) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (nn u64) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (b bool) str() string {
|
||||
if b {
|
||||
return 'true'
|
||||
}
|
||||
return 'false'
|
||||
}
|
||||
|
||||
pub fn (n int) hex() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (n i64) hex() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (a []byte) contains(val byte) bool {
|
||||
for aa in a {
|
||||
if aa == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (c rune) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (c byte) str() string {
|
||||
return '0'
|
||||
}
|
||||
|
||||
pub fn (c byte) is_capital() bool {
|
||||
return c >= `A` && c <= `Z`
|
||||
}
|
||||
|
||||
pub fn (b []byte) clone() []byte {
|
||||
mut res := [byte(0)].repeat2(b.len)
|
||||
for i := 0; i < b.len; i++ {
|
||||
res[i] = b[i]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
70
vlib/builtin/js/map.v
Normal file
70
vlib/builtin/js/map.v
Normal file
@ -0,0 +1,70 @@
|
||||
// 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 builtin
|
||||
|
||||
import strings
|
||||
|
||||
struct map {
|
||||
obj voidptr
|
||||
}
|
||||
|
||||
//fn (m mut map) insert(n mut mapnode, key string, val voidptr) {
|
||||
//}
|
||||
|
||||
//////fn (n & mapnode) find(key string, out voidptr, element_size int) bool{
|
||||
//return false
|
||||
//}
|
||||
|
||||
// same as `find`, but doesn't return a value. Used by `exists`
|
||||
//fn (n & mapnode) find2(key string, element_size int) bool{
|
||||
//return false
|
||||
//}
|
||||
|
||||
fn (m mut map) _set(key string, val voidptr) {
|
||||
}
|
||||
|
||||
//fn preorder_keys(node &mapnode, keys mut []string, key_i int) int {
|
||||
//return 0
|
||||
//}
|
||||
|
||||
pub fn (m mut map) keys() []string {
|
||||
return ['']
|
||||
}
|
||||
|
||||
fn (m map) get(key string, out voidptr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (m mut map) delete(key string) {
|
||||
}
|
||||
|
||||
fn (m map) _exists(key string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (m map) print() {
|
||||
println('<<<<<<<<')
|
||||
println('>>>>>>>>>>')
|
||||
}
|
||||
|
||||
pub fn (m map) free() {
|
||||
// C.free(m.table)
|
||||
// C.free(m.keys_table)
|
||||
}
|
||||
|
||||
pub fn (m map_string) str() string {
|
||||
/*
|
||||
if m.size == 0 {
|
||||
return '{}'
|
||||
}
|
||||
*/
|
||||
mut sb := strings.new_builder(50)
|
||||
sb.writeln('{')
|
||||
for key, val in m {
|
||||
//sb.writeln(' "$key" => "$val"')
|
||||
}
|
||||
sb.writeln('}')
|
||||
return sb.str()
|
||||
}
|
31
vlib/builtin/js/option.v
Normal file
31
vlib/builtin/js/option.v
Normal file
@ -0,0 +1,31 @@
|
||||
// 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 builtin
|
||||
|
||||
struct Option {
|
||||
data [255]byte
|
||||
error string
|
||||
ok bool
|
||||
}
|
||||
|
||||
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
||||
fn opt_ok(data voidptr, size int) Option {
|
||||
if size >= 255 {
|
||||
panic('option size too big')
|
||||
}
|
||||
res := Option {
|
||||
ok: true
|
||||
}
|
||||
C.memcpy(res.data, data, size)
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn error(s string) Option {
|
||||
return Option {
|
||||
error: s
|
||||
}
|
||||
}
|
||||
|
||||
|
318
vlib/builtin/js/string.v
Normal file
318
vlib/builtin/js/string.v
Normal file
@ -0,0 +1,318 @@
|
||||
// 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 builtin
|
||||
|
||||
struct string {
|
||||
//mut:
|
||||
//hash_cache int
|
||||
pub:
|
||||
str byteptr
|
||||
len int
|
||||
}
|
||||
|
||||
// For C strings only
|
||||
fn C.strlen(s byteptr) int
|
||||
|
||||
fn todo() { }
|
||||
|
||||
|
||||
pub fn (a string) clone() string {
|
||||
return a
|
||||
}
|
||||
|
||||
pub fn (s string) replace(rep, with_ string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (s string) int() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
pub fn (s string) i64() i64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
pub fn (s string) f32() f32 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
pub fn (s string) f64() f64 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
pub fn (s string) u32() u32 {
|
||||
return u32(0)
|
||||
}
|
||||
|
||||
pub fn (s string) u64() u64 {
|
||||
return u64(0)
|
||||
}
|
||||
|
||||
pub fn (s string) split(delim string) []string {
|
||||
return s.split(delim)
|
||||
}
|
||||
|
||||
pub fn (s string) split_single(delim byte) []string {
|
||||
return s.split(delim.str())
|
||||
}
|
||||
|
||||
pub fn (s string) split_into_lines() []string {
|
||||
return s.split('\n')
|
||||
}
|
||||
|
||||
// 'hello'.left(2) => 'he'
|
||||
pub fn (s string) left(n int) string {
|
||||
if n >= s.len {
|
||||
return s
|
||||
}
|
||||
return s.substr(0, n)
|
||||
}
|
||||
// 'hello'.right(2) => 'llo'
|
||||
pub fn (s string) right(n int) string {
|
||||
if n >= s.len {
|
||||
return ''
|
||||
}
|
||||
return s.substr(n, s.len)
|
||||
}
|
||||
|
||||
pub fn (s string) substr(start, end int) string {
|
||||
return 'a'
|
||||
}
|
||||
|
||||
pub fn (s string) index(p string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
pub fn (s string) index_any(chars string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
pub fn (s string) last_index(p string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
pub fn (s string) index_after(p string, start int) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// counts occurrences of substr in s
|
||||
pub fn (s string) count(substr string) int {
|
||||
return 0 // TODO can never get here - v doesn't know that
|
||||
}
|
||||
|
||||
pub fn (s string) contains(p string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (s string) starts_with(p string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (s string) ends_with(p string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO only works with ASCII
|
||||
pub fn (s string) to_lower() string {
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (s string) to_upper() string {
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (s string) capitalize() string {
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (s string) title() string {
|
||||
return s
|
||||
}
|
||||
|
||||
// 'hey [man] how you doin'
|
||||
// find_between('[', ']') == 'man'
|
||||
pub fn (s string) find_between(start, end string) string {
|
||||
start_pos := s.index(start)
|
||||
if start_pos == -1 {
|
||||
return ''
|
||||
}
|
||||
// First get everything to the right of 'start'
|
||||
val := s.right(start_pos + start.len)
|
||||
end_pos := val.index(end)
|
||||
if end_pos == -1 {
|
||||
return val
|
||||
}
|
||||
return val.left(end_pos)
|
||||
}
|
||||
|
||||
// TODO generic
|
||||
pub fn (ar []string) contains(val string) bool {
|
||||
for s in ar {
|
||||
if s == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO generic
|
||||
pub fn (ar []int) contains(val int) bool {
|
||||
for i, s in ar {
|
||||
if s == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
fn is_space(c byte) bool {
|
||||
return C.isspace(c)
|
||||
}
|
||||
|
||||
pub fn (c byte) is_space() bool {
|
||||
return is_space(c)
|
||||
}
|
||||
|
||||
pub fn (s string) trim_space() string {
|
||||
#return s.str.trim(' ');
|
||||
return ''
|
||||
}
|
||||
|
||||
pub fn (s string) trim(cutset string) string {
|
||||
#return s.str.trim(cutset);
|
||||
return ''
|
||||
}
|
||||
|
||||
pub fn (s string) trim_left(cutset string) string {
|
||||
#return s.str.trimLeft(cutset);
|
||||
return ''
|
||||
}
|
||||
|
||||
pub fn (s string) trim_right(cutset string) string {
|
||||
#return s.str.trimRight(cutset);
|
||||
return ''
|
||||
}
|
||||
|
||||
// fn print_cur_thread() {
|
||||
// //C.printf("tid = %08x \n", pthread_self());
|
||||
// }
|
||||
pub fn (s mut []string) sort() {
|
||||
|
||||
}
|
||||
|
||||
pub fn (s mut []string) sort_ignore_case() {
|
||||
}
|
||||
|
||||
pub fn (s mut []string) sort_by_len() {
|
||||
}
|
||||
|
||||
fn (s string) at(idx int) byte {
|
||||
if idx < 0 || idx >= s.len {
|
||||
panic('string index out of range')
|
||||
}
|
||||
return s.str[idx]
|
||||
}
|
||||
pub fn (c byte) is_digit() bool {
|
||||
return c >= `0` && c <= `9`
|
||||
}
|
||||
|
||||
pub fn (c byte) is_hex_digit() bool {
|
||||
return c.is_digit() || (c >= `a` && c <= `f`) || (c >= `A` && c <= `F`)
|
||||
}
|
||||
|
||||
pub fn (c byte) is_oct_digit() bool {
|
||||
return c >= `0` && c <= `7`
|
||||
}
|
||||
|
||||
pub fn (c byte) is_letter() bool {
|
||||
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
|
||||
}
|
||||
|
||||
pub fn (s string) free() {
|
||||
}
|
||||
|
||||
/*
|
||||
fn (arr []string) free() {
|
||||
for s in arr {
|
||||
s.free()
|
||||
}
|
||||
C.free(arr.data)
|
||||
}
|
||||
*/
|
||||
|
||||
// all_before('23:34:45.234', '.') == '23:34:45'
|
||||
pub fn (s string) all_before(dot string) string {
|
||||
pos := s.index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.left(pos)
|
||||
}
|
||||
|
||||
pub fn (s string) all_before_last(dot string) string {
|
||||
pos := s.last_index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.left(pos)
|
||||
}
|
||||
|
||||
pub fn (s string) all_after(dot string) string {
|
||||
pos := s.last_index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.right(pos + dot.len)
|
||||
}
|
||||
|
||||
// fn (s []string) substr(a, b int) string {
|
||||
// return join_strings(s.slice_fast(a, b))
|
||||
// }
|
||||
pub fn (a []string) join(del string) string {
|
||||
return ''
|
||||
}
|
||||
|
||||
pub fn (s []string) join_lines() string {
|
||||
return s.join('\n')
|
||||
}
|
||||
|
||||
pub fn (s string) reverse() string {
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (s string) limit(max int) string {
|
||||
if s.len <= max {
|
||||
return s
|
||||
}
|
||||
return s.substr(0, max)
|
||||
}
|
||||
|
||||
// TODO is_white_space()
|
||||
pub fn (c byte) is_white() bool {
|
||||
i := int(c)
|
||||
return i == 10 || i == 32 || i == 9 || i == 13 || c == `\r`
|
||||
}
|
||||
|
||||
|
||||
pub fn (s string) hash() int {
|
||||
//mut h := s.hash_cache
|
||||
mut h := 0
|
||||
if h == 0 && s.len > 0 {
|
||||
for c in s {
|
||||
h = h * 31 + int(c)
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
pub fn (s string) bytes() []byte {
|
||||
if s.len == 0 {
|
||||
return []byte
|
||||
}
|
||||
mut buf := [byte(0)].repeat2(s.len)
|
||||
C.memcpy(buf.data, s.str, s.len)
|
||||
return buf
|
||||
}
|
27
vlib/builtin/js/utf8.v
Normal file
27
vlib/builtin/js/utf8.v
Normal file
@ -0,0 +1,27 @@
|
||||
// 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 builtin
|
||||
|
||||
pub fn utf8_char_len(b byte) int {
|
||||
return (( 0xe5000000 >> (( b >> 3 ) & 0x1e )) & 3 ) + 1
|
||||
}
|
||||
|
||||
// Convert utf32 to utf8
|
||||
// utf32 == Codepoint
|
||||
pub fn utf32_to_str(code u32) string {
|
||||
return ''
|
||||
}
|
||||
|
||||
// TODO copypasta
|
||||
pub fn utf32_to_str_no_malloc(code u32, buf voidptr) string {
|
||||
return ''
|
||||
}
|
||||
|
||||
// Convert utf8 to utf32
|
||||
pub fn (_rune string) utf32_code() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ fn preorder_keys(node &mapnode, keys mut []string, key_i int) int {
|
||||
}
|
||||
|
||||
pub fn (m mut map) keys() []string {
|
||||
mut keys := [''; m.size]
|
||||
mut keys := [''].repeat2(m.size)
|
||||
if isnil(m.root) {
|
||||
return keys
|
||||
}
|
||||
|
@ -78,10 +78,9 @@ pub fn (s string) replace(rep, with string) string {
|
||||
if s.len == 0 || rep.len == 0 {
|
||||
return s
|
||||
}
|
||||
// println('"$s" replace "$rep" with "$with" rep.len=$rep.len')
|
||||
// TODO PERF Allocating ints is expensive. Should be a stack array
|
||||
// Get locations of all reps within this string
|
||||
mut idxs := []int{}
|
||||
mut idxs := []int
|
||||
mut rem := s
|
||||
mut rstart := 0
|
||||
for {
|
||||
@ -352,37 +351,58 @@ pub fn (s string) substr(start, end int) string {
|
||||
return res
|
||||
}
|
||||
|
||||
// KMP search
|
||||
pub fn (s string) index(p string) int {
|
||||
pub fn (s string) index_old(p string) int {
|
||||
if p.len > s.len {
|
||||
return -1
|
||||
}
|
||||
mut prefix := [0; p.len]
|
||||
mut j := 0
|
||||
for i := 1; i < p.len; i++ {
|
||||
for p[j] != p[i] && j > 0 {
|
||||
j = prefix[j - 1]
|
||||
}
|
||||
if p[j] == p[i] {
|
||||
j++
|
||||
}
|
||||
prefix[i] = j
|
||||
}
|
||||
j = 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
for p[j] != s[i] && j > 0 {
|
||||
j = prefix[j - 1]
|
||||
}
|
||||
if p[j] == s[i] {
|
||||
mut i := 0
|
||||
for i < s.len {
|
||||
mut j := 0
|
||||
mut ii := i
|
||||
for j < p.len && s[ii] == p[j] {
|
||||
j++
|
||||
ii++
|
||||
}
|
||||
if j == p.len {
|
||||
return i - p.len + 1
|
||||
}
|
||||
i++
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// KMP search
|
||||
pub fn (s string) index(p string) int {
|
||||
if p.len > s.len {
|
||||
return -1
|
||||
}
|
||||
mut prefix := [0].repeat2(p.len)
|
||||
mut j := 0
|
||||
for i := 1; i < p.len; i++ {
|
||||
for p[j] != p[i] && j > 0 {
|
||||
j = prefix[j - 1]
|
||||
}
|
||||
if p[j] == p[i] {
|
||||
j++
|
||||
}
|
||||
prefix[i] = j
|
||||
}
|
||||
j = 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
for p[j] != s[i] && j > 0 {
|
||||
j = prefix[j - 1]
|
||||
}
|
||||
if p[j] == s[i] {
|
||||
j++
|
||||
}
|
||||
if j == p.len {
|
||||
return i - p.len + 1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
|
||||
pub fn (s string) index_any(chars string) int {
|
||||
for c in chars {
|
||||
index := s.index(c.str())
|
||||
@ -874,7 +894,7 @@ pub fn (s string) bytes() []byte {
|
||||
if s.len == 0 {
|
||||
return []byte
|
||||
}
|
||||
mut buf := [byte(0); s.len]
|
||||
mut buf := [byte(0)].repeat2(s.len)
|
||||
C.memcpy(buf.data, s.str, s.len)
|
||||
return buf
|
||||
}
|
||||
|
22
vlib/os/os.v
22
vlib/os/os.v
@ -69,25 +69,6 @@ fn C.ftell(fp voidptr) int
|
||||
fn C.getenv(byteptr) byteptr
|
||||
fn C.sigaction(int, voidptr, int)
|
||||
|
||||
fn init_os_args(argc int, argv &byteptr) []string {
|
||||
mut args := []string
|
||||
$if windows {
|
||||
mut args_list := &voidptr(0)
|
||||
mut args_count := 0
|
||||
args_list = C.CommandLineToArgvW(C.GetCommandLine(), &args_count)
|
||||
for i := 0; i < args_count; i++ {
|
||||
args << string_from_wide(&u16(args_list[i]))
|
||||
}
|
||||
|
||||
C.LocalFree(args_list)
|
||||
} $else {
|
||||
for i := 0; i < argc; i++ {
|
||||
args << string(argv[i])
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
fn parse_windows_cmd_line(cmd byteptr) []string {
|
||||
s := string(cmd)
|
||||
return s.split(' ')
|
||||
@ -100,8 +81,7 @@ pub fn read_file(path string) ?string {
|
||||
$if windows {
|
||||
fp = C._wfopen(path.to_wide(), mode.to_wide())
|
||||
} $else {
|
||||
cpath := path.str
|
||||
fp = C.fopen(cpath, mode.str)
|
||||
fp = C.fopen(path.str, mode.str)
|
||||
}
|
||||
if isnil(fp) {
|
||||
return error('failed to open file "$path"')
|
||||
|
@ -7,6 +7,14 @@ const (
|
||||
PathSeparator = '/'
|
||||
)
|
||||
|
||||
fn init_os_args(argc int, argv &byteptr) []string {
|
||||
mut args := []string
|
||||
for i := 0; i < argc; i++ {
|
||||
args << string(argv[i])
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
|
||||
// get_error_msg return error code representation in string.
|
||||
pub fn get_error_msg(code int) string {
|
||||
|
@ -4,8 +4,8 @@ module os
|
||||
#include <winsock2.h>
|
||||
|
||||
const (
|
||||
PathSeparator = '\\'
|
||||
)
|
||||
PathSeparator = '\\'
|
||||
)
|
||||
|
||||
// Ref - https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types
|
||||
// A handle to an object.
|
||||
@ -30,7 +30,7 @@ mut:
|
||||
nFileSizeLow u32
|
||||
dwReserved0 u32
|
||||
dwReserved1 u32
|
||||
cFileName [260]u16 // MAX_PATH = 260
|
||||
cFileName [260]u16 // MAX_PATH = 260
|
||||
cAlternateFileName [14]u16 // 14
|
||||
dwFileType u32
|
||||
dwCreatorType u32
|
||||
@ -38,6 +38,18 @@ mut:
|
||||
}
|
||||
|
||||
|
||||
fn init_os_args(argc int, argv &byteptr) []string {
|
||||
mut args := []string
|
||||
mut args_list := &voidptr(0)
|
||||
mut args_count := 0
|
||||
args_list = C.CommandLineToArgvW(C.GetCommandLine(), &args_count)
|
||||
for i := 0; i < args_count; i++ {
|
||||
args << string_from_wide(&u16(args_list[i]))
|
||||
}
|
||||
C.LocalFree(args_list)
|
||||
return args
|
||||
}
|
||||
|
||||
|
||||
pub fn ls(path string) []string {
|
||||
mut find_file_data := win32finddata{}
|
||||
@ -54,7 +66,7 @@ pub fn ls(path string) []string {
|
||||
}
|
||||
// NOTE: Should eventually have path struct & os dependant path seperator (eg os.PATH_SEPERATOR)
|
||||
// we need to add files to path eg. c:\windows\*.dll or :\windows\*
|
||||
path_files := '$path\\*'
|
||||
path_files := '$path\\*'
|
||||
// NOTE:TODO: once we have a way to convert utf16 wide character to utf8
|
||||
// we should use FindFirstFileW and FindNextFileW
|
||||
h_find_files := C.FindFirstFile(path_files.to_wide(), &find_file_data)
|
||||
@ -70,7 +82,7 @@ pub fn ls(path string) []string {
|
||||
}
|
||||
C.FindClose(h_find_files)
|
||||
return dir_files
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dir_exists(path string) bool {
|
||||
_path := path.replace('/', '\\')
|
||||
@ -82,7 +94,7 @@ pub fn dir_exists(path string) bool {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// mkdir creates a new directory with the specified path.
|
||||
pub fn mkdir(path string) {
|
||||
@ -93,7 +105,7 @@ pub fn mkdir(path string) {
|
||||
mkdir(_path.all_before_last('\\'))
|
||||
}
|
||||
C.CreateDirectory(_path.to_wide(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Ref - https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2019
|
||||
// get_file_handle retrieves the operating-system file handle that is associated with the specified file descriptor.
|
||||
@ -108,10 +120,10 @@ pub fn get_file_handle(path string) HANDLE {
|
||||
}
|
||||
|
||||
// Ref - https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamea
|
||||
// get_module_filename retrieves the fully qualified path for the file that contains the specified module.
|
||||
// get_module_filename retrieves the fully qualified path for the file that contains the specified module.
|
||||
// The module must have been loaded by the current process.
|
||||
pub fn get_module_filename(handle HANDLE) ?string {
|
||||
mut sz := int(4096) // Optimized length
|
||||
mut sz := int(4096) // Optimized length
|
||||
mut buf := &u16(malloc(4096))
|
||||
for {
|
||||
status := C.GetModuleFileName(handle, &buf, sz)
|
||||
@ -142,15 +154,15 @@ const (
|
||||
SUBLANG_NEUTRAL = 0x00
|
||||
SUBLANG_DEFAULT = 0x01
|
||||
LANG_NEUTRAL = (SUBLANG_NEUTRAL)
|
||||
)
|
||||
)
|
||||
|
||||
// Ref - https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--12000-15999-
|
||||
const (
|
||||
MAX_ERROR_CODE = 15841 // ERROR_API_UNAVAILABLE
|
||||
)
|
||||
|
||||
// ptr_win_get_error_msg return string (voidptr)
|
||||
// representation of error, only for windows.
|
||||
// ptr_win_get_error_msg return string (voidptr)
|
||||
// representation of error, only for windows.
|
||||
fn ptr_win_get_error_msg(code u32) voidptr {
|
||||
mut buf := voidptr(0)
|
||||
// Check for code overflow
|
||||
|
@ -2,10 +2,10 @@
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module strings
|
||||
module strings
|
||||
|
||||
struct Builder {
|
||||
mut:
|
||||
mut:
|
||||
buf []byte
|
||||
pub:
|
||||
len int
|
||||
@ -13,19 +13,19 @@ pub:
|
||||
|
||||
pub fn new_builder(initial_size int) Builder {
|
||||
return Builder {
|
||||
buf: _make(0, initial_size, sizeof(byte))
|
||||
//buf: _make(0, initial_size, sizeof(byte))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (b mut Builder) write(s string) {
|
||||
b.buf._push_many(s.str, s.len)
|
||||
//b.buf << []byte(s) // TODO
|
||||
//b.buf << []byte(s) // TODO
|
||||
b.len += s.len
|
||||
}
|
||||
|
||||
pub fn (b mut Builder) writeln(s string) {
|
||||
b.buf._push_many(s.str, s.len)
|
||||
//b.buf << []byte(s) // TODO
|
||||
//b.buf << []byte(s) // TODO
|
||||
b.buf << `\n`
|
||||
b.len += s.len + 1
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
module strings
|
||||
|
||||
#-js
|
||||
|
||||
// use levenshtein distance algorithm to calculate
|
||||
// the distance between between two strings (lower is closer)
|
||||
pub fn levenshtein_distance(a, b string) int {
|
||||
mut f := [int(0); b.len+1]
|
||||
mut f := [0].repeat2(b.len+1)
|
||||
for ca in a {
|
||||
mut j := 1
|
||||
mut fj1 := f[0]
|
||||
|
@ -1,11 +1,11 @@
|
||||
module strings
|
||||
module strings
|
||||
|
||||
pub fn repeat(c byte, n int) string {
|
||||
if n <= 0 {
|
||||
return ''
|
||||
}
|
||||
mut arr := malloc(n + 1)
|
||||
//mut arr := [byte(0); n + 1]
|
||||
//mut arr := malloc(n + 1)
|
||||
mut arr := [byte(0)].repeat2(n + 1)
|
||||
for i := 0; i < n; i++ {
|
||||
arr[i] = c
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user