Compare commits
33 Commits
5b16d5fdf4
...
88d69dca77
Author | SHA1 | Date |
---|---|---|
Jef Roosens | 88d69dca77 | |
Delyan Angelov | b4b58d7d7d | |
yuyi | 04f8b0cb1e | |
Larpon | e22c3d2a64 | |
yuyi | eb674411c7 | |
Delyan Angelov | 048431f586 | |
Ben | 824c7e42f3 | |
spaceface | 086d1ded18 | |
yuyi | a8843ae549 | |
Spydr | 8d9ef9a7f3 | |
Larpon | d908613aa5 | |
Ben | d65fb03cab | |
Mikey | 10b9dcb06e | |
Delyan Angelov | 726938ca57 | |
Larpon | 701526e310 | |
David Valdespino Pavon | 1fb9253c0e | |
Larpon | c897cc15ef | |
Delyan Angelov | cb5278f77b | |
Delyan Angelov | 65dfe199c9 | |
Delyan Angelov | 5a5f432588 | |
yuyi | 44b15c0b93 | |
ChAoS_UnItY | 3ceadec7ad | |
yuyi | 24ae92cd0d | |
ChAoS_UnItY | 8241713fe4 | |
Delyan Angelov | 724f989c2d | |
Delyan Angelov | 54152c9555 | |
Alexander Medvednikov | 1157ccc073 | |
yuyi | cb4fdc3037 | |
Alexander Medvednikov | 972190dcc0 | |
yuyi | d28840036c | |
ChAoS_UnItY | c2315b6c86 | |
Leo Developer | e5f922df1f | |
Ikko Ashimine | 633f93d500 |
|
@ -45,7 +45,7 @@ jobs:
|
|||
- name: Compile to raw Android (non-graphic) compatible
|
||||
run: |
|
||||
# Test that V can compile non-graphic app to Android compatible code *without* using the -apk flag
|
||||
./v -os android examples/toml.v
|
||||
./v -os android -gc none examples/toml.v
|
||||
|
||||
linux-cross:
|
||||
runs-on: ubuntu-20.04
|
||||
|
@ -98,7 +98,7 @@ jobs:
|
|||
- name: toml.v can be compiled to raw Android C
|
||||
run: |
|
||||
# Test that V can compile non-graphic app to Android compatible code *without* using the -apk flag
|
||||
./v -os android examples/toml.v
|
||||
./v -os android -gc none examples/toml.v
|
||||
|
||||
|
||||
windows-cross:
|
||||
|
|
|
@ -228,7 +228,7 @@ jobs:
|
|||
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
|
||||
timeout-minutes: 180
|
||||
env:
|
||||
VFLAGS: -cc clang
|
||||
VFLAGS: -cc clang -gc none
|
||||
VJOBS: 1
|
||||
VTEST_SHOW_START: 1
|
||||
steps:
|
||||
|
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
alpine-docker-musl-gcc:
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
|
||||
timeout-minutes: 121
|
||||
timeout-minutes: 181
|
||||
container:
|
||||
# Alpine docker pre-built container
|
||||
image: thevlang/vlang:alpine-build
|
||||
|
@ -46,8 +46,8 @@ jobs:
|
|||
- name: All code is formatted
|
||||
run: ./v test-cleancode
|
||||
|
||||
- name: Test V fixed tests
|
||||
run: ./v test-self
|
||||
- name: Run only essential tests
|
||||
run: VTEST_JUST_ESSENTIAL=1 ./v test-self
|
||||
|
||||
ubuntu-docker-musl:
|
||||
runs-on: ubuntu-20.04
|
||||
|
@ -58,7 +58,7 @@ jobs:
|
|||
env:
|
||||
V_CI_MUSL: 1
|
||||
V_CI_UBUNTU_MUSL: 1
|
||||
VFLAGS: -cc musl-gcc
|
||||
VFLAGS: -cc musl-gcc -gc none
|
||||
volumes:
|
||||
- ${{github.workspace}}:/opt/vlang
|
||||
|
||||
|
|
|
@ -25,20 +25,14 @@ jobs:
|
|||
- name: Build V
|
||||
run: make && sudo ./v symlink
|
||||
|
||||
- name: Checkout vab
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: vlang/vab
|
||||
path: vab
|
||||
|
||||
- name: Build vab
|
||||
- name: Install vab
|
||||
run: |
|
||||
cd vab
|
||||
v -g vab.v
|
||||
sudo ln -s $(pwd)/vab /usr/local/bin/vab
|
||||
v install vab
|
||||
v -g ~/.vmodules/vab
|
||||
sudo ln -s ~/.vmodules/vab/vab /usr/local/bin/vab
|
||||
|
||||
- name: Run tests
|
||||
run: v test vab
|
||||
run: v test ~/.vmodules/vab
|
||||
|
||||
- name: Run vab --help
|
||||
run: vab --help
|
||||
|
|
|
@ -14,6 +14,8 @@ jobs:
|
|||
vinix-build:
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
|
||||
env:
|
||||
VFLAGS: -gc none
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
platform: 'linux/amd64'
|
||||
branches: ['master']
|
||||
|
||||
pipeline:
|
||||
gen-vc:
|
||||
# This is what the official CI uses as well
|
||||
image: 'ubuntu:latest'
|
||||
secrets:
|
||||
- deploy_key
|
||||
commands:
|
||||
# Install necessary dependencies
|
||||
- apt-get update -y && apt-get install openssh-client git build-essential -y
|
||||
# Build the compiler
|
||||
- make
|
||||
# Run ssh-agent
|
||||
- eval $(ssh-agent -s)
|
||||
# Add ssh key
|
||||
- echo "$DEPLOY_KEY" | tr -d '\r' | ssh-add -
|
||||
# Create ssh dir with proper permissions
|
||||
- mkdir -p ~/.ssh
|
||||
- chmod 700 ~/.ssh
|
||||
# Configure git credentials
|
||||
- git config --global user.email 'vbot@rustybever.be'
|
||||
- git config --global user.name 'vbot'
|
||||
# Verify SSH keys
|
||||
- ssh-keyscan git.rustybever.be > ~/.ssh/known_hosts
|
||||
|
||||
# The following is copied over from the official repo's CI
|
||||
# https://github.com/vlang/v/blob/master/.github/workflows/gen_vc.yml
|
||||
- export "COMMIT_HASH=$(git rev-parse --short HEAD)"
|
||||
- export "COMMIT_MSG=$(git log -1 --oneline --pretty='%s' HEAD)"
|
||||
- rm -rf vc
|
||||
- git clone --depth=1 'git@git.rustybever.be:vieter/vc.git'
|
||||
- rm -rf vc/v.c vc/v_win.c
|
||||
- ./v -o vc/v.c -os cross cmd/v
|
||||
- ./v -o vc/v_win.c -os windows -cc msvc cmd/v
|
||||
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v.c
|
||||
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v_win.c
|
||||
# ensure the C files are over 5000 lines long, as a safety measure
|
||||
- '[ $(wc -l < vc/v.c) -gt 5000 ]'
|
||||
- '[ $(wc -l < vc/v_win.c) -gt 5000 ]'
|
||||
- git -C vc add v.c v_win.c
|
||||
- 'git -C vc commit -m "[v:master] $COMMIT_HASH - $COMMIT_MSG"'
|
||||
# in case there are recent commits:
|
||||
- git -C vc pull --rebase origin main
|
||||
- git -C vc push
|
||||
when:
|
||||
event: push
|
||||
|
||||
publish:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
secrets: [ docker_username, docker_password ]
|
||||
settings:
|
||||
repo: chewingbever/vlang
|
||||
tag: latest
|
||||
dockerfile: Dockerfile.builder
|
||||
platforms: [ linux/arm64/v8, linux/amd64 ]
|
||||
# The build can run every time, because we should only push when there's
|
||||
# actual changes
|
||||
when:
|
||||
event: push
|
|
@ -28,6 +28,9 @@ endif
|
|||
ifeq ($(_SYS),Linux)
|
||||
LINUX := 1
|
||||
TCCOS := linux
|
||||
ifneq ($(shell ldd /bin/ls | grep musl),)
|
||||
TCCOS := linuxmusl
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(_SYS),Darwin)
|
||||
|
|
|
@ -6,7 +6,83 @@ import v.pref
|
|||
|
||||
const github_job = os.getenv('GITHUB_JOB')
|
||||
|
||||
const just_essential = os.getenv('VTEST_JUST_ESSENTIAL') != ''
|
||||
|
||||
const (
|
||||
essential_list = [
|
||||
'cmd/tools/vvet/vet_test.v',
|
||||
'vlib/arrays/arrays_test.v',
|
||||
'vlib/bitfield/bitfield_test.v',
|
||||
//
|
||||
'vlib/builtin/int_test.v',
|
||||
'vlib/builtin/array_test.v',
|
||||
'vlib/builtin/float_test.v',
|
||||
'vlib/builtin/byte_test.v',
|
||||
'vlib/builtin/rune_test.v',
|
||||
'vlib/builtin/builtin_test.v',
|
||||
'vlib/builtin/map_of_floats_test.v',
|
||||
'vlib/builtin/string_int_test.v',
|
||||
'vlib/builtin/utf8_test.v',
|
||||
'vlib/builtin/map_test.v',
|
||||
'vlib/builtin/string_test.v',
|
||||
'vlib/builtin/sorting_test.v',
|
||||
'vlib/builtin/gated_array_string_test.v',
|
||||
'vlib/builtin/array_shrinkage_test.v',
|
||||
'vlib/builtin/isnil_test.v',
|
||||
'vlib/builtin/string_match_glob_test.v',
|
||||
'vlib/builtin/string_strip_margin_test.v',
|
||||
//
|
||||
'vlib/cli/command_test.v',
|
||||
'vlib/crypto/md5/md5_test.v',
|
||||
'vlib/dl/dl_test.v',
|
||||
'vlib/encoding/base64/base64_test.v',
|
||||
'vlib/encoding/utf8/encoding_utf8_test.v',
|
||||
'vlib/encoding/utf8/utf8_util_test.v',
|
||||
'vlib/flag/flag_test.v',
|
||||
'vlib/json/json_decode_test.v',
|
||||
'vlib/math/math_test.v',
|
||||
'vlib/net/tcp_test.v',
|
||||
'vlib/net/http/http_test.v',
|
||||
'vlib/net/http/server_test.v',
|
||||
'vlib/net/http/request_test.v',
|
||||
'vlib/io/io_test.v',
|
||||
'vlib/io/os_file_reader_test.v',
|
||||
'vlib/os/process_test.v',
|
||||
'vlib/os/file_test.v',
|
||||
'vlib/os/notify/notify_test.v',
|
||||
'vlib/os/filepath_test.v',
|
||||
'vlib/os/environment_test.v',
|
||||
'vlib/os/glob_test.v',
|
||||
'vlib/os/os_test.v',
|
||||
'vlib/rand/random_numbers_test.v',
|
||||
'vlib/rand/wyrand/wyrand_test.v',
|
||||
'vlib/runtime/runtime_test.v',
|
||||
'vlib/semver/semver_test.v',
|
||||
'vlib/sync/stdatomic/atomic_test.v',
|
||||
'vlib/sync/thread_test.v',
|
||||
'vlib/sync/waitgroup_test.v',
|
||||
'vlib/sync/pool/pool_test.v',
|
||||
'vlib/strings/builder_test.v',
|
||||
'vlib/strconv/atof_test.v',
|
||||
'vlib/strconv/atoi_test.v',
|
||||
'vlib/strconv/f32_f64_to_string_test.v',
|
||||
'vlib/strconv/format_test.v',
|
||||
'vlib/strconv/number_to_base_test.v',
|
||||
'vlib/time/time_test.v',
|
||||
'vlib/toml/tests/toml_test.v',
|
||||
'vlib/v/compiler_errors_test.v',
|
||||
'vlib/v/doc/doc_test.v',
|
||||
'vlib/v/eval/interpret_test.v',
|
||||
'vlib/v/fmt/fmt_keep_test.v',
|
||||
'vlib/v/fmt/fmt_test.v',
|
||||
'vlib/v/gen/c/coutput_test.v',
|
||||
'vlib/v/gen/js/program_test.v',
|
||||
'vlib/v/gen/native/macho_test.v',
|
||||
'vlib/v/gen/native/tests/native_test.v',
|
||||
'vlib/v/pkgconfig/pkgconfig_test.v',
|
||||
'vlib/v/tests/inout/compiler_test.v',
|
||||
'vlib/x/json2/json2_test.v',
|
||||
]
|
||||
skip_test_files = [
|
||||
'cmd/tools/vdoc/html_tag_escape_test.v', /* can't locate local module: markdown */
|
||||
'cmd/tools/vdoc/tests/vdoc_file_test.v', /* fails on Windows; order of output is not as expected */
|
||||
|
@ -177,6 +253,11 @@ fn main() {
|
|||
all_test_files << os.walk_ext(os.join_path(vroot, 'cmd'), '_test.v')
|
||||
test_js_files := os.walk_ext(os.join_path(vroot, 'vlib'), '_test.js.v')
|
||||
all_test_files << test_js_files
|
||||
|
||||
if just_essential {
|
||||
rooted_essential_list := essential_list.map(os.join_path(vroot, it))
|
||||
all_test_files = all_test_files.filter(rooted_essential_list.contains(it))
|
||||
}
|
||||
testing.eheader(title)
|
||||
mut tsession := testing.new_test_session(cmd_prefix, true)
|
||||
tsession.files << all_test_files.filter(!it.contains('testdata' + os.path_separator))
|
||||
|
|
|
@ -11,6 +11,8 @@ const chunk_height = 2 // the image is recalculated in chunks, each chunk proces
|
|||
|
||||
const zoom_factor = 1.1
|
||||
|
||||
const max_iterations = 255
|
||||
|
||||
struct ViewRect {
|
||||
mut:
|
||||
x_min f64
|
||||
|
@ -33,12 +35,13 @@ mut:
|
|||
iidx int
|
||||
pixels &u32 = unsafe { vcalloc(pwidth * pheight * sizeof(u32)) }
|
||||
npixels &u32 = unsafe { vcalloc(pwidth * pheight * sizeof(u32)) } // all drawing happens here, results are swapped at the end
|
||||
view ViewRect = ViewRect{-2.7610033817025625, 1.1788897130338223, -1.824584023871934, 2.1153096311072788}
|
||||
view ViewRect = ViewRect{-3.0773593290970673, 1.4952456603855397, -2.019938598189011, 2.3106642054225945}
|
||||
scale int = 1
|
||||
ntasks int = runtime.nr_jobs()
|
||||
}
|
||||
|
||||
const colors = [gx.black, gx.blue, gx.red, gx.green, gx.yellow, gx.orange, gx.purple, gx.white,
|
||||
gx.indigo, gx.violet, gx.black].map(u32(it.abgr8()))
|
||||
gx.indigo, gx.violet, gx.black, gx.blue, gx.orange, gx.yellow, gx.green].map(u32(it.abgr8()))
|
||||
|
||||
struct MandelChunk {
|
||||
cview ViewRect
|
||||
|
@ -83,30 +86,35 @@ fn (mut state AppState) update() {
|
|||
}
|
||||
// everything is done, swap the buffer pointers
|
||||
state.pixels, state.npixels = state.npixels, state.pixels
|
||||
println('$state.ntasks threads; $sw.elapsed().milliseconds() ms / frame')
|
||||
println('${state.ntasks:2} threads; ${sw.elapsed().milliseconds():3} ms / frame; scale: ${state.scale:4}')
|
||||
oview = cview
|
||||
}
|
||||
}
|
||||
|
||||
[direct_array_access]
|
||||
fn (mut state AppState) worker(id int, input chan MandelChunk, ready chan bool) {
|
||||
for {
|
||||
chunk := <-input or { break }
|
||||
yscale := chunk.cview.height() / pheight
|
||||
xscale := chunk.cview.width() / pwidth
|
||||
mut x, mut y, mut iter := 0.0, 0.0, 0
|
||||
mut y0 := chunk.ymin * yscale + chunk.cview.y_min
|
||||
mut x0 := chunk.cview.x_min
|
||||
for y_pixel := chunk.ymin; y_pixel < chunk.ymax && y_pixel < pheight; y_pixel++ {
|
||||
y0 := y_pixel * yscale + chunk.cview.y_min
|
||||
for x_pixel := 0.0; x_pixel < pwidth; x_pixel++ {
|
||||
x0 := x_pixel * xscale + chunk.cview.x_min
|
||||
mut x, mut y := x0, y0
|
||||
mut iter := 0
|
||||
for ; iter < 80; iter++ {
|
||||
yrow := unsafe { &state.npixels[int(y_pixel * pwidth)] }
|
||||
y0 += yscale
|
||||
x0 = chunk.cview.x_min
|
||||
for x_pixel := 0; x_pixel < pwidth; x_pixel++ {
|
||||
x0 += xscale
|
||||
x, y = x0, y0
|
||||
for iter = 0; iter < max_iterations; iter++ {
|
||||
x, y = x * x - y * y + x0, 2 * x * y + y0
|
||||
if x * x + y * y > 4 {
|
||||
break
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
state.npixels[int(y_pixel * pwidth) + int(x_pixel)] = colors[iter & 7]
|
||||
yrow[x_pixel] = colors[iter & 15]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +136,7 @@ fn (mut state AppState) zoom(zoom_factor f64) {
|
|||
state.view.x_max = c_x + zoom_factor * d_x
|
||||
state.view.y_min = c_y - zoom_factor * d_y
|
||||
state.view.y_max = c_y + zoom_factor * d_y
|
||||
state.scale += if zoom_factor < 1 { 1 } else { -1 }
|
||||
}
|
||||
|
||||
fn (mut state AppState) center(s_x f64, s_y f64) {
|
||||
|
@ -182,7 +191,7 @@ fn graphics_keydown(code gg.KeyCode, mod gg.Modifier, mut state AppState) {
|
|||
// movement
|
||||
mut d_x, mut d_y := 0.0, 0.0
|
||||
if code == .enter {
|
||||
println('> $state.view.x_min | $state.view.x_max | $state.view.y_min | $state.view.y_max')
|
||||
println('> ViewRect{$state.view.x_min, $state.view.x_max, $state.view.y_min, $state.view.y_max}')
|
||||
}
|
||||
if state.gg.pressed_keys[int(gg.KeyCode.left)] {
|
||||
d_x -= s_x
|
||||
|
|
|
@ -614,7 +614,7 @@ fn (mut a array) set_unsafe(i int, val voidptr) {
|
|||
unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(i), val, a.element_size) }
|
||||
}
|
||||
|
||||
// Private function. Used to implement assigment to the array element.
|
||||
// Private function. Used to implement assignment to the array element.
|
||||
fn (mut a array) set(i int, val voidptr) {
|
||||
$if !no_bounds_checking ? {
|
||||
if i < 0 || i >= a.len {
|
||||
|
|
|
@ -1590,3 +1590,14 @@ fn test_inline_array_element_access() {
|
|||
a2 := [1][0]
|
||||
assert a2 == 1
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fn f(x int, y int) []int {
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
fn test_2d_array_init_with_it() {
|
||||
a := [][]int{len: 6, init: f(it, 2 * it)}
|
||||
assert a == [[0, 0], [1, 2], [2, 4], [3, 6], [4, 8], [5, 10]]
|
||||
}
|
||||
|
|
|
@ -3,16 +3,16 @@ module builtin
|
|||
$if dynamic_boehm ? {
|
||||
$if windows {
|
||||
$if tinyc {
|
||||
#flag -I@VEXEROOT/thirdparty/libgc/include
|
||||
#flag -L@VEXEROOT/thirdparty/tcc/lib
|
||||
#flag -I @VEXEROOT/thirdparty/libgc/include
|
||||
#flag -L @VEXEROOT/thirdparty/tcc/lib
|
||||
#flag -lgc
|
||||
} $else $if msvc {
|
||||
#flag -DGC_BUILTIN_ATOMIC=1
|
||||
#flag -I@VEXEROOT/thirdparty/libgc/include
|
||||
#flag -I @VEXEROOT/thirdparty/libgc/include
|
||||
} $else {
|
||||
#flag -DGC_WIN32_THREADS=1
|
||||
#flag -DGC_BUILTIN_ATOMIC=1
|
||||
#flag -I@VEXEROOT/thirdparty/libgc
|
||||
#flag -I @VEXEROOT/thirdparty/libgc
|
||||
#flag @VEXEROOT/thirdparty/libgc/gc.o
|
||||
}
|
||||
} $else {
|
||||
|
@ -31,7 +31,7 @@ $if dynamic_boehm ? {
|
|||
#flag -DGC_BUILTIN_ATOMIC=1
|
||||
$if macos || linux {
|
||||
#flag -DGC_PTHREADS=1
|
||||
#flag -I@VEXEROOT/thirdparty/libgc/include
|
||||
#flag -I @VEXEROOT/thirdparty/libgc/include
|
||||
$if (prod && !tinyc && !debug) || !(amd64 || arm64 || i386 || arm32) {
|
||||
// TODO: replace the architecture check with a `!$exists("@VEXEROOT/thirdparty/tcc/lib/libgc.a")` comptime call
|
||||
#flag @VEXEROOT/thirdparty/libgc/gc.o
|
||||
|
@ -45,7 +45,7 @@ $if dynamic_boehm ? {
|
|||
#flag -DBUS_PAGE_FAULT=T_PAGEFLT
|
||||
#flag -DGC_PTHREADS=1
|
||||
$if !tinyc {
|
||||
#flag -I@VEXEROOT/thirdparty/libgc/include
|
||||
#flag -I @VEXEROOT/thirdparty/libgc/include
|
||||
#flag @VEXEROOT/thirdparty/libgc/gc.o
|
||||
}
|
||||
$if tinyc {
|
||||
|
@ -62,11 +62,11 @@ $if dynamic_boehm ? {
|
|||
#flag -DGC_NOT_DLL=1
|
||||
#flag -DGC_WIN32_THREADS=1
|
||||
$if tinyc {
|
||||
#flag -I@VEXEROOT/thirdparty/libgc/include
|
||||
#flag -I @VEXEROOT/thirdparty/libgc/include
|
||||
#flag @VEXEROOT/thirdparty/tcc/lib/libgc.a
|
||||
#flag -luser32
|
||||
} $else {
|
||||
#flag -I@VEXEROOT/thirdparty/libgc/include
|
||||
#flag -I @VEXEROOT/thirdparty/libgc/include
|
||||
#flag @VEXEROOT/thirdparty/libgc/gc.o
|
||||
}
|
||||
} $else $if $pkgconfig('bdw-gc') {
|
||||
|
|
|
@ -14,6 +14,14 @@ $if js_freestanding {
|
|||
#globalPrint = globalThis.print
|
||||
}
|
||||
|
||||
pub fn flush_stdout() {
|
||||
// needed for parity with builtin.c.v
|
||||
}
|
||||
|
||||
pub fn flush_stderr() {
|
||||
// needed for parity with builtin.c.v
|
||||
}
|
||||
|
||||
pub fn println(s string) {
|
||||
$if js_freestanding {
|
||||
#globalPrint(s.str)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
## Description:
|
||||
|
||||
`compress` is a namespace for (multiple) compression algorithms supported by V.
|
||||
At the moment, only `compress.zlib` and `compress.deflate` are implemented.
|
||||
|
||||
At the moment, the following compression algorithms are implemented:
|
||||
- `compress.deflate`
|
||||
- `compress.gzip`
|
||||
- `compress.zlib`
|
||||
|
|
|
@ -9,7 +9,7 @@ fn C.tdefl_compress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_le
|
|||
fn C.tinfl_decompress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
|
||||
|
||||
// compresses an array of bytes based on providing flags and returns the compressed bytes in a new array
|
||||
// see `gzip.compress([]u8)` and `zlib.compress([]u8)` for default implementations.
|
||||
// NB: this is a low level api, a high level implementation like zlib/gzip should be preferred
|
||||
[manualfree]
|
||||
pub fn compress(data []u8, flags int) ?[]u8 {
|
||||
if u64(data.len) > compress.max_size {
|
||||
|
@ -28,7 +28,7 @@ pub fn compress(data []u8, flags int) ?[]u8 {
|
|||
}
|
||||
|
||||
// decompresses an array of bytes based on providing flags and returns the decompressed bytes in a new array
|
||||
// see `gzip.decompress([]u8)` and `zlib.decompress([]u8)` for default implementations.
|
||||
// NB: this is a low level api, a high level implementation like zlib/gzip should be preferred
|
||||
[manualfree]
|
||||
pub fn decompress(data []u8, flags int) ?[]u8 {
|
||||
mut out_len := usize(0)
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
`compress.deflate` is a module that assists in the compression and
|
||||
decompression of binary data using `deflate` compression
|
||||
|
||||
NOTE: To decompress gzip, discard first 10 bytes of compressed bytes
|
||||
then use `compress.deflate.decompress`. (Header validation won't be
|
||||
performed in this case)
|
||||
|
||||
## Examples:
|
||||
|
||||
|
|
|
@ -2,15 +2,14 @@ module deflate
|
|||
|
||||
import compress
|
||||
|
||||
// compresses an array of bytes using gzip and returns the compressed bytes in a new array
|
||||
// Example: compressed := gzip.compress(b)?
|
||||
// compresses an array of bytes using deflate and returns the compressed bytes in a new array
|
||||
// Example: compressed := deflate.compress(b)?
|
||||
pub fn compress(data []u8) ?[]u8 {
|
||||
return compress.compress(data, 0)
|
||||
}
|
||||
|
||||
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
|
||||
// Example: decompressed := zlib.decompress(b)?
|
||||
[manualfree]
|
||||
// decompresses an array of bytes using deflate and returns the decompressed bytes in a new array
|
||||
// Example: decompressed := deflate.decompress(b)?
|
||||
pub fn decompress(data []u8) ?[]u8 {
|
||||
return compress.decompress(data, 0)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
## Description:
|
||||
|
||||
`compress.gzip` is a module that assists in the compression and
|
||||
decompression of binary data using `gzip` compression
|
||||
|
||||
|
||||
## Examples:
|
||||
|
||||
```v
|
||||
import compress.gzip
|
||||
|
||||
fn main() {
|
||||
uncompressed := 'Hello world!'
|
||||
compressed := gzip.compress(uncompressed.bytes())?
|
||||
decompressed := gzip.decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
```
|
|
@ -0,0 +1,114 @@
|
|||
// [rfc1952](https://datatracker.ietf.org/doc/html/rfc1952) compliant
|
||||
// gzip compression/decompression
|
||||
|
||||
module gzip
|
||||
|
||||
import compress
|
||||
import hash.crc32
|
||||
|
||||
// compresses an array of bytes using gzip and returns the compressed bytes in a new array
|
||||
// Example: compressed := gzip.compress(b)?
|
||||
pub fn compress(data []u8) ?[]u8 {
|
||||
compressed := compress.compress(data, 0)?
|
||||
// header
|
||||
mut result := [
|
||||
u8(0x1f), // magic numbers (1F 8B)
|
||||
0x8b,
|
||||
0x08, // deflate
|
||||
0x00, // header flags
|
||||
0x00, // 4-byte timestamp, 0 = no timestamp (00 00 00 00)
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00, // extra flags
|
||||
0xff, // operating system id (0xff = unknown)
|
||||
] // 10 bytes
|
||||
result << compressed
|
||||
// trailer
|
||||
checksum := crc32.sum(data)
|
||||
length := data.len
|
||||
result << [
|
||||
u8(checksum >> 24),
|
||||
u8(checksum >> 16),
|
||||
u8(checksum >> 8),
|
||||
u8(checksum),
|
||||
u8(length >> 24),
|
||||
u8(length >> 16),
|
||||
u8(length >> 8),
|
||||
u8(length),
|
||||
] // 8 bytes
|
||||
return result
|
||||
}
|
||||
|
||||
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
|
||||
// Example: decompressed := gzip.decompress(b)?
|
||||
pub fn decompress(data []u8) ?[]u8 {
|
||||
if data.len < 18 {
|
||||
return error('data is too short, not gzip compressed?')
|
||||
} else if data[0] != 0x1f || data[1] != 0x8b {
|
||||
return error('wrong magic numbers, not gzip compressed?')
|
||||
} else if data[2] != 0x08 {
|
||||
return error('gzip data is not compressed with DEFLATE')
|
||||
}
|
||||
mut header_length := 10
|
||||
|
||||
// parse flags, we ignore most of them, but we still need to parse them
|
||||
// correctly, so we dont accidently decompress something that belongs
|
||||
// to the header
|
||||
|
||||
if data[4] & 0b1110_0000 > 0 { // reserved bits
|
||||
// rfc 1952 2.3.1.2 Compliance
|
||||
// A compliant decompressor must give an error indication if any
|
||||
// reserved bit is non-zero, since such a bit could indicate the
|
||||
// presence of a new field that would cause subsequent data to be
|
||||
// interpreted incorrectly.
|
||||
return error('reserved flags are set, unsupported field detected')
|
||||
}
|
||||
|
||||
// if data[4] & 0b0000_0001 {} // FTEXT
|
||||
if data[4] & 0b0000_0100 > 0 { // FEXTRA, extra data
|
||||
xlen := data[header_length]
|
||||
header_length += xlen + 1
|
||||
}
|
||||
if data[4] & 0b0000_1000 > 0 { // FNAME, file name
|
||||
// filename is zero-terminated, so skip until we hit a zero byte
|
||||
for header_length < data.len && data[header_length] != 0x00 {
|
||||
header_length++
|
||||
}
|
||||
header_length++
|
||||
}
|
||||
if data[4] & 0b0001_0000 > 0 { // FCOMMENT
|
||||
// comment is zero-terminated, so skip until we hit a zero byte
|
||||
for header_length < data.len && data[header_length] != 0x00 {
|
||||
header_length++
|
||||
}
|
||||
header_length++
|
||||
}
|
||||
if data[4] & 0b0000_0010 > 0 { // FHCRC, flag header crc
|
||||
if header_length + 12 > data.len {
|
||||
return error('data too short')
|
||||
}
|
||||
checksum_header := crc32.sum(data[..header_length])
|
||||
checksum_header_expected := (u32(data[header_length]) << 24) | (u32(data[header_length + 1]) << 16) | (u32(data[
|
||||
header_length + 2]) << 8) | data[header_length + 3]
|
||||
if checksum_header != checksum_header_expected {
|
||||
return error('header checksum verification failed')
|
||||
}
|
||||
header_length += 4
|
||||
}
|
||||
if header_length + 8 > data.len {
|
||||
return error('data too short')
|
||||
}
|
||||
|
||||
decompressed := compress.decompress(data[header_length..data.len - 8], 0)?
|
||||
length_expected := (u32(data[data.len - 4]) << 24) | (u32(data[data.len - 3]) << 16) | (u32(data[data.len - 2]) << 8) | data[data.len - 1]
|
||||
if decompressed.len != length_expected {
|
||||
return error('length verification failed, got $decompressed.len, expected $length_expected')
|
||||
}
|
||||
checksum := crc32.sum(decompressed)
|
||||
checksum_expected := (u32(data[data.len - 8]) << 24) | (u32(data[data.len - 7]) << 16) | (u32(data[data.len - 6]) << 8) | data[data.len - 5]
|
||||
if checksum != checksum_expected {
|
||||
return error('checksum verification failed')
|
||||
}
|
||||
return decompressed
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
module gzip
|
||||
|
||||
import hash.crc32
|
||||
|
||||
fn test_gzip() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
compressed := compress(uncompressed.bytes())?
|
||||
decompressed := decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
|
||||
fn assert_decompress_error(data []u8, reason string) ? {
|
||||
decompress(data) or {
|
||||
assert err.msg() == reason
|
||||
return
|
||||
}
|
||||
return error('did not error')
|
||||
}
|
||||
|
||||
fn test_gzip_invalid_too_short() ? {
|
||||
assert_decompress_error([]u8{}, 'data is too short, not gzip compressed?')?
|
||||
}
|
||||
|
||||
fn test_gzip_invalid_magic_numbers() ? {
|
||||
assert_decompress_error([]u8{len: 100}, 'wrong magic numbers, not gzip compressed?')?
|
||||
}
|
||||
|
||||
fn test_gzip_invalid_compression() ? {
|
||||
mut data := []u8{len: 100}
|
||||
data[0] = 0x1f
|
||||
data[1] = 0x8b
|
||||
assert_decompress_error(data, 'gzip data is not compressed with DEFLATE')?
|
||||
}
|
||||
|
||||
fn test_gzip_with_ftext() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b0000_0001 // FTEXT
|
||||
decompressed := decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
|
||||
fn test_gzip_with_fname() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b0000_1000
|
||||
compressed.insert(10, `h`)
|
||||
compressed.insert(11, `i`)
|
||||
compressed.insert(12, 0x00)
|
||||
decompressed := decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
|
||||
fn test_gzip_with_fcomment() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b0001_0000
|
||||
compressed.insert(10, `h`)
|
||||
compressed.insert(11, `i`)
|
||||
compressed.insert(12, 0x00)
|
||||
decompressed := decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
|
||||
fn test_gzip_with_fname_fcomment() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b0001_1000
|
||||
compressed.insert(10, `h`)
|
||||
compressed.insert(11, `i`)
|
||||
compressed.insert(12, 0x00)
|
||||
compressed.insert(10, `h`)
|
||||
compressed.insert(11, `i`)
|
||||
compressed.insert(12, 0x00)
|
||||
decompressed := decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
|
||||
fn test_gzip_with_fextra() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b0000_0100
|
||||
compressed.insert(10, 2)
|
||||
compressed.insert(11, `h`)
|
||||
compressed.insert(12, `i`)
|
||||
decompressed := decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
|
||||
fn test_gzip_with_hcrc() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b0000_0010
|
||||
checksum := crc32.sum(compressed[..10])
|
||||
compressed.insert(10, u8(checksum >> 24))
|
||||
compressed.insert(11, u8(checksum >> 16))
|
||||
compressed.insert(12, u8(checksum >> 8))
|
||||
compressed.insert(13, u8(checksum))
|
||||
decompressed := decompress(compressed)?
|
||||
assert decompressed == uncompressed.bytes()
|
||||
}
|
||||
|
||||
fn test_gzip_with_invalid_hcrc() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b0000_0010
|
||||
checksum := crc32.sum(compressed[..10])
|
||||
compressed.insert(10, u8(checksum >> 24))
|
||||
compressed.insert(11, u8(checksum >> 16))
|
||||
compressed.insert(12, u8(checksum >> 8))
|
||||
compressed.insert(13, u8(checksum + 1))
|
||||
assert_decompress_error(compressed, 'header checksum verification failed')?
|
||||
}
|
||||
|
||||
fn test_gzip_with_invalid_checksum() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[compressed.len - 5] += 1
|
||||
assert_decompress_error(compressed, 'checksum verification failed')?
|
||||
}
|
||||
|
||||
fn test_gzip_with_invalid_length() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[compressed.len - 1] += 1
|
||||
assert_decompress_error(compressed, 'length verification failed, got 12, expected 13')?
|
||||
}
|
||||
|
||||
fn test_gzip_with_invalid_flags() ? {
|
||||
uncompressed := 'Hello world!'
|
||||
mut compressed := compress(uncompressed.bytes())?
|
||||
compressed[4] |= 0b1000_0000
|
||||
assert_decompress_error(compressed, 'reserved flags are set, unsupported field detected')?
|
||||
}
|
|
@ -4,7 +4,6 @@ import compress
|
|||
|
||||
// compresses an array of bytes using zlib and returns the compressed bytes in a new array
|
||||
// Example: compressed := zlib.compress(b)?
|
||||
[manualfree]
|
||||
pub fn compress(data []u8) ?[]u8 {
|
||||
// flags = TDEFL_WRITE_ZLIB_HEADER (0x01000)
|
||||
return compress.compress(data, 0x01000)
|
||||
|
@ -12,7 +11,6 @@ pub fn compress(data []u8) ?[]u8 {
|
|||
|
||||
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
|
||||
// Example: decompressed := zlib.decompress(b)?
|
||||
[manualfree]
|
||||
pub fn decompress(data []u8) ?[]u8 {
|
||||
// flags = TINFL_FLAG_PARSE_ZLIB_HEADER (0x1)
|
||||
return compress.decompress(data, 0x1)
|
||||
|
|
|
@ -222,6 +222,26 @@ fn gg_init_sokol_window(user_data voidptr) {
|
|||
ctx.timage_pip = sgl.make_pipeline(&pipdesc)
|
||||
//
|
||||
if ctx.config.init_fn != voidptr(0) {
|
||||
$if android {
|
||||
// NOTE on Android sokol can emit resize events *before* the init function is
|
||||
// called (Android has to initialize a lot more through the Activity system to
|
||||
// reach a valid coontext) and thus the user's code will miss the resize event.
|
||||
// To prevent this we emit a custom window resize event, if the screen size has
|
||||
// changed meanwhile.
|
||||
win_size := ctx.window_size()
|
||||
if ctx.width != win_size.width || ctx.height != win_size.height {
|
||||
ctx.width = win_size.width
|
||||
ctx.height = win_size.height
|
||||
if ctx.config.resized_fn != voidptr(0) {
|
||||
e := Event{
|
||||
typ: .resized
|
||||
window_width: ctx.width
|
||||
window_height: ctx.height
|
||||
}
|
||||
ctx.config.resized_fn(&e, ctx.user_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.config.init_fn(ctx.user_data)
|
||||
}
|
||||
// Create images now that we can do that after sg is inited
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import json
|
||||
|
||||
struct Result<T> {
|
||||
ok bool
|
||||
result T
|
||||
}
|
||||
|
||||
struct User {
|
||||
id int
|
||||
username string
|
||||
}
|
||||
|
||||
fn func<T>() ?T {
|
||||
text := '{"ok": true, "result":{"id":37467243, "username": "ciao"}}'
|
||||
a := json.decode(Result<T>, text)?
|
||||
return a.result
|
||||
}
|
||||
|
||||
fn test_decode_with_generic_struct() ? {
|
||||
ret := func<User>()?
|
||||
println(ret)
|
||||
assert ret.id == 37467243
|
||||
assert ret.username == 'ciao'
|
||||
}
|
|
@ -68,10 +68,11 @@ pub fn read_cookies(h map[string][]string, filter string) []&Cookie {
|
|||
mut line := line_.trim_space()
|
||||
mut part := ''
|
||||
for line.len > 0 {
|
||||
if line.index_any(';') > 0 {
|
||||
line_parts := line.split(';')
|
||||
mut semicolon_position := line.index_any(';') // Store the position of the next semicolon
|
||||
if semicolon_position > 0 { // So, there is a semicolon, let's parse until that position
|
||||
line_parts := line[..semicolon_position].split(';') // split the line only until that semicolon
|
||||
line = line[(semicolon_position + 1)..] // and then skip everything before the semicolon
|
||||
part = line_parts[0]
|
||||
line = line_parts[1]
|
||||
} else {
|
||||
part = line
|
||||
line = ''
|
||||
|
|
|
@ -214,11 +214,17 @@ pub fn parse_request_head(mut reader io.BufferedReader) ?Request {
|
|||
}
|
||||
header.coerce(canonicalize: true)
|
||||
|
||||
mut request_cookies := map[string]string{}
|
||||
for _, cookie in read_cookies(header.data, '') {
|
||||
request_cookies[cookie.name] = cookie.value
|
||||
}
|
||||
|
||||
return Request{
|
||||
method: method
|
||||
url: target.str()
|
||||
header: header
|
||||
version: version
|
||||
cookies: request_cookies
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,14 @@ import strings.textscanner
|
|||
// therefore results may be different for certain operating systems.
|
||||
|
||||
const (
|
||||
fslash = `/`
|
||||
bslash = `\\`
|
||||
dot = `.`
|
||||
qmark = `?`
|
||||
dot_dot = '..'
|
||||
empty_str = ''
|
||||
dot_str = '.'
|
||||
fslash = `/`
|
||||
bslash = `\\`
|
||||
dot = `.`
|
||||
qmark = `?`
|
||||
fslash_str = '/'
|
||||
dot_dot = '..'
|
||||
empty_str = ''
|
||||
dot_str = '.'
|
||||
)
|
||||
|
||||
// is_abs_path returns `true` if the given `path` is absolute.
|
||||
|
@ -62,8 +63,13 @@ pub fn norm_path(path string) string {
|
|||
return os.dot_str
|
||||
}
|
||||
rooted := is_abs_path(path)
|
||||
volume := get_volume(path)
|
||||
volume_len := volume.len
|
||||
// get the volume name from the path
|
||||
// if the current operating system is Windows
|
||||
volume_len := win_volume_len(path)
|
||||
mut volume := path[..volume_len]
|
||||
if volume_len != 0 && volume.contains(os.fslash_str) {
|
||||
volume = volume.replace(os.fslash_str, path_separator)
|
||||
}
|
||||
cpath := clean_path(path[volume_len..])
|
||||
if cpath.len == 0 && volume_len == 0 {
|
||||
return os.dot_str
|
||||
|
@ -217,6 +223,9 @@ fn clean_path(path string) string {
|
|||
// win_volume_len returns the length of the
|
||||
// Windows volume/drive from the given `path`.
|
||||
fn win_volume_len(path string) int {
|
||||
$if !windows {
|
||||
return 0
|
||||
}
|
||||
plen := path.len
|
||||
if plen < 2 {
|
||||
return 0
|
||||
|
@ -244,20 +253,6 @@ fn win_volume_len(path string) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
fn get_volume(path string) string {
|
||||
$if !windows {
|
||||
return os.empty_str
|
||||
}
|
||||
volume := path[..win_volume_len(path)]
|
||||
if volume.len == 0 {
|
||||
return os.empty_str
|
||||
}
|
||||
if volume[0] == os.fslash {
|
||||
return volume.replace('/', '\\')
|
||||
}
|
||||
return volume
|
||||
}
|
||||
|
||||
fn is_slash(b u8) bool {
|
||||
$if windows {
|
||||
return b == os.bslash || b == os.fslash
|
||||
|
|
|
@ -152,3 +152,20 @@ fn test_existing_path() {
|
|||
assert existing_path('$wd//././/.//') or { '' } == '$wd//././/.//'
|
||||
assert existing_path('$wd//././/.//oh') or { '' } == '$wd//././/.//'
|
||||
}
|
||||
|
||||
fn test_windows_volume() {
|
||||
$if windows {
|
||||
assert windows_volume('C:/path\\to/file.v') == 'C:'
|
||||
assert windows_volume('D:\\.\\') == 'D:'
|
||||
assert windows_volume('G:') == 'G:'
|
||||
assert windows_volume('G') == ''
|
||||
assert windows_volume(r'\\Host\share\files\file.v') == r'\\Host\share'
|
||||
assert windows_volume('\\\\Host\\') == ''
|
||||
assert windows_volume(r'\\.\BootPartition2\\files\.\\') == r'\\.\BootPartition2'
|
||||
assert windows_volume(r'\/.\BootPartition2\\files\.\\') == r'\/.\BootPartition2'
|
||||
assert windows_volume(r'\\\.\BootPartition2\\files\.\\') == ''
|
||||
assert windows_volume('') == ''
|
||||
assert windows_volume('\\') == ''
|
||||
assert windows_volume('/') == ''
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
module os
|
||||
|
||||
// windows_volume returns the volume name from the given `path` on a Windows system.
|
||||
// An empty string is returned if no Windows volume is present.
|
||||
// Examples (on a Windows system):
|
||||
// ```v
|
||||
// assert os.windows_volume(r'C:\path\to\file.v') == 'C:'
|
||||
// assert os.windows_volume('D:') == 'D:'
|
||||
// assert os.windows_volume(r'\\Host\share\files\file.v') == r'\\Host\share'
|
||||
// ```
|
||||
pub fn windows_volume(path string) string {
|
||||
volume_len := win_volume_len(path)
|
||||
if volume_len == 0 {
|
||||
return empty_str
|
||||
}
|
||||
return path[..volume_len]
|
||||
}
|
|
@ -126,3 +126,24 @@ pub fn find_between_pair_string(input string, start string, end string) string {
|
|||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// split_capital returns an array containing the contents of `s` split by capital letters.
|
||||
// Example: assert strings.split_capital('XYZ') == ['X', 'Y', 'Z']
|
||||
// Example: assert strings.split_capital('XYStar') == ['X', 'Y', 'Star']
|
||||
pub fn split_capital(s string) []string {
|
||||
mut res := []string{}
|
||||
mut word_start := 0
|
||||
for idx, c in s {
|
||||
if c >= `A` && c <= `Z` {
|
||||
if word_start != idx {
|
||||
res << s#[word_start..idx]
|
||||
}
|
||||
word_start = idx
|
||||
continue
|
||||
}
|
||||
}
|
||||
if word_start != s.len {
|
||||
res << s#[word_start..]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -94,3 +94,14 @@ fn test_find_between_pair_family() {
|
|||
assert '$e1' == '$e2'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_split_capital() {
|
||||
assert strings.split_capital('') == []
|
||||
assert strings.split_capital('abc') == ['abc']
|
||||
assert strings.split_capital('X') == ['X']
|
||||
assert strings.split_capital('XX') == ['X', 'X']
|
||||
assert strings.split_capital('XYZ') == ['X', 'Y', 'Z']
|
||||
assert strings.split_capital('JohnWilliams') == ['John', 'Williams']
|
||||
assert strings.split_capital('JDStar') == ['J', 'D', 'Star']
|
||||
assert strings.split_capital('cpDumpRotarySpring') == ['cp', 'Dump', 'Rotary', 'Spring']
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ mut:
|
|||
}
|
||||
|
||||
fn event(e &tui.Event, x voidptr) {
|
||||
println(e)
|
||||
if e.typ == .key_down && e.code == .escape {
|
||||
exit(0)
|
||||
}
|
||||
|
|
|
@ -54,6 +54,28 @@ pub fn (attrs []Attr) contains(str string) bool {
|
|||
return attrs.any(it.name == str)
|
||||
}
|
||||
|
||||
[direct_array_access]
|
||||
pub fn (attrs []Attr) find_first(aname string) ?Attr {
|
||||
for a in attrs {
|
||||
if a.name == aname {
|
||||
return a
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
[direct_array_access]
|
||||
pub fn (attrs []Attr) find_last(aname string) ?Attr {
|
||||
for idx := attrs.len - 1; idx > -1; idx-- {
|
||||
a := attrs[idx]
|
||||
if a.name == aname {
|
||||
return a
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
[direct_array_access]
|
||||
pub fn (attrs []Attr) find_comptime_define() ?int {
|
||||
for idx in 0 .. attrs.len {
|
||||
if attrs[idx].kind == .comptime_define {
|
||||
|
|
|
@ -1947,12 +1947,12 @@ pub fn (mut t Table) replace_generic_type(typ Type, generic_types []Type) {
|
|||
|
||||
// generic struct instantiations to concrete types
|
||||
pub fn (mut t Table) generic_insts_to_concrete() {
|
||||
for mut typ in t.type_symbols {
|
||||
if typ.kind == .generic_inst {
|
||||
info := typ.info as GenericInst
|
||||
for mut sym in t.type_symbols {
|
||||
if sym.kind == .generic_inst {
|
||||
info := sym.info as GenericInst
|
||||
parent := t.type_symbols[info.parent_idx]
|
||||
if parent.kind == .placeholder {
|
||||
typ.kind = .placeholder
|
||||
sym.kind = .placeholder
|
||||
continue
|
||||
}
|
||||
match parent.info {
|
||||
|
@ -1982,15 +1982,15 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
|||
parent_info.concrete_types = info.concrete_types.clone()
|
||||
parent_info.fields = fields
|
||||
parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic)
|
||||
typ.info = Struct{
|
||||
sym.info = Struct{
|
||||
...parent_info
|
||||
is_generic: false
|
||||
concrete_types: info.concrete_types.clone()
|
||||
fields: fields
|
||||
parent_type: new_type(info.parent_idx).set_flag(.generic)
|
||||
}
|
||||
typ.is_pub = true
|
||||
typ.kind = parent.kind
|
||||
sym.is_pub = true
|
||||
sym.kind = parent.kind
|
||||
|
||||
parent_sym := t.sym(parent_info.parent_type)
|
||||
for method in parent_sym.methods {
|
||||
|
@ -2034,7 +2034,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
|||
param.typ = pt
|
||||
}
|
||||
}
|
||||
typ.register_method(method)
|
||||
sym.register_method(method)
|
||||
}
|
||||
mut all_methods := parent.methods
|
||||
for imethod in imethods {
|
||||
|
@ -2044,7 +2044,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
|||
}
|
||||
}
|
||||
}
|
||||
typ.info = Interface{
|
||||
sym.info = Interface{
|
||||
...parent_info
|
||||
is_generic: false
|
||||
concrete_types: info.concrete_types.clone()
|
||||
|
@ -2052,9 +2052,9 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
|||
methods: imethods
|
||||
parent_type: new_type(info.parent_idx).set_flag(.generic)
|
||||
}
|
||||
typ.is_pub = true
|
||||
typ.kind = parent.kind
|
||||
typ.methods = all_methods
|
||||
sym.is_pub = true
|
||||
sym.kind = parent.kind
|
||||
sym.methods = all_methods
|
||||
} else {
|
||||
util.verror('generic error', 'the number of generic types of interface `$parent.name` is inconsistent with the concrete types')
|
||||
}
|
||||
|
@ -2078,8 +2078,8 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
|||
}
|
||||
for i in 0 .. variants.len {
|
||||
if variants[i].has_flag(.generic) {
|
||||
sym := t.sym(variants[i])
|
||||
if sym.kind == .struct_ && variants[i].idx() != info.parent_idx {
|
||||
t_sym := t.sym(variants[i])
|
||||
if t_sym.kind == .struct_ && variants[i].idx() != info.parent_idx {
|
||||
variants[i] = t.unwrap_generic_type(variants[i], generic_names,
|
||||
info.concrete_types)
|
||||
} else {
|
||||
|
@ -2091,7 +2091,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
|||
}
|
||||
}
|
||||
}
|
||||
typ.info = SumType{
|
||||
sym.info = SumType{
|
||||
...parent_info
|
||||
is_generic: false
|
||||
concrete_types: info.concrete_types.clone()
|
||||
|
@ -2099,8 +2099,8 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
|||
variants: variants
|
||||
parent_type: new_type(info.parent_idx).set_flag(.generic)
|
||||
}
|
||||
typ.is_pub = true
|
||||
typ.kind = parent.kind
|
||||
sym.is_pub = true
|
||||
sym.kind = parent.kind
|
||||
} else {
|
||||
util.verror('generic error', 'the number of generic types of sumtype `$parent.name` is inconsistent with the concrete types')
|
||||
}
|
||||
|
|
|
@ -93,6 +93,13 @@ fn (mut v Builder) post_process_c_compiler_output(res os.Result) {
|
|||
}
|
||||
return
|
||||
}
|
||||
if res.exit_code != 0 && v.pref.gc_mode != .no_gc && res.output.contains('libgc.a') {
|
||||
$if windows {
|
||||
verror(r'Your V installation may be out-of-date. Try removing `thirdparty\tcc\` and running `.\make.bat`')
|
||||
} $else {
|
||||
verror('Your V installation may be out-of-date. Try removing `thirdparty/tcc/` and running `make`')
|
||||
}
|
||||
}
|
||||
for emsg_marker in [builder.c_verror_message_marker, 'error: include file '] {
|
||||
if res.output.contains(emsg_marker) {
|
||||
emessage := res.output.all_after(emsg_marker).all_before('\n').all_before('\r').trim_right('\r\n')
|
||||
|
|
|
@ -117,10 +117,14 @@ pub fn (cflags []CFlag) defines_others_libs() ([]string, []string, []string) {
|
|||
mut others := []string{}
|
||||
mut libs := []string{}
|
||||
for copt in copts_without_obj_files {
|
||||
if copt.starts_with('-l') || copt.ends_with('.a') {
|
||||
if copt.starts_with('-l') {
|
||||
libs << copt
|
||||
continue
|
||||
}
|
||||
if copt.ends_with('.a') {
|
||||
libs << '"$copt"'
|
||||
continue
|
||||
}
|
||||
if copt.starts_with('-D') {
|
||||
defines << copt
|
||||
continue
|
||||
|
|
|
@ -619,7 +619,7 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
|
|||
mut to_set := ast.void_type
|
||||
// resolve generic struct receiver
|
||||
if node.is_method && param.typ.has_flag(.generic) {
|
||||
sym := c.table.sym(node.receiver_type)
|
||||
sym := c.table.final_sym(node.receiver_type)
|
||||
match sym.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
if !isnil(c.table.cur_fn) && c.table.cur_fn.generic_names.len > 0 { // in generic fn
|
||||
|
|
|
@ -89,6 +89,7 @@ pub mut:
|
|||
inside_defer bool // true inside `defer {}` blocks
|
||||
inside_fn_arg bool // `a`, `b` in `a.f(b)`
|
||||
inside_ct_attr bool // true inside `[if expr]`
|
||||
inside_x_is_type bool // true inside the Type expression of `if x is Type {`
|
||||
inside_comptime_for_field bool
|
||||
skip_flags bool // should `#flag` and `#include` be skipped
|
||||
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
|
||||
|
@ -156,6 +157,7 @@ fn (mut c Checker) reset_checker_state_at_start_of_new_file() {
|
|||
c.inside_defer = false
|
||||
c.inside_fn_arg = false
|
||||
c.inside_ct_attr = false
|
||||
c.inside_x_is_type = false
|
||||
c.skip_flags = false
|
||||
c.fn_level = 0
|
||||
c.expr_level = 0
|
||||
|
@ -368,7 +370,7 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
|
|||
if !has_main_mod_file {
|
||||
c.error('project must include a `main` module or be a shared library (compile with `v -shared`)',
|
||||
token.Pos{})
|
||||
} else if !has_main_fn {
|
||||
} else if !has_main_fn && !c.pref.is_o {
|
||||
c.error('function `main` must be declared in the main module', token.Pos{})
|
||||
}
|
||||
}
|
||||
|
@ -1605,9 +1607,10 @@ fn (mut c Checker) assert_stmt(node ast.AssertStmt) {
|
|||
|
||||
fn (mut c Checker) block(node ast.Block) {
|
||||
if node.is_unsafe {
|
||||
prev_unsafe := c.inside_unsafe
|
||||
c.inside_unsafe = true
|
||||
c.stmts(node.stmts)
|
||||
c.inside_unsafe = false
|
||||
c.inside_unsafe = prev_unsafe
|
||||
} else {
|
||||
c.stmts(node.stmts)
|
||||
}
|
||||
|
@ -2021,7 +2024,9 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
|||
c.error('incorrect use of compile-time type', node.pos)
|
||||
}
|
||||
ast.EmptyExpr {
|
||||
print_backtrace()
|
||||
c.error('checker.expr(): unhandled EmptyExpr', token.Pos{})
|
||||
return ast.void_type
|
||||
}
|
||||
ast.CTempVar {
|
||||
return node.typ
|
||||
|
@ -2269,6 +2274,11 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
|||
return c.struct_init(mut node)
|
||||
}
|
||||
ast.TypeNode {
|
||||
if !c.inside_x_is_type && node.typ.has_flag(.generic) && unsafe { c.table.cur_fn != 0 }
|
||||
&& c.table.cur_fn.generic_names.len == 0 {
|
||||
c.error('unexpected generic variable in non-generic function `$c.table.cur_fn.name`',
|
||||
node.pos)
|
||||
}
|
||||
return node.typ
|
||||
}
|
||||
ast.TypeOf {
|
||||
|
|
|
@ -9,7 +9,9 @@ import v.util
|
|||
import v.pkgconfig
|
||||
|
||||
fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
||||
node.left_type = c.expr(node.left)
|
||||
if node.left !is ast.EmptyExpr {
|
||||
node.left_type = c.expr(node.left)
|
||||
}
|
||||
if node.method_name == 'compile_error' {
|
||||
c.error(node.args_var, node.pos)
|
||||
return ast.void_type
|
||||
|
|
|
@ -506,7 +506,12 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||
}
|
||||
expr := node.args[0].expr
|
||||
if expr is ast.TypeNode {
|
||||
sym := c.table.sym(c.unwrap_generic(expr.typ))
|
||||
mut unwrapped_typ := c.unwrap_generic(expr.typ)
|
||||
if c.table.sym(expr.typ).kind == .struct_ && expr.typ.has_flag(.generic) {
|
||||
unwrapped_typ = c.table.unwrap_generic_type(expr.typ, c.table.cur_fn.generic_names,
|
||||
c.table.cur_concrete_types)
|
||||
}
|
||||
sym := c.table.sym(unwrapped_typ)
|
||||
if c.table.known_type(sym.name) && sym.kind != .placeholder {
|
||||
mut kind := sym.kind
|
||||
if sym.info is ast.Alias {
|
||||
|
@ -898,7 +903,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||
continue
|
||||
}
|
||||
if param.typ.is_ptr() && !param.is_mut && !call_arg.typ.is_real_pointer()
|
||||
&& call_arg.expr.is_literal() && func.language == .v {
|
||||
&& call_arg.expr.is_literal() && func.language == .v && !c.pref.translated {
|
||||
c.error('literal argument cannot be passed as reference parameter `${c.table.type_to_str(param.typ)}`',
|
||||
call_arg.pos)
|
||||
}
|
||||
|
@ -1188,14 +1193,14 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
method = m
|
||||
has_method = true
|
||||
} else {
|
||||
if left_sym.kind in [.struct_, .sum_type, .interface_] {
|
||||
if final_left_sym.kind in [.struct_, .sum_type, .interface_] {
|
||||
mut parent_type := ast.void_type
|
||||
if left_sym.info is ast.Struct {
|
||||
parent_type = left_sym.info.parent_type
|
||||
} else if left_sym.info is ast.SumType {
|
||||
parent_type = left_sym.info.parent_type
|
||||
} else if left_sym.info is ast.Interface {
|
||||
parent_type = left_sym.info.parent_type
|
||||
if final_left_sym.info is ast.Struct {
|
||||
parent_type = final_left_sym.info.parent_type
|
||||
} else if final_left_sym.info is ast.SumType {
|
||||
parent_type = final_left_sym.info.parent_type
|
||||
} else if final_left_sym.info is ast.Interface {
|
||||
parent_type = final_left_sym.info.parent_type
|
||||
}
|
||||
if parent_type != 0 {
|
||||
type_sym := c.table.sym(parent_type)
|
||||
|
@ -1209,7 +1214,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
if !has_method {
|
||||
has_method = true
|
||||
mut embed_types := []ast.Type{}
|
||||
method, embed_types = c.table.find_method_from_embeds(left_sym, method_name) or {
|
||||
method, embed_types = c.table.find_method_from_embeds(final_left_sym, method_name) or {
|
||||
if err.msg() != '' {
|
||||
c.error(err.msg(), node.pos)
|
||||
}
|
||||
|
@ -1221,14 +1226,14 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
node.from_embed_types = embed_types
|
||||
}
|
||||
}
|
||||
if left_sym.kind == .aggregate {
|
||||
if final_left_sym.kind == .aggregate {
|
||||
// the error message contains the problematic type
|
||||
unknown_method_msg = err.msg()
|
||||
}
|
||||
}
|
||||
if has_method {
|
||||
// x is Bar<T>, x.foo() -> x.foo<T>()
|
||||
rec_sym := c.table.sym(node.left_type)
|
||||
rec_sym := c.table.final_sym(node.left_type)
|
||||
rec_is_generic := left_type.has_flag(.generic)
|
||||
mut rec_concrete_types := []ast.Type{}
|
||||
if rec_sym.info is ast.Struct {
|
||||
|
@ -1438,7 +1443,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
}
|
||||
continue
|
||||
}
|
||||
if param.typ.is_ptr() && !arg.typ.is_real_pointer() && arg.expr.is_literal() {
|
||||
if param.typ.is_ptr() && !arg.typ.is_real_pointer() && arg.expr.is_literal()
|
||||
&& !c.pref.translated {
|
||||
c.error('literal argument cannot be passed as reference parameter `${c.table.type_to_str(param.typ)}`',
|
||||
arg.pos)
|
||||
}
|
||||
|
|
|
@ -165,9 +165,12 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
|
|||
c.in_for_count++
|
||||
prev_loop_label := c.loop_label
|
||||
c.expected_type = ast.bool_type
|
||||
typ := c.expr(node.cond)
|
||||
if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated && !c.file.is_translated {
|
||||
c.error('non-bool used as for condition', node.pos)
|
||||
if node.cond !is ast.EmptyExpr {
|
||||
typ := c.expr(node.cond)
|
||||
if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated
|
||||
&& !c.file.is_translated {
|
||||
c.error('non-bool used as for condition', node.pos)
|
||||
}
|
||||
}
|
||||
if mut node.cond is ast.InfixExpr {
|
||||
if node.cond.op == .key_is {
|
||||
|
|
|
@ -12,7 +12,14 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
|||
mut left_type := c.expr(node.left)
|
||||
node.left_type = left_type
|
||||
c.expected_type = left_type
|
||||
|
||||
if node.op == .key_is {
|
||||
c.inside_x_is_type = true
|
||||
}
|
||||
mut right_type := c.expr(node.right)
|
||||
if node.op == .key_is {
|
||||
c.inside_x_is_type = false
|
||||
}
|
||||
node.right_type = right_type
|
||||
if left_type.is_number() && !left_type.is_ptr()
|
||||
&& right_type in [ast.int_literal_type, ast.float_literal_type] {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/generic_type_name_in_non_generic_function.vv:3:10: error: unexpected generic variable in non-generic function `main`
|
||||
1 | fn main() {
|
||||
2 | c := u8(`D`)
|
||||
3 | if c >= A && c <= Z {
|
||||
| ^
|
||||
4 | println('yes')
|
||||
5 | } else {
|
|
@ -0,0 +1,8 @@
|
|||
fn main() {
|
||||
c := u8(`D`)
|
||||
if c >= A && c <= Z {
|
||||
println('yes')
|
||||
} else {
|
||||
println('no')
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
@ -0,0 +1,9 @@
|
|||
module main
|
||||
|
||||
__global (
|
||||
heap = [10]u8{}
|
||||
)
|
||||
|
||||
fn main() {
|
||||
println(heap)
|
||||
}
|
|
@ -223,7 +223,7 @@ fn (mut g Gen) array_init_with_fields(node ast.ArrayInit, elem_type Type, is_amp
|
|||
}
|
||||
if is_default_array {
|
||||
g.write('($elem_styp[]){')
|
||||
g.expr(node.default_expr)
|
||||
g.write(g.type_default(node.elem_type))
|
||||
g.write('}[0])')
|
||||
} else if node.has_len && node.elem_type == ast.string_type {
|
||||
g.write('&($elem_styp[]){')
|
||||
|
@ -345,10 +345,15 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
|
|||
}
|
||||
left_type := if node.left_type.has_flag(.shared_f) {
|
||||
node.left_type.clear_flag(.shared_f).deref()
|
||||
} else if node.left_type.is_ptr() {
|
||||
node.left_type.deref()
|
||||
} else {
|
||||
node.left_type
|
||||
}
|
||||
g.write('${g.typ(left_type)} ${tmp}_orig = ')
|
||||
if !node.left_type.has_flag(.shared_f) && node.left_type.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
if node.left_type.has_flag(.shared_f) {
|
||||
g.write('->val')
|
||||
|
|
|
@ -2126,8 +2126,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
|||
g.expr(expr)
|
||||
return
|
||||
}
|
||||
if exp_sym.info is ast.Interface && got_type.idx() != expected_type.idx()
|
||||
&& !expected_type.has_flag(.optional) {
|
||||
if got_sym.info !is ast.Interface && exp_sym.info is ast.Interface
|
||||
&& got_type.idx() != expected_type.idx() && !expected_type.has_flag(.optional) {
|
||||
if expr is ast.StructInit && !got_type.is_ptr() {
|
||||
g.inside_cast_in_heap++
|
||||
got_styp := g.cc_type(got_type.ref(), true)
|
||||
|
@ -3925,8 +3925,8 @@ fn (mut g Gen) ident(node ast.Ident) {
|
|||
// `p_mobjthinker` => `P_MobjThinker`
|
||||
if f := g.table.find_fn(node.name) {
|
||||
// TODO PERF fn lookup for each fn call in translated mode
|
||||
if f.attrs.contains('c') {
|
||||
name = f.attrs[0].arg
|
||||
if cattr := f.attrs.find_first('c') {
|
||||
name = cattr.arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3970,8 +3970,8 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) {
|
|||
// TODO handle the type in fn casts, not just exprs
|
||||
/*
|
||||
info := sym.info as ast.FnType
|
||||
if info.func.attrs.contains('c') {
|
||||
// name = f.attrs[0].arg
|
||||
if cattr := info.func.attrs.find_first('c') {
|
||||
name = cattr.arg
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -4398,11 +4398,11 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
|
|||
}
|
||||
}
|
||||
name := c_name(field.name)
|
||||
const_name := if node.attrs.contains('export') && !g.is_builtin_mod {
|
||||
// TODO this only works for the first const in the group for now
|
||||
node.attrs[0].arg
|
||||
} else {
|
||||
'_const_' + name
|
||||
mut const_name := '_const_' + name
|
||||
if !g.is_builtin_mod {
|
||||
if cattr := node.attrs.find_first('export') {
|
||||
const_name = cattr.arg
|
||||
}
|
||||
}
|
||||
field_expr := field.expr
|
||||
match field.expr {
|
||||
|
@ -4667,7 +4667,8 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
|
|||
if field.has_expr || cinit {
|
||||
if g.pref.translated {
|
||||
g.definitions.write_string(' = ${g.expr_string(field.expr)}')
|
||||
} else if (field.expr.is_literal() && should_init) || cinit {
|
||||
} else if (field.expr.is_literal() && should_init) || cinit
|
||||
|| (field.expr is ast.ArrayInit && (field.expr as ast.ArrayInit).is_fixed) {
|
||||
// Simple literals can be initialized right away in global scope in C.
|
||||
// e.g. `int myglobal = 10;`
|
||||
g.definitions.write_string(' = ${g.expr_string(field.expr)}')
|
||||
|
@ -5486,8 +5487,9 @@ fn (mut g Gen) enum_val(node ast.EnumVal) {
|
|||
// && g.inside_switch
|
||||
if g.pref.translated && node.typ.is_number() {
|
||||
// Mostly in translated code, when C enums are used as ints in switches
|
||||
sym := g.table.sym(node.typ)
|
||||
g.write('/* $node enum val is_number $node.mod styp=$styp sym=$sym*/_const_main__$node.val')
|
||||
// sym := g.table.sym(node.typ)
|
||||
// g.write('/* $node enum val is_number $node.mod styp=$styp sym=$sym*/_const_main__$node.val')
|
||||
g.write('_const_main__$node.val')
|
||||
} else {
|
||||
g.write('${styp}__$node.val')
|
||||
}
|
||||
|
@ -5742,7 +5744,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
|
|||
|
||||
for method in methods {
|
||||
mut name := method.name
|
||||
if inter_info.parent_type.has_flag(.generic) {
|
||||
if method.generic_names.len > 0 && inter_info.parent_type.has_flag(.generic) {
|
||||
parent_sym := g.table.sym(inter_info.parent_type)
|
||||
match parent_sym.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
|
@ -5759,7 +5761,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
|
|||
}
|
||||
// .speak = Cat_speak
|
||||
if st_sym.info is ast.Struct {
|
||||
if st_sym.info.parent_type.has_flag(.generic) {
|
||||
if method.generic_names.len > 0 && st_sym.info.parent_type.has_flag(.generic) {
|
||||
name = g.generic_fn_name(st_sym.info.concrete_types, method.name,
|
||||
false)
|
||||
}
|
||||
|
|
|
@ -446,20 +446,16 @@ fn (mut g Gen) c_fn_name(node &ast.FnDecl) ?string {
|
|||
name = g.generic_fn_name(g.cur_concrete_types, name, true)
|
||||
}
|
||||
|
||||
if (g.pref.translated || g.file.is_translated) && node.attrs.contains('c') {
|
||||
// This fixes unknown symbols errors when building separate .c => .v files
|
||||
// into .o files
|
||||
//
|
||||
// example:
|
||||
// [c: 'P_TryMove']
|
||||
// fn p_trymove(thing &Mobj_t, x int, y int) bool
|
||||
//
|
||||
// =>
|
||||
//
|
||||
// bool P_TryMove(main__Mobj_t* thing, int x, int y);
|
||||
//
|
||||
// In fn_call every time `p_trymove` is called, `P_TryMove` will be generated instead.
|
||||
name = node.attrs[0].arg
|
||||
if g.pref.translated || g.file.is_translated {
|
||||
if cattr := node.attrs.find_first('c') {
|
||||
// This fixes unknown symbols errors when building separate .c => .v files into .o files
|
||||
// example:
|
||||
// [c: 'P_TryMove'] fn p_trymove(thing &Mobj_t, x int, y int) bool
|
||||
// translates to:
|
||||
// bool P_TryMove(main__Mobj_t* thing, int x, int y);
|
||||
// In fn_call every time `p_trymove` is called, `P_TryMove` will be generated instead.
|
||||
name = cattr.arg
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
@ -932,6 +928,10 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
return
|
||||
}
|
||||
}
|
||||
} else if node.left is ast.None {
|
||||
// none.str()
|
||||
g.gen_expr_to_string(node.left, ast.none_type)
|
||||
return
|
||||
}
|
||||
g.get_str_fn(rec_type)
|
||||
} else if node.name == 'free' {
|
||||
|
@ -1239,8 +1239,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
|||
// every time `p_trymove` is called, `P_TryMove` must be generated instead.
|
||||
if f := g.table.find_fn(node.name) {
|
||||
// TODO PERF fn lookup for each fn call in translated mode
|
||||
if f.attrs.contains('c') {
|
||||
name = f.attrs[0].arg
|
||||
if cattr := f.attrs.find_first('c') {
|
||||
name = cattr.arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -484,7 +484,7 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
if !node.is_option {
|
||||
g.or_block(tmp_opt, node.or_expr, elem_type)
|
||||
}
|
||||
g.write('\n$cur_line*($elem_type_str*)${tmp_opt}.data')
|
||||
g.write('\n${cur_line}(*($elem_type_str*)${tmp_opt}.data)')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// From issue #14679
|
||||
fn iterate_linear(value1 u32, value2 u32, length u32) []u32 {
|
||||
step := u32((value2 - value1) / (length - 1))
|
||||
return []u32{len: int(length), init: value1 + step * u32(it + 1)}
|
||||
}
|
||||
|
||||
pub fn iterate_rect_single(val1 u32, val2 u32, val3 u32, val4 u32, width u32, height u32) [][]u32 {
|
||||
left := iterate_linear(val1, val3, height)
|
||||
right := iterate_linear(val2, val4, height)
|
||||
return [][]u32{len: int(width), init: iterate_linear(left[it], right[it], width)}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
[1, 0, 0]
|
||||
[0, 1, 0]
|
||||
[0, 0, 1]
|
|
@ -0,0 +1,18 @@
|
|||
fn (mut a [][]f32) str() string {
|
||||
return a.map(it.str()).join('\n')
|
||||
}
|
||||
|
||||
fn identity(n int) [][]f32 {
|
||||
mut res := [][]f32{len: n, init: []f32{len: n, init: 0.0}}
|
||||
for i in 0 .. n {
|
||||
res[i][i] = 1.0
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := identity(3)
|
||||
|
||||
println(a)
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<none>
|
|
@ -0,0 +1 @@
|
|||
println(none.str())
|
|
@ -135,6 +135,24 @@ fn (mut g Gen) cmp_reg(reg Register, reg2 Register) {
|
|||
g.println('cmp $reg, $reg2')
|
||||
}
|
||||
|
||||
// cmp $reg, 0
|
||||
fn (mut g Gen) cmp_zero(reg Register) {
|
||||
g.write8(0x48)
|
||||
g.write8(0x39)
|
||||
|
||||
match reg {
|
||||
.rax {
|
||||
g.write8(0x04)
|
||||
g.write8(0x25)
|
||||
}
|
||||
else {
|
||||
panic('unhandled cmp $reg, 0')
|
||||
}
|
||||
}
|
||||
|
||||
g.write32(0)
|
||||
}
|
||||
|
||||
fn (mut g Gen) cmp_var_reg(var_name string, reg Register) {
|
||||
g.write8(0x48) // 83 for 1 byte?
|
||||
g.write8(0x39)
|
||||
|
@ -225,6 +243,26 @@ fn (mut g Gen) jmp(addr i64) {
|
|||
g.write8(offset)
|
||||
}
|
||||
*/
|
||||
|
||||
fn (mut g Gen) mov32(reg Register, val int) {
|
||||
match reg {
|
||||
.rax {
|
||||
g.write8(0xb8)
|
||||
}
|
||||
.rdi {
|
||||
g.write8(0xbf)
|
||||
}
|
||||
.rcx {
|
||||
g.write8(0xb9)
|
||||
}
|
||||
else {
|
||||
panic('unhandled mov32 $reg')
|
||||
}
|
||||
}
|
||||
g.write32(val)
|
||||
g.println('mov32 $reg, $val')
|
||||
}
|
||||
|
||||
fn (mut g Gen) mov64(reg Register, val i64) {
|
||||
match reg {
|
||||
.eax {
|
||||
|
@ -272,6 +310,21 @@ fn (mut g Gen) mov64(reg Register, val i64) {
|
|||
g.println('mov64 $reg, $val')
|
||||
}
|
||||
|
||||
fn (mut g Gen) movabs(reg Register, val i64) {
|
||||
match reg {
|
||||
.rsi {
|
||||
g.write8(0x48)
|
||||
g.write8(0xbe)
|
||||
}
|
||||
else {
|
||||
panic('unhandled movabs $reg, $val')
|
||||
}
|
||||
}
|
||||
|
||||
g.write64(val)
|
||||
g.println('movabs $reg, $val')
|
||||
}
|
||||
|
||||
fn (mut g Gen) mov_reg_to_var(var_offset int, reg Register) {
|
||||
// 89 7d fc mov DWORD PTR [rbp-0x4],edi
|
||||
match reg {
|
||||
|
@ -293,6 +346,27 @@ fn (mut g Gen) mov_reg_to_var(var_offset int, reg Register) {
|
|||
g.println('mov DWORD PTR[rbp-$var_offset.hex2()],$reg')
|
||||
}
|
||||
|
||||
fn (mut g Gen) lea_var_to_reg(reg Register, var_offset int) {
|
||||
match reg {
|
||||
.rax, .rbx, .rsi {
|
||||
g.write8(0x48)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
g.write8(0x8d)
|
||||
match reg {
|
||||
.eax, .rax { g.write8(0x45) }
|
||||
.edi, .rdi { g.write8(0x7d) }
|
||||
.rsi { g.write8(0x75) }
|
||||
.rdx { g.write8(0x55) }
|
||||
.rbx { g.write8(0x5d) }
|
||||
.rcx { g.write8(0x4d) }
|
||||
else { g.n_error('lea_var_to_reg $reg') }
|
||||
}
|
||||
g.write8(0xff - var_offset + 1)
|
||||
g.println('lea $reg, [rbp-$var_offset.hex2()]')
|
||||
}
|
||||
|
||||
fn (mut g Gen) mov_var_to_reg(reg Register, var_offset int) {
|
||||
// 8b 7d f8 mov edi,DWORD PTR [rbp-0x8]
|
||||
match reg {
|
||||
|
@ -473,6 +547,21 @@ pub fn (mut g Gen) allocate_string(s string, opsize int, typ RelocType) int {
|
|||
return str_pos
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) var_zero(vo int, size int) {
|
||||
g.mov32(.rcx, size)
|
||||
g.lea_var_to_reg(.rdi, vo)
|
||||
g.write8(0xb0)
|
||||
g.write8(0x00)
|
||||
g.println('mov al, 0')
|
||||
g.rep_stosb()
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) rep_stosb() {
|
||||
g.write8(0xf3)
|
||||
g.write8(0xaa)
|
||||
g.println('rep stosb')
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) cld_repne_scasb() {
|
||||
g.write8(0xfc)
|
||||
g.println('cld')
|
||||
|
@ -500,6 +589,20 @@ pub fn (mut g Gen) xor(r Register, v int) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) test_reg(r Register) {
|
||||
match r {
|
||||
.rdi {
|
||||
g.write8(0x48)
|
||||
g.write8(0x85)
|
||||
g.write8(0xff)
|
||||
g.println('test rdi, rdi')
|
||||
}
|
||||
else {
|
||||
panic('unhandled test $r, $r')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return length in .rax of string pointed by given register
|
||||
pub fn (mut g Gen) inline_strlen(r Register) {
|
||||
g.mov_reg(.rdi, r)
|
||||
|
@ -676,8 +779,11 @@ fn (mut g Gen) learel(reg Register, val int) {
|
|||
.rsi {
|
||||
g.write8(0x35)
|
||||
}
|
||||
.rcx {
|
||||
g.write8(0x0d)
|
||||
}
|
||||
else {
|
||||
g.n_error('learel must use rsi or rax')
|
||||
g.n_error('learel must use rsi, rcx or rax')
|
||||
}
|
||||
}
|
||||
g.write32(val)
|
||||
|
@ -823,6 +929,20 @@ fn (mut g Gen) mul_reg(a Register, b Register) {
|
|||
g.println('mul $a')
|
||||
}
|
||||
|
||||
fn (mut g Gen) imul_reg(r Register) {
|
||||
match r {
|
||||
.rsi {
|
||||
g.write8(0x48)
|
||||
g.write8(0xf7)
|
||||
g.write8(0xee)
|
||||
g.println('imul $r')
|
||||
}
|
||||
else {
|
||||
panic('unhandled imul $r')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) div_reg(a Register, b Register) {
|
||||
if a != .rax {
|
||||
panic('div always operates on rax')
|
||||
|
@ -846,11 +966,24 @@ fn (mut g Gen) div_reg(a Register, b Register) {
|
|||
g.println('div $a')
|
||||
}
|
||||
|
||||
fn (mut g Gen) mod_reg(a Register, b Register) {
|
||||
g.div_reg(a, b)
|
||||
g.mov_reg(.rdx, .rax)
|
||||
}
|
||||
|
||||
fn (mut g Gen) sub_reg(a Register, b Register) {
|
||||
if a == .rax && b == .rbx {
|
||||
g.write8(0x48)
|
||||
g.write8(0x29)
|
||||
g.write8(0xd8)
|
||||
} else if a == .rdx && b == .rax {
|
||||
g.write8(0x48)
|
||||
g.write8(0x29)
|
||||
g.write8(0xc2)
|
||||
} else if a == .rdi && b == .rax {
|
||||
g.write8(0x48)
|
||||
g.write8(0x29)
|
||||
g.write8(0xc7)
|
||||
} else {
|
||||
panic('unhandled add $a, $b')
|
||||
}
|
||||
|
@ -866,6 +999,10 @@ fn (mut g Gen) add_reg(a Register, b Register) {
|
|||
g.write8(0x48)
|
||||
g.write8(0x01)
|
||||
g.write8(0xf8)
|
||||
} else if a == .rax && b == .rax {
|
||||
g.write8(0x48)
|
||||
g.write8(0x01)
|
||||
g.write8(0xc0)
|
||||
} else {
|
||||
panic('unhandled add $a, $b')
|
||||
}
|
||||
|
@ -906,12 +1043,39 @@ fn (mut g Gen) mov_reg(a Register, b Register) {
|
|||
g.write8(0x48)
|
||||
g.write8(0x89)
|
||||
g.write8(0xc6)
|
||||
} else if a == .rdi && b == .rdx {
|
||||
g.write8(0x48)
|
||||
g.write8(0x89)
|
||||
g.write8(0xd7)
|
||||
} else if a == .rdi && b == .rax {
|
||||
g.write8(0x48)
|
||||
g.write8(0x89)
|
||||
g.write8(0xc7)
|
||||
} else {
|
||||
g.n_error('unhandled mov_reg combination for $a $b')
|
||||
}
|
||||
g.println('mov $a, $b')
|
||||
}
|
||||
|
||||
fn (mut g Gen) sar8(r Register, val u8) {
|
||||
g.write8(0x48)
|
||||
g.write8(0xc1)
|
||||
|
||||
match r {
|
||||
.rax {
|
||||
g.write8(0xf8)
|
||||
}
|
||||
.rdx {
|
||||
g.write8(0xfa)
|
||||
}
|
||||
else {
|
||||
panic('unhandled sar $r, $val')
|
||||
}
|
||||
}
|
||||
g.write8(val)
|
||||
g.println('sar $r, $val')
|
||||
}
|
||||
|
||||
// generates `mov rbp, rsp`
|
||||
fn (mut g Gen) mov_rbp_rsp() {
|
||||
g.write8(0x48)
|
||||
|
@ -1668,9 +1832,32 @@ pub fn (mut g Gen) allocate_var(name string, size int, initial_val int) int {
|
|||
g.write8(0xff - n + 1)
|
||||
g.stack_var_pos += size
|
||||
g.var_offset[name] = g.stack_var_pos
|
||||
|
||||
// Generate the value assigned to the variable
|
||||
g.write32(initial_val)
|
||||
match size {
|
||||
1 {
|
||||
g.write8(initial_val)
|
||||
}
|
||||
4 {
|
||||
g.write32(initial_val)
|
||||
}
|
||||
8 {
|
||||
g.write32(initial_val) // fixme: 64-bit segfaulting
|
||||
}
|
||||
else {
|
||||
g.n_error('allocate_var: bad size $size')
|
||||
}
|
||||
}
|
||||
|
||||
// println('allocate_var(size=$size, initial_val=$initial_val)')
|
||||
g.println('mov [rbp-$n.hex2()], $initial_val ; Allocate var `$name`')
|
||||
return g.stack_var_pos
|
||||
}
|
||||
|
||||
fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
|
||||
if r != .rax {
|
||||
g.mov_reg(.rax, r)
|
||||
}
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
|
|
@ -299,6 +299,12 @@ fn (mut g Gen) gen_typeof_expr(it ast.TypeOf, newline bool) {
|
|||
g.learel(.rax, g.allocate_string('$r$nl', 3, .rel32))
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_var_to_string(reg Register, vo int) {
|
||||
buffer := g.allocate_array('itoa-buffer', 1, 32) // 32 characters should be enough
|
||||
g.convert_int_to_string(reg, buffer)
|
||||
g.lea_var_to_reg(reg, buffer)
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
|
||||
newline := name in ['println', 'eprintln']
|
||||
fd := if name in ['eprint', 'eprintln'] { 2 } else { 1 }
|
||||
|
@ -316,16 +322,15 @@ pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
|
|||
}
|
||||
ast.Ident {
|
||||
vo := g.try_var_offset(expr.name)
|
||||
|
||||
if vo != -1 {
|
||||
g.n_error('Printing idents is not yet supported in the native backend')
|
||||
// g.mov_var_to_reg(.rsi, vo)
|
||||
// g.mov_reg(.rax, .rsi)
|
||||
// g.learel(.rax, vo * 8)
|
||||
// g.relpc(.rax, .rsi)
|
||||
// g.learel(.rax, g.allocate_string('$vo\n', 3, .rel32))
|
||||
// g.expr(expr)
|
||||
g.gen_var_to_string(.rax, vo)
|
||||
if newline {
|
||||
g.gen_print('\n', fd)
|
||||
}
|
||||
} else {
|
||||
g.gen_print_reg(.rax, 3, fd)
|
||||
}
|
||||
g.gen_print_reg(.rax, 3, fd)
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
g.learel(.rax, g.allocate_string('$expr.val\n', 3, .rel32))
|
||||
|
@ -775,7 +780,9 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
ast.PostfixExpr {
|
||||
g.postfix_expr(node)
|
||||
}
|
||||
ast.StringLiteral {}
|
||||
ast.StringLiteral {
|
||||
g.allocate_string(node.val, 3, .rel32)
|
||||
}
|
||||
ast.StructInit {}
|
||||
ast.GoExpr {
|
||||
g.v_error('native backend doesnt support threads yet', node.pos)
|
||||
|
|
|
@ -29,9 +29,15 @@ fn test_stderr() {
|
|||
eprintln('2(World)')
|
||||
}
|
||||
|
||||
fn test_idents() {
|
||||
x := 5
|
||||
// println(x) uncomment again when int-to-string conversion is working
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_stdout()
|
||||
test_stderr()
|
||||
test_numbers()
|
||||
test_oof()
|
||||
test_idents()
|
||||
}
|
||||
|
|
|
@ -85,6 +85,18 @@ pub fn (mut p Preferences) fill_with_defaults() {
|
|||
}
|
||||
rpath_name := os.file_name(rpath)
|
||||
p.building_v = !p.is_repl && (rpath_name == 'v' || rpath_name == 'vfmt.v')
|
||||
if p.gc_mode == .unknown {
|
||||
if p.backend != .c || p.building_v || p.is_bare || p.ccompiler == 'msvc' {
|
||||
p.gc_mode = .no_gc
|
||||
p.build_options << ['-gc', 'none']
|
||||
} else {
|
||||
// enable the GC by default
|
||||
p.gc_mode = .boehm_full_opt
|
||||
p.parse_define('gcboehm')
|
||||
p.parse_define('gcboehm_full')
|
||||
p.parse_define('gcboehm_opt')
|
||||
}
|
||||
}
|
||||
if p.os == ._auto {
|
||||
// No OS specifed? Use current system
|
||||
p.os = get_host_os()
|
||||
|
|
|
@ -24,6 +24,7 @@ pub enum AssertFailureMode {
|
|||
}
|
||||
|
||||
pub enum GarbageCollectionMode {
|
||||
unknown
|
||||
no_gc
|
||||
boehm_full // full garbage collection mode
|
||||
boehm_incr // incremental garbage colletion mode
|
||||
|
@ -204,7 +205,7 @@ pub mut:
|
|||
cleanup_files []string // list of temporary *.tmp.c and *.tmp.c.rsp files. Cleaned up on successfull builds.
|
||||
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
|
||||
cache_manager vcache.CacheManager
|
||||
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak, ...
|
||||
gc_mode GarbageCollectionMode = .unknown // .no_gc, .boehm, .boehm_leak, ...
|
||||
assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure
|
||||
message_limit int = 100 // the maximum amount of warnings/errors/notices that will be accumulated
|
||||
nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64
|
||||
|
@ -327,9 +328,15 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||
'-gc' {
|
||||
gc_mode := cmdline.option(current_args, '-gc', '')
|
||||
match gc_mode {
|
||||
'', 'none' {
|
||||
'none' {
|
||||
res.gc_mode = .no_gc
|
||||
}
|
||||
'', 'boehm' {
|
||||
res.gc_mode = .boehm_full_opt // default mode
|
||||
res.parse_define('gcboehm')
|
||||
res.parse_define('gcboehm_full')
|
||||
res.parse_define('gcboehm_opt')
|
||||
}
|
||||
'boehm_full' {
|
||||
res.gc_mode = .boehm_full
|
||||
res.parse_define('gcboehm')
|
||||
|
@ -352,12 +359,6 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||
res.parse_define('gcboehm_incr')
|
||||
res.parse_define('gcboehm_opt')
|
||||
}
|
||||
'boehm' {
|
||||
res.gc_mode = .boehm_full_opt // default mode
|
||||
res.parse_define('gcboehm')
|
||||
res.parse_define('gcboehm_full')
|
||||
res.parse_define('gcboehm_opt')
|
||||
}
|
||||
'boehm_leak' {
|
||||
res.gc_mode = .boehm_leak
|
||||
res.parse_define('gcboehm')
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
module main
|
||||
|
||||
interface TypeFactory<T> {
|
||||
get_type(type_name string) T
|
||||
}
|
||||
|
||||
enum NodeType {
|
||||
unknown
|
||||
expression
|
||||
statement
|
||||
literal
|
||||
}
|
||||
|
||||
struct EnumTypeFactory {}
|
||||
|
||||
fn (f &EnumTypeFactory) get_type(type_name string) NodeType {
|
||||
return match type_name {
|
||||
'expression' { NodeType.expression }
|
||||
'statement' { NodeType.statement }
|
||||
'literal' { NodeType.literal }
|
||||
else { NodeType.unknown }
|
||||
}
|
||||
}
|
||||
|
||||
struct RawNode {
|
||||
type_name string
|
||||
}
|
||||
|
||||
struct Node<T> {
|
||||
factory TypeFactory<T>
|
||||
type_name NodeType
|
||||
raw_node RawNode
|
||||
}
|
||||
|
||||
fn new_node<T>(factory TypeFactory<T>, raw_node RawNode) ?Node<T> {
|
||||
return Node<T>{
|
||||
factory: factory
|
||||
type_name: factory.get_type(raw_node.type_name)
|
||||
raw_node: raw_node
|
||||
}
|
||||
}
|
||||
|
||||
fn program<T>(factory TypeFactory<T>) ? {
|
||||
root1 := new_node<T>(factory, RawNode{'literal'})?
|
||||
println(root1)
|
||||
assert root1.type_name == .literal
|
||||
|
||||
root2 := new_node<T>(root1.factory, RawNode{'expression'})?
|
||||
println(root2)
|
||||
assert root2.type_name == .expression
|
||||
}
|
||||
|
||||
fn test_generic_interface_with_non_generic_method() ? {
|
||||
program<NodeType>(&EnumTypeFactory{})?
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
module main
|
||||
|
||||
struct Container<T> {
|
||||
value T
|
||||
}
|
||||
|
||||
fn (c Container<T>) id() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
type Text = Container<string>
|
||||
|
||||
fn test_generic_method_on_receiver_aliases_type() {
|
||||
t := Text{'test'}
|
||||
println(t.id())
|
||||
assert t.id() == 1
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
struct Foo {
|
||||
mut:
|
||||
foo map[int]Bar
|
||||
}
|
||||
|
||||
struct Bar {
|
||||
mut:
|
||||
bar map[int]string
|
||||
}
|
||||
|
||||
fn test_nested_map_index() ? {
|
||||
f := Foo{
|
||||
foo: {
|
||||
11: Bar{
|
||||
bar: {
|
||||
22: 'hello'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ret := f.foo[11]?.bar[22]?
|
||||
println(ret)
|
||||
assert ret == 'hello'
|
||||
}
|
|
@ -86,7 +86,7 @@ fn test_all() {
|
|||
base_filename := os.file_name(test).replace('.v', '')
|
||||
exe_filename := '$wrkdir/$base_filename'
|
||||
full_path_to_source_file := os.join_path(vroot, test)
|
||||
compile_cmd := '${os.quoted_path(vexe)} -o ${os.quoted_path(exe_filename)} -cg -cflags "-w" -experimental -autofree ${os.quoted_path(full_path_to_source_file)}'
|
||||
compile_cmd := '${os.quoted_path(vexe)} -o ${os.quoted_path(exe_filename)} -cg -cflags "-w" -experimental -gc none -autofree ${os.quoted_path(full_path_to_source_file)}'
|
||||
vprintln('compile cmd: ${bold(compile_cmd)}')
|
||||
res := os.execute(compile_cmd)
|
||||
if res.exit_code != 0 {
|
||||
|
|
|
@ -39,6 +39,7 @@ pub fn new_cache_manager(opts []string) CacheManager {
|
|||
if vcache_basepath == '' {
|
||||
vcache_basepath = os.join_path(os.vmodules_dir(), 'cache')
|
||||
}
|
||||
nlog(@FN, 'vcache_basepath: $vcache_basepath\n opts: $opts\n os.args: ${os.args.join(' ')}')
|
||||
dlog(@FN, 'vcache_basepath: $vcache_basepath | opts:\n $opts')
|
||||
if !os.is_dir(vcache_basepath) {
|
||||
os.mkdir_all(vcache_basepath) or { panic(err) }
|
||||
|
@ -54,7 +55,14 @@ pub fn new_cache_manager(opts []string) CacheManager {
|
|||
os.write_file(readme_file, readme_content) or { panic(err) }
|
||||
dlog(@FN, 'created readme_file:\n $readme_file')
|
||||
}
|
||||
original_vopts := opts.join('|')
|
||||
mut deduped_opts := map[string]bool{}
|
||||
for o in opts {
|
||||
deduped_opts[o] = true
|
||||
}
|
||||
deduped_opts_keys := deduped_opts.keys().filter(it != '' && !it.starts_with("['gcboehm', "))
|
||||
// TODO: do not filter the gcboehm options here, instead just start `v build-module vlib/builtin` without the -d gcboehm etc.
|
||||
// Note: the current approach of filtering the gcboehm keys may interfere with (potential) other gc modes.
|
||||
original_vopts := deduped_opts_keys.join('|')
|
||||
return CacheManager{
|
||||
basepath: vcache_basepath
|
||||
vopts: original_vopts
|
||||
|
@ -125,6 +133,15 @@ pub fn (mut cm CacheManager) load(postfix string, key string) ?string {
|
|||
|
||||
[if trace_usecache ?]
|
||||
pub fn dlog(fname string, s string) {
|
||||
xlog(fname, s)
|
||||
}
|
||||
|
||||
[if trace_usecache_n ?]
|
||||
fn nlog(fname string, s string) {
|
||||
xlog(fname, s)
|
||||
}
|
||||
|
||||
fn xlog(fname string, s string) {
|
||||
pid := unsafe { mypid() }
|
||||
if fname[0] != `|` {
|
||||
eprintln('> VCache | pid: $pid | CacheManager.$fname $s')
|
||||
|
|
|
@ -252,7 +252,7 @@ pub fn (mut ctx Context) file(f_path string) Result {
|
|||
return Result{}
|
||||
}
|
||||
content_type := vweb.mime_types[ext]
|
||||
if content_type.len == O {
|
||||
if content_type.len == 0 {
|
||||
eprintln('no MIME type found for extension $ext')
|
||||
ctx.server_error(500)
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue