cgen: treat the main module like any other v module

pull/5582/head^2
Delyan Angelov 2020-07-01 01:53:53 +03:00 committed by GitHub
parent 81e4d3fd09
commit 78e1127d99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 720 additions and 535 deletions

View File

@ -8,7 +8,7 @@ uname -a
make -j4
./v -version
./v version
du -s .

View File

@ -10,7 +10,12 @@ du -s .
ls -lat
./v test-compiler
##./v test-compiler
## try running the known failing tests first to get faster feedback
./v test vlib/builtin/string_test.v vlib/strings/builder_test.v
./v test-fixed
./v build-vbinaries

View File

@ -55,15 +55,15 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
# - name: Build V
# uses: spytheman/docker_alpine_v@v7.0
# with:
# entrypoint: .github/workflows/alpine.build.sh
# - name: Test V
# uses: spytheman/docker_alpine_v@v7.0
# with:
# entrypoint: .github/workflows/alpine.test.sh
- name: Build V
uses: spytheman/docker_alpine_v@v7.0
with:
entrypoint: .github/workflows/alpine.build.sh
- name: Test V
uses: spytheman/docker_alpine_v@v7.0
with:
entrypoint: .github/workflows/alpine.test.sh
macos:
runs-on: ${{ matrix.os }}
@ -176,8 +176,6 @@ jobs:
../../vprod -backend x64 -o 1m 1m.v
echo "Running it..."
ls
- name: V self compilation with -autofree
run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
# - name: SDL examples
# run: git clone --depth 1 https://github.com/vlang/sdl && cd sdl
@ -188,6 +186,18 @@ jobs:
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
ubuntu-autofree-selfcompile:
runs-on: ubuntu-18.04
env:
VFLAGS: -cc gcc
steps:
- uses: actions/checkout@v2
- name: Build V
run: make -j4
- name: V self compilation with -autofree
run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
ubuntu-musl:
runs-on: ubuntu-18.04
env:

View File

@ -3,3 +3,7 @@ module main
// This prelude is loaded in every v program compiled with -live,
// in both the main executable, and in the shared library.
import live
const (
no_warning_live_is_used = live.is_used
)

View File

@ -3,3 +3,7 @@ module main
// This prelude is loaded in every v program compiled with -live,
// but only for the main executable.
import live.executable
const (
no_warning_live_executable_is_used = executable.is_used
)

View File

@ -3,3 +3,7 @@ module main
// This prelude is loaded in every v program compiled with -live,
// but only for the shared library.
import live.shared
const (
no_warning_live_shared_is_used = shared.is_used
)

View File

@ -1,6 +1,7 @@
module main
import os
import term
// //////////////////////////////////////////////////////////////////
// / This file will get compiled as part of the main program,
// / for a _test.v file.
@ -11,7 +12,7 @@ import os
// //////////////////////////////////////////////////////////////////
// TODO copy pasta builtin.v fn ___print_assert_failure
fn cb_assertion_failed(i &VAssertMetaInfo) {
// color_on := term.can_show_color_on_stderr()
use_color := term.can_show_color_on_stderr()
use_relative_paths := match os.getenv('VERROR_PATHS') {
'absolute' {
false
@ -20,18 +21,22 @@ fn cb_assertion_failed(i &VAssertMetaInfo) {
}
}
final_filename := if use_relative_paths { i.fpath } else { os.real_path(i.fpath) }
final_funcname := i.fn_name.replace('main__', '').replace('__', '.')
final_funcname := i.fn_name.replace('main.', '').replace('__', '.')
final_src := if use_color { term.bold(i.src) } else { i.src }
eprintln('')
eprintln('$final_filename:${i.line_nr+1}: failed assert in ${final_funcname}')
eprintln('Source : ${i.src}')
eprintln('$final_filename:${i.line_nr+1}: failed assert in function ${final_funcname}')
eprintln('Source : `${final_src}`')
if i.op.len > 0 && i.op != 'call' {
eprintln(' left value: ${i.llabel} = ${i.lvalue}')
if i.rlabel == i.rvalue {
eprintln(' right value: $i.rlabel')
}
else {
eprintln(' right value: ${i.rlabel} = ${i.rvalue}')
mut slvalue := '${i.lvalue}'
mut srvalue := '${i.rvalue}'
lpostfix := if slvalue == i.llabel { '.' } else { '<= `${i.llabel}`' }
rpostfix := if srvalue == i.rlabel { '.' } else { '<= `${i.rlabel}`' }
if use_color {
slvalue = term.bold(term.yellow(slvalue))
srvalue = term.bold(term.yellow(srvalue))
}
eprintln(' left value: ${slvalue} ${lpostfix}')
eprintln(' right value: ${srvalue} ${rpostfix}')
}
}

View File

@ -36,7 +36,7 @@ fn start_testing(total_number_of_tests int, vfilename string) BenchedTests {
// Called before each test_ function, defined in file_test.v
fn (mut b BenchedTests) testing_step_start(stepfunc string) {
b.step_func_name = stepfunc.replace('main__', '').replace('__', '.')
b.step_func_name = stepfunc.replace('main.', '').replace('__', '.')
b.oks = C.g_test_oks
b.fails = C.g_test_fails
b.bench.step()

View File

@ -15,6 +15,7 @@ const (
'vlib/sqlite/sqlite_test.v',
'vlib/orm/orm_test.v',
'vlib/clipboard/clipboard_test.v',
'vlib/v/gen/js/jsgen_test.v',
]
skip_on_linux = []string{}
skip_on_non_linux = [

View File

@ -67,8 +67,8 @@ pub fn (input BitField) str() string {
pub fn new(size int) BitField {
output := BitField{
size: size
//field: *u32(calloc(bitnslots(size) * slot_size / 8))
field: []u32{len:bitnslots(size)}
//field: *u32(calloc(zbitnslots(size) * slot_size / 8))
field: []u32{len:zbitnslots(size)}
}
return output
}
@ -105,7 +105,7 @@ pub fn (mut instance BitField) clear_bit(bitnr int) {
// set_all sets all bits in the array to 1.
pub fn (mut instance BitField) set_all() {
for i in 0..bitnslots(instance.size) {
for i in 0..zbitnslots(instance.size) {
instance.field[i] = u32(-1)
}
instance.clear_tail()
@ -113,7 +113,7 @@ pub fn (mut instance BitField) set_all() {
// clear_all clears (sets to zero) all bits in the array.
pub fn (mut instance BitField) clear_all() {
for i in 0..bitnslots(instance.size) {
for i in 0..zbitnslots(instance.size) {
instance.field[i] = u32(0)
}
}
@ -132,7 +132,7 @@ pub fn (mut instance BitField) toggle_bit(bitnr int) {
// the tail of the longer one is ignored.
pub fn bf_and(input1 BitField, input2 BitField) BitField {
size := min(input1.size, input2.size)
bitnslots := bitnslots(size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
output.field[i] = input1.field[i] & input2.field[i]
@ -144,7 +144,7 @@ pub fn bf_and(input1 BitField, input2 BitField) BitField {
// bf_not toggles all bits in a bit array and returns the result as a new array.
pub fn bf_not(input BitField) BitField {
size := input.size
bitnslots := bitnslots(size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
output.field[i] = ~input.field[i]
@ -158,7 +158,7 @@ pub fn bf_not(input BitField) BitField {
// the tail of the longer one is ignored.
pub fn bf_or(input1 BitField, input2 BitField) BitField {
size := min(input1.size, input2.size)
bitnslots := bitnslots(size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
output.field[i] = input1.field[i] | input2.field[i]
@ -172,7 +172,7 @@ pub fn bf_or(input1 BitField, input2 BitField) BitField {
// the tail of the longer one is ignored.
pub fn bf_xor(input1 BitField, input2 BitField) BitField {
size := min(input1.size, input2.size)
bitnslots := bitnslots(size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
output.field[i] = input1.field[i] ^ input2.field[i]
@ -186,7 +186,7 @@ pub fn join(input1 BitField, input2 BitField) BitField {
output_size := input1.size + input2.size
mut output := new(output_size)
// copy the first input to output as is
for i in 0..bitnslots(input1.size) {
for i in 0..zbitnslots(input1.size) {
output.field[i] = input1.field[i]
}
@ -194,7 +194,7 @@ pub fn join(input1 BitField, input2 BitField) BitField {
offset_bit := input1.size % slot_size
offset_slot := input1.size / slot_size
for i in 0..bitnslots(input2.size) {
for i in 0..zbitnslots(input2.size) {
output.field[i + offset_slot] |=
u32(input2.field[i] << u32(offset_bit))
}
@ -213,12 +213,12 @@ pub fn join(input1 BitField, input2 BitField) BitField {
* If offset_bit is zero, no additional copies needed.
*/
if (output_size - 1) % slot_size < (input2.size - 1) % slot_size {
for i in 0..bitnslots(input2.size) {
for i in 0..zbitnslots(input2.size) {
output.field[i + offset_slot + 1] |=
u32(input2.field[i] >> u32(slot_size - offset_bit))
}
} else if (output_size - 1) % slot_size > (input2.size - 1) % slot_size {
for i in 0..bitnslots(input2.size) - 1 {
for i in 0..zbitnslots(input2.size) - 1 {
output.field[i + offset_slot + 1] |=
u32(input2.field[i] >> u32(slot_size - offset_bit))
}
@ -233,7 +233,7 @@ pub fn (instance BitField) get_size() int {
// clone creates a copy of a bit array.
pub fn (instance BitField) clone() BitField {
bitnslots := bitnslots(instance.size)
bitnslots := zbitnslots(instance.size)
mut output := new(instance.size)
for i in 0..bitnslots {
output.field[i] = instance.field[i]
@ -245,7 +245,7 @@ pub fn (instance BitField) clone() BitField {
// identical by length and contents and 'false' otherwise.
pub fn (instance BitField) cmp(input BitField) bool {
if instance.size != input.size {return false}
for i in 0..bitnslots(instance.size) {
for i in 0..zbitnslots(instance.size) {
if instance.field[i] != input.field[i] {return false}
}
return true
@ -254,7 +254,7 @@ pub fn (instance BitField) cmp(input BitField) bool {
// pop_count returns the number of set bits (ones) in the array.
pub fn (instance BitField) pop_count() int {
size := instance.size
bitnslots := bitnslots(size)
bitnslots := zbitnslots(size)
tail := size % slot_size
mut count := 0
for i in 0..bitnslots - 1 {
@ -318,7 +318,7 @@ pub fn (input BitField) slice(_start int, _end int) BitField {
end_offset := (end - 1) % slot_size
start_slot := start / slot_size
end_slot := (end - 1) / slot_size
output_slots := bitnslots(end - start)
output_slots := zbitnslots(end - start)
if output_slots > 1 {
if start_offset != 0 {
@ -371,7 +371,7 @@ pub fn (input BitField) slice(_start int, _end int) BitField {
// last, the second with the last but one and so on).
pub fn (instance BitField) reverse() BitField {
size := instance.size
bitnslots := bitnslots(size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i:= 0; i < (bitnslots - 1); i++ {
for j in 0..slot_size {
@ -391,9 +391,9 @@ pub fn (instance BitField) reverse() BitField {
// resize changes the size of the bit array to 'new_size'.
pub fn (mut instance BitField) resize(new_size int) {
new_bitnslots := bitnslots(new_size)
new_bitnslots := zbitnslots(new_size)
old_size := instance.size
old_bitnslots := bitnslots(old_size)
old_bitnslots := zbitnslots(old_size)
mut field := []u32{len:new_bitnslots}
for i := 0; i < old_bitnslots && i < new_bitnslots; i++ {
field[i] = instance.field[i]
@ -439,7 +439,7 @@ fn (mut instance BitField) clear_tail() {
// create a mask for the tail
mask := u32((1 << tail) - 1)
// clear the extra bits
instance.field[bitnslots(instance.size) - 1] = instance.field[bitnslots(instance.size) - 1] & mask
instance.field[zbitnslots(instance.size) - 1] = instance.field[zbitnslots(instance.size) - 1] & mask
}
}
@ -460,6 +460,6 @@ fn min(input1 int, input2 int) int {
}
}
fn bitnslots(length int) int {
fn zbitnslots(length int) int {
return (length - 1) / slot_size + 1
}

View File

@ -168,22 +168,22 @@ pub fn v_realloc(b byteptr, n int) byteptr {
if ptr == 0 {
panic('realloc($n) failed')
}
return ptr
}
[unsafe_fn]
pub fn v_calloc(n int) byteptr {
return C.calloc(n, 1)
return C.calloc(1, n)
}
[unsafe_fn]
pub fn vcalloc(n int) byteptr {
if n < 0 {
panic('calloc(<=0)')
} else if n == 0 {
return byteptr(0)
} else {
return C.calloc(n, 1)
}
return C.calloc(1, n)
}
[unsafe_fn]
@ -223,7 +223,7 @@ fn __as_cast(obj voidptr, obj_type, expected_type int) voidptr {
// VAssertMetaInfo is used during assertions. An instance of it
// is filled in by compile time generated code, when an assertion fails.
struct VAssertMetaInfo {
pub struct VAssertMetaInfo {
pub:
fpath string // the source file path of the assertion
line_nr int // the line number of the assertion

View File

@ -126,9 +126,9 @@ fn test_various_map_value() {
m12['test'] = f64(0.0)
assert m12['test'] == f64(0.0)
mut m13 := map[string]rune
m13['test'] = rune(0)
assert m13['test'] == rune(0)
//mut m13 := map[string]rune
//m13['test'] = rune(0)
//assert m13['test'] == rune(0)
mut m14 := map[string]voidptr
m14['test'] = voidptr(0)

View File

@ -1369,7 +1369,11 @@ pub fn (s string) fields() []string {
}
pub fn (s string) map(func fn(byte) byte) string {
return string(s.bytes().map(func(it)))
mut res := malloc(s.len + 1)
for i in 0..s.len {
res[i] = func(s[i])
}
return tos(res, s.len)
}
// Allows multi-line strings to be formatted in a way that removes white-space

View File

@ -762,10 +762,20 @@ fn test_string_map() {
$if windows {
return // TODO
}
a := 'Hello'.map(fn (b byte) byte {
original := 'Hello'
println('original.len = $original.len')
a := original.map(fn (b byte) byte {
return b + 1
})
assert a == 'Ifmmp'
expected := 'Ifmmp'
println('a[0] = ' + a[0].str())
println('a[1] = ' + a[1].str())
println('a[2] = ' + a[2].str())
println('a[3] = ' + a[3].str())
println('a[4] = ' + a[4].str())
println('a.len = $a.len')
assert a.len == expected.len
assert a == expected
assert 'foo'.map(foo) == r'\ee'
}

View File

@ -1,5 +1,9 @@
module live
pub const (
is_used = 1
)
pub type FNLinkLiveSymbols = fn (linkcb voidptr)
pub type FNLiveReloadCB = fn (info &LiveReloadInfo)

View File

@ -6,6 +6,10 @@ import dl
import strconv
import live
pub const (
is_used = 1
)
// The live reloader code is implemented here.
// NB: new_live_reload_info will be called by generated C code inside main()

View File

@ -1,3 +1,7 @@
module shared
import live
pub const (
is_used = 1
)

View File

@ -203,10 +203,10 @@ pub fn mean_absdev(arr []f64) f64 {
if arr.len == 0 {
return f64(0)
}
mean := mean(arr)
amean := mean(arr)
mut sum := f64(0)
for v in arr {
sum += math.abs(v-mean)
sum += math.abs(v-amean)
}
return sum/f64(arr.len)
}

View File

@ -1,5 +1,3 @@
module main
import net.ftp
// NB: this function makes network calls to external servers,

View File

@ -49,21 +49,26 @@ pub fn (mut b Builder) go_back(n int) {
b.len -= n
}
fn bytes2string(b []byte) string {
mut copy := b.clone()
copy << `\0`
res := tos(copy.data, copy.len-1)
return res
}
pub fn (mut b Builder) cut_last(n int) string {
buf := b.buf[b.len-n..]
s := string(buf.clone())
res := bytes2string( b.buf[b.len-n..] )
b.buf.trim(b.buf.len-n)
b.len -= n
return s
return res
}
/*
pub fn (mut b Builder) cut_to(pos int) string {
buf := b.buf[pos..]
s := string(buf.clone())
res := bytes2string( b.buf[pos..] )
b.buf.trim(pos)
b.len = pos
return s
return res
}
*/
@ -88,8 +93,7 @@ pub fn (b &Builder) last_n(n int) string {
if n > b.len {
return ''
}
buf := b.buf[b.len-n..]
return string(buf.clone())
return bytes2string( b.buf[b.len-n..] )
}
// buf == 'hello world'
@ -98,10 +102,7 @@ pub fn (b &Builder) after(n int) string {
if n >= b.len {
return ''
}
buf := b.buf[n..]
mut copy := buf.clone()
copy << `\0`
return string(copy)
return bytes2string( b.buf[n..] )
}
// NB: in order to avoid memleaks and additional memory copies, after a call to b.str(),

View File

@ -6,7 +6,8 @@ fn test_sb() {
sb.write('!')
sb.write('hello')
assert sb.len == 8
assert sb.str() == 'hi!hello'
sb_end := sb.str()
assert sb_end == 'hi!hello'
assert sb.len == 0
///
sb = strings.new_builder(10)
@ -19,39 +20,33 @@ fn test_sb() {
// TODO msvc bug
sb = strings.new_builder(10)
sb.write('123456')
assert sb.cut_last(2) == '56'
assert sb.str() == '1234'
last_2 := sb.cut_last(2)
assert last_2 == '56'
final_sb := sb.str()
assert final_sb == '1234'
}
///
/*
sb = strings.new_builder(10)
sb.write('123456')
x := sb.cut_to(2)
assert x == '456'
assert sb.str() == '123'
*/
}
const (
n = 100000
maxn = 100000
)
fn test_big_sb() {
mut sb := strings.new_builder(100)
mut sb2 := strings.new_builder(10000)
for i in 0..n {
for i in 0..maxn {
sb.writeln(i.str())
sb2.write('+')
}
s := sb.str()
lines := s.split_into_lines()
assert lines.len == n
assert lines.len == maxn
assert lines[0] == '0'
assert lines[1] == '1'
assert lines[777] == '777'
assert lines[98765] == '98765'
println(sb2.len)
assert sb2.len == n
assert sb2.len == maxn
}
@ -64,5 +59,6 @@ fn test_byte_write() {
count++
assert count == sb.len
}
assert sb.str() == temp_str
sb_final := sb.str()
assert sb_final == temp_str
}

View File

@ -48,13 +48,13 @@ pub fn (t Time) md() string {
// - a date string in "MMM D HH:MM" format (24h) for date of current year
// - a date string formatted with format function for other dates
pub fn (t Time) clean() string {
now := time.now()
znow := time.now()
// Today
if t.month == now.month && t.year == now.year && t.day == now.day {
if t.month == znow.month && t.year == znow.year && t.day == znow.day {
return t.get_fmt_time_str(.hhmm24)
}
// This year
if t.year == now.year {
if t.year == znow.year {
return t.get_fmt_str(.space, .hhmm24, .mmmd)
}
return t.format()
@ -65,13 +65,13 @@ pub fn (t Time) clean() string {
// - a date string in "MMM D HH:MM" format (12h) for date of current year
// - a date string formatted with format function for other dates
pub fn (t Time) clean12() string {
now := time.now()
znow := time.now()
// Today
if t.month == now.month && t.year == now.year && t.day == now.day {
if t.month == znow.month && t.year == znow.year && t.day == znow.day {
return t.get_fmt_time_str(.hhmm12)
}
// This year
if t.year == now.year {
if t.year == znow.year {
return t.get_fmt_str(.space, .hhmm12, .mmmd)
}
return t.format()

View File

@ -189,8 +189,8 @@ fn since(t Time) int {
// relative returns a string representation of difference between time
// and current time.
pub fn (t Time) relative() string {
now := time.now()
secs := now.unix - t.unix
znow := time.now()
secs := znow.unix - t.unix
if secs <= 30 {
// right now or in the future
// TODO handle time in the future
@ -227,8 +227,8 @@ pub fn (t Time) relative() string {
}
pub fn (t Time) relative_short() string {
now := time.now()
secs := now.unix - t.unix
znow := time.now()
secs := znow.unix - t.unix
if secs <= 30 {
// right now or in the future
// TODO handle time in the future

View File

@ -323,10 +323,10 @@ pub struct File {
pub:
path string
mod Module
stmts []Stmt
scope &Scope
global_scope &Scope
pub mut:
stmts []Stmt
imports []Import
errors []errors.Error
warnings []errors.Warning
@ -361,9 +361,9 @@ pub struct Ident {
pub:
language table.Language
tok_kind token.Kind
mod string
pos token.Position
pub mut:
mod string
name string
kind IdentKind
info IdentInfo
@ -753,8 +753,11 @@ pub mut:
pub struct SizeOf {
pub:
is_type bool
typ table.Type
type_name string
expr Expr
pos token.Position
}
pub struct Likely {
@ -933,7 +936,9 @@ pub fn (expr Expr) position() token.Position {
SelectorExpr {
return expr.pos
}
// ast.SizeOf { }
SizeOf {
return expr.pos
}
StringLiteral {
return expr.pos
}

View File

@ -204,7 +204,7 @@ pub fn (v Builder) get_user_files() []string {
if line[0] == `/` && line[1] == `/` {
continue
}
if line.starts_with('module ') && !line.starts_with('module main') {
if line.starts_with('module ') {
is_internal_module_test = true
break
}

View File

@ -105,15 +105,33 @@ pub fn (mut c Checker) check2(ast_file ast.File) []errors.Error {
pub fn (mut c Checker) check_files(ast_files []ast.File) {
mut has_main_mod_file := false
mut has_main_fn := false
for file in ast_files {
mut files_from_main_module := []&ast.File{}
for i in 0..ast_files.len {
file := &ast_files[i]
c.check(file)
if file.mod.name == 'main' {
files_from_main_module << file
has_main_mod_file = true
if c.check_file_in_main(file) {
has_main_fn = true
}
}
}
if has_main_mod_file && !has_main_fn && files_from_main_module.len > 0 {
if c.pref.is_script && !c.pref.is_test {
first_main_file := files_from_main_module[0]
first_main_file.stmts << ast.FnDecl{
name: 'main.main'
mod: 'main'
file: first_main_file.path
return_type: table.void_type
}
has_main_fn = true
}
}
// Make sure fn main is defined in non lib builds
if c.pref.build_mode == .build_module || c.pref.is_test {
return
@ -159,7 +177,7 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool {
}
}
ast.FnDecl {
if stmt.name == 'main' {
if stmt.name == 'main.main' {
has_main_fn = true
if stmt.is_pub {
c.error('function `main` cannot be declared public', stmt.pos)
@ -214,7 +232,7 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool {
}
fn (mut c Checker) check_valid_snake_case(name, identifier string, pos token.Position) {
if name[0] == `_` && !c.pref.is_vweb {
if !c.pref.is_vweb && ( name[0] == `_` || name.contains('._') ) {
c.error('$identifier `$name` cannot start with `_`', pos)
}
if util.contains_capital(name) {
@ -231,8 +249,8 @@ fn stripped_name(name string) string {
}
fn (mut c Checker) check_valid_pascal_case(name, identifier string, pos token.Position) {
stripped_name := stripped_name(name)
if !stripped_name[0].is_capital() {
sname := stripped_name(name)
if !sname[0].is_capital() {
c.error('$identifier `$name` must begin with capital letter', pos)
}
}
@ -945,7 +963,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
return table.string_type
}
if call_expr.generic_type.has_flag(.generic) {
if c.mod != '' && c.mod != 'main' {
if c.mod != '' {
// Need to prepend the module when adding a generic type to a function
// `fn_gen_types['mymod.myfn'] == ['string', 'int']`
c.table.register_fn_gen_type(c.mod + '.' + fn_name, c.cur_generic_type)
@ -975,12 +993,12 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
call_expr.return_type = ret_type
return ret_type
}
// look for function in format `mod.fn` or `fn` (main/builtin)
// look for function in format `mod.fn` or `fn` (builtin)
mut f := table.Fn{}
mut found := false
mut found_in_args := false
// try prefix with current module as it would have never gotten prefixed
if !fn_name.contains('.') && call_expr.mod !in ['builtin', 'main'] {
if !fn_name.contains('.') && call_expr.mod !in ['builtin'] {
name_prefixed := '${call_expr.mod}.$fn_name'
if f1 := c.table.find_fn(name_prefixed) {
call_expr.name = name_prefixed
@ -1014,7 +1032,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
c.error('unknown function: $fn_name', call_expr.pos)
return table.void_type
}
if !found_in_args && call_expr.mod in ['builtin', 'main'] {
if !found_in_args {
scope := c.file.scope.innermost(call_expr.pos.pos)
if _ := scope.find_var(fn_name) {
c.error('ambiguous call to: `$fn_name`, may refer to fn `$fn_name` or variable `$fn_name`',
@ -1219,7 +1237,7 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) t
pub fn (mut c Checker) check_or_expr(mut or_expr ast.OrExpr, ret_type table.Type) {
if or_expr.kind == .propagate {
if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main' {
if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' {
c.error('to propagate the optional call, `$c.cur_fn.name` must itself return an optional',
or_expr.pos)
}
@ -1669,7 +1687,7 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type {
// scope.find(it.name) or {
// c.error('undefined ident: `$it.name`', array_init.pos)
// }
mut full_const_name := if it.mod == 'main' { it.name } else { it.mod + '.' + it.name }
mut full_const_name := it.mod + '.' + it.name
if obj := c.file.global_scope.find_const(full_const_name) {
if cint := const_int_value(obj) {
fixed_size = cint
@ -2177,7 +2195,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
// TODO: move this
if c.const_deps.len > 0 {
mut name := ident.name
if !name.contains('.') && ident.mod !in ['builtin', 'main'] {
if !name.contains('.') && ident.mod !in ['builtin'] {
name = '${ident.mod}.$ident.name'
}
if name == c.const_decl {
@ -2255,7 +2273,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
}
// prepend mod to look for fn call or const
mut name := ident.name
if !name.contains('.') && ident.mod !in ['builtin', 'main'] {
if !name.contains('.') && ident.mod !in ['builtin'] {
name = '${ident.mod}.$ident.name'
}
if obj := c.file.global_scope.find(name) {
@ -2297,6 +2315,17 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
return field.typ
}
}
if ident.kind == .unresolved && ident.mod != 'builtin' {
// search in the `builtin` idents, for example
// main.compare_f32 may actually be builtin.compare_f32
saved_mod := ident.mod
ident.mod = 'builtin'
builtin_type := c.ident( ident )
if builtin_type != table.void_type {
return builtin_type
}
ident.mod = saved_mod
}
c.error('undefined ident: `$ident.name`', ident.pos)
}
if c.table.known_type(ident.name) {

View File

@ -7,6 +7,7 @@ import v.ast
import v.table
import v.token
import strings
import v.util
const (
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t',
@ -441,10 +442,10 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
f.writeln(' {')
match it.kind as k {
.insert {
f.writeln('\tinsert $it.object_var_name into $it.table_name')
f.writeln('\tinsert $it.object_var_name into ${util.strip_mod_name(it.table_name)}')
}
.update {
f.write('\tupdate $it.table_name set ')
f.write('\tupdate ${util.strip_mod_name(it.table_name)} set ')
for i, col in it.updated_columns {
f.write('$col = ')
f.expr(it.update_exprs[i])
@ -888,13 +889,19 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.write(node.field_name)
}
ast.SizeOf {
f.write('sizeof(')
if node.type_name != '' {
f.write(node.type_name)
if node.is_type {
f.write('sizeof(')
if node.type_name != '' {
f.write(node.type_name)
} else {
f.write(f.type_to_str(node.typ))
}
f.write(')')
} else {
f.write(f.type_to_str(node.typ))
f.write('sizeof(')
f.expr(node.expr)
f.write(')')
}
f.write(')')
}
ast.SqlExpr {
// sql app.db { select from Contributor where repo == id && user == 0 }
@ -918,7 +925,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.write(' ')
}
}
f.write('from $node.table_name')
f.write('from ${util.strip_mod_name(node.table_name)}')
f.write(' ')
if node.has_where {
f.write('where ')

View File

@ -83,7 +83,6 @@ mut:
attrs []string // attributes before next decl stmt
is_builtin_mod bool
hotcode_fn_names []string
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
cur_fn &ast.FnDecl = 0
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
sql_i int
@ -93,6 +92,7 @@ mut:
inside_return bool
strs_to_free string
inside_call bool
has_main bool
}
const (
@ -126,8 +126,6 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
table: table
pref: pref
fn_decl: 0
fn_main: 0
cur_fn: 0
autofree: true
indent: -1
module_built: pref.path.after('vlib/')
@ -285,10 +283,8 @@ pub fn (mut g Gen) finish() {
if g.pref.is_livemain || g.pref.is_liveshared {
g.generate_hotcode_reloader_code()
}
if g.fn_main != voidptr(0) {
g.out.writeln('')
g.fn_decl = g.fn_main
g.gen_fn_decl(g.fn_main)
if !g.pref.is_test {
g.gen_c_main()
}
}
@ -301,12 +297,12 @@ pub fn (mut g Gen) write_typeof_functions() {
tidx := g.table.find_type_idx(typ.name)
g.writeln('char * v_typeof_sumtype_${tidx}(int sidx) { /* $typ.name */ ')
g.writeln(' switch(sidx) {')
g.writeln(' case $tidx: return "$typ.name";')
g.writeln(' case $tidx: return "${util.strip_main_name(typ.name)}";')
for v in sum_info.variants {
subtype := g.table.get_type_symbol(v)
g.writeln(' case $v: return "$subtype.name";')
g.writeln(' case $v: return "${util.strip_main_name(subtype.name)}";')
}
g.writeln(' default: return "unknown $typ.name";')
g.writeln(' default: return "unknown ${util.strip_main_name(typ.name)}";')
g.writeln(' }')
g.writeln('}')
}
@ -383,7 +379,7 @@ fn (mut g Gen) register_optional(t table.Type) string {
g.typedefs2.writeln('typedef struct $styp $styp;')
g.options.write(g.optional_type_text(styp, base))
g.options.writeln(';\n')
g.optionals << styp
g.optionals << styp.clone()
}
return styp
}
@ -392,7 +388,7 @@ fn (mut g Gen) register_optional(t table.Type) string {
// i.e. it's always just Cat, not Cat_ptr:
fn (g &Gen) cc_type(t table.Type) string {
sym := g.table.get_type_symbol(g.unwrap_generic(t))
mut styp := sym.name.replace('.', '__')
mut styp := util.no_dots(sym.name)
if sym.kind == .struct_ {
info := sym.info as table.Struct
if info.generic_types.len > 0 {
@ -436,21 +432,20 @@ typedef struct {
match typ.kind {
.alias {
parent := &g.table.types[typ.parent_idx]
styp := typ.name.replace('.', '__')
styp := util.no_dots(typ.name)
is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == `.`
parent_styp := if is_c_parent { 'struct ' + parent.name[2..].replace('.', '__') } else { parent.name.replace('.',
'__') }
parent_styp := if is_c_parent { 'struct ' + util.no_dots(parent.name[2..]) } else { util.no_dots(parent.name) }
g.type_definitions.writeln('typedef $parent_styp $styp;')
}
.array {
styp := typ.name.replace('.', '__')
styp := util.no_dots(typ.name)
g.type_definitions.writeln('typedef array $styp;')
}
.interface_ {
g.type_definitions.writeln('typedef _Interface ${c_name(typ.name)};')
}
.map {
styp := typ.name.replace('.', '__')
styp := util.no_dots(typ.name)
g.type_definitions.writeln('typedef map $styp;')
}
.function {
@ -462,7 +457,7 @@ typedef struct {
not_anon := !info.is_anon
if !info.has_decl && !is_multi && (not_anon || is_fn_sig) {
fn_name := if func.language == .c {
func.name.replace('.', '__')
util.no_dots(func.name)
} else if info.is_anon {
typ.name
} else {
@ -492,7 +487,7 @@ pub fn (mut g Gen) write_multi_return_types() {
if typ.kind != .multi_return {
continue
}
name := typ.name.replace('.', '__')
name := util.no_dots(typ.name)
info := typ.info as table.MultiReturn
g.type_definitions.writeln('typedef struct {')
// TODO copy pasta StructDecl
@ -643,7 +638,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.defer_stmts << defer_stmt
}
ast.EnumDecl {
enum_name := node.name.replace('.', '__')
enum_name := util.no_dots(node.name)
g.enum_typedefs.writeln('typedef enum {')
mut cur_enum_expr := ''
mut cur_enum_offset := 0
@ -688,23 +683,21 @@ fn (mut g Gen) stmt(node ast.Stmt) {
println('build module `$g.module_built` fn `$node.name`')
}
}
keep_fn_decl := g.fn_decl
g.fn_decl = node // &it
if node.name == 'main' {
// just remember `it`; main code will be generated in finish()
g.fn_main = node
} else {
if node.name == 'backtrace' ||
node.name == 'backtrace_symbols' ||
node.name == 'backtrace_symbols_fd' {
g.write('\n#ifndef __cplusplus\n')
}
g.gen_fn_decl(node)
if node.name == 'backtrace' ||
node.name == 'backtrace_symbols' ||
node.name == 'backtrace_symbols_fd' {
g.write('\n#endif\n')
}
keep_fn_decl := g.fn_decl
g.fn_decl = node
if node.name == 'main.main' {
g.has_main = true
}
if node.name == 'backtrace' ||
node.name == 'backtrace_symbols' ||
node.name == 'backtrace_symbols_fd' {
g.write('\n#ifndef __cplusplus\n')
}
g.gen_fn_decl(node)
if node.name == 'backtrace' ||
node.name == 'backtrace_symbols' ||
node.name == 'backtrace_symbols_fd' {
g.write('\n#endif\n')
}
g.fn_decl = keep_fn_decl
if skip {
@ -786,7 +779,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.sql_stmt(node)
}
ast.StructDecl {
name := if node.language == .c { node.name.replace('.', '__') } else { c_name(node.name) }
name := if node.language == .c { util.no_dots(node.name) } else { c_name(node.name) }
// g.writeln('typedef struct {')
// for field in it.fields {
// field_type_sym := g.table.get_type_symbol(field.typ)
@ -967,11 +960,11 @@ fn (mut g Gen) gen_assert_stmt(a ast.AssertStmt) {
g.writeln('{')
g.writeln(' g_test_oks++;')
metaname_ok := g.gen_assert_metainfo(a)
g.writeln(' cb_assertion_ok(&$metaname_ok);')
g.writeln(' main__cb_assertion_ok(&$metaname_ok);')
g.writeln('} else {')
g.writeln(' g_test_fails++;')
metaname_fail := g.gen_assert_metainfo(a)
g.writeln(' cb_assertion_failed(&$metaname_fail);')
g.writeln(' main__cb_assertion_failed(&$metaname_fail);')
g.writeln(' longjmp(g_jump_buffer, 1);')
g.writeln(' // TODO')
g.writeln(' // Maybe print all vars in a test function if it fails?')
@ -1605,24 +1598,28 @@ fn (mut g Gen) expr(node ast.Expr) {
// Only used in IndexExpr
}
ast.SizeOf {
mut styp := node.type_name
if node.type_name == '' {
styp = g.typ(node.typ)
} else {
sym := g.table.get_type_symbol(node.typ)
if sym.kind == .struct_ {
info := sym.info as table.Struct
if !info.is_typedef {
styp = 'struct ' + styp
if node.is_type {
mut styp := node.type_name
if styp.starts_with('C.') {
styp = styp[2..]
}
if node.type_name == '' {
styp = g.typ(node.typ)
} else {
sym := g.table.get_type_symbol(node.typ)
if sym.kind == .struct_ {
info := sym.info as table.Struct
if !info.is_typedef {
styp = 'struct ' + styp
}
}
}
g.write('/*SizeOfType*/ sizeof(${util.no_dots(styp)})')
} else {
g.write('/*SizeOfVar*/ sizeof(')
g.expr(node.expr)
g.write(')')
}
/*
if styp.starts_with('C__') {
styp = styp[3..]
}
*/
g.write('sizeof($styp)')
}
ast.SqlExpr {
g.sql_select_expr(node)
@ -1685,7 +1682,7 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
} else if sym.kind == .array_fixed {
fixed_info := sym.info as table.ArrayFixed
typ_name := g.table.get_type_name(fixed_info.elem_type)
g.write('tos_lit("[$fixed_info.size]$typ_name")')
g.write('tos_lit("[$fixed_info.size]${util.strip_main_name(typ_name)}")')
} else if sym.kind == .function {
info := sym.info as table.FnType
fn_info := info.func
@ -1694,15 +1691,15 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
if i > 0 {
repr += ', '
}
repr += g.table.get_type_name(arg.typ)
repr += util.strip_main_name(g.table.get_type_name(arg.typ))
}
repr += ')'
if fn_info.return_type != table.void_type {
repr += ' ${g.table.get_type_name(fn_info.return_type)}'
repr += ' ${util.strip_main_name(g.table.get_type_name(fn_info.return_type))}'
}
g.write('tos_lit("$repr")')
} else {
g.write('tos_lit("$sym.name")')
g.write('tos_lit("${util.strip_main_name(sym.name)}")')
}
}
@ -2039,7 +2036,7 @@ fn (mut g Gen) ident(node ast.Ident) {
return
}
if node.name.starts_with('C.') {
g.write(node.name[2..].replace('.', '__'))
g.write(util.no_dots(node.name[2..]))
return
}
if node.kind == .constant { // && !node.name.starts_with('g_') {
@ -2308,10 +2305,6 @@ fn (g Gen) expr_is_multi_return_call(expr ast.Expr) bool {
}
fn (mut g Gen) return_statement(node ast.Return) {
if g.fn_decl.name == 'main' {
g.writeln('return 0;')
return
}
if node.exprs.len > 0 {
// skip `retun $vweb.html()`
if node.exprs[0] is ast.ComptimeCall {
@ -2772,7 +2765,7 @@ fn (mut g Gen) write_init_function() {
for mod_name in g.table.imports {
init_fn_name := '${mod_name}.init'
if _ := g.table.find_fn(init_fn_name) {
mod_c_name := mod_name.replace('.', '__')
mod_c_name := util.no_dots(mod_name)
init_fn_c_name := '${mod_c_name}__init'
g.writeln('\t${init_fn_c_name}();')
}
@ -2832,7 +2825,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
continue
}
// sym := g.table.get_type_symbol(typ)
mut name := typ.name.replace('.', '__')
mut name := util.no_dots(typ.name)
match typ.info as info {
table.Struct {
if info.generic_types.len > 0 {
@ -2896,7 +2889,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
}
table.ArrayFixed {
// .array_fixed {
styp := typ.name.replace('.', '__')
styp := util.no_dots(typ.name)
// array_fixed_char_300 => char x[300]
mut fixed := styp[12..]
len := styp.after('_')
@ -3073,12 +3066,12 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
}
ast.Ident {
if it.kind == .function {
g.write('${it.name}(it)')
g.write('${c_name(it.name)}(it)')
} else if it.kind == .variable {
var_info := it.var_info()
sym := g.table.get_type_symbol(var_info.typ)
if sym.kind == .function {
g.write('${it.name}(it)')
g.write('${c_name(it.name)}(it)')
} else {
g.expr(node.args[0].expr)
}
@ -3125,12 +3118,12 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) {
}
ast.Ident {
if it.kind == .function {
g.write('${it.name}(it)')
g.write('${c_name(it.name)}(it)')
} else if it.kind == .variable {
var_info := it.var_info()
sym_t := g.table.get_type_symbol(var_info.typ)
if sym_t.kind == .function {
g.write('${it.name}(it)')
g.write('${c_name(it.name)}(it)')
} else {
g.expr(node.args[0].expr)
}
@ -3283,7 +3276,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table.
g.stmts(stmts)
}
} else if or_block.kind == .propagate {
if g.file.mod.name == 'main' && g.cur_fn.name == 'main' {
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
// In main(), an `opt()?` call is sugar for `opt() or { panic(err) }`
if g.pref.is_debug {
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos)
@ -3471,7 +3464,7 @@ fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) string {
[inline]
fn c_name(name_ string) string {
name := name_.replace('.', '__')
name := util.no_dots(name_)
if name in c_reserved {
return 'v_$name'
}
@ -3482,7 +3475,7 @@ fn (g Gen) type_default(typ table.Type) string {
sym := g.table.get_type_symbol(typ)
if sym.kind == .array {
elem_sym := g.typ(sym.array_info().elem_type)
mut elem_type_str := elem_sym.replace('.', '__')
mut elem_type_str := util.no_dots(elem_sym)
if elem_type_str.starts_with('C__') {
elem_type_str = elem_type_str[3..]
}
@ -3562,21 +3555,21 @@ pub fn (mut g Gen) write_tests_main() {
g.writeln('')
all_tfuncs := g.get_all_test_function_names()
if g.pref.is_stats {
g.writeln('\tBenchedTests bt = start_testing($all_tfuncs.len, tos_lit("$g.pref.path"));')
g.writeln('\tmain__BenchedTests bt = main__start_testing($all_tfuncs.len, tos_lit("$g.pref.path"));')
}
for t in all_tfuncs {
g.writeln('')
if g.pref.is_stats {
g.writeln('\tBenchedTests_testing_step_start(&bt, tos_lit("$t"));')
g.writeln('\tmain__BenchedTests_testing_step_start(&bt, tos_lit("$t"));')
}
g.writeln('\tif (!setjmp(g_jump_buffer)) ${t}();')
if g.pref.is_stats {
g.writeln('\tBenchedTests_testing_step_end(&bt);')
g.writeln('\tmain__BenchedTests_testing_step_end(&bt);')
}
}
g.writeln('')
if g.pref.is_stats {
g.writeln('\tBenchedTests_end_testing(&bt);')
g.writeln('\tmain__BenchedTests_end_testing(&bt);')
}
g.writeln('')
if g.autofree {
@ -3628,7 +3621,7 @@ fn (g Gen) get_all_test_function_names() []string {
}
mut all_tfuncs_c := []string{}
for f in all_tfuncs {
all_tfuncs_c << f.replace('.', '__')
all_tfuncs_c << util.no_dots(f)
}
return all_tfuncs_c
}
@ -3640,12 +3633,12 @@ fn (g Gen) is_importing_os() bool {
fn (mut g Gen) go_stmt(node ast.GoStmt) {
tmp := g.new_tmp_var()
expr := node.call_expr as ast.CallExpr
mut name := expr.name // .replace('.', '__')
mut name := expr.name // util.no_dots(expr.name)
if expr.is_method {
receiver_sym := g.table.get_type_symbol(expr.receiver_type)
name = receiver_sym.name + '_' + name
}
name = name.replace('.', '__')
name = util.no_dots(name)
g.writeln('// go')
wrapper_struct_name := 'thread_arg_' + name
wrapper_fn_name := name + '_thread_wrapper'
@ -3750,7 +3743,7 @@ fn (mut g Gen) is_expr(node ast.InfixExpr) {
// `_Animal_Dog_index`
sub_type := node.right as ast.Type
sub_sym := g.table.get_type_symbol(sub_type.typ)
g.write('_${sym.name}_${sub_sym.name}_index')
g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index')
return
} else if sym.kind == .sum_type {
g.write('typ $eq ')
@ -3859,7 +3852,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) {
}
fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) {
s := styp.replace('.', '__')
s := util.no_dots(styp)
g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { /* gen_str_for_enum */')
g.auto_str_funcs.writeln('\tswitch(it) {')
@ -3896,6 +3889,7 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
deref_typ := styp
g.auto_str_funcs.writeln('\t$deref_typ *it = &x;')
}
clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
// generate ident / indent length = 4 spaces
g.auto_str_funcs.writeln('\tstring indents = tos_lit("");')
g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; ++i) {')

65
vlib/v/gen/cmain.v 100644
View File

@ -0,0 +1,65 @@
module gen
pub fn (mut g Gen) gen_c_main() {
if !g.has_main {
return
}
if g.pref.is_liveshared {
return
}
g.out.writeln('')
g.gen_c_main_header()
g.writeln('\tmain__main();')
g.gen_c_main_footer()
}
fn (mut g Gen) gen_c_main_header() {
if g.pref.os == .windows {
if g.is_gui_app() {
// GUI application
g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd){')
} else {
// Console application
g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]){')
}
} else {
g.writeln('int main(int ___argc, char** ___argv){')
}
if g.pref.os == .windows && g.is_gui_app() {
g.writeln('\ttypedef LPWSTR*(WINAPI *cmd_line_to_argv)(LPCWSTR, int*);')
g.writeln('\tHMODULE shell32_module = LoadLibrary(L"shell32.dll");')
g.writeln('\tcmd_line_to_argv CommandLineToArgvW = (cmd_line_to_argv)GetProcAddress(shell32_module, "CommandLineToArgvW");')
g.writeln('\tint ___argc;')
g.writeln('\twchar_t** ___argv = CommandLineToArgvW(cmd_line, &___argc);')
}
g.writeln('\t_vinit();')
if g.pref.is_prof {
g.writeln('')
g.writeln('\tatexit(vprint_profile_stats);')
g.writeln('')
}
if g.is_importing_os() {
if g.autofree {
g.writeln('free(_const_os__args.data); // empty, inited in _vinit()')
}
if g.pref.os == .windows {
g.writeln('\t_const_os__args = os__init_os_args_wide(___argc, ___argv);')
} else {
g.writeln('\t_const_os__args = os__init_os_args(___argc, (byteptr*)___argv);')
}
}
if g.pref.is_livemain {
g.generate_hotcode_reloading_main_caller()
}
}
pub fn (mut g Gen) gen_c_main_footer() {
if g.autofree {
g.writeln('\t_vcleanup();')
}
g.writeln('\treturn 0;')
g.writeln('}')
}

View File

@ -5,6 +5,7 @@ module gen
import v.ast
import v.table
import v.util
fn (g &Gen) comptime_call(node ast.ComptimeCall) {
if node.is_vweb {
@ -12,7 +13,7 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) {
if stmt is ast.FnDecl {
fn_decl := stmt as ast.FnDecl
// insert stmts from vweb_tmpl fn
if fn_decl.name.starts_with('vweb_tmpl') {
if fn_decl.name.starts_with('main.vweb_tmpl') {
g.inside_vweb_tmpl = true
g.stmts(fn_decl.stmts)
g.inside_vweb_tmpl = false
@ -40,7 +41,7 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) {
g.write(' else ')
}
g.write('if (string_eq($node.method_name, tos_lit("$method.name"))) ')
g.write('${node.sym.name}_${method.name}($amp ')
g.write('${util.no_dots(node.sym.name)}_${method.name}($amp ')
g.expr(node.left)
g.writeln(');')
j++
@ -66,5 +67,5 @@ fn (mut g Gen) comp_if(it ast.CompIf) {
g.stmts(it.else_stmts)
g.defer_ifdef = ''
}
g.writeln('\n// } $it.val\n#endif\n')
g.writeln('\n#endif\n// } $it.val\n')
}

View File

@ -15,12 +15,6 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
// if g.fileis('vweb.v') {
// println('\ngen_fn_decl() $it.name $it.is_generic $g.cur_generic_type')
// }
former_cur_fn := g.cur_fn
g.cur_fn = &it
defer {
g.cur_fn = former_cur_fn
}
is_main := it.name == 'main'
if it.is_generic && g.cur_generic_type == 0 { // need the cur_generic_type check to avoid inf. recursion
// loop thru each generic type and generate a function
for gen_type in g.table.fn_gen_types[it.name] {
@ -34,9 +28,6 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
g.cur_generic_type = 0
return
}
if is_main && g.pref.is_liveshared {
return
}
fn_start_pos := g.out.len
msvc_attrs := g.write_fn_attrs()
// Live
@ -49,165 +40,108 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
eprintln('INFO: compile with `v -live $g.pref.path `, if you want to use the [live] function $it.name .')
}
//
if is_main {
if g.pref.os == .windows {
if g.is_gui_app() {
// GUI application
g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd){')
g.last_fn_c_name = 'wWinMain'
} else {
// Console application
g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]){')
g.last_fn_c_name = 'wmain'
}
} else {
g.writeln('int main(int ___argc, char** ___argv){')
g.last_fn_c_name = it.name
mut name := it.name
if name[0] in [`+`, `-`, `*`, `/`, `%`] {
name = util.replace_op(name)
}
if it.is_method {
name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name
}
if it.language == .c {
name = util.no_dots(name)
} else {
name = c_name(name)
}
mut type_name := g.typ(it.return_type)
if g.cur_generic_type != 0 {
// foo<T>() => foo_int(), foo_string() etc
gen_name := g.typ(g.cur_generic_type)
name += '_' + gen_name
type_name = type_name.replace('T', gen_name)
}
// if g.pref.show_cc && it.is_builtin {
// println(name)
// }
// type_name := g.table.Type_to_str(it.return_type)
// Live functions are protected by a mutex, because otherwise they
// can be changed by the live reload thread, *while* they are
// running, with unpredictable results (usually just crashing).
// For this purpose, the actual body of the live function,
// is put under a non publicly accessible function, that is prefixed
// with 'impl_live_' .
if is_livemain {
g.hotcode_fn_names << name
}
mut impl_fn_name := name
if is_live_wrap {
impl_fn_name = 'impl_live_${name}'
}
g.last_fn_c_name = impl_fn_name
//
if is_live_wrap {
if is_livemain {
g.definitions.write('$type_name (* $impl_fn_name)(')
g.write('$type_name no_impl_${name}(')
}
if is_liveshared {
g.definitions.write('$type_name ${impl_fn_name}(')
g.write('$type_name ${impl_fn_name}(')
}
} else {
mut name := it.name
if name[0] in [`+`, `-`, `*`, `/`, `%`] {
name = util.replace_op(name)
}
if it.is_method {
name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name
}
if it.language == .c {
name = name.replace('.', '__')
} else {
name = c_name(name)
}
mut type_name := g.typ(it.return_type)
if g.cur_generic_type != 0 {
// foo<T>() => foo_int(), foo_string() etc
gen_name := g.typ(g.cur_generic_type)
name += '_' + gen_name
// type_name = type_name.replace('T', gen_name)
}
// if g.pref.show_cc && it.is_builtin {
// println(name)
// }
// type_name := g.table.Type_to_str(it.return_type)
// Live functions are protected by a mutex, because otherwise they
// can be changed by the live reload thread, *while* they are
// running, with unpredictable results (usually just crashing).
// For this purpose, the actual body of the live function,
// is put under a non publicly accessible function, that is prefixed
// with 'impl_live_' .
if is_livemain {
g.hotcode_fn_names << name
}
mut impl_fn_name := if is_live_wrap { 'impl_live_$name' } else { name }
g.last_fn_c_name = impl_fn_name
//
if is_live_wrap {
if is_livemain {
g.definitions.write('$type_name (* $impl_fn_name)(')
g.write('$type_name no_impl_${name}(')
}
if is_liveshared {
g.definitions.write('$type_name ${impl_fn_name}(')
g.write('$type_name ${impl_fn_name}(')
}
} else {
if !(it.is_pub || g.pref.is_debug) {
g.write('static ')
g.definitions.write('static ')
}
fn_header := if msvc_attrs.len > 0 { '$type_name $msvc_attrs ${name}(' } else { '$type_name ${name}(' }
g.definitions.write(fn_header)
g.write(fn_header)
}
fargs, fargtypes := g.fn_args(it.args, it.is_variadic)
if it.no_body || (g.pref.use_cache && it.is_builtin) {
// Just a function header. Builtin function bodies are defined in builtin.o
g.definitions.writeln(');')
g.writeln(');')
return
if !(it.is_pub || g.pref.is_debug) {
g.write('static ')
g.definitions.write('static ')
}
fn_header := if msvc_attrs.len > 0 { '$type_name $msvc_attrs ${name}(' } else { '$type_name ${name}(' }
g.definitions.write(fn_header)
g.write(fn_header)
}
fargs, fargtypes := g.fn_args(it.args, it.is_variadic)
if it.no_body || (g.pref.use_cache && it.is_builtin) {
// Just a function header. Builtin function bodies are defined in builtin.o
g.definitions.writeln(');')
g.writeln(') {')
if is_live_wrap {
// The live function just calls its implementation dual, while ensuring
// that the call is wrapped by the mutex lock & unlock calls.
// Adding the mutex lock/unlock inside the body of the implementation
// function is not reliable, because the implementation function can do
// an early exit, which will leave the mutex locked.
mut fn_args_list := []string{}
for ia, fa in fargs {
fn_args_list << '${fargtypes[ia]} $fa'
}
mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');'
mut live_fnreturn := ''
if type_name != 'void' {
live_fncall = '$type_name res = $live_fncall'
live_fnreturn = 'return res;'
}
g.definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') +
');')
g.hotcode_definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') +
'){')
g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);')
g.hotcode_definitions.writeln(' $live_fncall')
g.hotcode_definitions.writeln(' pthread_mutex_unlock(&live_fn_mutex);')
g.hotcode_definitions.writeln(' $live_fnreturn')
g.hotcode_definitions.writeln('}')
}
g.writeln(');')
return
}
if is_main {
if g.pref.os == .windows && g.is_gui_app() {
g.writeln('\ttypedef LPWSTR*(WINAPI *cmd_line_to_argv)(LPCWSTR, int*);')
g.writeln('\tHMODULE shell32_module = LoadLibrary(L"shell32.dll");')
g.writeln('\tcmd_line_to_argv CommandLineToArgvW = (cmd_line_to_argv)GetProcAddress(shell32_module, "CommandLineToArgvW");')
g.writeln('\tint ___argc;')
g.writeln('\twchar_t** ___argv = CommandLineToArgvW(cmd_line, &___argc);')
g.definitions.writeln(');')
g.writeln(') {')
if is_live_wrap {
// The live function just calls its implementation dual, while ensuring
// that the call is wrapped by the mutex lock & unlock calls.
// Adding the mutex lock/unlock inside the body of the implementation
// function is not reliable, because the implementation function can do
// an early exit, which will leave the mutex locked.
mut fn_args_list := []string{}
for ia, fa in fargs {
fn_args_list << '${fargtypes[ia]} ${fa}'
}
g.writeln('\t_vinit();')
if g.is_importing_os() {
if g.autofree {
g.writeln('free(_const_os__args.data); // empty, inited in _vinit()')
}
if g.pref.os == .windows {
g.writeln('\t_const_os__args = os__init_os_args_wide(___argc, ___argv);')
}
//
else {
g.writeln('\t_const_os__args = os__init_os_args(___argc, (byteptr*)___argv);')
}
mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');'
mut live_fnreturn := ''
if type_name != 'void' {
live_fncall = '${type_name} res = ${live_fncall}'
live_fnreturn = 'return res;'
}
}
if g.pref.is_livemain && is_main {
g.generate_hotcode_reloading_main_caller()
g.definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') + ');')
g.hotcode_definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') +
'){')
g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);')
g.hotcode_definitions.writeln(' $live_fncall')
g.hotcode_definitions.writeln(' pthread_mutex_unlock(&live_fn_mutex);')
g.hotcode_definitions.writeln(' $live_fnreturn')
g.hotcode_definitions.writeln('}')
}
// Profiling mode? Start counting at the beginning of the function (save current time).
if g.pref.is_prof {
g.profile_fn(it.name, is_main)
}
if is_main {
g.indent++
g.profile_fn(it.name)
}
g.stmts(it.stmts)
if is_main {
g.indent--
}
// ////////////
if is_main {
if g.autofree {
g.writeln('\t_vcleanup();')
}
if g.is_test {
verror('test files cannot have function `main`')
}
}
g.write_defer_stmts_when_needed()
// /////////
if g.autofree && !is_main {
if g.autofree {
// TODO: remove this, when g.write_autofree_stmts_when_needed works properly
g.writeln(g.autofree_scope_vars(it.body_pos.pos))
}
if is_main {
g.writeln('\treturn 0;')
}
g.writeln('}')
g.defer_stmts = []
if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list {
@ -249,7 +183,7 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) ([]string, []string)
caname := c_name(arg.name)
typ := g.unwrap_generic(arg.typ)
arg_type_sym := g.table.get_type_symbol(typ)
mut arg_type_name := g.typ(typ) // arg_type_sym.name.replace('.', '__')
mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name)
// if arg.name == 'xxx' {
// println('xxx arg type= ' + arg_type_name)
// }
@ -356,7 +290,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
// mut receiver_type_name := g.cc_type(node.receiver_type)
// mut receiver_type_name := g.typ(node.receiver_type)
typ_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver_type))
mut receiver_type_name := typ_sym.name.replace('.', '__')
mut receiver_type_name := util.no_dots(typ_sym.name)
if typ_sym.kind == .interface_ {
// Speaker_name_table[s._interface_idx].speak(s._object)
g.write('${c_name(receiver_type_name)}_name_table[')
@ -413,7 +347,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
g.write('*($return_type_str*)')
}
}
mut name := '${receiver_type_name}_$node.name'.replace('.', '__')
mut name := util.no_dots('${receiver_type_name}_$node.name')
// Check if expression is: arr[a..b].clone(), arr[a..].clone()
// if so, then instead of calling array_clone(&array_slice(...))
// call array_clone_static(array_slice(...))
@ -424,7 +358,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if idx is ast.RangeExpr {
// expr is arr[range].clone()
// use array_clone_static instead of array_clone
name = '${receiver_type_name}_${node.name}_static'.replace('.', '__')
name = util.no_dots('${receiver_type_name}_${node.name}_static')
is_range_slice = true
}
}
@ -507,13 +441,13 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
if node.language == .c {
// Skip "C."
g.is_c_call = true
name = name[2..].replace('.', '__')
name = util.no_dots(name[2..])
} else {
name = c_name(name)
}
if is_json_encode {
// `json__encode` => `json__encode_User`
name += '_' + json_type_str.replace('.', '__')
name += '_' + util.no_dots(json_type_str)
}
if node.generic_type != table.void_type && node.generic_type != 0 {
// `foo<int>()` => `foo_int()`

View File

@ -1337,8 +1337,8 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) {
// key_typ_sym := g.table.get_type_symbol(it.key_type)
// value_typ_sym := g.table.get_type_symbol(it.value_type)
// key_typ_str := key_typ_sym.name.replace('.', '__')
// value_typ_str := value_typ_sym.name.replace('.', '__')
// key_typ_str := util.no_dots(key_typ_sym.name)
// value_typ_str := util.no_dots(value_typ_sym.name)
if it.vals.len > 0 {
g.writeln('new Map([')
g.inc_indent()

View File

@ -4,6 +4,7 @@
module gen
import v.table
import v.util
import strings
// TODO replace with comptime code generation.
@ -128,12 +129,12 @@ $enc_fn_dec {
fn js_enc_name(typ string) string {
name := 'json__encode_$typ'
return name.replace('.', '__')
return util.no_dots(name)
}
fn js_dec_name(typ string) string {
name := 'json__decode_$typ'
return name.replace('.', '__')
return util.no_dots(name)
}
fn is_js_prim(typ string) bool {

View File

@ -6,15 +6,10 @@ pub struct ProfileCounterMeta{
vpc_calls string
}
fn (mut g Gen) profile_fn(fn_name string, is_main bool){
if is_main {
g.writeln('')
g.writeln('\tatexit(vprint_profile_stats);')
g.writeln('')
}
fn (mut g Gen) profile_fn(fn_name string){
if g.pref.profile_no_inline && 'inline' in g.attrs {
g.defer_profile_code = ''
return
return
}
if fn_name.starts_with('time.vpc_now') {
g.defer_profile_code = ''
@ -33,7 +28,7 @@ fn (mut g Gen) profile_fn(fn_name string, is_main bool){
pub fn (mut g Gen) gen_vprint_profile_stats() {
g.pcs_declarations.writeln('void vprint_profile_stats(){')
fstring := '"%14llu %14.3fms %14.0fns %s \\n"'
fstring := '"%14lu %14.3fms %14.0fns %s \\n"'
if g.pref.profile_file == '-' {
for pc_meta in g.pcs {
g.pcs_declarations.writeln('\tif (${pc_meta.vpc_calls}) printf($fstring, ${pc_meta.vpc_calls}, ${pc_meta.vpc_name}/1000000.0, ${pc_meta.vpc_name}/${pc_meta.vpc_calls}, "${pc_meta.fn_name}" );')

View File

@ -5,6 +5,7 @@ module gen
import v.ast
import strings
import v.table
import v.util
// pg,mysql etc
const (
@ -26,16 +27,16 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
g.writeln(';')
g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("')
if node.kind == .insert {
g.write('insert into `$node.table_name` (')
g.write('INSERT INTO `${util.strip_mod_name(node.table_name)}` (')
} else {
g.write('update `$node.table_name` set ')
g.write('UPDATE `${util.strip_mod_name(node.table_name)}` SET ')
}
if node.kind == .insert {
for i, field in node.fields {
if field.name == 'id' {
continue
}
g.write(field.name)
g.write('`${field.name}`')
if i < node.fields.len - 1 {
g.write(', ')
}
@ -59,7 +60,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
g.write(', ')
}
}
g.write(' where ')
g.write(' WHERE ')
}
if node.kind == .update {
g.expr_to_sql(node.where_expr)
@ -101,24 +102,24 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) {
```
*/
cur_line := g.go_before_stmt(0)
mut q := 'select '
mut sql_query := 'SELECT '
if node.is_count {
// `select count(*) from User`
q += 'count(*) from `$node.table_name`'
sql_query += 'COUNT(*) FROM `${util.strip_mod_name(node.table_name)}` '
} else {
// `select id, name, country from User`
for i, field in node.fields {
q += '$field.name'
sql_query += '`${field.name}`'
if i < node.fields.len - 1 {
q += ', '
sql_query += ', '
}
}
q += ' from `$node.table_name`'
sql_query += ' FROM `${util.strip_mod_name(node.table_name)}`'
}
if node.has_where {
q += ' where '
sql_query += ' WHERE '
}
// g.write('${dbtype}__DB_q_int(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$q')
// g.write('${dbtype}__DB_q_int(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$sql_query')
g.sql_stmt_name = g.new_tmp_var()
db_name := g.new_tmp_var()
g.writeln('\n\t// sql select')
@ -126,18 +127,19 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) {
g.write('${dbtype}__DB $db_name = ') // $node.db_var_name;')
g.expr(node.db_expr)
g.writeln(';')
// g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$q')
g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("$q')
// g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$sql_query')
g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("')
g.write(sql_query)
if node.has_where && node.where_expr is ast.InfixExpr {
g.expr_to_sql(node.where_expr)
}
g.write(' order by id ')
g.write(' ORDER BY id ')
if node.has_limit {
g.write(' limit ')
g.write(' LIMIT ')
g.expr_to_sql(node.limit_expr)
}
if node.has_offset {
g.write(' offset ')
g.write(' OFFSET ')
g.expr_to_sql(node.offset_expr)
}
g.writeln('"));')

View File

@ -799,7 +799,7 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
fn (mut g Gen) fn_decl(node ast.FnDecl) {
println(term.green('\n$node.name:'))
g.stack_var_pos = 0
is_main := node.name == 'main'
is_main := node.name == 'main.main'
// println('saving addr $node.name $g.buf.len.hex2()')
if is_main {
g.save_main_fn_addr()

View File

@ -121,7 +121,7 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
for stmt in file.stmts {
if stmt is ast.FnDecl {
fn_decl := stmt as ast.FnDecl
if fn_decl.name == 'vweb_tmpl_$p.cur_fn_name' {
if fn_decl.name == 'main.vweb_tmpl_${p.cur_fn_name}' {
tmpl_scope := file.scope.innermost(fn_decl.body_pos.pos)
for _, obj in p.scope.objects {
if obj is ast.Var {

View File

@ -35,7 +35,8 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
// In case of `foo<T>()`
// T is unwrapped and registered in the checker.
if !generic_type.has_flag(.generic) {
p.table.register_fn_gen_type(fn_name, generic_type)
full_generic_fn_name := if fn_name.contains('.') { fn_name } else { p.prepend_mod(fn_name) }
p.table.register_fn_gen_type(full_generic_fn_name, generic_type)
}
}
p.check(.lpar)
@ -227,7 +228,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
// Register
if is_method {
mut type_sym := p.table.get_type_symbol(rec_type)
// p.warn('reg method $type_sym.name . $name ()')
// p.warn('reg method $type_sym.name . $name ()')
type_sym.register_method(table.Fn{
name: name
args: args
@ -237,6 +238,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_pub: is_pub
is_deprecated: is_deprecated
ctdefine: ctdefine
mod: p.mod
})
} else {
if language == .c {
@ -249,6 +251,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
if _ := p.table.find_fn(name) {
p.fn_redefinition_error(name)
}
//p.warn('reg functn $name ()')
p.table.register_fn(table.Fn{
name: name
args: args
@ -372,7 +375,8 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
mut args := []table.Arg{}
mut is_variadic := false
// `int, int, string` (no names, just types)
types_only := p.tok.kind in [.amp, .ellipsis, .key_fn] || (p.peek_tok.kind == .comma && p.table.known_type(p.tok.lit)) ||
argname := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() { p.prepend_mod(p.tok.lit) } else { p.tok.lit }
types_only := p.tok.kind in [.amp, .ellipsis, .key_fn] || (p.peek_tok.kind == .comma && p.table.known_type(argname)) ||
p.peek_tok.kind == .rpar
// TODO copy pasta, merge 2 branches
if types_only {
@ -500,16 +504,13 @@ fn (mut p Parser) fn_redefinition_error(name string) {
}
fn have_fn_main(stmts []ast.Stmt) bool {
mut has_main_fn := false
for stmt in stmts {
match stmt {
ast.FnDecl {
if stmt.name == 'main' {
has_main_fn = true
}
if stmt is ast.FnDecl {
f := stmt as ast.FnDecl
if f.name == 'main.main' && f.mod == 'main' {
return true
}
else {}
}
}
return has_main_fn
return false
}

View File

@ -12,7 +12,7 @@ fn (p &Parser) prepend_mod(name string) string {
if p.expr_mod != '' {
return p.expr_mod + '.' + name
}
if p.builtin_mod || p.mod == 'main' {
if p.builtin_mod {
return name
}
return '${p.mod}.$name'

View File

@ -167,7 +167,7 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool) table
name = '${p.imports[name]}.$p.tok.lit'
} else if p.expr_mod != '' {
name = p.expr_mod + '.' + name
} else if p.mod !in ['builtin', 'main'] && name !in table.builtin_type_names && name.len > 1 {
} else if p.mod !in ['builtin'] && name !in p.table.type_idxs && name.len > 1 {
// `Foo` in module `mod` means `mod.Foo`
name = p.mod + '.' + name
}

View File

@ -148,16 +148,7 @@ fn (mut p Parser) parse() ast.File {
}
for {
if p.tok.kind == .eof {
if p.pref.is_script && !p.pref.is_test && p.mod == 'main' && !have_fn_main(stmts) {
stmts << ast.FnDecl{
name: 'main'
mod: p.mod
file: p.file_name
return_type: table.void_type
}
} else {
p.check_unused_imports()
}
p.check_unused_imports()
break
}
// println('stmt at ' + p.tok.str())
@ -449,8 +440,8 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
stmts << p.stmt(false)
}
return ast.FnDecl{
name: 'main'
mod: p.mod
name: 'main.main'
mod: 'main'
stmts: stmts
file: p.file_name
return_type: table.void_type
@ -845,19 +836,11 @@ pub fn (mut p Parser) name_expr() ast.Expr {
if p.tok.lit in ['r', 'c', 'js'] && p.peek_tok.kind == .string && !p.inside_str_interp {
return p.string_expr()
}
mut known_var := false
if obj := p.scope.find(p.tok.lit) {
match mut obj {
ast.Var {
known_var = true
obj.is_used = true
}
else {}
}
}
known_var := p.mark_var_as_used( p.tok.lit )
mut is_mod_cast := false
if p.peek_tok.kind == .dot && !known_var &&
(language != .v || p.known_import(p.tok.lit) || p.mod.all_after_last('.') == p.tok.lit) {
(language != .v || p.known_import(p.tok.lit) ||
p.mod.all_after_last('.') == p.tok.lit) {
if language == .c {
mod = 'C'
} else if language == .js {
@ -1490,10 +1473,10 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' }
p.scanner.codegen('
//
$pubfn ( e &$name) has(flag $name) bool { return (int(*e) & (1 << int(flag))) != 0 }
$pubfn (mut e $name) set(flag $name) { unsafe{ *e = int(*e) | (1 << int(flag)) } }
$pubfn (mut e $name) clear(flag $name) { unsafe{ *e = int(*e) & ~(1 << int(flag)) } }
$pubfn (mut e $name) toggle(flag $name) { unsafe{ *e = int(*e) ^ (1 << int(flag)) } }
$pubfn ( e &$enum_name) has(flag $enum_name) bool { return (int(*e) & (1 << int(flag))) != 0 }
$pubfn (mut e $enum_name) set(flag $enum_name) { unsafe{ *e = int(*e) | (1 << int(flag)) } }
$pubfn (mut e $enum_name) clear(flag $enum_name) { unsafe{ *e = int(*e) & ~(1 << int(flag)) } }
$pubfn (mut e $enum_name) toggle(flag $enum_name) { unsafe{ *e = int(*e) ^ (1 << int(flag)) } }
//
')
}
@ -1687,3 +1670,16 @@ fn (mut p Parser) rewind_scanner_to_current_token_in_new_mode() {
}
}
}
pub fn (mut p Parser) mark_var_as_used(varname string) bool {
if obj := p.scope.find(varname) {
match mut obj {
ast.Var {
obj.is_used = true
return true
}
else {}
}
}
return false
}

View File

@ -94,19 +94,24 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
}
}
.key_sizeof {
pos := p.tok.position()
p.next() // sizeof
p.check(.lpar)
sizeof_type := p.parse_type()
if p.tok.lit == 'C' {
p.next()
p.check(.dot)
is_known_var := p.mark_var_as_used( p.tok.lit )
if is_known_var {
expr := p.parse_ident(table.Language.v)
node = ast.SizeOf{
type_name: p.check_name()
typ: sizeof_type
is_type: false
expr: expr
pos: pos
}
} else {
sizeof_type := p.parse_type()
node = ast.SizeOf{
is_type: true
typ: sizeof_type
type_name: p.table.get_type_symbol(sizeof_type).name
pos: pos
}
}
p.check(.rpar)

View File

@ -151,7 +151,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
} else if kind == .update {
if !p.pref.is_fmt {
// NB: in vfmt mode, v parses just a single file and table_name may not have been registered
idx := p.table.find_type_idx(table_name)
idx := p.table.find_type_idx(p.prepend_mod(table_name))
table_type = table.new_type(idx)
}
p.check_sql_keyword('where')

View File

@ -0,0 +1,93 @@
module scanner
import os
struct TestStruct {
test string
}
fn (mut t TestStruct) test_struct() {
assert @STRUCT == 'TestStruct'
}
fn (mut t TestStruct) test_struct_w_return() string {
assert @STRUCT == 'TestStruct'
return t.test
}
fn (mut t TestStruct) test_struct_w_high_order(cb fn(int)string) string {
assert @STRUCT == 'TestStruct'
return 'test'+cb(2)
}
struct TestFn { }
fn (mut t TestFn) tst_1() {
assert @FN == 'tst_1'
}
fn (mut t TestFn) tst_2(cb fn(int)) {
assert @FN == 'tst_2'
cb(1)
}
fn fn_name_mod_level() {
assert @FN == 'fn_name_mod_level'
}
fn fn_name_mod_level_high_order(cb fn(int)) {
assert @FN == 'fn_name_mod_level_high_order'
cb(1)
}
fn test_at_file() {
// Test @FILE
f := os.file_name( @FILE )
assert f == 'scanner_at_literals_test.v'
}
fn test_at_fn() {
// Test @FN
assert @FN == 'test_at_fn'
fn_name_mod_level()
fn_name_mod_level_high_order(fn(i int){
t := i + 1
assert t == 2
})
mut tfn := TestFn{}
tfn.tst_1()
tfn.tst_2(fn(i int){
t := i + 1
assert t == 2
})
}
fn test_at_mod() {
// Test @MOD
assert @MOD == 'scanner'
}
fn test_at_struct() {
// Test @STRUCT
assert @STRUCT == ''
mut ts := TestStruct { test: "test" }
ts.test_struct()
r1 := ts.test_struct_w_return()
r2 := ts.test_struct_w_high_order(fn(i int)string{
assert @STRUCT == ''
return i.str()
})
assert r1 == 'test'
assert r2 == 'test2'
}
fn test_vmod_file() {
content := @VMOD_FILE
assert content.len > 0
assert content.contains('Module {')
assert content.contains('name:')
assert content.contains('version:')
assert content.contains('description:')
}

View File

@ -5,45 +5,6 @@ module scanner
import v.token
struct TestStruct {
test string
}
fn (mut t TestStruct) test_struct() {
assert @STRUCT == 'TestStruct'
}
fn (mut t TestStruct) test_struct_w_return() string {
assert @STRUCT == 'TestStruct'
return t.test
}
fn (mut t TestStruct) test_struct_w_high_order(cb fn(int)string) string {
assert @STRUCT == 'TestStruct'
return 'test'+cb(2)
}
struct TestFn { }
fn (mut t TestFn) tst_1() {
assert @FN == 'tst_1'
}
fn (mut t TestFn) tst_2(cb fn(int)) {
assert @FN == 'tst_2'
cb(1)
}
fn fn_name_mod_level() {
assert @FN == 'fn_name_mod_level'
}
fn fn_name_mod_level_high_order(cb fn(int)) {
assert @FN == 'fn_name_mod_level_high_order'
cb(1)
}
fn scan_kinds(text string) []token.Kind {
mut scanner := new_scanner(text, .skip_comments, false)
mut token_kinds := []token.Kind{}
@ -66,14 +27,18 @@ fn test_scan() {
assert token_kinds[3] == .plus
assert token_kinds[4] == .number
assert token_kinds[5] == .rpar
// test number costants input format
}
fn test_number_constant_input_format() {
mut c := 0xa0
assert c == 0xa0
c = 0b1001
assert c == 9
c = 1000000
assert c == 1000000
// test float conversion and reading
}
fn test_float_conversion_and_reading() {
d := 23000000e-3
assert int(d) == 23000
mut e := 1.2E3 * -1e-1
@ -83,92 +48,62 @@ fn test_scan() {
assert 1.23e+10 == 1.23e10
assert 1.23e+10 == 1.23e0010
assert (-1.23e+10) == (1.23e0010 * -1.0)
// Test @FN
assert @FN == 'test_scan'
fn_name_mod_level()
fn_name_mod_level_high_order(fn(i int){
t := i + 1
assert t == 2
})
mut tfn := TestFn{}
tfn.tst_1()
tfn.tst_2(fn(i int){
t := i + 1
assert t == 2
})
// Test @MOD
assert @MOD == 'scanner'
// Test @STRUCT
assert @STRUCT == ''
mut ts := TestStruct { test: "test" }
ts.test_struct()
r1 := ts.test_struct_w_return()
r2 := ts.test_struct_w_high_order(fn(i int)string{
assert @STRUCT == ''
return i.str()
})
assert r1 == 'test'
assert r2 == 'test2'
}
fn test_vmod_file() {
content := @VMOD_FILE
assert content.len > 0
assert content.contains('Module {')
assert content.contains('name:')
assert content.contains('version:')
assert content.contains('description:')
}
fn test_reference() {
mut result := scan_kinds('true && false')
fn test_reference_bools() {
result := scan_kinds('true && false')
assert result.len == 3
assert result[0] == .key_true
assert result[1] == .and
assert result[2] == .key_false
}
result = scan_kinds('&foo')
fn test_reference_var() {
result := scan_kinds('&foo')
assert result.len == 2
assert result[0] == .amp
assert result[1] == .name
}
result = scan_kinds('[]&foo')
fn test_array_of_references() {
result := scan_kinds('[]&foo')
assert result.len == 4
assert result[0] == .lsbr
assert result[1] == .rsbr
assert result[2] == .amp
assert result[3] == .name
}
result = scan_kinds('&[]&foo')
fn test_ref_array_of_references() {
result := scan_kinds('&[]&foo')
assert result.len == 5
assert result[0] == .amp
assert result[1] == .lsbr
assert result[2] == .rsbr
assert result[3] == .amp
assert result[4] == .name
}
result = scan_kinds('&&foo')
fn test_ref_ref_foo() {
result := scan_kinds('&&foo')
assert result.len == 3
assert result[0] == .amp
assert result[1] == .amp
assert result[2] == .name
}
result = scan_kinds('[]&&foo')
fn test_array_of_ref_ref_foo() {
result := scan_kinds('[]&&foo')
assert result.len == 5
assert result[0] == .lsbr
assert result[1] == .rsbr
assert result[2] == .amp
assert result[3] == .amp
assert result[4] == .name
}
result = scan_kinds('&&[]&&foo')
fn test_ref_ref_array_ref_ref_foo() {
result := scan_kinds('&&[]&&foo')
assert result.len == 7
assert result[0] == .amp
assert result[1] == .amp

View File

@ -108,11 +108,12 @@ pub fn (t &Table) known_fn(name string) bool {
}
pub fn (mut t Table) register_fn(new_fn Fn) {
// println('reg fn $new_fn.name nr_args=$new_fn.args.len')
// println('reg fn $new_fn.name nr_args=$new_fn.args.len')
t.fns[new_fn.name] = new_fn
}
pub fn (mut t TypeSymbol) register_method(new_fn Fn) {
// println('reg me $new_fn.name nr_args=$new_fn.args.len')
t.methods << new_fn
}

View File

@ -0,0 +1,25 @@
This folder contains a V project,
intended to be used as a demonstration
for how to test functions defined inside
a main module.
See my_test.v and my_other_test.v .
These files work as any other internal module tests,
i.e. they do `module main` at their top, so that v knows,
that they are internal tests, and for which module they apply.
When you do `v my_test.v`, v will try to find other *.v files in
the same folder that also have `module main` at their top,
then it will process them and process the my_test.v file too.
The v `fn main(){}` function that you most likely also have will get
compiled as normal to `void main__main(){...}`, but it will NOT be
called by anything, so it will not mess up your tests.
Instead, your test_ functions will get called inside the generated
`int main(){...}` test runner, just like it is the case with all _test.v
files (internal or external ones).
NB: each _test.v file is compiled separately from all other _test.v
files, so you can have conflicting test_ functions in them without a
problem too.

View File

@ -0,0 +1,9 @@
fn iadd(x int, y int) int {
return x + y
}
fn main(){
println('Hello world')
println('iadd: ' + iadd(1,2).str())
}

View File

@ -0,0 +1,6 @@
module main
fn test_iadd_3_4(){
a := iadd(3,4)
assert a == 7
assert iadd(10,20) == 30
}

View File

@ -0,0 +1,9 @@
module main
fn test_iadd_3_4(){
a := iadd(3,4)
assert a == 7
}
fn test_iadd_5_6(){
a := iadd(5,6)
assert a == 11
}

View File

@ -0,0 +1,5 @@
Module {
name: 'project_with_tests_for_main',
description: 'This project demonstrates the ability to test functions in the main module',
dependencies: []
}

View File

@ -63,7 +63,8 @@ fn color(kind, msg string) string {
}
// formatted_error - `kind` may be 'error' or 'warn'
pub fn formatted_error(kind, emsg, filepath string, pos token.Position) string {
pub fn formatted_error(kind, omsg, filepath string, pos token.Position) string {
emsg := omsg.replace('main.', '')
mut path := filepath
verror_paths_override := os.getenv('VERROR_PATHS')
if verror_paths_override == 'absolute' {

View File

@ -331,3 +331,15 @@ pub fn ensure_modules_for_all_tools_are_installed(is_verbose bool) {
}
}
}
pub fn strip_mod_name(name string) string {
return name.all_after_last('.')
}
pub fn strip_main_name(name string) string {
return name.replace('main.','')
}
pub fn no_dots(s string) string {
return s.replace('.', '__')
}