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 make -j4
./v -version ./v version
du -s . du -s .

View File

@ -10,7 +10,12 @@ du -s .
ls -lat 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 ./v build-vbinaries

View File

@ -55,15 +55,15 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
# - name: Build V - name: Build V
# uses: spytheman/docker_alpine_v@v7.0 uses: spytheman/docker_alpine_v@v7.0
# with: with:
# entrypoint: .github/workflows/alpine.build.sh entrypoint: .github/workflows/alpine.build.sh
# - name: Test V - name: Test V
# uses: spytheman/docker_alpine_v@v7.0 uses: spytheman/docker_alpine_v@v7.0
# with: with:
# entrypoint: .github/workflows/alpine.test.sh entrypoint: .github/workflows/alpine.test.sh
macos: macos:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -176,8 +176,6 @@ jobs:
../../vprod -backend x64 -o 1m 1m.v ../../vprod -backend x64 -o 1m 1m.v
echo "Running it..." echo "Running it..."
ls 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 # - name: SDL examples
# run: git clone --depth 1 https://github.com/vlang/sdl && cd sdl # run: git clone --depth 1 https://github.com/vlang/sdl && cd sdl
@ -188,6 +186,18 @@ jobs:
# with: # with:
# github-token: ${{ secrets.GITHUB_TOKEN }} # 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: ubuntu-musl:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
env: env:

View File

@ -3,3 +3,7 @@ module main
// This prelude is loaded in every v program compiled with -live, // This prelude is loaded in every v program compiled with -live,
// in both the main executable, and in the shared library. // in both the main executable, and in the shared library.
import live 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, // This prelude is loaded in every v program compiled with -live,
// but only for the main executable. // but only for the main executable.
import live.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, // This prelude is loaded in every v program compiled with -live,
// but only for the shared library. // but only for the shared library.
import live.shared import live.shared
const (
no_warning_live_shared_is_used = shared.is_used
)

View File

@ -1,6 +1,7 @@
module main module main
import os import os
import term
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
// / This file will get compiled as part of the main program, // / This file will get compiled as part of the main program,
// / for a _test.v file. // / for a _test.v file.
@ -11,7 +12,7 @@ import os
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
// TODO copy pasta builtin.v fn ___print_assert_failure // TODO copy pasta builtin.v fn ___print_assert_failure
fn cb_assertion_failed(i &VAssertMetaInfo) { 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') { use_relative_paths := match os.getenv('VERROR_PATHS') {
'absolute' { 'absolute' {
false 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_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('')
eprintln('$final_filename:${i.line_nr+1}: failed assert in ${final_funcname}') eprintln('$final_filename:${i.line_nr+1}: failed assert in function ${final_funcname}')
eprintln('Source : ${i.src}') eprintln('Source : `${final_src}`')
if i.op.len > 0 && i.op != 'call' { if i.op.len > 0 && i.op != 'call' {
eprintln(' left value: ${i.llabel} = ${i.lvalue}') mut slvalue := '${i.lvalue}'
if i.rlabel == i.rvalue { mut srvalue := '${i.rvalue}'
eprintln(' right value: $i.rlabel') lpostfix := if slvalue == i.llabel { '.' } else { '<= `${i.llabel}`' }
} rpostfix := if srvalue == i.rlabel { '.' } else { '<= `${i.rlabel}`' }
else { if use_color {
eprintln(' right value: ${i.rlabel} = ${i.rvalue}') 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 // Called before each test_ function, defined in file_test.v
fn (mut b BenchedTests) testing_step_start(stepfunc string) { 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.oks = C.g_test_oks
b.fails = C.g_test_fails b.fails = C.g_test_fails
b.bench.step() b.bench.step()

View File

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

View File

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

View File

@ -168,22 +168,22 @@ pub fn v_realloc(b byteptr, n int) byteptr {
if ptr == 0 { if ptr == 0 {
panic('realloc($n) failed') panic('realloc($n) failed')
} }
return ptr return ptr
} }
[unsafe_fn]
pub fn v_calloc(n int) byteptr { 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 { pub fn vcalloc(n int) byteptr {
if n < 0 { if n < 0 {
panic('calloc(<=0)') panic('calloc(<=0)')
} else if n == 0 { } else if n == 0 {
return byteptr(0) return byteptr(0)
} else {
return C.calloc(n, 1)
} }
return C.calloc(1, n)
} }
[unsafe_fn] [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 // VAssertMetaInfo is used during assertions. An instance of it
// is filled in by compile time generated code, when an assertion fails. // is filled in by compile time generated code, when an assertion fails.
struct VAssertMetaInfo { pub struct VAssertMetaInfo {
pub: pub:
fpath string // the source file path of the assertion fpath string // the source file path of the assertion
line_nr int // the line number 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) m12['test'] = f64(0.0)
assert m12['test'] == f64(0.0) assert m12['test'] == f64(0.0)
mut m13 := map[string]rune //mut m13 := map[string]rune
m13['test'] = rune(0) //m13['test'] = rune(0)
assert m13['test'] == rune(0) //assert m13['test'] == rune(0)
mut m14 := map[string]voidptr mut m14 := map[string]voidptr
m14['test'] = voidptr(0) 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 { 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 // 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 { $if windows {
return // TODO 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 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' assert 'foo'.map(foo) == r'\ee'
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,3 @@
module main
import net.ftp import net.ftp
// NB: this function makes network calls to external servers, // 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 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 { pub fn (mut b Builder) cut_last(n int) string {
buf := b.buf[b.len-n..] res := bytes2string( b.buf[b.len-n..] )
s := string(buf.clone())
b.buf.trim(b.buf.len-n) b.buf.trim(b.buf.len-n)
b.len -= n b.len -= n
return s return res
} }
/* /*
pub fn (mut b Builder) cut_to(pos int) string { pub fn (mut b Builder) cut_to(pos int) string {
buf := b.buf[pos..] res := bytes2string( b.buf[pos..] )
s := string(buf.clone())
b.buf.trim(pos) b.buf.trim(pos)
b.len = 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 { if n > b.len {
return '' return ''
} }
buf := b.buf[b.len-n..] return bytes2string( b.buf[b.len-n..] )
return string(buf.clone())
} }
// buf == 'hello world' // buf == 'hello world'
@ -98,10 +102,7 @@ pub fn (b &Builder) after(n int) string {
if n >= b.len { if n >= b.len {
return '' return ''
} }
buf := b.buf[n..] return bytes2string( b.buf[n..] )
mut copy := buf.clone()
copy << `\0`
return string(copy)
} }
// NB: in order to avoid memleaks and additional memory copies, after a call to b.str(), // 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('!')
sb.write('hello') sb.write('hello')
assert sb.len == 8 assert sb.len == 8
assert sb.str() == 'hi!hello' sb_end := sb.str()
assert sb_end == 'hi!hello'
assert sb.len == 0 assert sb.len == 0
/// ///
sb = strings.new_builder(10) sb = strings.new_builder(10)
@ -19,39 +20,33 @@ fn test_sb() {
// TODO msvc bug // TODO msvc bug
sb = strings.new_builder(10) sb = strings.new_builder(10)
sb.write('123456') sb.write('123456')
assert sb.cut_last(2) == '56' last_2 := sb.cut_last(2)
assert sb.str() == '1234' 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 ( const (
n = 100000 maxn = 100000
) )
fn test_big_sb() { fn test_big_sb() {
mut sb := strings.new_builder(100) mut sb := strings.new_builder(100)
mut sb2 := strings.new_builder(10000) mut sb2 := strings.new_builder(10000)
for i in 0..n { for i in 0..maxn {
sb.writeln(i.str()) sb.writeln(i.str())
sb2.write('+') sb2.write('+')
} }
s := sb.str() s := sb.str()
lines := s.split_into_lines() lines := s.split_into_lines()
assert lines.len == n assert lines.len == maxn
assert lines[0] == '0' assert lines[0] == '0'
assert lines[1] == '1' assert lines[1] == '1'
assert lines[777] == '777' assert lines[777] == '777'
assert lines[98765] == '98765' assert lines[98765] == '98765'
println(sb2.len) println(sb2.len)
assert sb2.len == n assert sb2.len == maxn
} }
@ -64,5 +59,6 @@ fn test_byte_write() {
count++ count++
assert count == sb.len 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 in "MMM D HH:MM" format (24h) for date of current year
// - a date string formatted with format function for other dates // - a date string formatted with format function for other dates
pub fn (t Time) clean() string { pub fn (t Time) clean() string {
now := time.now() znow := time.now()
// Today // 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) return t.get_fmt_time_str(.hhmm24)
} }
// This year // This year
if t.year == now.year { if t.year == znow.year {
return t.get_fmt_str(.space, .hhmm24, .mmmd) return t.get_fmt_str(.space, .hhmm24, .mmmd)
} }
return t.format() 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 in "MMM D HH:MM" format (12h) for date of current year
// - a date string formatted with format function for other dates // - a date string formatted with format function for other dates
pub fn (t Time) clean12() string { pub fn (t Time) clean12() string {
now := time.now() znow := time.now()
// Today // 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) return t.get_fmt_time_str(.hhmm12)
} }
// This year // This year
if t.year == now.year { if t.year == znow.year {
return t.get_fmt_str(.space, .hhmm12, .mmmd) return t.get_fmt_str(.space, .hhmm12, .mmmd)
} }
return t.format() return t.format()

View File

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

View File

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

View File

@ -204,7 +204,7 @@ pub fn (v Builder) get_user_files() []string {
if line[0] == `/` && line[1] == `/` { if line[0] == `/` && line[1] == `/` {
continue continue
} }
if line.starts_with('module ') && !line.starts_with('module main') { if line.starts_with('module ') {
is_internal_module_test = true is_internal_module_test = true
break 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) { pub fn (mut c Checker) check_files(ast_files []ast.File) {
mut has_main_mod_file := false mut has_main_mod_file := false
mut has_main_fn := 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) c.check(file)
if file.mod.name == 'main' { if file.mod.name == 'main' {
files_from_main_module << file
has_main_mod_file = true has_main_mod_file = true
if c.check_file_in_main(file) { if c.check_file_in_main(file) {
has_main_fn = true 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 // Make sure fn main is defined in non lib builds
if c.pref.build_mode == .build_module || c.pref.is_test { if c.pref.build_mode == .build_module || c.pref.is_test {
return return
@ -159,7 +177,7 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool {
} }
} }
ast.FnDecl { ast.FnDecl {
if stmt.name == 'main' { if stmt.name == 'main.main' {
has_main_fn = true has_main_fn = true
if stmt.is_pub { if stmt.is_pub {
c.error('function `main` cannot be declared public', stmt.pos) 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) { 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) c.error('$identifier `$name` cannot start with `_`', pos)
} }
if util.contains_capital(name) { 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) { fn (mut c Checker) check_valid_pascal_case(name, identifier string, pos token.Position) {
stripped_name := stripped_name(name) sname := stripped_name(name)
if !stripped_name[0].is_capital() { if !sname[0].is_capital() {
c.error('$identifier `$name` must begin with capital letter', pos) 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 return table.string_type
} }
if call_expr.generic_type.has_flag(.generic) { 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 // Need to prepend the module when adding a generic type to a function
// `fn_gen_types['mymod.myfn'] == ['string', 'int']` // `fn_gen_types['mymod.myfn'] == ['string', 'int']`
c.table.register_fn_gen_type(c.mod + '.' + fn_name, c.cur_generic_type) 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 call_expr.return_type = ret_type
return 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 f := table.Fn{}
mut found := false mut found := false
mut found_in_args := false mut found_in_args := false
// try prefix with current module as it would have never gotten prefixed // 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' name_prefixed := '${call_expr.mod}.$fn_name'
if f1 := c.table.find_fn(name_prefixed) { if f1 := c.table.find_fn(name_prefixed) {
call_expr.name = 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) c.error('unknown function: $fn_name', call_expr.pos)
return table.void_type 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) scope := c.file.scope.innermost(call_expr.pos.pos)
if _ := scope.find_var(fn_name) { if _ := scope.find_var(fn_name) {
c.error('ambiguous call to: `$fn_name`, may refer to fn `$fn_name` or variable `$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) { pub fn (mut c Checker) check_or_expr(mut or_expr ast.OrExpr, ret_type table.Type) {
if or_expr.kind == .propagate { 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', c.error('to propagate the optional call, `$c.cur_fn.name` must itself return an optional',
or_expr.pos) 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 { // scope.find(it.name) or {
// c.error('undefined ident: `$it.name`', array_init.pos) // 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 obj := c.file.global_scope.find_const(full_const_name) {
if cint := const_int_value(obj) { if cint := const_int_value(obj) {
fixed_size = cint fixed_size = cint
@ -2177,7 +2195,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
// TODO: move this // TODO: move this
if c.const_deps.len > 0 { if c.const_deps.len > 0 {
mut name := ident.name mut name := ident.name
if !name.contains('.') && ident.mod !in ['builtin', 'main'] { if !name.contains('.') && ident.mod !in ['builtin'] {
name = '${ident.mod}.$ident.name' name = '${ident.mod}.$ident.name'
} }
if name == c.const_decl { 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 // prepend mod to look for fn call or const
mut name := ident.name mut name := ident.name
if !name.contains('.') && ident.mod !in ['builtin', 'main'] { if !name.contains('.') && ident.mod !in ['builtin'] {
name = '${ident.mod}.$ident.name' name = '${ident.mod}.$ident.name'
} }
if obj := c.file.global_scope.find(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 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) c.error('undefined ident: `$ident.name`', ident.pos)
} }
if c.table.known_type(ident.name) { if c.table.known_type(ident.name) {

View File

@ -7,6 +7,7 @@ import v.ast
import v.table import v.table
import v.token import v.token
import strings import strings
import v.util
const ( 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', 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(' {') f.writeln(' {')
match it.kind as k { match it.kind as k {
.insert { .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 { .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 { for i, col in it.updated_columns {
f.write('$col = ') f.write('$col = ')
f.expr(it.update_exprs[i]) f.expr(it.update_exprs[i])
@ -888,6 +889,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.write(node.field_name) f.write(node.field_name)
} }
ast.SizeOf { ast.SizeOf {
if node.is_type {
f.write('sizeof(') f.write('sizeof(')
if node.type_name != '' { if node.type_name != '' {
f.write(node.type_name) f.write(node.type_name)
@ -895,6 +897,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.write(f.type_to_str(node.typ)) f.write(f.type_to_str(node.typ))
} }
f.write(')') f.write(')')
} else {
f.write('sizeof(')
f.expr(node.expr)
f.write(')')
}
} }
ast.SqlExpr { ast.SqlExpr {
// sql app.db { select from Contributor where repo == id && user == 0 } // 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(' ')
} }
} }
f.write('from $node.table_name') f.write('from ${util.strip_mod_name(node.table_name)}')
f.write(' ') f.write(' ')
if node.has_where { if node.has_where {
f.write('where ') f.write('where ')

View File

@ -83,7 +83,6 @@ mut:
attrs []string // attributes before next decl stmt attrs []string // attributes before next decl stmt
is_builtin_mod bool is_builtin_mod bool
hotcode_fn_names []string 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_fn &ast.FnDecl = 0
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()` cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
sql_i int sql_i int
@ -93,6 +92,7 @@ mut:
inside_return bool inside_return bool
strs_to_free string strs_to_free string
inside_call bool inside_call bool
has_main bool
} }
const ( const (
@ -126,8 +126,6 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
table: table table: table
pref: pref pref: pref
fn_decl: 0 fn_decl: 0
fn_main: 0
cur_fn: 0
autofree: true autofree: true
indent: -1 indent: -1
module_built: pref.path.after('vlib/') 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 { if g.pref.is_livemain || g.pref.is_liveshared {
g.generate_hotcode_reloader_code() g.generate_hotcode_reloader_code()
} }
if g.fn_main != voidptr(0) { if !g.pref.is_test {
g.out.writeln('') g.gen_c_main()
g.fn_decl = g.fn_main
g.gen_fn_decl(g.fn_main)
} }
} }
@ -301,12 +297,12 @@ pub fn (mut g Gen) write_typeof_functions() {
tidx := g.table.find_type_idx(typ.name) tidx := g.table.find_type_idx(typ.name)
g.writeln('char * v_typeof_sumtype_${tidx}(int sidx) { /* $typ.name */ ') g.writeln('char * v_typeof_sumtype_${tidx}(int sidx) { /* $typ.name */ ')
g.writeln(' switch(sidx) {') 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 { for v in sum_info.variants {
subtype := g.table.get_type_symbol(v) 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(' }')
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.typedefs2.writeln('typedef struct $styp $styp;')
g.options.write(g.optional_type_text(styp, base)) g.options.write(g.optional_type_text(styp, base))
g.options.writeln(';\n') g.options.writeln(';\n')
g.optionals << styp g.optionals << styp.clone()
} }
return styp 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: // i.e. it's always just Cat, not Cat_ptr:
fn (g &Gen) cc_type(t table.Type) string { fn (g &Gen) cc_type(t table.Type) string {
sym := g.table.get_type_symbol(g.unwrap_generic(t)) 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_ { if sym.kind == .struct_ {
info := sym.info as table.Struct info := sym.info as table.Struct
if info.generic_types.len > 0 { if info.generic_types.len > 0 {
@ -436,21 +432,20 @@ typedef struct {
match typ.kind { match typ.kind {
.alias { .alias {
parent := &g.table.types[typ.parent_idx] 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] == `.` 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;') g.type_definitions.writeln('typedef $parent_styp $styp;')
} }
.array { .array {
styp := typ.name.replace('.', '__') styp := util.no_dots(typ.name)
g.type_definitions.writeln('typedef array $styp;') g.type_definitions.writeln('typedef array $styp;')
} }
.interface_ { .interface_ {
g.type_definitions.writeln('typedef _Interface ${c_name(typ.name)};') g.type_definitions.writeln('typedef _Interface ${c_name(typ.name)};')
} }
.map { .map {
styp := typ.name.replace('.', '__') styp := util.no_dots(typ.name)
g.type_definitions.writeln('typedef map $styp;') g.type_definitions.writeln('typedef map $styp;')
} }
.function { .function {
@ -462,7 +457,7 @@ typedef struct {
not_anon := !info.is_anon not_anon := !info.is_anon
if !info.has_decl && !is_multi && (not_anon || is_fn_sig) { if !info.has_decl && !is_multi && (not_anon || is_fn_sig) {
fn_name := if func.language == .c { fn_name := if func.language == .c {
func.name.replace('.', '__') util.no_dots(func.name)
} else if info.is_anon { } else if info.is_anon {
typ.name typ.name
} else { } else {
@ -492,7 +487,7 @@ pub fn (mut g Gen) write_multi_return_types() {
if typ.kind != .multi_return { if typ.kind != .multi_return {
continue continue
} }
name := typ.name.replace('.', '__') name := util.no_dots(typ.name)
info := typ.info as table.MultiReturn info := typ.info as table.MultiReturn
g.type_definitions.writeln('typedef struct {') g.type_definitions.writeln('typedef struct {')
// TODO copy pasta StructDecl // TODO copy pasta StructDecl
@ -643,7 +638,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.defer_stmts << defer_stmt g.defer_stmts << defer_stmt
} }
ast.EnumDecl { ast.EnumDecl {
enum_name := node.name.replace('.', '__') enum_name := util.no_dots(node.name)
g.enum_typedefs.writeln('typedef enum {') g.enum_typedefs.writeln('typedef enum {')
mut cur_enum_expr := '' mut cur_enum_expr := ''
mut cur_enum_offset := 0 mut cur_enum_offset := 0
@ -689,11 +684,10 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
} }
keep_fn_decl := g.fn_decl keep_fn_decl := g.fn_decl
g.fn_decl = node // &it g.fn_decl = node
if node.name == 'main' { if node.name == 'main.main' {
// just remember `it`; main code will be generated in finish() g.has_main = true
g.fn_main = node }
} else {
if node.name == 'backtrace' || if node.name == 'backtrace' ||
node.name == 'backtrace_symbols' || node.name == 'backtrace_symbols' ||
node.name == 'backtrace_symbols_fd' { node.name == 'backtrace_symbols_fd' {
@ -705,7 +699,6 @@ fn (mut g Gen) stmt(node ast.Stmt) {
node.name == 'backtrace_symbols_fd' { node.name == 'backtrace_symbols_fd' {
g.write('\n#endif\n') g.write('\n#endif\n')
} }
}
g.fn_decl = keep_fn_decl g.fn_decl = keep_fn_decl
if skip { if skip {
g.out.go_back_to(pos) g.out.go_back_to(pos)
@ -786,7 +779,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.sql_stmt(node) g.sql_stmt(node)
} }
ast.StructDecl { 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 {') // g.writeln('typedef struct {')
// for field in it.fields { // for field in it.fields {
// field_type_sym := g.table.get_type_symbol(field.typ) // 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.writeln(' g_test_oks++;') g.writeln(' g_test_oks++;')
metaname_ok := g.gen_assert_metainfo(a) 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('} else {')
g.writeln(' g_test_fails++;') g.writeln(' g_test_fails++;')
metaname_fail := g.gen_assert_metainfo(a) 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(' longjmp(g_jump_buffer, 1);')
g.writeln(' // TODO') g.writeln(' // TODO')
g.writeln(' // Maybe print all vars in a test function if it fails?') g.writeln(' // Maybe print all vars in a test function if it fails?')
@ -1605,7 +1598,11 @@ fn (mut g Gen) expr(node ast.Expr) {
// Only used in IndexExpr // Only used in IndexExpr
} }
ast.SizeOf { ast.SizeOf {
if node.is_type {
mut styp := node.type_name mut styp := node.type_name
if styp.starts_with('C.') {
styp = styp[2..]
}
if node.type_name == '' { if node.type_name == '' {
styp = g.typ(node.typ) styp = g.typ(node.typ)
} else { } else {
@ -1617,12 +1614,12 @@ fn (mut g Gen) expr(node ast.Expr) {
} }
} }
} }
/* g.write('/*SizeOfType*/ sizeof(${util.no_dots(styp)})')
if styp.starts_with('C__') { } else {
styp = styp[3..] g.write('/*SizeOfVar*/ sizeof(')
g.expr(node.expr)
g.write(')')
} }
*/
g.write('sizeof($styp)')
} }
ast.SqlExpr { ast.SqlExpr {
g.sql_select_expr(node) g.sql_select_expr(node)
@ -1685,7 +1682,7 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
} else if sym.kind == .array_fixed { } else if sym.kind == .array_fixed {
fixed_info := sym.info as table.ArrayFixed fixed_info := sym.info as table.ArrayFixed
typ_name := g.table.get_type_name(fixed_info.elem_type) 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 { } else if sym.kind == .function {
info := sym.info as table.FnType info := sym.info as table.FnType
fn_info := info.func fn_info := info.func
@ -1694,15 +1691,15 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
if i > 0 { if i > 0 {
repr += ', ' repr += ', '
} }
repr += g.table.get_type_name(arg.typ) repr += util.strip_main_name(g.table.get_type_name(arg.typ))
} }
repr += ')' repr += ')'
if fn_info.return_type != table.void_type { 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")') g.write('tos_lit("$repr")')
} else { } 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 return
} }
if node.name.starts_with('C.') { if node.name.starts_with('C.') {
g.write(node.name[2..].replace('.', '__')) g.write(util.no_dots(node.name[2..]))
return return
} }
if node.kind == .constant { // && !node.name.starts_with('g_') { 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) { 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 { if node.exprs.len > 0 {
// skip `retun $vweb.html()` // skip `retun $vweb.html()`
if node.exprs[0] is ast.ComptimeCall { 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 { for mod_name in g.table.imports {
init_fn_name := '${mod_name}.init' init_fn_name := '${mod_name}.init'
if _ := g.table.find_fn(init_fn_name) { 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' init_fn_c_name := '${mod_c_name}__init'
g.writeln('\t${init_fn_c_name}();') g.writeln('\t${init_fn_c_name}();')
} }
@ -2832,7 +2825,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
continue continue
} }
// sym := g.table.get_type_symbol(typ) // sym := g.table.get_type_symbol(typ)
mut name := typ.name.replace('.', '__') mut name := util.no_dots(typ.name)
match typ.info as info { match typ.info as info {
table.Struct { table.Struct {
if info.generic_types.len > 0 { if info.generic_types.len > 0 {
@ -2896,7 +2889,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
} }
table.ArrayFixed { table.ArrayFixed {
// .array_fixed { // .array_fixed {
styp := typ.name.replace('.', '__') styp := util.no_dots(typ.name)
// array_fixed_char_300 => char x[300] // array_fixed_char_300 => char x[300]
mut fixed := styp[12..] mut fixed := styp[12..]
len := styp.after('_') len := styp.after('_')
@ -3073,12 +3066,12 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
} }
ast.Ident { ast.Ident {
if it.kind == .function { if it.kind == .function {
g.write('${it.name}(it)') g.write('${c_name(it.name)}(it)')
} else if it.kind == .variable { } else if it.kind == .variable {
var_info := it.var_info() var_info := it.var_info()
sym := g.table.get_type_symbol(var_info.typ) sym := g.table.get_type_symbol(var_info.typ)
if sym.kind == .function { if sym.kind == .function {
g.write('${it.name}(it)') g.write('${c_name(it.name)}(it)')
} else { } else {
g.expr(node.args[0].expr) g.expr(node.args[0].expr)
} }
@ -3125,12 +3118,12 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) {
} }
ast.Ident { ast.Ident {
if it.kind == .function { if it.kind == .function {
g.write('${it.name}(it)') g.write('${c_name(it.name)}(it)')
} else if it.kind == .variable { } else if it.kind == .variable {
var_info := it.var_info() var_info := it.var_info()
sym_t := g.table.get_type_symbol(var_info.typ) sym_t := g.table.get_type_symbol(var_info.typ)
if sym_t.kind == .function { if sym_t.kind == .function {
g.write('${it.name}(it)') g.write('${c_name(it.name)}(it)')
} else { } else {
g.expr(node.args[0].expr) 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) g.stmts(stmts)
} }
} else if or_block.kind == .propagate { } 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) }` // In main(), an `opt()?` call is sugar for `opt() or { panic(err) }`
if g.pref.is_debug { if g.pref.is_debug {
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) 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] [inline]
fn c_name(name_ string) string { fn c_name(name_ string) string {
name := name_.replace('.', '__') name := util.no_dots(name_)
if name in c_reserved { if name in c_reserved {
return 'v_$name' return 'v_$name'
} }
@ -3482,7 +3475,7 @@ fn (g Gen) type_default(typ table.Type) string {
sym := g.table.get_type_symbol(typ) sym := g.table.get_type_symbol(typ)
if sym.kind == .array { if sym.kind == .array {
elem_sym := g.typ(sym.array_info().elem_type) 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__') { if elem_type_str.starts_with('C__') {
elem_type_str = elem_type_str[3..] elem_type_str = elem_type_str[3..]
} }
@ -3562,21 +3555,21 @@ pub fn (mut g Gen) write_tests_main() {
g.writeln('') g.writeln('')
all_tfuncs := g.get_all_test_function_names() all_tfuncs := g.get_all_test_function_names()
if g.pref.is_stats { 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 { for t in all_tfuncs {
g.writeln('') g.writeln('')
if g.pref.is_stats { 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}();') g.writeln('\tif (!setjmp(g_jump_buffer)) ${t}();')
if g.pref.is_stats { if g.pref.is_stats {
g.writeln('\tBenchedTests_testing_step_end(&bt);') g.writeln('\tmain__BenchedTests_testing_step_end(&bt);')
} }
} }
g.writeln('') g.writeln('')
if g.pref.is_stats { if g.pref.is_stats {
g.writeln('\tBenchedTests_end_testing(&bt);') g.writeln('\tmain__BenchedTests_end_testing(&bt);')
} }
g.writeln('') g.writeln('')
if g.autofree { if g.autofree {
@ -3628,7 +3621,7 @@ fn (g Gen) get_all_test_function_names() []string {
} }
mut all_tfuncs_c := []string{} mut all_tfuncs_c := []string{}
for f in all_tfuncs { for f in all_tfuncs {
all_tfuncs_c << f.replace('.', '__') all_tfuncs_c << util.no_dots(f)
} }
return all_tfuncs_c return all_tfuncs_c
} }
@ -3640,12 +3633,12 @@ fn (g Gen) is_importing_os() bool {
fn (mut g Gen) go_stmt(node ast.GoStmt) { fn (mut g Gen) go_stmt(node ast.GoStmt) {
tmp := g.new_tmp_var() tmp := g.new_tmp_var()
expr := node.call_expr as ast.CallExpr 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 { if expr.is_method {
receiver_sym := g.table.get_type_symbol(expr.receiver_type) receiver_sym := g.table.get_type_symbol(expr.receiver_type)
name = receiver_sym.name + '_' + name name = receiver_sym.name + '_' + name
} }
name = name.replace('.', '__') name = util.no_dots(name)
g.writeln('// go') g.writeln('// go')
wrapper_struct_name := 'thread_arg_' + name wrapper_struct_name := 'thread_arg_' + name
wrapper_fn_name := name + '_thread_wrapper' wrapper_fn_name := name + '_thread_wrapper'
@ -3750,7 +3743,7 @@ fn (mut g Gen) is_expr(node ast.InfixExpr) {
// `_Animal_Dog_index` // `_Animal_Dog_index`
sub_type := node.right as ast.Type sub_type := node.right as ast.Type
sub_sym := g.table.get_type_symbol(sub_type.typ) 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 return
} else if sym.kind == .sum_type { } else if sym.kind == .sum_type {
g.write('typ $eq ') 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) { 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.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('string ${str_fn_name}($styp it) { /* gen_str_for_enum */')
g.auto_str_funcs.writeln('\tswitch(it) {') 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 deref_typ := styp
g.auto_str_funcs.writeln('\t$deref_typ *it = &x;') 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 // generate ident / indent length = 4 spaces
g.auto_str_funcs.writeln('\tstring indents = tos_lit("");') g.auto_str_funcs.writeln('\tstring indents = tos_lit("");')
g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; ++i) {') 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.ast
import v.table import v.table
import v.util
fn (g &Gen) comptime_call(node ast.ComptimeCall) { fn (g &Gen) comptime_call(node ast.ComptimeCall) {
if node.is_vweb { if node.is_vweb {
@ -12,7 +13,7 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) {
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
fn_decl := stmt as ast.FnDecl fn_decl := stmt as ast.FnDecl
// insert stmts from vweb_tmpl fn // 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.inside_vweb_tmpl = true
g.stmts(fn_decl.stmts) g.stmts(fn_decl.stmts)
g.inside_vweb_tmpl = false g.inside_vweb_tmpl = false
@ -40,7 +41,7 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) {
g.write(' else ') g.write(' else ')
} }
g.write('if (string_eq($node.method_name, tos_lit("$method.name"))) ') 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.expr(node.left)
g.writeln(');') g.writeln(');')
j++ j++
@ -66,5 +67,5 @@ fn (mut g Gen) comp_if(it ast.CompIf) {
g.stmts(it.else_stmts) g.stmts(it.else_stmts)
g.defer_ifdef = '' 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') { // if g.fileis('vweb.v') {
// println('\ngen_fn_decl() $it.name $it.is_generic $g.cur_generic_type') // 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 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 // loop thru each generic type and generate a function
for gen_type in g.table.fn_gen_types[it.name] { 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 g.cur_generic_type = 0
return return
} }
if is_main && g.pref.is_liveshared {
return
}
fn_start_pos := g.out.len fn_start_pos := g.out.len
msvc_attrs := g.write_fn_attrs() msvc_attrs := g.write_fn_attrs()
// Live // Live
@ -49,22 +40,6 @@ 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 .') 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
}
} else {
mut name := it.name mut name := it.name
if name[0] in [`+`, `-`, `*`, `/`, `%`] { if name[0] in [`+`, `-`, `*`, `/`, `%`] {
name = util.replace_op(name) name = util.replace_op(name)
@ -73,7 +48,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name
} }
if it.language == .c { if it.language == .c {
name = name.replace('.', '__') name = util.no_dots(name)
} else { } else {
name = c_name(name) name = c_name(name)
} }
@ -82,7 +57,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
// foo<T>() => foo_int(), foo_string() etc // foo<T>() => foo_int(), foo_string() etc
gen_name := g.typ(g.cur_generic_type) gen_name := g.typ(g.cur_generic_type)
name += '_' + gen_name name += '_' + gen_name
// type_name = type_name.replace('T', gen_name) type_name = type_name.replace('T', gen_name)
} }
// if g.pref.show_cc && it.is_builtin { // if g.pref.show_cc && it.is_builtin {
// println(name) // println(name)
@ -97,7 +72,10 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
if is_livemain { if is_livemain {
g.hotcode_fn_names << name g.hotcode_fn_names << name
} }
mut impl_fn_name := if is_live_wrap { 'impl_live_$name' } else { name } mut impl_fn_name := name
if is_live_wrap {
impl_fn_name = 'impl_live_${name}'
}
g.last_fn_c_name = impl_fn_name g.last_fn_c_name = impl_fn_name
// //
if is_live_wrap { if is_live_wrap {
@ -135,16 +113,15 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
// an early exit, which will leave the mutex locked. // an early exit, which will leave the mutex locked.
mut fn_args_list := []string{} mut fn_args_list := []string{}
for ia, fa in fargs { for ia, fa in fargs {
fn_args_list << '${fargtypes[ia]} $fa' fn_args_list << '${fargtypes[ia]} ${fa}'
} }
mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');' mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');'
mut live_fnreturn := '' mut live_fnreturn := ''
if type_name != 'void' { if type_name != 'void' {
live_fncall = '$type_name res = $live_fncall' live_fncall = '${type_name} res = ${live_fncall}'
live_fnreturn = 'return res;' live_fnreturn = 'return res;'
} }
g.definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') + 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('$type_name ${name}(' + fn_args_list.join(', ') +
'){') '){')
g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);') g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);')
@ -153,61 +130,18 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
g.hotcode_definitions.writeln(' $live_fnreturn') g.hotcode_definitions.writeln(' $live_fnreturn')
g.hotcode_definitions.writeln('}') g.hotcode_definitions.writeln('}')
} }
}
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.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);')
}
}
}
if g.pref.is_livemain && is_main {
g.generate_hotcode_reloading_main_caller()
}
// Profiling mode? Start counting at the beginning of the function (save current time). // Profiling mode? Start counting at the beginning of the function (save current time).
if g.pref.is_prof { if g.pref.is_prof {
g.profile_fn(it.name, is_main) g.profile_fn(it.name)
}
if is_main {
g.indent++
} }
g.stmts(it.stmts) 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() 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 // TODO: remove this, when g.write_autofree_stmts_when_needed works properly
g.writeln(g.autofree_scope_vars(it.body_pos.pos)) g.writeln(g.autofree_scope_vars(it.body_pos.pos))
} }
if is_main {
g.writeln('\treturn 0;')
}
g.writeln('}') g.writeln('}')
g.defer_stmts = [] g.defer_stmts = []
if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list { 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) caname := c_name(arg.name)
typ := g.unwrap_generic(arg.typ) typ := g.unwrap_generic(arg.typ)
arg_type_sym := g.table.get_type_symbol(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' { // if arg.name == 'xxx' {
// println('xxx arg type= ' + arg_type_name) // 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.cc_type(node.receiver_type)
// mut receiver_type_name := g.typ(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)) 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_ { if typ_sym.kind == .interface_ {
// Speaker_name_table[s._interface_idx].speak(s._object) // Speaker_name_table[s._interface_idx].speak(s._object)
g.write('${c_name(receiver_type_name)}_name_table[') 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*)') 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() // Check if expression is: arr[a..b].clone(), arr[a..].clone()
// if so, then instead of calling array_clone(&array_slice(...)) // if so, then instead of calling array_clone(&array_slice(...))
// call array_clone_static(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 { if idx is ast.RangeExpr {
// expr is arr[range].clone() // expr is arr[range].clone()
// use array_clone_static instead of array_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 is_range_slice = true
} }
} }
@ -507,13 +441,13 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
if node.language == .c { if node.language == .c {
// Skip "C." // Skip "C."
g.is_c_call = true g.is_c_call = true
name = name[2..].replace('.', '__') name = util.no_dots(name[2..])
} else { } else {
name = c_name(name) name = c_name(name)
} }
if is_json_encode { if is_json_encode {
// `json__encode` => `json__encode_User` // `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 { if node.generic_type != table.void_type && node.generic_type != 0 {
// `foo<int>()` => `foo_int()` // `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) { fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) {
// key_typ_sym := g.table.get_type_symbol(it.key_type) // key_typ_sym := g.table.get_type_symbol(it.key_type)
// value_typ_sym := g.table.get_type_symbol(it.value_type) // value_typ_sym := g.table.get_type_symbol(it.value_type)
// key_typ_str := key_typ_sym.name.replace('.', '__') // key_typ_str := util.no_dots(key_typ_sym.name)
// value_typ_str := value_typ_sym.name.replace('.', '__') // value_typ_str := util.no_dots(value_typ_sym.name)
if it.vals.len > 0 { if it.vals.len > 0 {
g.writeln('new Map([') g.writeln('new Map([')
g.inc_indent() g.inc_indent()

View File

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

View File

@ -6,12 +6,7 @@ pub struct ProfileCounterMeta{
vpc_calls string vpc_calls string
} }
fn (mut g Gen) profile_fn(fn_name string, is_main bool){ fn (mut g Gen) profile_fn(fn_name string){
if is_main {
g.writeln('')
g.writeln('\tatexit(vprint_profile_stats);')
g.writeln('')
}
if g.pref.profile_no_inline && 'inline' in g.attrs { if g.pref.profile_no_inline && 'inline' in g.attrs {
g.defer_profile_code = '' g.defer_profile_code = ''
return return
@ -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() { pub fn (mut g Gen) gen_vprint_profile_stats() {
g.pcs_declarations.writeln('void 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 == '-' { if g.pref.profile_file == '-' {
for pc_meta in g.pcs { 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}" );') 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 v.ast
import strings import strings
import v.table import v.table
import v.util
// pg,mysql etc // pg,mysql etc
const ( const (
@ -26,16 +27,16 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
g.writeln(';') g.writeln(';')
g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("') g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("')
if node.kind == .insert { if node.kind == .insert {
g.write('insert into `$node.table_name` (') g.write('INSERT INTO `${util.strip_mod_name(node.table_name)}` (')
} else { } else {
g.write('update `$node.table_name` set ') g.write('UPDATE `${util.strip_mod_name(node.table_name)}` SET ')
} }
if node.kind == .insert { if node.kind == .insert {
for i, field in node.fields { for i, field in node.fields {
if field.name == 'id' { if field.name == 'id' {
continue continue
} }
g.write(field.name) g.write('`${field.name}`')
if i < node.fields.len - 1 { if i < node.fields.len - 1 {
g.write(', ') g.write(', ')
} }
@ -59,7 +60,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
g.write(', ') g.write(', ')
} }
} }
g.write(' where ') g.write(' WHERE ')
} }
if node.kind == .update { if node.kind == .update {
g.expr_to_sql(node.where_expr) 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) cur_line := g.go_before_stmt(0)
mut q := 'select ' mut sql_query := 'SELECT '
if node.is_count { if node.is_count {
// `select count(*) from User` // `select count(*) from User`
q += 'count(*) from `$node.table_name`' sql_query += 'COUNT(*) FROM `${util.strip_mod_name(node.table_name)}` '
} else { } else {
// `select id, name, country from User` // `select id, name, country from User`
for i, field in node.fields { for i, field in node.fields {
q += '$field.name' sql_query += '`${field.name}`'
if i < node.fields.len - 1 { 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 { 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() g.sql_stmt_name = g.new_tmp_var()
db_name := g.new_tmp_var() db_name := g.new_tmp_var()
g.writeln('\n\t// sql select') 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.write('${dbtype}__DB $db_name = ') // $node.db_var_name;')
g.expr(node.db_expr) g.expr(node.db_expr)
g.writeln(';') 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(*(${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("$q') 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 { if node.has_where && node.where_expr is ast.InfixExpr {
g.expr_to_sql(node.where_expr) g.expr_to_sql(node.where_expr)
} }
g.write(' order by id ') g.write(' ORDER BY id ')
if node.has_limit { if node.has_limit {
g.write(' limit ') g.write(' LIMIT ')
g.expr_to_sql(node.limit_expr) g.expr_to_sql(node.limit_expr)
} }
if node.has_offset { if node.has_offset {
g.write(' offset ') g.write(' OFFSET ')
g.expr_to_sql(node.offset_expr) g.expr_to_sql(node.offset_expr)
} }
g.writeln('"));') 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) { fn (mut g Gen) fn_decl(node ast.FnDecl) {
println(term.green('\n$node.name:')) println(term.green('\n$node.name:'))
g.stack_var_pos = 0 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()') // println('saving addr $node.name $g.buf.len.hex2()')
if is_main { if is_main {
g.save_main_fn_addr() g.save_main_fn_addr()

View File

@ -121,7 +121,7 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
for stmt in file.stmts { for stmt in file.stmts {
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
fn_decl := stmt as 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) tmpl_scope := file.scope.innermost(fn_decl.body_pos.pos)
for _, obj in p.scope.objects { for _, obj in p.scope.objects {
if obj is ast.Var { 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>()` // In case of `foo<T>()`
// T is unwrapped and registered in the checker. // T is unwrapped and registered in the checker.
if !generic_type.has_flag(.generic) { 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) p.check(.lpar)
@ -237,6 +238,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_pub: is_pub is_pub: is_pub
is_deprecated: is_deprecated is_deprecated: is_deprecated
ctdefine: ctdefine ctdefine: ctdefine
mod: p.mod
}) })
} else { } else {
if language == .c { if language == .c {
@ -249,6 +251,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
if _ := p.table.find_fn(name) { if _ := p.table.find_fn(name) {
p.fn_redefinition_error(name) p.fn_redefinition_error(name)
} }
//p.warn('reg functn $name ()')
p.table.register_fn(table.Fn{ p.table.register_fn(table.Fn{
name: name name: name
args: args args: args
@ -372,7 +375,8 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
mut args := []table.Arg{} mut args := []table.Arg{}
mut is_variadic := false mut is_variadic := false
// `int, int, string` (no names, just types) // `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 p.peek_tok.kind == .rpar
// TODO copy pasta, merge 2 branches // TODO copy pasta, merge 2 branches
if types_only { if types_only {
@ -500,16 +504,13 @@ fn (mut p Parser) fn_redefinition_error(name string) {
} }
fn have_fn_main(stmts []ast.Stmt) bool { fn have_fn_main(stmts []ast.Stmt) bool {
mut has_main_fn := false
for stmt in stmts { for stmt in stmts {
match stmt { if stmt is ast.FnDecl {
ast.FnDecl { f := stmt as ast.FnDecl
if stmt.name == 'main' { if f.name == 'main.main' && f.mod == 'main' {
has_main_fn = true return true
} }
} }
else {}
} }
} return false
return has_main_fn
} }

View File

@ -12,7 +12,7 @@ fn (p &Parser) prepend_mod(name string) string {
if p.expr_mod != '' { if p.expr_mod != '' {
return p.expr_mod + '.' + name return p.expr_mod + '.' + name
} }
if p.builtin_mod || p.mod == 'main' { if p.builtin_mod {
return name return name
} }
return '${p.mod}.$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' name = '${p.imports[name]}.$p.tok.lit'
} else if p.expr_mod != '' { } else if p.expr_mod != '' {
name = p.expr_mod + '.' + name 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` // `Foo` in module `mod` means `mod.Foo`
name = p.mod + '.' + name name = p.mod + '.' + name
} }

View File

@ -148,16 +148,7 @@ fn (mut p Parser) parse() ast.File {
} }
for { for {
if p.tok.kind == .eof { 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 break
} }
// println('stmt at ' + p.tok.str()) // println('stmt at ' + p.tok.str())
@ -449,8 +440,8 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
stmts << p.stmt(false) stmts << p.stmt(false)
} }
return ast.FnDecl{ return ast.FnDecl{
name: 'main' name: 'main.main'
mod: p.mod mod: 'main'
stmts: stmts stmts: stmts
file: p.file_name file: p.file_name
return_type: table.void_type 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 { if p.tok.lit in ['r', 'c', 'js'] && p.peek_tok.kind == .string && !p.inside_str_interp {
return p.string_expr() return p.string_expr()
} }
mut known_var := false known_var := p.mark_var_as_used( p.tok.lit )
if obj := p.scope.find(p.tok.lit) {
match mut obj {
ast.Var {
known_var = true
obj.is_used = true
}
else {}
}
}
mut is_mod_cast := false mut is_mod_cast := false
if p.peek_tok.kind == .dot && !known_var && 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 { if language == .c {
mod = 'C' mod = 'C'
} else if language == .js { } 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' } pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' }
p.scanner.codegen(' p.scanner.codegen('
// //
$pubfn ( e &$name) has(flag $name) bool { return (int(*e) & (1 << int(flag))) != 0 } $pubfn ( e &$enum_name) has(flag $enum_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 $enum_name) set(flag $enum_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 $enum_name) clear(flag $enum_name) { unsafe{ *e = int(*e) & ~(1 << int(flag)) } }
$pubfn (mut e $name) toggle(flag $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 { .key_sizeof {
pos := p.tok.position()
p.next() // sizeof p.next() // sizeof
p.check(.lpar) p.check(.lpar)
sizeof_type := p.parse_type() is_known_var := p.mark_var_as_used( p.tok.lit )
if p.tok.lit == 'C' { if is_known_var {
p.next() expr := p.parse_ident(table.Language.v)
p.check(.dot)
node = ast.SizeOf{ node = ast.SizeOf{
type_name: p.check_name() is_type: false
typ: sizeof_type expr: expr
pos: pos
} }
} else { } else {
sizeof_type := p.parse_type()
node = ast.SizeOf{ node = ast.SizeOf{
is_type: true
typ: sizeof_type typ: sizeof_type
type_name: p.table.get_type_symbol(sizeof_type).name
pos: pos
} }
} }
p.check(.rpar) p.check(.rpar)

View File

@ -151,7 +151,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
} else if kind == .update { } else if kind == .update {
if !p.pref.is_fmt { if !p.pref.is_fmt {
// NB: in vfmt mode, v parses just a single file and table_name may not have been registered // 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) table_type = table.new_type(idx)
} }
p.check_sql_keyword('where') 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 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 { fn scan_kinds(text string) []token.Kind {
mut scanner := new_scanner(text, .skip_comments, false) mut scanner := new_scanner(text, .skip_comments, false)
mut token_kinds := []token.Kind{} mut token_kinds := []token.Kind{}
@ -66,14 +27,18 @@ fn test_scan() {
assert token_kinds[3] == .plus assert token_kinds[3] == .plus
assert token_kinds[4] == .number assert token_kinds[4] == .number
assert token_kinds[5] == .rpar assert token_kinds[5] == .rpar
// test number costants input format }
fn test_number_constant_input_format() {
mut c := 0xa0 mut c := 0xa0
assert c == 0xa0 assert c == 0xa0
c = 0b1001 c = 0b1001
assert c == 9 assert c == 9
c = 1000000 c = 1000000
assert c == 1000000 assert c == 1000000
// test float conversion and reading }
fn test_float_conversion_and_reading() {
d := 23000000e-3 d := 23000000e-3
assert int(d) == 23000 assert int(d) == 23000
mut e := 1.2E3 * -1e-1 mut e := 1.2E3 * -1e-1
@ -83,92 +48,62 @@ fn test_scan() {
assert 1.23e+10 == 1.23e10 assert 1.23e+10 == 1.23e10
assert 1.23e+10 == 1.23e0010 assert 1.23e+10 == 1.23e0010
assert (-1.23e+10) == (1.23e0010 * -1.0) 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() { fn test_reference_bools() {
content := @VMOD_FILE result := scan_kinds('true && false')
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')
assert result.len == 3 assert result.len == 3
assert result[0] == .key_true assert result[0] == .key_true
assert result[1] == .and assert result[1] == .and
assert result[2] == .key_false assert result[2] == .key_false
}
result = scan_kinds('&foo') fn test_reference_var() {
result := scan_kinds('&foo')
assert result.len == 2 assert result.len == 2
assert result[0] == .amp assert result[0] == .amp
assert result[1] == .name assert result[1] == .name
}
result = scan_kinds('[]&foo') fn test_array_of_references() {
result := scan_kinds('[]&foo')
assert result.len == 4 assert result.len == 4
assert result[0] == .lsbr assert result[0] == .lsbr
assert result[1] == .rsbr assert result[1] == .rsbr
assert result[2] == .amp assert result[2] == .amp
assert result[3] == .name assert result[3] == .name
}
result = scan_kinds('&[]&foo') fn test_ref_array_of_references() {
result := scan_kinds('&[]&foo')
assert result.len == 5 assert result.len == 5
assert result[0] == .amp assert result[0] == .amp
assert result[1] == .lsbr assert result[1] == .lsbr
assert result[2] == .rsbr assert result[2] == .rsbr
assert result[3] == .amp assert result[3] == .amp
assert result[4] == .name assert result[4] == .name
}
result = scan_kinds('&&foo') fn test_ref_ref_foo() {
result := scan_kinds('&&foo')
assert result.len == 3 assert result.len == 3
assert result[0] == .amp assert result[0] == .amp
assert result[1] == .amp assert result[1] == .amp
assert result[2] == .name 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.len == 5
assert result[0] == .lsbr assert result[0] == .lsbr
assert result[1] == .rsbr assert result[1] == .rsbr
assert result[2] == .amp assert result[2] == .amp
assert result[3] == .amp assert result[3] == .amp
assert result[4] == .name 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.len == 7
assert result[0] == .amp assert result[0] == .amp
assert result[1] == .amp assert result[1] == .amp

View File

@ -113,6 +113,7 @@ pub fn (mut t Table) register_fn(new_fn Fn) {
} }
pub fn (mut t TypeSymbol) register_method(new_fn 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 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' // 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 mut path := filepath
verror_paths_override := os.getenv('VERROR_PATHS') verror_paths_override := os.getenv('VERROR_PATHS')
if verror_paths_override == 'absolute' { 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('.', '__')
}