Browse Source

Merge remote-tracking branch 'upstream/master'

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

+ 42
- 22
compiler/cc.v View File

@@ -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
- 0
compiler/cflags.v View File

@@ -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
}
}
}

+ 69
- 53
compiler/cgen.v View File

@@ -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
}


+ 5
- 8
compiler/cheaders.v View File

@@ -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;


+ 16
- 40
compiler/comptime.v View File

@@ -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
- 0
compiler/depgraph.v View 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.

// 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)
}
}
}

+ 13
- 14
compiler/fn.v View File

@@ -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


+ 2
- 2
compiler/jsgen.v View File

@@ -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 {


+ 76
- 39
compiler/main.v View File

@@ -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) {


+ 16
- 135
compiler/modules.v View File

@@ -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
}

+ 74
- 105
compiler/msvc.v View File

@@ -5,6 +5,7 @@ import os
#flag windows -l shell32

struct MsvcResult {
full_cl_exe_path string
exe_path string

um_lib_path string
@@ -187,6 +188,7 @@ fn find_msvc() ?MsvcResult {
}

return MsvcResult {
full_cl_exe_path: os.realpath( vs.exe_path + os.PathSeparator + 'cl.exe' )
exe_path: vs.exe_path,

um_lib_path: wk.um_lib_path,
@@ -205,22 +207,17 @@ fn find_msvc() ?MsvcResult {
}
}

struct ParsedFlag {
f string
arg string
}

pub fn (v mut V) cc_msvc() {
r := find_msvc() or {
// TODO: code reuse
if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' {
os.rm('.$v.out_name_c')
os.rm(v.out_name_c)
}
cerror('Cannot find MSVC on this OS.')
return
}

out_name_obj := v.out_name_c + '.obj'
out_name_obj := os.realpath( v.out_name_c + '.obj' )

// Default arguments

@@ -228,7 +225,7 @@ pub fn (v mut V) cc_msvc() {
// -w: no warnings
// 2 unicode defines
// /Fo sets the object file name - needed so we can clean up after ourselves properly
mut a := ['-w', '/we4013', '/volatile:ms', '/D_UNICODE', '/DUNICODE', '/Fo$out_name_obj']
mut a := ['-w', '/we4013', '/volatile:ms', '/D_UNICODE', '/DUNICODE', '/Fo"$out_name_obj"']

if v.pref.is_prod {
a << '/O2'
@@ -249,15 +246,18 @@ pub fn (v mut V) cc_msvc() {
v.out_name = v.out_name + '.exe'
}

mut libs := ''// builtin.o os.o http.o etc
v.out_name = os.realpath( v.out_name )

mut alibs := []string // builtin.o os.o http.o etc
if v.pref.build_mode == .build {
}
else if v.pref.build_mode == .embed_vlib {
//
}
else if v.pref.build_mode == .default_mode {
libs = '"$ModPath/vlib/builtin.obj"'
if !os.file_exists(libs) {
b := os.realpath( '$ModPath/vlib/builtin.obj' )
alibs << '"$b"'
if !os.file_exists(b) {
println('`builtin.obj` not found')
exit(1)
}
@@ -265,7 +265,7 @@ pub fn (v mut V) cc_msvc() {
if imp == 'webview' {
continue
}
libs += ' "$ModPath/vlib/${imp}.obj"'
alibs << '"' + os.realpath( '$ModPath/vlib/${imp}.obj' ) + '"'
}
}

@@ -275,7 +275,7 @@ pub fn (v mut V) cc_msvc() {

// The C file we are compiling
//a << '"$TmpPath/$v.out_name_c"'
a << '"$v.out_name_c"'
a << '"' + os.realpath( v.out_name_c ) + '"'

// Emily:
// Not all of these are needed (but the compiler should discard them if they are not used)
@@ -296,85 +296,54 @@ pub fn (v mut V) cc_msvc() {
'vcruntime.lib',
]

mut inc_paths := []string{}
mut lib_paths := []string{}
mut other_flags := []string{}

// Emily:
// this is a hack to try and support -l -L and object files
// passed on the command line
for f in v.table.flags {
// People like to put multiple flags per line (which really complicates things)
// ...so we need to handle that
mut rest := f

mut flags := []ParsedFlag{}
for {
mut base := rest

fl := if rest.starts_with('-') {
base = rest.right(2).trim_space()
rest.left(2)
} else {
''
}

// Which ever one of these is lowest we use
// TODO: we really shouldnt support all of these cmon
mut lowest := base.index('-')
for x in [base.index(' '), base.index(',')] {
if (x < lowest && x != -1) || lowest == -1 {
lowest = x
}
}
arg := if lowest != -1 {
rest = base.right(lowest).trim_space().trim(',')
base.left(lowest).trim_space().trim(',')
} else {
rest = ''
base.trim_space()
}

flags << ParsedFlag {
fl, arg
}
for flag in v.get_os_cflags() {
mut arg := flag.value
//println('fl: $flag.name | flag arg: $arg')

if rest.len == 0 {
break
// We need to see if the flag contains -l
// -l isnt recognised and these libs will be passed straight to the linker
// by the compiler
if flag.name == '-l' {
if arg.ends_with('.dll') {
cerror('MSVC cannot link against a dll (`#flag -l $arg`)')
}
// MSVC has no method of linking against a .dll
// TODO: we should look for .defs aswell
lib_lib := arg + '.lib'
real_libs << lib_lib
}

for flag in flags {
fl := flag.f
arg := flag.arg
// We need to see if the flag contains -l
// -l isnt recognised and these libs will be passed straight to the linker
// by the compiler
if fl == '-l' {
if arg.ends_with('.dll') {
cerror('MSVC cannot link against a dll (`#flag -l $arg`)')
}

// MSVC has no method of linking against a .dll
// TODO: we should look for .defs aswell
lib_lib := arg + '.lib'
real_libs << lib_lib
}
else if fl == '-L' {
lib_paths << f.right(2).trim_space()
}
else if arg.ends_with('.o') {
// msvc expects .obj not .o
other_flags << arg + 'bj'
}
else {
other_flags << arg
}
else if flag.name == '-I' {
inc_paths << ' ' + flag.format() + ' '
}
else if flag.name == '-L' {
lpath := flag.value
lib_paths << '"' + lpath + '"'
lib_paths << '"' + lpath + os.PathSeparator + 'msvc' + '"'
// The above allows putting msvc specific .lib files in a subfolder msvc/ ,
// where gcc will NOT find them, but cl will do...
// NB: gcc is smart enough to not need .lib files at all in most cases, the .dll is enough.
// When both a msvc .lib file and .dll file are present in the same folder,
// as for example for glfw3, compilation with gcc would fail.
}
else if flag.value.ends_with('.o') {
other_flags << flag.format().replace('.o', '.obj')
}
else {
other_flags << arg
}
}

// Include the base paths
a << '-I "$r.ucrt_include_path" -I "$r.vs_include_path" -I "$r.um_include_path" -I "$r.shared_include_path"'
a << ' -I "$r.ucrt_include_path" '
a << ' -I "$r.vs_include_path" '
a << ' -I "$r.um_include_path" '
a << ' -I "$r.shared_include_path" '

a << inc_paths

a << other_flags

@@ -383,14 +352,14 @@ pub fn (v mut V) cc_msvc() {

a << '/link'
a << '/NOLOGO'
a << '/OUT:$v.out_name'
a << '/OUT:"$v.out_name"'
a << '/LIBPATH:"$r.ucrt_lib_path"'
a << '/LIBPATH:"$r.um_lib_path"'
a << '/LIBPATH:"$r.vs_lib_path"'
a << '/INCREMENTAL:NO' // Disable incremental linking

for l in lib_paths {
a << '/LIBPATH:"$l"'
a << '/LIBPATH:"' + os.realpath(l) + '"'
}

if !v.pref.is_prod {
@@ -401,16 +370,13 @@ pub fn (v mut V) cc_msvc() {

args := a.join(' ')

// println('$args')
// println('$exe_path')

escaped_path := r.exe_path

cmd := '""$escaped_path\\cl.exe" $args"'

cmd := '""$r.full_cl_exe_path" $args"'
// It is hard to see it at first, but the quotes above ARE balanced :-| ...
// Also the double quotes at the start ARE needed.
if v.pref.show_c_cmd || v.pref.is_verbose {
println('\n==========')
println('\n========== cl cmd line:')
println(cmd)
println('==========\n')
}

// println('$cmd')
@@ -427,45 +393,48 @@ pub fn (v mut V) cc_msvc() {
// println('C OUTPUT:')

if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' {
os.rm('.$v.out_name_c')
os.rm(v.out_name_c)
}

// Always remove the object file - it is completely unnecessary
os.rm('$out_name_obj')
os.rm(out_name_obj)
}

fn build_thirdparty_obj_file_with_msvc(flag string) {
fn build_thirdparty_obj_file_with_msvc(path string) {
msvc := find_msvc() or {
println('Could not find visual studio')
return
}

mut obj_path := flag.all_after(' ')
// msvc expects .obj not .o
mut obj_path := '${path}bj'

if obj_path.ends_with('.o') {
// msvc expects .obj not .o
obj_path = obj_path + 'bj'
}
obj_path = os.realpath(obj_path)

if os.file_exists(obj_path) {
println('$obj_path already build.')
return
}

println('$obj_path not found, building it (with msvc)...')
parent := obj_path.all_before_last('/').trim_space()
parent := os.dir(obj_path)
files := os.ls(parent)

mut cfiles := ''
for file in files {
if file.ends_with('.c') {
cfiles += parent + '/' + file + ' '
}
cfiles += '"' + os.realpath( parent + os.PathSeparator + file ) + '" '
}
}

include_string := '-I "$msvc.ucrt_include_path" -I "$msvc.vs_include_path" -I "$msvc.um_include_path" -I "$msvc.shared_include_path"'

println('$cfiles')
//println('cfiles: $cfiles')

res := os.exec('""$msvc.exe_path\\cl.exe" /volatile:ms /Z7 $include_string /c $cfiles /Fo"$obj_path" /D_UNICODE /DUNICODE"') or {
cmd := '""$msvc.full_cl_exe_path" /volatile:ms /Z7 $include_string /c $cfiles /Fo"$obj_path" /D_UNICODE /DUNICODE"'
//NB: the quotes above ARE balanced.
println('thirdparty cmd line: $cmd')
res := os.exec(cmd) or {
cerror(err)
return
}


+ 109
- 60
compiler/parser.v View File

@@ -18,17 +18,17 @@ struct Parser {
// C ifdef guard clause that must be put before
// the #include directives in the parsed .v file
mut:
v *V
scanner *Scanner
v &V
scanner &Scanner
// tokens []Token // TODO cache all tokens, right now they have to be scanned twice
token_idx int
tok Token
prev_tok Token
prev_tok2 Token // TODO remove these once the tokens are cached
lit string
cgen *CGen
table *Table
import_table *FileImportTable // Holds imports for just the file being parsed
cgen &CGen
table &Table
import_table &FileImportTable // Holds imports for just the file being parsed
pass Pass
os OS
mod string
@@ -40,7 +40,7 @@ mut:
expected_type string
tmp_cnt int
is_script bool
pref *Preferences // Setting and Preferences shared from V struct
pref &Preferences // Setting and Preferences shared from V struct
builtin_mod bool
vh_lines []string
inside_if_expr bool
@@ -50,7 +50,7 @@ mut:
for_expr_cnt int // to detect whether `continue` can be used
ptr_cast bool
calling_c bool
cur_fn *Fn
cur_fn &Fn
returns bool
vroot string
is_c_struct_init bool
@@ -429,14 +429,19 @@ fn (p mut Parser) type_decl() {
if p.tok == .key_struct {
p.error('use `struct $name {` instead of `type $name struct {`')
}
parent := p.get_type()
nt_pair := p.table.cgen_name_type_pair(name, parent)
parent := p.get_type2()
nt_pair := p.table.cgen_name_type_pair(name, parent.name)
// TODO dirty C typedef hacks for DOOM
// Unknown type probably means it's a struct, and it's used before the struct is defined,
// so specify "struct"
_struct := if !parent.contains('[') && !parent.starts_with('fn ') && !p.table.known_type(parent){'struct'} else { ''}
p.gen_typedef('typedef $_struct $nt_pair; // type alias name="$name" parent="$parent"')
p.register_type_with_parent(name, parent)
_struct := if parent.cat != .array && parent.cat != .func &&
!p.table.known_type(parent.name) {
'struct'
} else {
''
}
p.gen_typedef('typedef $_struct $nt_pair; //type alias name="$name" parent=`$parent.name`')
p.register_type_with_parent(name, parent.name)
}

fn (p mut Parser) interface_method(field_name, receiver string) &Fn {
@@ -517,15 +522,17 @@ fn (p mut Parser) struct_decl() {
mut typ := p.table.find_type(name)
mut is_ph := false
if typ.is_placeholder {
// Update the placeholder
is_ph = true
typ.name = name
typ.mod = p.mod
typ.is_c = is_c
typ.is_placeholder = false
typ.cat = cat
p.table.rewrite_type(typ)
}
else {
typ = &Type {
typ = Type {
name: name
mod: p.mod
is_c: is_c
@@ -552,6 +559,12 @@ fn (p mut Parser) struct_decl() {
}
println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
*/

if !is_ph && p.first_pass() {
p.table.register_type2(typ)
//println('registering 1 nrfields=$typ.fields.len')
}
mut did_gen_something := false
for p.tok != .rcbr {
if p.tok == .key_pub {
@@ -599,7 +612,10 @@ fn (p mut Parser) struct_decl() {
// We are in an interface?
// `run() string` => run is a method, not a struct field
if is_interface {
typ.add_method(p.interface_method(field_name, name))
f := p.interface_method(field_name, name)
if p.first_pass() {
p.table.add_method(typ.name, f)
}
continue
}
// `pub` access mod
@@ -624,18 +640,21 @@ fn (p mut Parser) struct_decl() {
if attr == 'raw' && field_type != 'string' {
p.error('struct field with attribute "raw" should be of type "string" but got "$field_type"')
}
did_gen_something = true

typ.add_field(field_name, field_type, is_mut, attr, access_mod)
did_gen_something = true
if p.first_pass() {
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
}
p.fgenln('')
}

if !is_ph && p.first_pass() {
p.table.register_type2(typ)
//println('registering 1 nrfields=$typ.fields.len')
}

p.check(.rcbr)
if !is_c {
if !did_gen_something {
if p.first_pass() {
p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private)
}
}
}
p.fgenln('\n')
}

@@ -666,7 +685,7 @@ fn (p mut Parser) enum_decl(_enum_name string) {
p.table.register_const(name, enum_name, p.mod)
val++
}
p.table.register_type2(&Type {
p.table.register_type2(Type {
name: enum_name
mod: p.mod
parent: 'int'
@@ -739,11 +758,14 @@ if p.scanner.line_comment != '' {
}
}

fn (p mut Parser) warn(s string) {
println('warning: $p.scanner.file_path:${p.scanner.line_nr+1}: $s')
}

fn (p mut Parser) error(s string) {
// Dump all vars and types for debugging
if p.pref.is_debug {
// os.write_to_file('/var/tmp/lang.types', '')//pes(p.table.types))
// os.write_to_file('/var/tmp/lang.vars', q.J(p.table.vars))
os.write_file('fns.txt', p.table.debug_fns())
}
if p.pref.is_verbose || p.pref.is_debug {
@@ -755,13 +777,16 @@ fn (p mut Parser) error(s string) {
if !p.pref.is_repl && !p.pref.is_test && ( p.file_path.contains('v/compiler') || cur_path.contains('v/compiler') ){
println('\n=========================')
println('It looks like you are building V. It is being frequently updated every day.')
println('If you didn\'t modify the compiler\'s code, most likely there was a change that ')
println('If you didn\'t modify V\'s code, most likely there was a change that ')
println('lead to this error.')
println('\nRun `git pull && make`, that will most likely fix it.')
println('\nRun `v up`, that will most likely fix it.')
//println('\nIf this doesn\'t help, re-install V from source or download a precompiled' + ' binary from\nhttps://vlang.io.')
println('\nIf this doesn\'t help, please create a GitHub issue.')
println('=========================\n')
}
if p.pref.is_debug {
print_backtrace()
}
// p.scanner.debug_tokens()
// Print `[]int` instead of `array_int` in errors
p.scanner.error(s.replace('array_', '[]').replace('__', '.').replace('Option_', '?'))
@@ -773,24 +798,17 @@ fn (p &Parser) first_pass() bool {

// TODO return Type instead of string?
fn (p mut Parser) get_type() string {
debug := p.fileis('fn_test') && false
mut mul := false
mut nr_muls := 0
mut typ := ''
// fn type
if p.tok == .func {
if debug {
println('\nget_type() .key_goT FN TYP line=$p.scanner.line_nr')
}
mut f := Fn{name: '_', mod: p.mod}
p.next()
line_nr := p.scanner.line_nr
p.fn_args(mut f)
// Same line, it's a return type
if p.scanner.line_nr == line_nr {
if debug {
println('same line getting type')
}
if p.tok == .name {
f.typ = p.get_type()
}
@@ -858,6 +876,9 @@ fn (p mut Parser) get_type() string {
}
//
for p.tok == .mul {
if p.first_pass() {
p.warn('use `&Foo` instead of `*Foo`')
}
mul = true
nr_muls++
p.check(.mul)
@@ -894,6 +915,9 @@ fn (p mut Parser) get_type() string {
typ += '__$p.lit'
}
mut t := p.table.find_type(typ)
if typ == 'V' {
//println('QQ V res=$t.name')
}
// "typ" not found? try "mod__typ"
if t.name == '' && !p.builtin_mod {
// && !p.first_pass() {
@@ -1603,6 +1627,12 @@ fn (p mut Parser) name_expr() string {
p.error('undefined: `$name`')
}
else {
if orig_name == 'i32' {
println('`i32` alias was removed, use `int` instead')
}
if orig_name == 'u8' {
println('`u8` alias was removed, use `byte` instead')
}
p.error('undefined: `$orig_name`')
}
}
@@ -1616,9 +1646,16 @@ fn (p mut Parser) name_expr() string {
// TODO verify this and handle errors
peek := p.peek()
if peek != .lpar && peek != .lt {
// Register anon fn type
fn_typ := Type {
name: f.typ_str()// 'fn (int, int) string'
mod: p.mod
func: f
}
p.table.register_type2(fn_typ)
p.gen(p.table.cgen_name(f))
p.next()
return 'void*'
return f.typ_str() //'void*'
}
// TODO bring back
if f.typ == 'void' && !p.inside_if_expr {
@@ -1649,9 +1686,10 @@ fn (p mut Parser) var_expr(v Var) string {
p.next()
mut typ := v.typ
// Function pointer?
if typ.starts_with('fn ') {
//println('CALLING FN PTR')
//p.print_tok()

//println('CALLING FN PTR')
//p.print_tok()
if typ.starts_with('fn ') && p.tok == .lpar {
T := p.table.find_type(typ)
p.gen('(')
p.fn_call_args(mut T.func)
@@ -1749,7 +1787,7 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
mut has_method := p.table.type_has_method(typ, field_name)
// generate `.str()`
if !has_method && field_name == 'str' && typ.name.starts_with('array_') {
p.gen_array_str(mut typ)
p.gen_array_str(typ)
has_method = true
}
if !typ.is_c && !p.is_c_fn_call && !has_field && !has_method && !p.first_pass() {
@@ -1791,7 +1829,6 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
f := p.first_immutable_field
p.error('cannot modify immutable field `$f.name` (type `$f.parent_fn`)\n' +
'declare the field with `mut:`