Browse Source

Merge remote-tracking branch 'upstream/master'

master
Luna 2 years ago
parent
commit
06dc9f5e7f
  1. 64
      compiler/cc.v
  2. 115
      compiler/cflags.v
  3. 122
      compiler/cgen.v
  4. 13
      compiler/cheaders.v
  5. 56
      compiler/comptime.v
  6. 130
      compiler/depgraph.v
  7. 27
      compiler/fn.v
  8. 4
      compiler/jsgen.v
  9. 115
      compiler/main.v
  10. 151
      compiler/modules.v
  11. 179
      compiler/msvc.v
  12. 169
      compiler/parser.v
  13. 173
      compiler/parser2.v
  14. 2
      compiler/scanner.v
  15. 159
      compiler/table.v
  16. 6
      compiler/tests/fn_test.v
  17. 6
      compiler/tests/interface_test.v
  18. 2
      compiler/tests/msvc_test.v
  19. 4
      compiler/tests/repl/repl_test.v
  20. 24
      compiler/tests/str_gen_test.v
  21. 4
      examples/hot_reload/bounce.v
  22. 2
      examples/hot_reload/graph.v
  23. 4
      examples/tetris/tetris.v
  24. 18
      examples/vweb/test_app.v
  25. 4
      make.bat
  26. BIN
      thirdparty/glfw/msvc/glfw3.lib
  27. 16
      vlib/builtin/array.v
  28. 2
      vlib/builtin/builtin.v
  29. 20
      vlib/builtin/int.v
  30. 24
      vlib/builtin/int_test.v
  31. 10
      vlib/builtin/map.v
  32. 129
      vlib/builtin/map_test.v
  33. 37
      vlib/builtin/string.v
  34. 2
      vlib/builtin/string_test.v
  35. 16
      vlib/crypto/aes/block_generic.v
  36. 4
      vlib/crypto/md5/md5.v
  37. 21
      vlib/crypto/rand/rand_test.v
  38. 18
      vlib/crypto/rc4/rc4.v
  39. 2
      vlib/crypto/sha1/sha1.v
  40. 7
      vlib/crypto/sha256/sha256.v
  41. 15
      vlib/crypto/sha512/sha512.v
  42. 2
      vlib/encoding/csv/reader.v
  43. 2
      vlib/encoding/csv/writer.v
  44. 4
      vlib/freetype/freetype.v
  45. 4
      vlib/gg/gg.v
  46. 5
      vlib/glfw/glfw.v
  47. 10
      vlib/glm/glm.v
  48. 16
      vlib/http/http.v
  49. 1
      vlib/http/http_test.v
  50. 73
      vlib/json/json_primitives.v
  51. 8
      vlib/math/bits/bits.v
  52. 2
      vlib/net/socket.v
  53. 6
      vlib/net/urllib/urllib.v
  54. 26
      vlib/os/os.v
  55. 367
      vlib/readline/readline_lin.v
  56. 2
      vlib/term/colors.v
  57. 2
      vlib/term/colors_nix.v
  58. 2
      vlib/time/time.v
  59. 193
      vlib/vweb/vweb.v

64
compiler/cc.v

@ -10,6 +10,9 @@ 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') {
os.mv(v.out_name_c, v.out_name)
@ -29,15 +32,18 @@ fn (v mut V) cc() {
}
}
linux_host := os.user_os() == 'linux'
//linux_host := os.user_os() == 'linux'
v.log('cc() isprod=$v.pref.is_prod outname=$v.out_name')
mut a := [v.pref.cflags, '-std=gnu11', '-w'] // arguments for the C compiler
flags := v.table.flags.join(' ')
//mut shared := ''
if v.pref.is_so {
a << '-shared -fPIC '// -Wl,-z,defs'
v.out_name = v.out_name + '.so'
}
if v.pref.build_mode == .build {
v.out_name = ModPath + v.dir + '.o' //v.out_name
println('Building ${v.out_name}...')
}
if v.pref.is_prod {
a << '-O2'
}
@ -65,9 +71,9 @@ fn (v mut V) cc() {
//
}
else if v.pref.build_mode == .default_mode {
libs = '"$ModPath/vlib/builtin.o"'
libs = '$ModPath/vlib/builtin.o'
if !os.file_exists(libs) {
println('`builtin.o` not found')
println('object file `$libs` not found')
exit(1)
}
for imp in v.table.imports {
@ -80,7 +86,7 @@ fn (v mut V) cc() {
// -I flags
/*
mut args := ''
for flag in v.table.flags {
for flag in v.get_os_cflags() {
if !flag.starts_with('-l') {
args += flag
args += ' '
@ -90,7 +96,8 @@ mut args := ''
if v.pref.sanitize {
a << '-fsanitize=leak'
}
// Cross compiling linux
// Cross compiling linux TODO
/*
sysroot := '/Users/alex/tmp/lld/linuxroot/'
if v.os == .linux && !linux_host {
// Build file.o
@ -100,10 +107,10 @@ mut args := ''
v.out_name = v.out_name + '.o'
}
}
*/
// Cross compiling windows
// sysroot := '/Users/alex/tmp/lld/linuxroot/'
//
// Output executable name
// else {
a << '-o $v.out_name'
if os.dir_exists(v.out_name) {
cerror('\'$v.out_name\' is a directory')
@ -120,7 +127,9 @@ mut args := ''
if v.os == .mac {
a << '-mmacosx-version-min=10.7'
}
a << flags
for flag in v.get_os_cflags() {
a << flag.format()
}
a << libs
// macOS code can include objective C TODO remove once objective C is replaced with C
// Without these libs compilation will fail on Linux
@ -178,6 +187,7 @@ mut args := ''
println('=========\n')
}
// Link it if we are cross compiling and need an executable
/*
if v.os == .linux && !linux_host && v.pref.build_mode != .build {
v.out_name = v.out_name.replace('.o', '')
obj_file := v.out_name + '.o'
@ -197,6 +207,7 @@ mut args := ''
println(ress.output)
println('linux cross compilation done. resulting binary: "$v.out_name"')
}
*/
if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' {
os.rm(v.out_name_c)
}
@ -209,9 +220,9 @@ fn (c mut V) cc_windows_cross() {
}
mut args := '-o $c.out_name -w -L. '
// -I flags
for flag in c.table.flags {
if !flag.starts_with('-l') {
args += flag
for flag in c.get_os_cflags() {
if flag.name != '-l' {
args += flag.format()
args += ' '
}
}
@ -219,7 +230,7 @@ fn (c mut V) cc_windows_cross() {
if c.pref.build_mode == .default_mode {
libs = '"$ModPath/vlib/builtin.o"'
if !os.file_exists(libs) {
println('`builtin.o` not found')
println('`$libs` not found')
exit(1)
}
for imp in c.table.imports {
@ -228,9 +239,9 @@ fn (c mut V) cc_windows_cross() {
}
args += ' $c.out_name_c '
// -l flags (libs)
for flag in c.table.flags {
if flag.starts_with('-l') {
args += flag
for flag in c.get_os_cflags() {
if flag.name == '-l' {
args += flag.format()
args += ' '
}
}
@ -274,6 +285,19 @@ fn (c mut V) cc_windows_cross() {
println('Done!')
}
fn (c V) build_thirdparty_obj_files() {
for flag in c.get_os_cflags() {
if flag.value.ends_with('.o') {
if c.os == .msvc {
build_thirdparty_obj_file_with_msvc(flag.value)
}
else {
build_thirdparty_obj_file(flag.value)
}
}
}
}
fn find_c_compiler() string {
args := env_vflags_and_os_args().join(' ')
defaultcc := find_c_compiler_default()
@ -291,10 +315,6 @@ fn find_c_compiler_default() string {
}
fn find_c_compiler_thirdparty_options() string {
$if windows { return '' }
$if windows { return '' }
return '-fPIC'
}

115
compiler/cflags.v

@ -0,0 +1,115 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import os
// parsed cflag
struct CFlag{
os string // eg. windows | darwin | linux
name string // eg. -I
value string // eg. /path/to/include
}
// get flags for current os
fn (v V) get_os_cflags() []CFlag {
mut flags := []CFlag
for flag in v.table.cflags {
if flag.os == ''
|| (flag.os == 'linux' && v.os == .linux)
|| (flag.os == 'darwin' && v.os == .mac)
|| (flag.os == 'windows' && (v.os == .windows || v.os == .msvc)) {
flags << flag
}
}
return flags
}
// format flag
fn (cf &CFlag) format() string {
mut value := cf.value
// convert to absolute path
if cf.name == '-I' || cf.name == '-L' || value.ends_with('.o') {
value = '"'+os.realpath(value)+'"'
}
return '$cf.name $value'.trim_space()
}
// check if cflag is in table
fn (table &Table) has_cflag(cflag CFlag) bool {
for cf in table.cflags {
if cf.os == cflag.os && cf.name == cflag.name && cf.value == cflag.value {
return true
}
}
return false
}
// parse the flags to (table.cflags) []CFlag
// Note: clean up big time (joe-c)
fn (table mut Table) parse_cflag(cflag string) {
allowed_flags := [
'framework',
'library',
'I', 'l', 'L',
]
mut flag := cflag.trim_space()
if flag == '' {
return
}
mut fos := ''
mut name := ''
if flag.starts_with('linux') || flag.starts_with('darwin') || flag.starts_with('windows') {
pos := flag.index(' ')
fos = flag.left(pos).trim_space()
flag = flag.right(pos).trim_space()
}
for {
mut index := -1
mut value := ''
if flag[0] == `-` {
for f in allowed_flags {
i := 1+f.len
if i < flag.len && f == flag.substr(1,i) {
name = flag.left(i).trim_space()
flag = flag.right(i).trim_space()
break
}
}
}
for i in [flag.index(' '), flag.index(',')] {
if index == -1 || (i != -1 && i < index) {
index = i
}
} if index != -1 && flag[index] == ` ` && flag[index+1] == `-` {
for f in allowed_flags {
i := index+f.len
if i < flag.len && f == flag.substr(index, i) {
index = i
break
}
}
value = flag.left(index).trim_space()
flag = flag.right(index).trim_space()
} else if index != -1 && index < flag.len-2 && flag[index] == `,` {
value = flag.left(index).trim_space()
flag = flag.right(index+1).trim_space()
} else {
value = flag.trim_space()
index = -1
}
cf := CFlag{
os: fos,
name: name,
value: value
}
if !table.has_cflag(cf) {
table.cflags << cf
}
if index == -1 {
break
}
}
}

122
compiler/cgen.v

@ -39,7 +39,7 @@ mut:
cut_pos int
}
fn new_cgen(out_name_c string) *CGen {
fn new_cgen(out_name_c string) &CGen {
path := out_name_c
out := os.create(path) or {
println('failed to create $path')
@ -184,6 +184,7 @@ fn (c mut V) prof_counters() string {
//res << 'double ${c.table.cgen_name(f)}_time;'
//}
// Methods
/*
for typ in c.table.types {
// println('')
for f in typ.methods {
@ -192,6 +193,7 @@ fn (c mut V) prof_counters() string {
// println(f.cgen_name())
}
}
*/
return res.join(';\n')
}
@ -203,6 +205,7 @@ fn (p mut Parser) print_prof_counters() string {
//res << 'if ($counter) printf("%%f : $f.name \\n", $counter);'
//}
// Methods
/*
for typ in p.table.types {
// println('')
for f in typ.methods {
@ -214,6 +217,7 @@ fn (p mut Parser) print_prof_counters() string {
// println(f.cgen_name())
}
}
*/
return res.join(';\n')
}
@ -236,24 +240,25 @@ fn (g mut CGen) add_to_main(s string) {
}
fn build_thirdparty_obj_file(flag string) {
obj_path := flag.all_after(' ')
fn build_thirdparty_obj_file(path string) {
obj_path := os.realpath(path)
if os.file_exists(obj_path) {
return
}
println('$obj_path not found, building it...')
parent := obj_path.all_before_last('/').trim_space()
parent := os.dir(obj_path)
files := os.ls(parent)
//files := os.ls(parent).filter(_.ends_with('.c')) TODO
mut cfiles := ''
for file in files {
if file.ends_with('.c') {
cfiles += parent + '/' + file + ' '
cfiles += '"' + os.realpath( parent + os.PathSeparator + file ) + '" '
}
}
cc := find_c_compiler()
cc_thirdparty_options := find_c_compiler_thirdparty_options()
res := os.exec('$cc $cc_thirdparty_options -c -o $obj_path $cfiles') or {
cmd := '$cc $cc_thirdparty_options -c -o "$obj_path" $cfiles'
res := os.exec(cmd) or {
println('failed thirdparty object build cmd: $cmd')
cerror(err)
return
}
@ -276,15 +281,15 @@ fn os_name_to_ifdef(name string) string {
}
fn platform_postfix_to_ifdefguard(name string) string {
switch name {
case '.v': return '' // no guard needed
case '_win.v': return '#ifdef _WIN32'
case '_nix.v': return '#ifndef _WIN32'
case '_lin.v': return '#ifdef __linux__'
case '_mac.v': return '#ifdef __APPLE__'
}
cerror('bad platform_postfix "$name"')
return ''
switch name {
case '.v': return '' // no guard needed
case '_win.v': return '#ifdef _WIN32'
case '_nix.v': return '#ifndef _WIN32'
case '_lin.v': return '#ifdef __linux__'
case '_mac.v': return '#ifdef __APPLE__'
}
cerror('bad platform_postfix "$name"')
return ''
}
// C struct definitions, ordered
@ -292,29 +297,25 @@ fn platform_postfix_to_ifdefguard(name string) string {
// are added before them.
fn (v mut V) c_type_definitions() string {
mut types := []Type // structs that need to be sorted
mut top_types := []Type // builtin types and types that only have primitive fields
for t in v.table.types {
if !t.name[0].is_capital() {
top_types << t
continue
}
mut only_builtin_fields := true
for field in t.fields {
if field.typ[0].is_capital() {
only_builtin_fields = false
break
}
}
if only_builtin_fields {
top_types << t
mut builtin_types := []Type // builtin types
// builtin types need to be on top
builtins := ['string', 'array', 'map', 'Option']
for builtin in builtins {
typ := v.table.typesmap[builtin]
builtin_types << typ
}
// everything except builtin will get sorted
for t_name, t in v.table.typesmap {
if t_name in builtins {
continue
}
types << t
}
sort_structs(mut types)
// sort structs
types_sorted := sort_structs(types)
// Generate C code
return types_to_c(top_types, v.table) + '\n/*----*/\n' +
types_to_c(types, v.table)
return types_to_c(builtin_types,v.table) + '\n//----\n' +
types_to_c(types_sorted, v.table)
}
fn types_to_c(types []Type, table &Table) string {
@ -343,27 +344,42 @@ fn types_to_c(types []Type, table &Table) string {
return sb.str()
}
// pretty inefficient algo, works fine with N < 1000 (TODO optimize)
fn sort_structs(types mut []Type) {
mut cnt := 0
for i := 0; i < types.len; i++ {
for j in 0 .. i {
t := types[i]
//t2 := types[j]
// check if any of the types before `t` reference `t`
if types[j].contains_field_type(t.name) {
//println('moving up: $t.name len=$types.len')
types.insert(j, t)
types.delete(i+1)
i = 0 // Start from scratch
cnt++
if cnt > 500 {
println('infinite type loop (perhaps you have a recursive struct `$t.name`?)')
exit(1)
}
// sort structs by dependant fields
fn sort_structs(types []Type) []Type {
mut dep_graph := new_dep_graph()
// types name list
mut type_names := []string
for t in types {
type_names << t.name
}
// loop over types
for t in types {
// create list of deps
mut field_deps := []string
for field in t.fields {
// skip if not in types list or already in deps
if !(field.typ in type_names) || field.typ in field_deps {
continue
}
field_deps << field.typ
}
// add type and dependant types to graph
dep_graph.add(t.name, field_deps)
}
// sort graph
dep_graph_sorted := dep_graph.resolve()
if !dep_graph_sorted.acyclic {
cerror('error: cgen.sort_structs() DGNAC.\nplease create a new issue here: https://github.com/vlang/v/issues and tag @joe-conigliaro')
}
// sort types
mut types_sorted := []Type
for node in dep_graph_sorted.nodes {
for t in types {
if t.name == node.name {
types_sorted << t
continue
}
}
}
return types_sorted
}

13
compiler/cheaders.v

@ -43,7 +43,9 @@ CommonCHeaders = '
#include <windows.h>
// must be included after <windows.h>
#ifndef __TINYC__
#include <shellapi.h>
#endif
#include <io.h> // _waccess
#include <fcntl.h> // _O_U8TEXT
@ -54,8 +56,8 @@ CommonCHeaders = '
#define _Atomic volatile
// MSVC cannot parse some things properly
#undef EMPTY_STRUCT_DECLARATION
#define EMPTY_STRUCT_DECLARATION void *____dummy_variable;
//#undef EMPTY_STRUCT_DECLARATION
//#define EMPTY_STRUCT_DECLARATION void *____dummy_variable
#undef OPTION_CAST
#define OPTION_CAST(x)
#endif
@ -73,16 +75,13 @@ void pthread_mutex_unlock(HANDLE *m) {
//================================== TYPEDEFS ================================*/
typedef unsigned char byte;
typedef unsigned int uint;
typedef int64_t i64;
typedef int32_t i32;
typedef int16_t i16;
typedef int8_t i8;
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef uint8_t byte;
typedef uint32_t rune;
typedef float f32;
typedef double f64;
@ -94,8 +93,6 @@ typedef struct map map;
typedef array array_string;
typedef array array_int;
typedef array array_byte;
typedef array array_uint;
typedef array array_float;
typedef array array_f32;
typedef array array_f64;
typedef map map_int;

56
compiler/comptime.v

@ -141,44 +141,11 @@ fn (p mut Parser) chash() {
is_sig := p.is_sig()
if hash.starts_with('flag ') {
mut flag := hash.right(5)
// No the right os? Skip!
// mut ok := true
if hash.contains('linux') && p.os != .linux {
return
}
else if hash.contains('darwin') && p.os != .mac {
return
}
else if hash.contains('windows') && (p.os != .windows && p.os != .msvc) {
return
}
// Remove "linux" etc from flag
if flag.contains('linux') || flag.contains('darwin') || flag.contains('windows') {
pos := flag.index(' ')
flag = flag.right(pos)
}
has_vroot := flag.contains('@VROOT')
flag = flag.trim_space().replace('@VROOT', p.vroot)
if p.table.flags.contains(flag) {
return
}
// expand `@VMOD/pg/pg.o` to absolute path
has_vmod := flag.contains('@VMOD')
flag = flag.trim_space().replace('@VMOD', ModPath)
if p.table.flags.contains(flag) {
return
}
// expand `@VROOT` `@VMOD` to absolute path
flag = flag.replace('@VROOT', p.vroot)
flag = flag.replace('@VMOD', ModPath)
p.log('adding flag "$flag"')
// `@VROOT/thirdparty/glad/glad.o`, make sure it exists, otherwise build it
if (has_vroot || has_vmod) && flag.contains('.o') {
if p.os == .msvc {
build_thirdparty_obj_file_with_msvc(flag)
}
else {
build_thirdparty_obj_file(flag)
}
}
p.table.flags << flag
p.table.parse_cflag(flag)
return
}
if hash.starts_with('include') {
@ -242,8 +209,9 @@ fn (p mut Parser) comptime_method_call(typ Type) {
}
}
fn (p mut Parser) gen_array_str(typ mut Type) {
typ.add_method(Fn{
fn (p mut Parser) gen_array_str(typ Type) {
//println('gen array str "$typ.name"')
p.table.add_method(typ.name, Fn{
name: 'str',
typ: 'string'
args: [Var{typ: typ.name, is_arg:true}]
@ -251,9 +219,17 @@ fn (p mut Parser) gen_array_str(typ mut Type) {
is_public: true
receiver_typ: typ.name
})
/*
tt := p.table.find_type(typ.name)
for m in tt.methods {
println(m.name + ' ' + m.typ)
}
*/
t := typ.name
elm_type := t.right(6)
if p.typ_to_fmt(elm_type, 0) == '' && !p.table.type_has_method(p.table.find_type(elm_type), 'str') {
elm_type2 := p.table.find_type(elm_type)
if p.typ_to_fmt(elm_type, 0) == '' &&
!p.table.type_has_method(elm_type2, 'str') {
p.error('cant print ${elm_type}[], unhandled print of ${elm_type}')
}
p.cgen.fns << '

130
compiler/depgraph.v

@ -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.
// Directed acyclic graph
// this implementation is specifically suited to ordering dependencies
module main
struct DepGraphNode {
mut:
name string
deps []string
last_cycle string
}
struct DepGraph {
pub:
mut:
acyclic bool
nodes []DepGraphNode
}
struct DepSet {
mut:
items []string
}
pub fn(dset mut DepSet) add(item string) {
dset.items << item
}
pub fn(dset &DepSet) diff(otherset DepSet) DepSet {
mut diff := DepSet{}
for item in dset.items {
if !item in otherset.items {
diff.items << item
}
}
return diff
}
pub fn(dset &DepSet) size() int {
return dset.items.len
}
pub fn new_dep_graph() &DepGraph {
return &DepGraph{
acyclic: true
}
}
pub fn(graph mut DepGraph) add(mod string, deps []string) {
graph.nodes << DepGraphNode{
name: mod,
deps: deps
}
}
pub fn(graph &DepGraph) resolve() &DepGraph {
mut node_names := map[string]DepGraphNode
mut node_deps := map[string]DepSet
for _, node in graph.nodes {
node_names[node.name] = node
mut dep_set := DepSet{}
for _, dep in node.deps {
dep_set.add(dep)
}
node_deps[node.name] = dep_set
}
mut resolved := new_dep_graph()
for node_deps.size != 0 {
mut ready_set := DepSet{}
for name, deps in node_deps {
if deps.size() == 0 {
ready_set.add(name)
}
}
if ready_set.size() == 0 {
mut g := new_dep_graph()
g.acyclic = false
ndk := node_deps.keys()
for name, _ in node_deps {
mut node := node_names[name]
if name == ndk[node_deps.size-1] {
node.last_cycle = node_deps[name].items[node_deps[name].items.len-1]
}
g.nodes << node
}
return g
}
for name in ready_set.items {
node_deps.delete(name)
resolved.nodes << node_names[name]
}
for name, deps in node_deps {
node_deps[name] = deps.diff(ready_set)
}
}
return resolved
}
pub fn(graph &DepGraph) last_node() DepGraphNode {
return graph.nodes[graph.nodes.len-1]
}
pub fn(graph &DepGraph) last_cycle() string {
return graph.last_node().last_cycle
}
pub fn(graph &DepGraph) display() {
for i:=0; i<graph.nodes.len; i++ {
node := graph.nodes[i]
for dep in node.deps {
mut out := ' * $node.name -> $dep'
if !graph.acyclic && i == graph.nodes.len-1 && dep == node.last_cycle {
out += ' <-- last cycle'
}
println(out)
}
}
}

27
compiler/fn.v

@ -103,7 +103,7 @@ fn (p mut Parser) is_sig() bool {
(p.file_path.contains(ModPath))
}
fn new_fn(mod string, is_public bool) *Fn {
fn new_fn(mod string, is_public bool) &Fn {
return &Fn {
mod: mod
local_vars: [Var{} ; MaxLocalVars]
@ -185,7 +185,7 @@ fn (p mut Parser) fn_decl() {
// is_sig := p.builtin_mod && p.pref.build_mode == default_mode
// is_sig := p.pref.build_mode == default_mode && (p.builtin_mod || p.file.contains(LANG_TMP))
is_sig := p.is_sig()
// println('\n\nfn decl !!is_sig=$is_sig name=$f.name $p.builtin_mod')
// println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ')
if is_c {
p.check(.dot)
f.name = p.check_name()
@ -330,15 +330,15 @@ fn (p mut Parser) fn_decl() {
// No such type yet? It could be defined later. Create a new type.
// struct declaration later will modify it instead of creating a new one.
if p.first_pass() && receiver_t.name == '' {
// println('fn decl !!!!!!! REG PH $receiver_typ')
p.table.register_type2(Type {
//println('fn decl ! registering placeholder $receiver_typ')
receiver_t = Type {
name: receiver_typ.replace('*', '')
mod: p.mod
is_placeholder: true
})
}
p.table.register_type2(receiver_t)
}
// f.idx = p.table.fn_cnt
receiver_t.add_method(f)
p.table.add_method(receiver_t.name, f)
}
else {
// println('register_fn typ=$typ isg=$is_generic')
@ -358,7 +358,6 @@ fn (p mut Parser) fn_decl() {
if p.tok == .rcbr {
closed_scopes++
}
p.next()
// find `foo<Bar>()` in function bodies and register generic types
// TODO remove this once tokens are cached
if p.tok == .gt && p.prev_tok == .name && p.prev_tok2 == .lt &&
@ -376,7 +375,7 @@ fn (p mut Parser) fn_decl() {
p.name_expr()
p.scanner.pos = temp_scanner_pos
}
if p.tok.is_decl() && !(p.prev_tok == .dot && p.tok == .key_type) {
if p.tok.is_decl() {
break
}
// fn body ended, and a new fn attribute declaration like [live] is starting?
@ -385,6 +384,7 @@ fn (p mut Parser) fn_decl() {
break
}
}
p.next()
}
}
// Live code reloading? Load all fns from .so
@ -485,7 +485,6 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
// p.error('unclosed {')
}
// Make sure all vars in this function are used (only in main for now)
// if p.builtin_mod || p.mod == 'os' ||p.mod=='http'{
if p.mod != 'main' {
if !is_generic {
p.genln('}')
@ -687,7 +686,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
}
// `(int, string, int)`
// Just register fn arg types
types_only := p.tok == .mul || (p.peek() == .comma && p.table.known_type(p.lit)) || p.peek() == .rpar// (int, string)
types_only := p.tok == .mul || p.tok == .amp || (p.peek() == .comma && p.table.known_type(p.lit)) || p.peek() == .rpar// (int, string)
if types_only {
for p.tok != .rpar {
typ := p.get_type()
@ -756,7 +755,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
}
// foo *(1, 2, 3, mut bar)*
fn (p mut Parser) fn_call_args(f mut Fn) *Fn {
fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
// p.gen('(')
// println('fn_call_args() name=$f.name args.len=$f.args.len')
// C func. # of args is not known
@ -852,7 +851,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) *Fn {
if !T.has_method('str') {
// Arrays have automatic `str()` methods
if T.name.starts_with('array_') {
p.gen_array_str(mut T)
p.gen_array_str(T)
p.cgen.set_placeholder(ph, '${typ}_str(')
p.gen(')')
continue
@ -977,7 +976,7 @@ fn (f Fn) typ_str() string {
}
// f.args => "int a, string b"
fn (f &Fn) str_args(table *Table) string {
fn (f &Fn) str_args(table &Table) string {
mut s := ''
for i, arg in f.args {
// Interfaces are a special case. We need to pass the object + pointers

4
compiler/jsgen.v

@ -124,8 +124,8 @@ string res = tos2("");
fn is_js_prim(typ string) bool {
return typ == 'int' || typ == 'string' ||
typ == 'bool' || typ == 'f32' || typ == 'f64' ||
typ == 'i8' || typ == 'i16' || typ == 'i32' || typ == 'i64' ||
typ == 'u8' || typ == 'u16' || typ == 'u32' || typ == 'u64'
typ == 'i8' || typ == 'i16' || typ == 'i64' ||
typ == 'u16' || typ == 'u32' || typ == 'u64'
}
fn (p mut Parser) decode_array(array_type string) string {

115
compiler/main.v

@ -62,9 +62,9 @@ mut:
out_name_c string // name of the temporary C file
files []string // all Vore files that need to be parsed and compiled
dir string // directory (or file) being compiled (TODO rename to path?)
table *Table // table with types, vars, functions etc
cgen *CGen // C code generator
pref *Preferences // all the prefrences and settings extracted to a struct for reusability
table &Table // table with types, vars, functions etc
cgen &CGen // C code generator
pref &Preferences // all the prefrences and settings extracted to a struct for reusability
lang_dir string // "~/code/v"
out_name string // "program.exe"
vroot string
@ -162,11 +162,6 @@ fn main() {
os.mkdir(ModPath)
}
}
// No args? REPL
if args.len < 2 || (args.len == 2 && args[1] == '-') {
run_repl()
return
}
// Construct the V object from command line arguments
mut v := new_v(args)
if v.pref.is_verbose {
@ -185,6 +180,12 @@ fn main() {
v.run_compiled_executable_and_exit()
}
// No args? REPL
if args.len < 2 || (args.len == 2 && args[1] == '-') || 'runrepl' in args {
run_repl()
return
}
v.compile()
if v.pref.is_test {
@ -205,6 +206,10 @@ fn (v mut V) compile() {
for i, file in v.files {
// v.parsers << v.new_parser(file)
}
if v.pref.is_verbose {
println('all .v files before:')
println(v.files)
}
v.add_v_files_to_compile()
if v.pref.is_verbose {
println('all .v files:')
@ -289,7 +294,9 @@ fn (v mut V) compile() {
cgen.save()
if v.pref.is_verbose {
v.log('flags=')
println(v.table.flags)
for flag in v.get_os_cflags() {
println(' * ' + flag.format())
}
}
v.cc()
}
@ -401,7 +408,7 @@ fn (v V) run_compiled_executable_and_exit() {
if v.pref.is_verbose {
println('============ running $v.out_name ============')
}
mut cmd := final_target_out_name(v.out_name).replace('.exe','')
mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe','') + '"'
if os.args.len > 3 {
cmd += ' ' + os.args.right(3).join(' ')
}
@ -480,7 +487,7 @@ fn (v mut V) add_v_files_to_compile() {
dir = dir.all_before('/')
}
else {
// Add files from the dir user is compiling (only .v files)
// Add .v files from the directory being compied
files := v.v_files_from_dir(dir)
for file in files {
user_files << file
@ -546,7 +553,7 @@ fn (v mut V) add_v_files_to_compile() {
println(v.table.imports)
}
// graph deps
mut dep_graph := new_mod_dep_graph()
mut dep_graph := new_dep_graph()
dep_graph.from_import_tables(v.table.file_imports)
deps_resolved := dep_graph.resolve()
if !deps_resolved.acyclic {
@ -570,17 +577,30 @@ fn (v mut V) add_v_files_to_compile() {
*/
vfiles := v.v_files_from_dir(mod_path)
for file in vfiles {
if !file in v.files {
if !(file in v.files) {
v.files << file
}
}
}
// add remaining files (not modules)
for fit in v.table.file_imports {
//println('fit $fit.file_path')
if !fit.file_path in v.files {
v.files << fit.file_path
// Add remaining user files
mut j := 0
mut len := -1
for i, fit in v.table.file_imports {
// Don't add a duplicate; builtin files are always there
if fit.file_path in v.files || fit.module_name == 'builtin' {
continue
}
if len == -1 {
len = i
}
j++
// TODO remove this once imports work with .build
if v.pref.build_mode == .build && j >= len / 2{
break
}
//println(fit)
//println('fit $fit.file_path')
v.files << fit.file_path
}
}
@ -620,7 +640,7 @@ fn (v &V) log(s string) {
println(s)
}
fn new_v(args[]string) *V {
fn new_v(args[]string) &V {
joined_args := args.join(' ')
target_os := get_arg(joined_args, 'os', '')
mut out_name := get_arg(joined_args, 'o', 'a.out')
@ -629,6 +649,9 @@ fn new_v(args[]string) *V {
if args.contains('run') {
dir = get_all_after(joined_args, 'run', '')
}
if dir.ends_with('/') {
dir = dir.all_before_last('/')
}
if args.len < 2 {
dir = ''
}
@ -640,9 +663,13 @@ fn new_v(args[]string) *V {
if joined_args.contains('build module ') {
build_mode = .build
// v -lib ~/v/os => os.o
mod = os.dir(dir)
mod = mod.all_after('/')
println('Building module "${mod}" dir="$dir"...')
//mod = os.dir(dir)
mod = if dir.contains('/') {
dir.all_after('/')
} else {
dir
}
println('Building module "${mod}" (dir="$dir")...')
//out_name = '$TmpPath/vlib/${base}.o'
out_name = mod + '.o'
// Cross compiling? Use separate dirs for each os
@ -725,17 +752,17 @@ fn new_v(args[]string) *V {
vroot := os.dir(os.executable())
//println('VROOT=$vroot')
// v.exe's parent directory should contain vlib
if os.dir_exists(vroot) && os.dir_exists(vroot + '/vlib/builtin') {
if os.dir_exists(vroot) || os.dir_exists(vroot + '/vlib/builtin') {
} else {
println('vlib not found. It should be next to the Vore executable. ')
println('Go to https://vorelang.io/ to install Vore.')
exit(1)
}
//println('out_name:$out_name')
mut out_name_c := os.realpath( out_name ) + '.tmp.c'
mut files := []string
// Add builtin files
if !out_name.contains('builtin.o') {
//if !out_name.contains('builtin.o') {
for builtin in builtins {
mut f := '$vroot/vlib/builtin/$builtin'
// In default mode we use precompiled vlib.o, point to .vh files with signatures
@ -744,7 +771,7 @@ fn new_v(args[]string) *V {
}
files << f
}
}
//}
mut cflags := ''
for ci, cv in args {
@ -781,7 +808,7 @@ fn new_v(args[]string) *V {
if pref.is_so {
out_name_c = out_name.all_after('/') + '_shared_lib.c'
}
return &V {
return &V{
os: _os
out_name: out_name
files: files
@ -805,7 +832,6 @@ Options:
- Read from stdin (Default; Interactive mode if in a tty)
-h, help Display this information.
-v, version Display compiler version.
-lib Generate object file.
-prod Build an optimized executable.
-o <file> Place output into <file>.
-obf Obfuscate the resulting binary.
@ -815,6 +841,7 @@ Options:
fmt Run vfmt to format the source code.
up Update Vore.
run Build and execute a Vore program. You can add arguments after the file name.
build module Compile a module into an object file.
Files:
@ -878,7 +905,7 @@ fn test_v() {
mut joined_args := env_vflags_and_os_args().right(1).join(' ')
joined_args = joined_args.left(joined_args.last_index('test'))
println('$joined_args')
mut failed := false
test_files := os.walk_ext('.', '_test.v')
for dot_relative_file in test_files {
relative_file := dot_relative_file.replace('./', '')
@ -886,12 +913,13 @@ fn test_v() {
tmpcfilepath := file.replace('_test.v', '_test.tmp.c')
print(relative_file + ' ')
r := os.exec('$vexe $joined_args -debug $file') or {
cerror('failed on $file')
return
failed = true
println('FAIL')
continue
}
if r.exit_code != 0 {
println('failed `$file` (\n$r.output\n)')
exit(1)
println('FAIL `$file` (\n$r.output\n)')
failed = true
} else {
println('OK')
}
@ -904,24 +932,33 @@ fn test_v() {
tmpcfilepath := file.replace('.v', '.tmp.c')
print(relative_file + ' ')
r := os.exec('$vexe $joined_args -debug $file') or {
cerror('failed on $file')
return
failed = true
println('FAIL')
continue
}
if r.exit_code != 0 {
println('failed `$file` (\n$r.output\n)')
exit(1)
println('FAIL `$file` (\n$r.output\n)')
failed = true
} else {
println('OK')
}
os.rm(tmpcfilepath)
}
if failed {
exit(1)
}
}
fn create_symlink() {
vexe := os.executable()
link_path := '/usr/local/bin/v'
os.system('ln -sf $vexe $link_path')
println('symlink "$link_path" has been created')
ret := os.system('ln -sf $vexe $link_path')
if ret == 0 {
println('symlink "$link_path" has been created')
} else {
println('failed to create symlink "$link_path", '+
'make sure you run with sudo')
}
}
pub fn cerror(s string) {

151
compiler/modules.v

@ -4,52 +4,10 @@
module main
import os
import os
struct ModDepGraphNode {
mut:
name string
deps []string
last_cycle string
}
struct ModDepGraph {
pub:
mut:
acyclic bool
nodes []ModDepGraphNode
}
struct DepSet {
mut:
items []string
}
pub fn(dset mut DepSet) add(item string) {
dset.items << item
}
pub fn(dset &DepSet) diff(otherset DepSet) DepSet {
mut diff := DepSet{}
for item in dset.items {
if !item in otherset.items {
diff.items << item
}
}
return diff
}
pub fn(dset &DepSet) size() int {
return dset.items.len
}
pub fn new_mod_dep_graph() *ModDepGraph {
return &ModDepGraph{
acyclic: true
}
}
pub fn(graph mut ModDepGraph) from_import_tables(import_tables []FileImportTable) {
// add a module and its deps (module speficic dag method)
pub fn(graph mut DepGraph) from_import_tables(import_tables []FileImportTable) {
for fit in import_tables {
mut deps := []string
for _, m in fit.imports {
@ -59,64 +17,8 @@ pub fn(graph mut ModDepGraph) from_import_tables(import_tables []FileImportTable
}
}
pub fn(graph mut ModDepGraph) add(mod string, deps []string) {
graph.nodes << ModDepGraphNode{
name: mod,
deps: deps
}
}
pub fn(graph &ModDepGraph) resolve() *ModDepGraph {
mut node_names := map[string]ModDepGraphNode
mut node_deps := map[string]DepSet
for _, node in graph.nodes {
node_names[node.name] = node
mut dep_set := DepSet{}
for _, dep in node.deps {
dep_set.add(dep)
}
node_deps[node.name] = dep_set
}
mut resolved := new_mod_dep_graph()
for node_deps.size != 0 {
mut ready_set := DepSet{}
for name, deps in node_deps {
if deps.size() == 0 {
ready_set.add(name)
}
}
if ready_set.size() == 0 {
mut g := new_mod_dep_graph()
g.acyclic = false
ndk := node_deps.keys()
for name, _ in node_deps {
mut node := node_names[name]
if name == ndk[node_deps.size-1] {
node.last_cycle = node_deps[name].items[node_deps[name].items.len-1]
}
g.nodes << node
}
return g
}
for name in ready_set.items {
node_deps.delete(name)
resolved.nodes << node_names[name]
}
for name, deps in node_deps {
node_deps[name] = deps.diff(ready_set)
}
}
return resolved
}
pub fn(graph &ModDepGraph) imports() []string {
// get ordered imports (module speficic dag method)
pub fn(graph &DepGraph) imports() []string {
mut mods := []string
for node in graph.nodes {
if node.name == 'main' {
@ -127,45 +29,24 @@ pub fn(graph &ModDepGraph) imports() []string {
return mods
}
pub fn(graph &ModDepGraph) last_node() ModDepGraphNode {
return graph.nodes[graph.nodes.len-1]
}
pub fn(graph &ModDepGraph) last_cycle() string {
return graph.last_node().last_cycle
}
pub fn(graph &ModDepGraph) display() {
for i:=0; i<graph.nodes.len; i++ {
node := graph.nodes[i]
for dep in node.deps {
mut out := ' * $node.name -> $dep'
if !graph.acyclic && i == graph.nodes.len-1 && dep == node.last_cycle {
out += ' <-- last cycle'
}
println(out)
}
}
}
// 'strings' => 'VROOT/vlib/strings'
// 'installed_mod' => '~/.vmodules/installed_mod'
// 'local_mod' => '/path/to/current/dir/local_mod'
// 'strings' => 'VROOT/vlib/strings'
// 'installed_mod' => '~/.vmodules/installed_mod'
// 'local_mod' => '/path/to/current/dir/local_mod'
fn (v &V) find_module_path(mod string) string {
mod_path := v.module_path(mod)
// First check for local modules in the same directory
// First check for local modules in the same directory
mut import_path := os.getwd() + '/$mod_path'
// Now search in vlib/
// Now search in vlib/
if !os.dir_exists(import_path) {
import_path = '$v.lang_dir/vlib/$mod_path'
}
//println('ip=$import_path')
// Finally try modules installed with vpm (~/.vmodules)
}
//println('ip=$import_path')
// Finally try modules installed with vpm (~/.vmodules)
if !os.dir_exists(import_path) {
import_path = '$ModPath/$mod_path'
if !os.dir_exists(import_path){
cerror('module "$mod" not found')
}
}
}
return import_path
}
return import_path
}

179