1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

Compare commits

...

57 Commits

Author SHA1 Message Date
Alexander Medvednikov
428fd7f57f all: do not allow uninitialized function pointers 2023-08-03 21:06:36 +03:00
yuyi
453137384e
ast: clean up stringify_fn_decl() and stringify_anon_decl(), make them methods on ast.Table (#19053) 2023-08-03 19:50:37 +03:00
Swastik Baranwal
6a4bfef2c5
parser: disallow having builtin type as type names for enum, sum type and alias (#19043) 2023-08-03 11:42:31 +03:00
shove
9f5e9ba1cf
cgen: fix an error with ptr interpolation (fix #19048) (#19049) 2023-08-03 11:18:22 +03:00
blackshirt
ef5c3cdb73
math.unsigned: change uint256_from_dec_str to return a result, instead of an option (#19041) 2023-08-03 10:18:21 +03:00
yuyi
5ec7ee916a
parser: fix formatting comptime if expr script (v fmt no longer eats the body of top level $if xyz ? {}) (#19044) 2023-08-03 09:30:00 +03:00
shove
b556f1302f
parser: fix parse error in the type of a ref array when the element type is a structure of another mod(fix #19033) (#19039) 2023-08-03 09:25:03 +03:00
kbkpbot
fe9bdd4168
sync: make sync.Direction public (#19047) 2023-08-03 09:22:48 +03:00
Delyan Angelov
8ee1667a9a
tools: fix exiting the v repl, after just Enter (regression happened in bf00ac6) 2023-08-02 10:58:31 +03:00
yuyi
1d9835f0e4
parser, cgen: fix for i++; i<10; i++ { (fix #18445) (#19035) 2023-08-02 10:47:52 +03:00
Turiiya
6b978a6b5a
vdoc: refactor theme files (#19024) 2023-08-02 10:46:18 +03:00
yuyi
43800a05e8
fmt: fix formatting of fn with argument comments (#19038) 2023-08-02 10:40:09 +03:00
Lucas V. Araujo
ecca3b155e
net.mbedtls: add SSLListener to allow creating SSL servers (#19022) 2023-08-01 20:45:50 +03:00
yuyi
600f891d3a
checker, cgen: implement fixed array of threads wait() (#19032) 2023-08-01 20:45:00 +03:00
shove
b6d6d4b037
parser: fix improper token advancement when parsing the types of struct thread type fields(fix #19029) (#19030) 2023-08-01 19:10:52 +03:00
Delyan Angelov
ecf8fcd45a
ci: bump timeout-minutes: to 20, for v-compiles-os-android, gg-regressions, doom-regressions, to reduce false positives 2023-08-01 12:09:41 +03:00
Delyan Angelov
177bb30013
net: change default of the socket used by net.listen_tcp, to dualstack, even if the OS has a different default. Allow changing the listen backlog too
With this change, example vweb programs, will continue to be available to both
ipv6 and ipv4 connections from the same machine, even after doing (on linux):
`echo 1 | sudo tee /proc/sys/net/ipv6/bindv6only`

Previously, after that, vweb programs responded only to ipv6 connections, but not to ipv4 ones,
i.e. opening http://127.0.0.1:8082/ stopped working, for `v run examples/vweb/vweb_example.v` .

Note: GO web servers have the same behaviour, which is convenient for development/testing,
since it makes the programs more consistent and robust in the face of OS settings changes.
2023-08-01 11:32:08 +03:00
yuyi
367e38d7d1
parser: change fn_args() to fn_params() (#19027) 2023-08-01 07:27:53 +03:00
Turiiya
def0161281
github: update issue template presets (#19026) 2023-08-01 07:25:27 +03:00
yuyi
a1aca4c578
checker: fix generic struct field init recursively (related #19014) (#19025) 2023-08-01 07:20:33 +03:00
Turiiya
5061aeee64
vdoc: fix toc height (accidental height removal in 9750061) (#19023) 2023-08-01 07:11:44 +03:00
Delyan Angelov
f4c2ecfaa9
crypto.sha512: make the new384/0, new512_256/0, new512_224/0 functions public 2023-08-01 06:55:41 +03:00
yuyi
9be80198fc
checker: fix generic struct field with default fn_type value (fix #19011) (#19014) 2023-07-31 21:30:12 +03:00
jacksonmowry
a609d6c9d1
db.pg: add parameter syntax to docs (#19003) 2023-07-31 21:26:45 +03:00
Kim Shrier
fd81bae361
net.websocket: remove unnecessary manual frees of static strings (#19009) 2023-07-31 21:23:33 +03:00
yuyi
8861538c66
ast, parser, fmt: implement inline comments (#19012) 2023-07-31 21:22:51 +03:00
Artem Yurchenko
0f861db9b0
gg: implement Android specific APK asset loading for the create_image function (#19015) 2023-07-31 17:40:16 +03:00
Turiiya
9750061d70
vdoc: fix scrollspy and initial keyboard navigability (#19017) 2023-07-31 17:38:25 +03:00
Delyan Angelov
81e99a2af3
term: fix vlib/term/termios/termios_test.v on windows 2023-07-31 14:22:03 +03:00
Delyan Angelov
367289a1f1
sync: fix compilation on windows 2023-07-31 11:18:12 +03:00
Delyan Angelov
2cd5b8a86d
time: reduce the diff for v run cmd/tools/check_os_api_parity time 2023-07-31 11:02:10 +03:00
Delyan Angelov
618961fab5
tests: reduce sensitivity/flakyness of vlib/v/slow_tests/crun_mode/crun_test.v 2023-07-31 10:58:30 +03:00
Delyan Angelov
d97423d385
tools: add a test for v -os wasm32_emscripten examples/2048 in v test-all 2023-07-31 10:57:57 +03:00
Delyan Angelov
30d4e25385
term: fix v run cmd/tools/check_os_api_parity.v term, for term.clear 2023-07-31 10:39:28 +03:00
Delyan Angelov
32114a679a
os,term.termios: add termios.set_state/2, state.disable_echo/0, use them in os.input_password, to fix v -os wasm32_emscripten examples/2048/ 2023-07-31 10:28:45 +03:00
Delyan Angelov
37e7d5f5ae
Revert "sokol: use GLCORE33 on linux", since it is already done by a #flag linux -DSOKOL_GLCORE33 later on
This reverts commit 7cec70e525.
2023-07-30 18:32:24 +03:00
Swastik Baranwal
8735694d13
parser: disallow declaring static functions as method receivers (#19007) 2023-07-30 06:11:11 +03:00
Turiiya
77049600e6
tests: make projects_with_c_code closer to actual projects (#19008) 2023-07-30 06:00:48 +03:00
katekyy
b622dca915
builtin: fix split_nth() and rsplit_nth() on an empty delimeter (#19005) 2023-07-30 00:12:51 +03:00
Turiiya
e78e468d5f
vdoc: include the project root folder, when searching for readme of src/ (#19000) 2023-07-30 00:00:18 +03:00
Delyan Angelov
1c2b4e76dc
tests: fix diff.color_compare_strings parameter order in test files, to make analysing the results easier 2023-07-29 16:42:40 +03:00
yuyi
112a1278bb
parser, fmt: fix formatting interface method with pre-comments (#18998) 2023-07-29 15:40:45 +03:00
shove
8586f18383
checker: fix compiler crashes when passing an extra decompose parameter to a function(fix: 18995) (#18996) 2023-07-29 15:38:39 +03:00
yuyi
a61a2fd328
parser: fix for_c_stmt that init with var assign (#19004) 2023-07-29 15:29:35 +03:00
Turiiya
c4a679186f
examples, readme: fix typos (#18994) 2023-07-29 15:27:03 +03:00
Turiiya
490a014bf6
tools: support a toc for projects, with single exposing module, in v doc (#19001) 2023-07-29 15:21:15 +03:00
Delyan Angelov
4ed9703e22
ci: bump --max_time to 1731 for the performance-regressions job, to reduce the chance of false positive failures again 2023-07-29 15:18:45 +03:00
Delyan Angelov
c881e7284d
cgen: allow dump(unsafe{nil}) and dump(voidptr(123)) in the same program 2023-07-29 10:14:07 +03:00
yuyi
2f2dde8ad0
ast, parser, fmt: fix formatting struct declaration with comments (fix #18982) (#18992) 2023-07-28 15:30:15 +03:00
l-m
2fa177e310
picoev: bugfixes and UB mitigation (#18991) 2023-07-28 12:37:21 +03:00
sigmaSd
2266ccecf3
readme: update web tutorial, fix code (#18989) 2023-07-28 12:36:46 +03:00
yuyi
b25288338c
parser, fmt: fix formatting interface fields with pre-comments (fix #18980) (#18988) 2023-07-28 11:42:10 +03:00
Turiiya
e0aba77cc5
readme: update ide-plugins section (#18990) 2023-07-28 11:20:45 +03:00
Turiiya
c7f708e64d
v.help: fix typos (#18987) 2023-07-28 02:29:48 +03:00
Delyan Angelov
aa3d560b05
sokol: fix v -cc gcc ~/.vmodules/sdl/examples/sdl_opengl_and_sokol/ on Linux (add missing -lm for gcc/clang) 2023-07-28 02:27:43 +03:00
Delyan Angelov
76e3b7dff8
builder: fix ./v -os windows run examples/hello_world.v (eliminate the "No such file or directory; code: 2" msg) 2023-07-28 01:53:57 +03:00
Delyan Angelov
fe87d20f20
builder: cleanup commented cross compilation code in cc.v 2023-07-28 01:18:50 +03:00
181 changed files with 2879 additions and 1709 deletions

View File

@ -6,7 +6,7 @@ end_of_line = lf
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.v] [*.{v,js,css}]
indent_style = tab indent_style = tab
[*.{bat,cmd}] [*.{bat,cmd}]

View File

@ -1,7 +1,6 @@
name: 🐛 Bug Report name: 🐛 Bug Report
description: Report a bug description: Report a bug
title: (bug report summary) labels: [Bug]
labels: Bug
body: body:
- type: textarea - type: textarea
id: description id: description

View File

@ -1,7 +1,6 @@
name: 🚀 Feature Request name: 🚀 Feature Request
description: Suggest an idea for this project description: Suggest an idea for this project
title: (feature request summary) labels: [Feature Request]
labels: Feature Request
body: body:
- type: textarea - type: textarea
id: description id: description

View File

@ -60,7 +60,7 @@ jobs:
doom-regressions: doom-regressions:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v' if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 10 timeout-minutes: 20
env: env:
VFLAGS: -cc tcc VFLAGS: -cc tcc
DISPLAY: :99 DISPLAY: :99

View File

@ -12,7 +12,7 @@ jobs:
gg-regressions: gg-regressions:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v' if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 10 timeout-minutes: 20
env: env:
VFLAGS: -cc tcc VFLAGS: -cc tcc
DISPLAY: :99 DISPLAY: :99

19
.github/workflows/module_docs_lint.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Code CI vlib modules
on:
push:
paths:
- '**/cmd/tools/vdoc/theme/**'
pull_request:
paths:
- '**/cmd/tools/vdoc/theme/**'
jobs:
lint-module-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check Formatting
uses: actionsx/prettier@v2
with:
args: --check cmd/tools/vdoc/theme

View File

@ -63,7 +63,7 @@ jobs:
- name: Repeat -o hw.c examples/hello_world.v - name: Repeat -o hw.c examples/hello_world.v
run: cmd/tools/repeat --max_time 251 --series 3 --count 20 --nmins 2 --nmaxs 5 --warmup 3 --fail_percent 10 -t 'cd {T} ; ./v -show-timings -o hw.c examples/hello_world.v' . ./vmaster run: cmd/tools/repeat --max_time 251 --series 3 --count 20 --nmins 2 --nmaxs 5 --warmup 3 --fail_percent 10 -t 'cd {T} ; ./v -show-timings -o hw.c examples/hello_world.v' . ./vmaster
- name: Repeat -o v.c cmd/v - name: Repeat -o v.c cmd/v
run: cmd/tools/repeat --max_time 1701 --series 3 --count 20 --nmins 2 --nmaxs 5 --warmup 3 --fail_percent 10 -t 'cd {T} ; ./v -show-timings -o v.c cmd/v' . ./vmaster run: cmd/tools/repeat --max_time 1731 --series 3 --count 20 --nmins 2 --nmaxs 5 --warmup 3 --fail_percent 10 -t 'cd {T} ; ./v -show-timings -o v.c cmd/v' . ./vmaster
misc-tooling: misc-tooling:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

View File

@ -52,7 +52,7 @@ jobs:
v-compiles-os-android: v-compiles-os-android:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v' if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 10 timeout-minutes: 20
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build V - name: Build V

View File

@ -201,34 +201,23 @@ cd v
make make
``` ```
## Installing editor/IDE plugin ## Editor/IDE Plugins
V has plugins for many editors: To bring IDE functions for the V programming languages to your editor, check out
[v-analyzer](https://github.com/v-analyzer/v-analyzer). It provides a
[VS Code extension](https://marketplace.visualstudio.com/items?itemName=VOSCA.vscode-v-analyzer)
and language server capabilities for other editors.
The plugin for JetBrains IDEs (IntelliJ, CLion, GoLand, etc.) also offers a great development
experience with V. You can find all features in [its documentation](https://plugins.jetbrains.com/plugin/20287-vlang/docs/syntax-highlighting.html).
Other Plugins:
- [VS Code plugin](https://github.com/vlang/vscode-vlang)
- [JetBrains IDE plugin](https://plugins.jetbrains.com/plugin/20287-vlang/docs/quick-start-guide.html)
- [Vim plugins](https://github.com/vlang/awesome-v#vim) - [Vim plugins](https://github.com/vlang/awesome-v#vim)
- [Emacs plugins](https://github.com/vlang/awesome-v#emacs) - [Emacs plugins](https://github.com/vlang/awesome-v#emacs)
- [Sublime Text 3 plugins](https://github.com/vlang/awesome-v#sublime-text-3) - [Sublime Text 3 plugins](https://github.com/vlang/awesome-v#sublime-text-3)
- [Atom plugins](https://github.com/vlang/awesome-v#atom) - [Atom plugins](https://github.com/vlang/awesome-v#atom)
### IntelliSense
V has a Language Server:
[VLS](https://github.com/vlang/vls).
The VS Code plugin provides built-in support for VLS.
> **Note**
>
> VLS may be unstable at the moment.
> If you encounter any problem, please create a new
> [issue](https://github.com/vlang/vls/issues).
The plugin for JetBrains IDE (IntelliJ, CLion, GoLand, etc) at the moment is
the best choice if you want a great V development experience.
You can see all its features in
[its documentation](https://plugins.jetbrains.com/plugin/20287-vlang/docs/syntax-highlighting.html).
## Testing and running the examples ## Testing and running the examples
Make sure V can compile itself: Make sure V can compile itself:

View File

@ -12,7 +12,7 @@ import term
const ( const (
base_os = 'linux' base_os = 'linux'
os_names = ['linux', 'macos', 'windows'] os_names = ['linux', 'macos', 'windows', 'freebsd', 'openbsd', 'solaris', 'termux']
skip_modules = [ skip_modules = [
'builtin.bare', 'builtin.bare',
'builtin.linux_bare.old', 'builtin.linux_bare.old',
@ -103,7 +103,7 @@ fn (app App) gen_api_for_module_in_os(mod_name string, os_name string) string {
for s in f.stmts { for s in f.stmts {
if s is ast.FnDecl { if s is ast.FnDecl {
if s.is_pub { if s.is_pub {
fn_signature := s.stringify_fn_decl(b.table, mod_name, map[string]string{}) fn_signature := b.table.stringify_fn_decl(&s, mod_name, map[string]string{})
fn_mod := s.modname() fn_mod := s.modname()
if fn_mod == mod_name { if fn_mod == mod_name {
fline := '${fn_mod}: ${fn_signature}' fline := '${fn_mod}: ${fn_signature}'

View File

@ -10,6 +10,7 @@ import v.ast
import v.token import v.token
import v.doc import v.doc
import v.pref import v.pref
import v.util { tabs }
const ( const (
css_js_assets = ['doc.css', 'normalize.css', 'doc.js', 'dark-mode.js'] css_js_assets = ['doc.css', 'normalize.css', 'doc.js', 'dark-mode.js']
@ -249,27 +250,27 @@ fn (vd VDoc) gen_html(d doc.Doc) string {
version).replace('{{ light_icon }}', vd.assets['light_icon']).replace('{{ dark_icon }}', version).replace('{{ light_icon }}', vd.assets['light_icon']).replace('{{ dark_icon }}',
vd.assets['dark_icon']).replace('{{ menu_icon }}', vd.assets['menu_icon']).replace('{{ head_assets }}', vd.assets['dark_icon']).replace('{{ menu_icon }}', vd.assets['menu_icon']).replace('{{ head_assets }}',
if cfg.inline_assets { if cfg.inline_assets {
'\n${tabs[0]}<style>' + vd.assets['doc_css'] + '</style>\n${tabs[0]}<style>' + '<style>${vd.assets['doc_css']}</style>
vd.assets['normalize_css'] + '</style>\n${tabs[0]}<script>' + ${tabs(2)}<style>${vd.assets['normalize_css']}</style>
vd.assets['dark_mode_js'] + '</script>' ${tabs(2)}<script>${vd.assets['dark_mode_js']}</script>'
} else { } else {
'\n${tabs[0]}<link rel="stylesheet" href="' + vd.assets['doc_css'] + '<link rel="stylesheet" href="${vd.assets['doc_css']}" />
'" />\n${tabs[0]}<link rel="stylesheet" href="' + vd.assets['normalize_css'] + ${tabs(2)}<link rel="stylesheet" href="${vd.assets['normalize_css']}" />
'" />\n${tabs[0]}<script src="' + vd.assets['dark_mode_js'] + '"></script>' ${tabs(2)}<script src="${vd.assets['dark_mode_js']}"></script>'
}).replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 { }).replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 {
modules_toc_str modules_toc_str
} else { } else {
symbols_toc_str symbols_toc_str
}).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi }).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi
&& vd.docs.len > 1 && d.head.name != 'README' { && d.head.name != 'README' {
'<div class="doc-toc"><ul>' + symbols_toc_str + '</ul></div>' '<div class="doc-toc"><ul>${symbols_toc_str}</ul></div>'
} else { } else {
'' ''
}).replace('{{ footer_content }}', gen_footer_text(d, !cfg.no_timestamp)).replace('{{ footer_assets }}', }).replace('{{ footer_content }}', gen_footer_text(d, !cfg.no_timestamp)).replace('{{ footer_assets }}',
if cfg.inline_assets { if cfg.inline_assets {
'<script>' + vd.assets['doc_js'] + '</script>' '<script>${vd.assets['doc_js']}</script>'
} else { } else {
'<script src="' + vd.assets['doc_js'] + '"></script>' '<script src="${vd.assets['doc_js']}"></script>'
}) })
return result return result
} }
@ -407,12 +408,12 @@ fn doc_node_html(dn doc.DocNode, link string, head bool, include_examples bool,
node_id = 'readme_${node_id}' node_id = 'readme_${node_id}'
hash_link = ' <a href="#${node_id}">#</a>' hash_link = ' <a href="#${node_id}">#</a>'
} }
dnw.writeln('${tabs[1]}<section id="${node_id}" class="doc-node${node_class}">') dnw.writeln('${tabs(2)}<section id="${node_id}" class="doc-node${node_class}">')
if dn.name.len > 0 { if dn.name.len > 0 {
if dn.kind == .const_group { if dn.kind == .const_group {
dnw.write_string('${tabs[2]}<div class="title"><${head_tag}>${sym_name}${hash_link}</${head_tag}>') dnw.write_string('${tabs(3)}<div class="title"><${head_tag}>${sym_name}${hash_link}</${head_tag}>')
} else { } else {
dnw.write_string('${tabs[2]}<div class="title"><${head_tag}>${dn.kind} ${sym_name}${hash_link}</${head_tag}>') dnw.write_string('${tabs(3)}<div class="title"><${head_tag}>${dn.kind} ${sym_name}${hash_link}</${head_tag}>')
} }
if link.len != 0 { if link.len != 0 {
dnw.write_string('<a class="link" rel="noreferrer" target="_blank" href="${link}">${link_svg}</a>') dnw.write_string('<a class="link" rel="noreferrer" target="_blank" href="${link}">${link_svg}</a>')

View File

@ -0,0 +1 @@
hello from readme

View File

@ -0,0 +1,3 @@
module foo
fn bar()

View File

@ -0,0 +1,4 @@
hello from readme
module foo
fn bar()

View File

@ -0,0 +1,3 @@
module foo
pub fn bar() {}

View File

@ -52,6 +52,11 @@ fn check_path(vexe string, dir string, tests []string) int {
cmd: '${os.quoted_path(vexe)} doc -comments ${os.quoted_path(program)}' cmd: '${os.quoted_path(vexe)} doc -comments ${os.quoted_path(program)}'
out_filename: 'main.comments.out' out_filename: 'main.comments.out'
) )
fails += check_output(
program: program
cmd: '${os.quoted_path(vexe)} doc -readme -comments ${os.quoted_path(program)}'
out_filename: 'main.readme.comments.out'
)
total_fails += fails total_fails += fails
if fails == 0 { if fails == 0 {
println(term.green('OK')) println(term.green('OK'))
@ -71,7 +76,7 @@ fn print_compare(expected string, found string) {
println(found) println(found)
println('============\n') println('============\n')
println('diff:') println('diff:')
println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected)) println(diff.color_compare_strings(diff_cmd, rand.ulid(), expected, found))
println('============\n') println('============\n')
} }

View File

@ -0,0 +1,5 @@
{
"useTabs": true,
"printWidth": 100,
"singleQuote": true
}

View File

@ -1,6 +1,5 @@
(function() { (function () {
var html = document.getElementsByTagName('html')[0]; if (localStorage.getItem('dark-mode') === 'true') {
if (localStorage.getItem('dark-mode') === 'true') { document.querySelector('html').classList.add('dark');
html.classList.add('dark'); }
}
})(); })();

File diff suppressed because it is too large Load Diff

View File

@ -1,235 +1,204 @@
(function () { (function () {
if (document.body.scrollIntoView) { const docnav = document.querySelector('.doc-nav');
var docnav = document.querySelector('.doc-nav'); const active = docnav.querySelector('li.active');
var active = docnav.querySelector('li.active'); active?.scrollIntoView({ block: 'center', inline: 'nearest' });
if (active) { setupMobileToggle();
active.scrollIntoView({ block: 'center', inline: 'nearest' }); setupDarkMode();
} setupScrollSpy();
} setupSearch();
setupScrollSpy(); setupCollapse();
setupMobileToggle();
setupDarkMode();
setupSearch();
setupCollapse();
})(); })();
function setupScrollSpy() { function setupScrollSpy() {
var sectionPositions = []; const sections = document.querySelectorAll('section');
var sections = document.querySelectorAll('section'); const sectionPositions = Array.from(sections).map((section) => section.offsetTop);
sections.forEach(function (section) { let scrollPos = 0;
sectionPositions.push(section.offsetTop); window.addEventListener('scroll', () => {
}); const toc = document.querySelector('.doc-toc');
var scrollPos = 0; // Reset classes
window.addEventListener('scroll', function (_) { toc.querySelectorAll('a[class="active"]').forEach((link) => link.classList.remove('active'));
// Reset classes // Set current menu link as active
document.querySelectorAll('.doc-toc a[class="active"]').forEach(function (link) { let scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
link.classList.remove('active'); for (const [i, position] of sectionPositions.entries()) {
}); if (position >= scrollPosition) {
// Set current menu link as active const section = sections[i];
var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop; const link = toc.querySelector('a[href="#' + section.id + '"]');
for (var i = 0; i < sectionPositions.length; i++) { if (link) {
var section = sections[i]; link.classList.add('active');
var position = sectionPositions[i]; const tocHeight = toc.clientHeight;
if (position >= scrollPosition) { const scrollTop = toc.scrollTop;
var link = document.querySelector('.doc-toc a[href="#' + section.id + '"]'); if (
if (link) { document.body.getBoundingClientRect().top < scrollPos &&
link.classList.add('active'); scrollTop < link.offsetTop - 10
var docToc = document.querySelector('.doc-toc'); ) {
var tocHeight = docToc.clientHeight; toc.scrollTop = link.clientHeight + link.offsetTop - tocHeight + 10;
var scrollTop = docToc.scrollTop; } else if (scrollTop > link.offsetTop - 10) {
if ((document.body.getBoundingClientRect()).top < scrollPos && scrollTop < link.offsetTop - 10) { toc.scrollTop = link.offsetTop - 10;
docToc.scrollTop = link.clientHeight + link.offsetTop - tocHeight + 10; }
} else if (scrollTop > link.offsetTop - 10) { }
docToc.scrollTop = link.offsetTop - 10; break;
} }
} }
break; scrollPos = document.body.getBoundingClientRect().top;
} });
}
scrollPos = (document.body.getBoundingClientRect()).top;
});
} }
function setupMobileToggle() { function setupMobileToggle() {
var toggle = document.getElementById('toggle-menu'); document.getElementById('toggle-menu').addEventListener('click', () => {
toggle.addEventListener('click', function (_) { const docNav = document.querySelector('.doc-nav');
var docNav = document.querySelector('.doc-nav'); const isHidden = docNav.classList.contains('hidden');
var isHidden = docNav.classList.contains('hidden'); docNav.classList.toggle('hidden');
docNav.classList.toggle('hidden'); const search = docNav.querySelector('.search');
var search = document.querySelector('.doc-nav > .search'); // console.log(search);
console.log(search); const searchHasResults = search.classList.contains('has-results');
var searchHasResults = search.classList.contains('has-results'); if (isHidden && searchHasResults) {
if (isHidden && searchHasResults) { search.classList.remove('mobile-hidden');
search.classList.remove('mobile-hidden'); } else {
} else { search.classList.add('mobile-hidden');
search.classList.add('mobile-hidden'); }
} const content = docNav.querySelector('.content');
var content = document.querySelector('.doc-nav .content'); content.classList.toggle('hidden');
content.classList.toggle('hidden'); content.classList.toggle('show');
content.classList.toggle('show'); });
});
} }
function setupDarkMode() { function setupDarkMode() {
var html = document.getElementsByTagName('html')[0]; const html = document.querySelector('html');
var darkModeToggle = document.getElementById('dark-mode-toggle'); const darkModeToggle = document.getElementById('dark-mode-toggle');
darkModeToggle.addEventListener('click', function () { darkModeToggle.addEventListener('click', () => {
html.classList.toggle('dark'); html.classList.toggle('dark');
var isDarkModeEnabled = html.classList.contains('dark'); const isDarkModeEnabled = html.classList.contains('dark');
localStorage.setItem('dark-mode', isDarkModeEnabled); localStorage.setItem('dark-mode', isDarkModeEnabled);
darkModeToggle.setAttribute('aria-checked', isDarkModeEnabled) darkModeToggle.setAttribute('aria-checked', isDarkModeEnabled);
}); });
// Check if css var() is supported and enable dark mode toggle
if (window.CSS && CSS.supports('color', 'var(--fake-var)')) {
darkModeToggle.style.visibility = 'unset';
}
} }
function setupSearch() { function setupSearch() {
var searchInput = document.getElementById('search'); const searchInput = document.getElementById('search');
var onInputChange = debounce(function (e) { const onInputChange = debounce((e) => {
var searchValue = e.target.value.toLowerCase(); const searchValue = e.target.value.toLowerCase();
var menu = document.querySelector('.doc-nav > .content'); const docNav = document.querySelector('.doc-nav');
var search = document.querySelector('.doc-nav > .search'); const menu = docNav.querySelector('.content');
if (searchValue === '') { const search = docNav.querySelector('.search');
// reset to default if (searchValue === '') {
menu.style.display = ''; // reset to default
if (!search.classList.contains('hidden')) { menu.style.display = '';
search.classList.add('hidden'); if (!search.classList.contains('hidden')) {
search.classList.remove('has-results'); search.classList.add('hidden');
} search.classList.remove('has-results');
} else if (searchValue.length >= 2) { }
// search for less than 2 characters can display too much results } else if (searchValue.length >= 2) {
search.innerHTML = ''; // search for less than 2 characters can display too much results
menu.style.display = 'none'; search.innerHTML = '';
if (search.classList.contains('hidden')) { menu.style.display = 'none';
search.classList.remove('hidden'); if (search.classList.contains('hidden')) {
search.classList.remove('mobile-hidden'); search.classList.remove('hidden');
search.classList.add('has-results'); search.classList.remove('mobile-hidden');
} search.classList.add('has-results');
// cache length for performance }
var foundModule = false; // cache length for performance
var searchModuleIndexLength = searchModuleIndex.length; let foundModule = false;
var ul = document.createElement('ul'); const ul = document.createElement('ul');
search.appendChild(ul); search.appendChild(ul);
for (var i = 0; i < searchModuleIndexLength; i++) { for (const [i, title] of searchModuleIndex.entries()) {
// no toLowerCase needed because modules are always lowercase // no toLowerCase needed because modules are always lowercase
var title = searchModuleIndex[i]; if (title.indexOf(searchValue) === -1) {
if (title.indexOf(searchValue) === -1) { continue;
continue }
} foundModule = true;
foundModule = true; // [description, link]
// [description, link] const data = searchModuleData[i];
var data = searchModuleData[i]; const el = createSearchResult({
var description = data[0]; badge: 'module',
var link = data[1]; description: data[0],
var el = createSearchResult({ link: data[1],
link: link, title: title,
title: title, });
description: description, ul.appendChild(el);
badge: 'module', }
}); if (foundModule) {
ul.appendChild(el); const hr = document.createElement('hr');
} hr.classList.add('separator');
if (foundModule) { search.appendChild(hr);
var hr = document.createElement('hr'); }
hr.classList.add('separator'); let results = [];
search.appendChild(hr); for (const [i, title] of searchIndex.entries()) {
} if (title.toLowerCase().indexOf(searchValue) === -1) {
var searchIndexLength = searchIndex.length; continue;
var results = []; }
for (var i = 0; i < searchIndexLength; i++) { // [badge, description, link]
var title = searchIndex[i]; const data = searchData[i];
if (title.toLowerCase().indexOf(searchValue) === -1) { results.push({
continue badge: data[0],
} description: data[1],
// [badge, description, link] link: data[2],
var data = searchData[i]; title: data[3] + ' ' + title,
var badge = data[0]; });
var description = data[1]; }
var link = data[2]; results.sort((a, b) => (a.title < b.title ? -1 : a.title > b.title ? 1 : 0));
var prefix = data[3]; const ul_ = document.createElement('ul');
results.push({ search.appendChild(ul_);
badge: badge, results.forEach((result) => {
description: description, const el = createSearchResult(result);
link: link, ul_.appendChild(el);
title: prefix + ' ' + title, });
}); }
} });
results.sort(function (a, b) { searchInput.addEventListener('input', onInputChange);
if (a.title < b.title) {
return -1;
}
if (a.title > b.title) {
return 1;
}
return 0;
});
var ul = document.createElement('ul');
search.appendChild(ul);
for (var i = 0; i < results.length; i++) {
var result = results[i];
var el = createSearchResult(result);
ul.appendChild(el);
}
}
});
searchInput.addEventListener('input', onInputChange);
} }
function createSearchResult(data) { function createSearchResult(data) {
var li = document.createElement('li'); const li = document.createElement('li');
li.classList.add('result'); li.classList.add('result');
var a = document.createElement('a'); const a = document.createElement('a');
a.href = data.link; a.href = data.link;
a.classList.add('link'); a.classList.add('link');
li.appendChild(a); li.appendChild(a);
var defintion = document.createElement('div'); const defintion = document.createElement('div');
defintion.classList.add('definition'); defintion.classList.add('definition');
a.appendChild(defintion); a.appendChild(defintion);
if (data.description) { if (data.description) {
var description = document.createElement('div'); const description = document.createElement('div');
description.classList.add('description'); description.classList.add('description');
description.textContent = data.description; description.textContent = data.description;
a.appendChild(description); a.appendChild(description);
} }
var title = document.createElement('span'); const title = document.createElement('span');
title.classList.add('title'); title.classList.add('title');
title.textContent = data.title; title.textContent = data.title;
defintion.appendChild(title); defintion.appendChild(title);
var badge = document.createElement('badge'); const badge = document.createElement('badge');
badge.classList.add('badge'); badge.classList.add('badge');
badge.textContent = data.badge; badge.textContent = data.badge;
defintion.appendChild(badge); defintion.appendChild(badge);
return li; return li;
} }
function setupCollapse() { function setupCollapse() {
var dropdownArrows = document.querySelectorAll('.dropdown-arrow'); const dropdownArrows = document.querySelectorAll('.dropdown-arrow');
for (var i = 0; i < dropdownArrows.length; i++) { dropdownArrows.forEach((arrow) => {
var dropdownArrow = dropdownArrows[i]; arrow.addEventListener('click', (e) => {
dropdownArrow.addEventListener('click', function (e) { const parent = e.target.parentElement.parentElement.parentElement;
var parent = e.target.parentElement.parentElement.parentElement; parent.classList.toggle('open');
parent.classList.toggle('open'); });
}); });
}
} }
function debounce(func, timeout) { function debounce(func, timeout) {
var timer; let timer;
return (...args) => { return (...args) => {
const next = () => func(...args); const next = () => func(...args);
if (timer) { if (timer) {
clearTimeout(timer); clearTimeout(timer);
} }
timer = setTimeout(next, timeout > 0 ? timeout : 300); timer = setTimeout(next, timeout > 0 ? timeout : 300);
} };
} }
document.addEventListener('keypress', (ev) => { document.addEventListener('keypress', (ev) => {
if (ev.key == '/') { if (ev.key == '/') {
let search = document.getElementById('search'); const search = document.getElementById('search');
ev.preventDefault(); ev.preventDefault();
search.focus(); search.focus();
} }
}); });

View File

@ -1,19 +1,19 @@
{ {
"name": "", "name": "",
"short_name": "", "short_name": "",
"icons": [ "icons": [
{ {
"src": "/android-chrome-192x192.png", "src": "/android-chrome-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "/android-chrome-512x512.png", "src": "/android-chrome-512x512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
} }
], ],
"theme_color": "#ffffff", "theme_color": "#ffffff",
"background_color": "#ffffff", "background_color": "#ffffff",
"display": "standalone" "display": "standalone"
} }

View File

@ -1,63 +1,72 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="x-ua-compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ title }} | vdoc</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Jost:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap"
rel="stylesheet"
/>
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png" />
<link rel="manifest" href="site.webmanifest" />
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
{{ head_assets }}
</head>
<head> <body>
<meta charset="UTF-8"> <div><a id="skip-to-content-link" href="#main-content">Skip to content</a></div>
<meta http-equiv="x-ua-compatible" content="IE=edge" /> <div id="page">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <header class="doc-nav hidden">
<title>{{ title }} | vdoc</title> <div class="heading-container">
<link rel="preconnect" href="https://fonts.googleapis.com"> <div class="heading">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <div class="info">
<link href="https://fonts.googleapis.com/css2?family=Jost:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <div class="module">{{ head_name }}</div>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap" rel="stylesheet"> <div class="toggle-version-container">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png"> <span>{{ version }}</span>
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png"> <div
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png"> id="dark-mode-toggle"
<link rel="manifest" href="site.webmanifest"> role="switch"
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#5bbad5"> aria-checked="false"
<meta name="msapplication-TileColor" content="#da532c"> aria-label="Toggle dark mode"
<meta name="theme-color" content="#ffffff"> >
{{ head_assets }} {{ light_icon }}{{ dark_icon }}
</head> </div>
</div>
<body> {{ menu_icon }}
<div><a id="skip-to-content-link" href="#main-content">Skip to content</a></div> </div>
<div id="page"> <input type="text" id="search" placeholder="Search... (beta)" autocomplete="off" />
<header class="doc-nav hidden"> </div>
<div class="heading-container"> </div>
<div class="heading"> <nav class="search hidden"></nav>
<div class="info"> <nav class="content hidden">
<div class="module">{{ head_name }}</div> <ul>
<div class="toggle-version-container"> {{ toc_links }}
<span>{{ version }}</span> </ul>
<div id="dark-mode-toggle" role="switch" aria-checked="false" aria-label="Toggle dark mode">{{ light_icon }}{{ dark_icon }}</div> </nav>
</div> </header>
{{ menu_icon }} <div class="doc-scrollview" id="main-content">
</div> <div class="doc-container">
<input type="text" id="search" placeholder="Search... (beta)" autocomplete="off"> <div class="doc-content">
</div> {{ contents }}
</div> <div class="footer">{{ footer_content }}</div>
<nav class="search hidden"></nav> </div>
<nav class="content hidden"> {{ right_content }}
<ul> </div>
{{ toc_links }} </div>
</ul> </div>
</nav> {{ footer_assets }}
</header> <script async src="search_index.js"></script>
<div class="doc-scrollview" id="main-content"> </body>
<div class="doc-container">
<div class="doc-content">
{{ contents }}
<div class="footer">
{{ footer_content }}
</div>
</div>
{{ right_content }}
</div>
</div>
</div>
{{ footer_assets }}
<script async src="search_index.js"></script>
</body>
</html> </html>

View File

@ -91,24 +91,24 @@ select {
} }
button, button,
[type="button"], [type='button'],
[type="reset"], [type='reset'],
[type="submit"] { [type='submit'] {
-webkit-appearance: button; -webkit-appearance: button;
} }
button::-moz-focus-inner, button::-moz-focus-inner,
[type="button"]::-moz-focus-inner, [type='button']::-moz-focus-inner,
[type="reset"]::-moz-focus-inner, [type='reset']::-moz-focus-inner,
[type="submit"]::-moz-focus-inner { [type='submit']::-moz-focus-inner {
border-style: none; border-style: none;
padding: 0; padding: 0;
} }
button:-moz-focusring, button:-moz-focusring,
[type="button"]:-moz-focusring, [type='button']:-moz-focusring,
[type="reset"]:-moz-focusring, [type='reset']:-moz-focusring,
[type="submit"]:-moz-focusring { [type='submit']:-moz-focusring {
outline: 1px dotted ButtonText; outline: 1px dotted ButtonText;
} }
@ -133,23 +133,23 @@ textarea {
overflow: auto; overflow: auto;
} }
[type="checkbox"], [type='checkbox'],
[type="radio"] { [type='radio'] {
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
} }
[type="number"]::-webkit-inner-spin-button, [type='number']::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button { [type='number']::-webkit-outer-spin-button {
height: auto; height: auto;
} }
[type="search"] { [type='search'] {
-webkit-appearance: textfield; -webkit-appearance: textfield;
outline-offset: -2px; outline-offset: -2px;
} }
[type="search"]::-webkit-search-decoration { [type='search']::-webkit-search-decoration {
-webkit-appearance: none; -webkit-appearance: none;
} }

View File

@ -16,7 +16,6 @@ const (
allowed_formats = ['md', 'markdown', 'json', 'text', 'stdout', 'html', 'htm'] allowed_formats = ['md', 'markdown', 'json', 'text', 'stdout', 'html', 'htm']
vexe = os.getenv_opt('VEXE') or { @VEXE } vexe = os.getenv_opt('VEXE') or { @VEXE }
vroot = os.dir(vexe) vroot = os.dir(vexe)
tabs = ['\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t']
) )
enum OutputType { enum OutputType {
@ -233,6 +232,9 @@ fn (vd VDoc) get_readme(path string) string {
} }
} }
if fname == '' { if fname == '' {
if path.all_after_last(os.path_separator) == 'src' {
return vd.get_readme(path.all_before_last(os.path_separator))
}
return '' return ''
} }
readme_path := os.join_path(path, '${fname}.md') readme_path := os.join_path(path, '${fname}.md')

View File

@ -312,7 +312,7 @@ fn run_repl(workdir string, vrepl_prefix string) int {
if line == '' && oline.ends_with('\n') { if line == '' && oline.ends_with('\n') {
continue continue
} }
if line.len <= -1 || line == '' || line == 'exit' { if line.len <= -1 || line == 'exit' {
break break
} }
if exit_pos := line.index('exit') { if exit_pos := line.index('exit') {

View File

@ -180,6 +180,15 @@ fn get_all_commands() []Command {
okmsg: 'V can compile 2048 with -skip-unused.' okmsg: 'V can compile 2048 with -skip-unused.'
rmfile: 'examples/2048/2048' rmfile: 'examples/2048/2048'
} }
if _ := os.find_abs_path_of_executable('emcc') {
res << Command{
line: '${vexe} -os wasm32_emscripten examples/2048'
okmsg: 'V can compile 2048 with -os wasm32_emscripten, using emcc.'
rmfile: 'examples/2048/2048'
}
} else {
println('> emcc not found, skipping `v -os wasm32_emscripten examples/2048`.')
}
res << Command{ res << Command{
line: '${vexe} -skip-unused -live examples/hot_reload/bounce.v' line: '${vexe} -skip-unused -live examples/hot_reload/bounce.v'
okmsg: 'V can compile the hot code reloading bounce.v example with both: -skip-unused -live' okmsg: 'V can compile the hot code reloading bounce.v example with both: -skip-unused -live'

View File

@ -52,7 +52,7 @@ fn check_path(vexe string, dir string, tests []string) int {
println(found) println(found)
println('============\n') println('============\n')
println('diff:') println('diff:')
println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected)) println(diff.color_compare_strings(diff_cmd, rand.ulid(), expected, found))
println('============\n') println('============\n')
nb_fail++ nb_fail++
} else { } else {

View File

@ -1,150 +1,148 @@
# Serve # JS DOM Cube
This project has a no dependence serve in `./server.js` path. ## Compiling
That can be run with `node` command after build.
or just run: `npm run start`
To install node, you can access node [download page](https://nodejs.org/en/download/)
or [package manager](https://nodejs.org/en/download/package-manager)
Drawing with mouse events using DOM API. Adopted from MDN examples.
# Compiling
``` ```
v -b js_browser examples/js_dom_cube/cube.js.v v -b js_browser examples/js_dom_cube/cube.js.v
``` ```
Drawing with mouse events using DOM API. Adopted from MDN examples. Then you can open `index.html` in your favorite browser.
Then you can open `index.html` with your favourite browser.
# Serve examples
### JS server ## Serve Examples
After run `npm init -y` code and genared `./package.json`
You can put `start` and `build` at script in jason leaf. ### JS Server
`path './package.json'`
```json > **NOTE**\
"scripts": { > The JS server example in the following steps requires Node.js.
"start": "npm run build && node server.js", > To install Node, please refer to the [download page](https://nodejs.org/en/download/)
"build":"v -b js_browser cube.js.v" > or the installation via your operating systems [package manager](https://nodejs.org/en/download/package-manager).
},
Initialize the example as a Node project
``` ```
here is the pure javascript server code cd examples/js_dom_cube/
`path './server.js'` npm init -y
```
Add a `start` and `build` script to the generated `./package.json` file.
```json
"scripts": {
...
"start": "npm run build && node server.js",
"build": "v -b js_browser cube.js.v"
},
```
Below is an example of a Node.js server without external dependencies.
You can use it for `./server.js`.
```javascript ```javascript
const http = require("http"); const http = require('http');
const fs = require("fs"); const fs = require('fs');
var path = require('path'); var path = require('path');
const host = "localhost"; const host = 'localhost';
const port = 3000; const port = 3000;
const reqListener = function (req, res) { const reqListener = function (req, res) {
console.log('[route] - ', req.url); console.log('[route] - ', req.url);
var filePath = '.' + req.url; var filePath = '.' + req.url;
if (filePath == './') { if (filePath == './') {
filePath = './index.html'; filePath = './index.html';
} }
var extname = String(path.extname(filePath)).toLowerCase(); var extname = String(path.extname(filePath)).toLowerCase();
var mimeTypes = { var mimeTypes = {
'.html': 'text/html', '.html': 'text/html',
'.js': 'text/javascript', '.js': 'text/javascript',
'.css': 'text/css', '.css': 'text/css',
'.json': 'application/json', '.json': 'application/json',
'.png': 'image/png', '.png': 'image/png',
'.jpg': 'image/jpg', '.jpg': 'image/jpg',
'.gif': 'image/gif', '.gif': 'image/gif',
'.svg': 'image/svg+xml', '.svg': 'image/svg+xml',
'.wav': 'audio/wav', '.wav': 'audio/wav',
'.mp4': 'video/mp4', '.mp4': 'video/mp4',
'.woff': 'application/font-woff', '.woff': 'application/font-woff',
'.ttf': 'application/font-ttf', '.ttf': 'application/font-ttf',
'.eot': 'application/vnd.ms-fontobject', '.eot': 'application/vnd.ms-fontobject',
'.otf': 'application/font-otf', '.otf': 'application/font-otf',
'.wasm': 'application/wasm' '.wasm': 'application/wasm',
}; };
var contentType = mimeTypes[extname] || 'application/octet-stream'; var contentType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, function(error, content) { fs.readFile(filePath, function (error, content) {
if (error) { if (error) {
if(error.code == 'ENOENT') { if (error.code == 'ENOENT') {
fs.readFile('./404.html', function(error, content) { fs.readFile('./404.html', function (error, content) {
res.writeHead(404, { 'Content-Type': 'text/html' }); res.writeHead(404, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8'); res.end(content, 'utf-8');
}); });
} } else {
else { res.writeHead(500);
res.writeHead(500); res.end('Sorry, check with the site admin for error: ' + error.code + ' ..\n');
res.end('Sorry, check with the site admin for error: '+error.code+' ..\n'); }
} } else {
} res.writeHead(200, { 'Content-Type': contentType });
else { res.end(content, 'utf-8');
res.writeHead(200, { 'Content-Type': contentType }); }
res.end(content, 'utf-8'); });
}
});
}; };
const server = http.createServer(reqListener); const server = http.createServer(reqListener);
server.listen(port, host, () => { server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`); console.log(`Server is running on http://${host}:${port}`);
}); });
``` ```
This project has a no dependence serve in `./server.js` path. Now you can build and run the project with the added scripts.
That can be run with `node` command after build.
or just run: `npm run start`
To install node, you can access node [download page](https://nodejs.org/en/download/)
or [package manager](https://nodejs.org/en/download/package-manager)
```sh
npm run build
npm run start
``` ```
$ cd examples/js_dom_draw/
$ npm run build
```
### V server ### V server
```v ignore
The example below uses `vweb` to serve the project.
```v
module main module main
import vweb import vweb
import os import os
const ( const (
http_port = 3001 http_port = 3001
) )
struct App { struct App {
vweb.Context vweb.Context
} }
fn main() { fn main() {
vweb.run(new_app(), http_port) vweb.run(new_app(), http_port)
} }
pub fn (mut app App) before_request() { pub fn (mut app App) before_request() {
// This build server json files // Build the cube.js javascript file
os.execute_or_panic('v -b js_browser cube.js.v ') os.execute_or_panic('v -b js_browser cube.js.v ')
} }
fn new_app() &App { fn new_app() &App {
mut app := &App{} mut app := &App{}
app.serve_static('/favicon.ico', 'favicon.ico') app.serve_static('/favicon.ico', 'favicon.ico')
app.serve_static('/cube.js', 'cube.js') app.serve_static('/cube.js', 'cube.js')
app.mount_static_folder_at(os.resource_abs_path('.'), '/') app.mount_static_folder_at(os.resource_abs_path('.'), '/')
return app return app
} }
['/'; get] ['/'; get]
pub fn (mut app App) controller_get_all_task() vweb.Result { pub fn (mut app App) controller_get_all_task() vweb.Result {
file :=os.read_file('./index.html') or { panic(err) } file := os.read_file('./index.html') or { panic(err) }
return app.html(file) return app.html(file)
} }
``` ```

View File

@ -1,140 +1,150 @@
Drawing with mouse events using DOM API. Adopted from MDN examples. # JS DOM Draw
# Compiling Drawing with mouse events using the DOM API. Adopted from MDN examples.
```
## Compiling
```sh
v -b js_browser examples/js_dom_draw/draw.js.v v -b js_browser examples/js_dom_draw/draw.js.v
``` ```
Then you can open `index.html` with your favourite browser. Then you can open `index.html` in your favorite browser.
# Serve examples
### JS server ## Serve Examples
After run `npm init -y` code and genared `./package.json`
You can put `start` and `build` at script in jason leaf. ### JS Server
`path './package.json'`
```json > **NOTE**\
"scripts": { > The JS server example in the following steps requires Node.js.
"start": "npm run build && node server.js", > To install Node, please refer to the [download page](https://nodejs.org/en/download/)
"build":"v -b js_browser draw.js.v" > or the installation via your operating systems [package manager](https://nodejs.org/en/download/package-manager).
},
Initialize the example as a Node project
``` ```
here is the pure javascript server code cd examples/js_dom_draw/
`path './server.js'` npm init -y
```
Add a `start` and `build` script to the generated `./package.json` file.
```json
"scripts": {
...
"start": "npm run build && node server.js",
"build": "v -b js_browser draw.js.v"
},
```
Below is an example of a Node.js server without external dependencies.
You can use it for `./server.js`.
```javascript ```javascript
const http = require("http"); const http = require('http');
const fs = require("fs"); const fs = require('fs');
var path = require('path'); var path = require('path');
const host = "localhost"; const host = 'localhost';
const port = 3000; const port = 3000;
const reqListener = function (req, res) { const reqListener = function (req, res) {
console.log('[route] - ', req.url); console.log('[route] - ', req.url);
var filePath = '.' + req.url; var filePath = '.' + req.url;
if (filePath == './') { if (filePath == './') {
filePath = './index.html'; filePath = './index.html';
} }
var extname = String(path.extname(filePath)).toLowerCase(); var extname = String(path.extname(filePath)).toLowerCase();
var mimeTypes = { var mimeTypes = {
'.html': 'text/html', '.html': 'text/html',
'.js': 'text/javascript', '.js': 'text/javascript',
'.css': 'text/css', '.css': 'text/css',
'.json': 'application/json', '.json': 'application/json',
'.png': 'image/png', '.png': 'image/png',
'.jpg': 'image/jpg', '.jpg': 'image/jpg',
'.gif': 'image/gif', '.gif': 'image/gif',
'.svg': 'image/svg+xml', '.svg': 'image/svg+xml',
'.wav': 'audio/wav', '.wav': 'audio/wav',
'.mp4': 'video/mp4', '.mp4': 'video/mp4',
'.woff': 'application/font-woff', '.woff': 'application/font-woff',
'.ttf': 'application/font-ttf', '.ttf': 'application/font-ttf',
'.eot': 'application/vnd.ms-fontobject', '.eot': 'application/vnd.ms-fontobject',
'.otf': 'application/font-otf', '.otf': 'application/font-otf',
'.wasm': 'application/wasm' '.wasm': 'application/wasm',
}; };
var contentType = mimeTypes[extname] || 'application/octet-stream'; var contentType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, function(error, content) { fs.readFile(filePath, function (error, content) {
if (error) { if (error) {
if(error.code == 'ENOENT') { if (error.code == 'ENOENT') {
fs.readFile('./404.html', function(error, content) { fs.readFile('./404.html', function (error, content) {
res.writeHead(404, { 'Content-Type': 'text/html' }); res.writeHead(404, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8'); res.end(content, 'utf-8');
}); });
} } else {
else { res.writeHead(500);
res.writeHead(500); res.end('Sorry, check with the site admin for error: ' + error.code + ' ..\n');
res.end('Sorry, check with the site admin for error: '+error.code+' ..\n'); }
} } else {
} res.writeHead(200, { 'Content-Type': contentType });
else { res.end(content, 'utf-8');
res.writeHead(200, { 'Content-Type': contentType }); }
res.end(content, 'utf-8'); });
}
});
}; };
const server = http.createServer(reqListener); const server = http.createServer(reqListener);
server.listen(port, host, () => { server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`); console.log(`Server is running on http://${host}:${port}`);
}); });
``` ```
This project has a no dependence serve in `./server.js` path. Now you can build and run the project with the added scripts.
That can be run with `node` command after build.
or just run: `npm run start`
To install node, you can access node [download page](https://nodejs.org/en/download/)
or [package manager](https://nodejs.org/en/download/package-manager)
```sh
npm run build
npm run start
``` ```
$ cd examples/js_dom_draw/
$ npm run build
```
### V server ### V server
```v ignore
The example below uses `vweb` to serve the project.
```v
module main module main
import vweb import vweb
import os import os
const ( const (
http_port = 3001 http_port = 3001
) )
struct App { struct App {
vweb.Context vweb.Context
} }
fn main() { fn main() {
vweb.run(new_app(), http_port) vweb.run(new_app(), http_port)
} }
pub fn (mut app App) before_request() { pub fn (mut app App) before_request() {
// This build server json files // Build the draw.js javascript file
os.execute_or_panic('v -b js_browser draw.js.v ') os.execute_or_panic('v -b js_browser draw.js.v ')
} }
fn new_app() &App { fn new_app() &App {
mut app := &App{} mut app := &App{}
app.serve_static('/favicon.ico', 'favicon.ico') app.serve_static('/favicon.ico', 'favicon.ico')
app.serve_static('/draw.js', 'draw.js') app.serve_static('/draw.js', 'draw.js')
app.mount_static_folder_at(os.resource_abs_path('.'), '/') app.mount_static_folder_at(os.resource_abs_path('.'), '/')
return app return app
} }
['/'; get] ['/'; get]
pub fn (mut app App) controller_get_all_task() vweb.Result { pub fn (mut app App) controller_get_all_task() vweb.Result {
file :=os.read_file('./index.html') or { panic(err) } file := os.read_file('./index.html') or { panic(err) }
return app.html(file) return app.html(file)
} }
``` ```

View File

@ -1,35 +1,57 @@
# JS DOM Benchmark Chart
![image](https://user-images.githubusercontent.com/63821277/186010833-2ea36f3a-4738-4025-9b23-ac62afe74b81.png) ![image](https://user-images.githubusercontent.com/63821277/186010833-2ea36f3a-4738-4025-9b23-ac62afe74b81.png)
# To run app ## Running the App
## From root
- run typescript project
`npm i --prefix examples/js_dom_draw_bechmark_chart/typescript_vanilla_typeorm`
`npm run start:dev --prefix examples/js_dom_draw_bechmark_chart/typescript_vanilla_typeorm`
- run v project > **NOTE**\
`v run examples/js_dom_draw_bechmark_chart/v_vweb_orm ` > The following steps require Node.js.
> To install Node, please refer to the [download page](https://nodejs.org/en/download/)
> or the installation via your operating systems [package manager](https://nodejs.org/en/download/package-manager).
- running v chart The steps below assume that your current directory path is the examples project directory.
`cd examples/js_dom_draw_bechmark_chart/chart && v run .`
Dockerfile ```
[docker build]=> Docker image cd examples/js_dom_draw_bechmark_chart
[docker run]=> Docker container ```
`sudo docker build -t <name> .` Execute the following commands in separate terminal instances.
`sudo docker run --name <container name> --interactive --tty --publish 3001:3001 <name>` Run the Benchmarks Typescript Part
`v run .` ```sh
npm i --prefix typescript_vanilla_typeorm
npm run start:dev --prefix typescript_vanilla_typeorm
```
A message like `[Vweb] Running app on http://localhost:3001/` should appear Run the Benchmarks V Part
`exit` ```sh
v run v_vweb_orm
```
# To implement new benchmarks in v Run the Chart
In `examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v` path ```
Create a route returning a `Response` struct like: cd chart/ && v run .
```
## Dockerfile
> [docker build] => Docker image\
> [docker run] => Docker container
```sh
sudo docker build -t <name> .
sudo docker run --name <container name> --interactive --tty --publish 3001:3001 <name>
v run .
# A message like `[Vweb] Running app on http://localhost:3001/` should appear
exit
```
## Implementing New Benchmarks in V
In `v_vweb_orm/src/main.v`, create a route that returns a `Response` struct.
```v ignore ```v ignore
['/sqlite-memory/:count'] ['/sqlite-memory/:count']
@ -65,18 +87,17 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result {
}! }!
response := Response{ response := Response{
insert: insert_stopwatchs insert: insert_stopwatchs
@select:select_stopwatchs @select: select_stopwatchs
update: update_stopwatchs update: update_stopwatchs
} }
return app.json(response) return app.json(response)
} }
``` ```
In `examples/chart/services.v` path In `chart/main.v`, create a service to request the benchmark data and decode the response as
Create a service to request the benchmarks data by http `FrameworkBenchmarkResponse`.
Decode the info to `FrameworkBenchmarkResponse`
```v ignore ```v ignore
fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse { fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:3000/sqlite-memory/${benchmark_loop_length}' url := 'http://localhost:3000/sqlite-memory/${benchmark_loop_length}'
@ -86,26 +107,13 @@ fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
} }
``` ```
In `examples/chart/main.v` path Then update `insert_framework_benchmark_times()`, `select_framework_benchmark_times()` and
Create a service to request the benchmarks data by http `update_framework_benchmark_times()` to include the `numbers := FrameworkPlatform{` for the newly
Decode the info to `FrameworkBenchmarkResponse` added function.
```v ignore
fn typescript_sqlite_memory() ?FrameworkBenchmarkResponse {
url := 'http://localhost:3000/sqlite-memory/${benchmark_loop_length}'
res := http.get(url) or { panic(err) }
framework_benchmark_response := json.decode(FrameworkBenchmarkResponse, res.body)!
return framework_benchmark_response
}
```
Then, update:
`insert_framework_benchmark_times()`;
`select_framework_benchmark_times()`;
`update_framework_benchmark_times()`.
with the new function
## Roadmap
# ROADMAP
02/09/2022 02/09/2022
- [ ] select bench (easy) - [ ] select bench (easy)
- [ ] vsql (easy) - [ ] vsql (easy)

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIUTbcFMmB84wg6eqDRJbmo49aOTdMwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzA3MzAxNzM0MThaGA8yMDUw
MTIxNDE3MzQxOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBANPfvg3G/3+j2NX5qDf0gw9iSnCiUa1T1inxlnfP
vMVINDkH22yUSeValyuIrpF94m23ANo0yOXQJxbIt8PbwaRYTq1EzxlGkkXUHob3
m1qJLH1qLacJeLMPj3J7kUXVWL65Qb7d2gtwMvegJ5I5U4ntLjXAmIV4z4PpZ2tP
MsERacj/alb0EDS77P4JcbRzYvP/3FyFokel5TF/nLV3hXc5Eu6LjCzbEEus1MLd
rgcpODUlw8Gf0M0nAjxijVpXAVo6XYRQ/00+zjPaKxjtQ/ds/O7zRTNxnUVvh1oH
Pnif9rkVBLkVjmSU/C7jvAKqrWPU9b24hXpnfSIirkn3tO8CAwEAAaNTMFEwHQYD
VR0OBBYEFPx8Ivgj5Gi4XyHFZ/zGgZU4kGc7MB8GA1UdIwQYMBaAFPx8Ivgj5Gi4
XyHFZ/zGgZU4kGc7MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
AJYSckwJSZlj8baEMvAJLs55Bm5wR1QZaj7mz8tBxK8zd9PdW3fTebil6jtGLpXT
CHi+vaa5HsM2QXnYaZCSlawD3WWZ+LZ9lJVuWC+iWfRq6TC/gEd+3zCE70CeeAAu
0pZC2Luvvgm5a6qfKoA4lEvlB2Yr0pX2GhXYOGvIeSMWpohKyKmiJEi83kJvzjnl
BsFIR6FB1wO2+nrfLCzmjwPQx0ie2h+fPwf5Y2C0pPBYVwXpP94EEZW+lQgPXx5I
6X8HPVNMtu4lToe746ctQlA4YDwge5mmiGUgF95Y3/O9Z2vjPqeN826oR89YFFZF
JFtrrBskGW5fOzKOXLc96pE=
-----END CERTIFICATE-----

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEEzCCAvsCFG64Q2g46jZb3kRbDOJWX/BwjSp5MA0GCSqGSIb3DQEBCwUAMEUx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjMwNzMwMTczNDQzWhgPMjA1MDEyMTQx
NzM0NDNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDqDmHNMesBQ6S/ZU+xW4sO18Q+WfvXEaMdEfN5D8wyazP3
mA7VTsNjliHrLFDiaTX9dR9Js+SwqmLpZTZoxBzn6wDuuM9fswvISnVERgZZdFas
lkgci4iXVE2TREEqh6Ak4CAUyGiBhHm1CaT7txvq7wTghs+cgDR5hItK7EpxD7gv
jc1sNco9Ha1GecUB0L4fCmp3ss88vvDU4ta4eLTsW/SZbTKRMawih4hp00d0v83s
Of5iIP6kWsM9X5Oe2Fm5XaObMlCdWHIBP2aB5i2ZqYUKTl8uKExpeobcsmpvHiSb
tAAaeFo/5shyCAXx/i55KYA2oRm71XeIBnllIBMEfKPV3AReO6p+MZFnioPxUWY0
gWqlSzsoLJ6NxHYWeC15OQhKQ6jEO7u3SAivacG5VVEywQ7EiY3b+ncwcZt9QK8q
UckwwnbWHZJwJcp0vHj3/Mmvawm1dWR3CVauFW3ze/l3Ik92wJBDkULhv6r07zfv
SlL2Am7+wPWfeOomx+Kh/rnGcXATcH17dVFH/mjhTjclOuM38e7oGoQhm9lZ9kaD
wIuu1X0+XutuY55uze9yVY9qRSIJraBuZ9mreVIVj9FG0kpVplrTr7oBlGA1Nc3i
1IuilE36T4qmP50WzJ6Yy38+J7My23bUyyOXIFhX/LTpPVjhZq5tVH+WmmqW0wID
AQABMA0GCSqGSIb3DQEBCwUAA4IBAQAbD+EVc4Ev7uXKUQV0s3nETp2odz1G8eZ/
drAVnWJpZEeWW98ZeVfyYglqWc9G8McYpiKeac9WF+gga8F2Cn3RjMvufqr0Ggcy
byytgJeLolukhTV/JJk8o+CUAB2xgk8+DVEiZ+7G4L/4V613VmL1B+jRHWknO/Js
uArppuSduvmkakWOGMBGLUPUcep/vIepHByjOeq1czsdrsLokjBXjMAwXAVRSkBs
mazD9yK95R5oG0KDmIPpfDddwvp7Xq4t3pTtMwXPYaDG+sPovsGsGYRkTnouOlKh
ae4/jRb8ut9AqOxZRUsBzT8MJXg4cFec8qrhPe3abzxN4amqOxlj
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,26 @@
No extensions in certificate
-----BEGIN CERTIFICATE-----
MIIEOTCCAyECFG64Q2g46jZb3kRbDOJWX/BwjSp4MA0GCSqGSIb3DQEBCwUAMEUx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjMwNzMwMTczNDE4WhgPMjA1MDEyMTQx
NzM0MThaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYD
VQQHDAtMb3MgQW5nZWxlczEdMBsGA1UECgwUQ2F0YWx5c3QgRGV2ZWxvcG1lbnQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
ggIBAKUcJ5cizbhreUlC0plQngCeE+/ysZJo8TmnhEF4cFnTVCHmvzz7f5w7/Zma
4maRzE3KT52qSA2PkX6Wr9RgnM93K93a3dfiUHaZ+DbWFnb1w/gp7TG5bgvwfjcb
KVnkP+BIMbT90kQrLI0H89LrouvCHDvl74EvAdyYgupLTdjZ5Q5Xe9P5rUhLajw6
iN8+NtfrQh2hDOWnbg9nQK/zjIVvvvjMQOUzda9Rq4ViAphiLNufojQhHJrlIpkP
oR+YENiDXRcaire5RVWjzfoQGDbTJLJL8fNV1HMjJArUuCg8yIyL1wooe+XWqscg
MzJoiCjSdai5zTF+lzcBLrm0olqB7YDCHS3x3K4C3DDRSoyFSEHRQpbSQbc6jTo6
TuWcC9o/5fIMzzHrahqoRZG7syFyHYNtezhsFobqMX4E15gV6tBf6CMpTlXMH3ss
DreZl900n1UcNtp7aQw2JIdRrLMW2I6UAit1o/FBYjkg/QCqPL7lw1nv0COraI74
CU+I7opYJJgEalOkZciapB6ARMgWD1/J9qO4q98H0jETP1bh78lwLPxRe1W5fukD
YCazJONIt4Bgdff8C2r+KhEoWQxK5VnjIdbL9E1nxscPr/4U77siCmHKdPINCrsD
CWqxUHiuyJmkw9NCrerZoIxTBFAUc+sdibvq1M528jf1IvXXAgMBAAEwDQYJKoZI
hvcNAQELBQADggEBAGoxRyyY+YraVOlFMzmyf+TeCQVibOJvKmY/HUjqCujgoj6o
7UwYDAPyfzuVerDj2IzJ9bFB818iOIQUaO8Gw70EOzDifZFCm9/pET3SD7aGd4Kk
DjCbjml/MNidVezPXNcdAM+XJ56jd2owk/pB4jaw3bkNcmn0aSnFg421fZibrTcS
VRPCHIvV8gSqJk9ENO5EGIGU1VmTR909aIYB2fYyyW/1fyP/gn7d1Afy8CimZ1uD
mUl5qQ5/CY378MmnODiGhsHJeH+ws77yLPwnh8eVtISwYMu9qfNRYFnotK7a5mxU
CWpoSR77NmCUCQOarzb34soL2zZlIK9Lx+4VQPo=
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,17 @@
import io
import os
import net.mbedtls
fn main() {
mut client := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
verify: os.resource_abs_path('cert/ca.crt')
cert: os.resource_abs_path('cert/client.crt')
cert_key: os.resource_abs_path('cert/client.key')
validate: true
})!
client.dial('localhost', 8443)!
client.write_string('GET / HTTP/1.1\r\n\r\n')!
mut reader := io.new_buffered_reader(reader: client)
println(reader.read_line()!)
}

View File

@ -0,0 +1,20 @@
import io
import os
import net.mbedtls
fn main() {
mut server := mbedtls.new_ssl_listener('0.0.0.0:8443', mbedtls.SSLConnectConfig{
verify: os.resource_abs_path('cert/ca.crt')
cert: os.resource_abs_path('cert/server.crt')
cert_key: os.resource_abs_path('cert/server.key')
validate: true // mTLS
})!
mut client := server.accept()!
mut reader := io.new_buffered_reader(reader: client)
mut request := reader.read_line()!
println(request)
client.write_string('HTTP/1.1 200 OK\r\n')!
client.shutdown()!
server.shutdown()!
}

View File

@ -49,7 +49,7 @@ V projects can be created anywhere and don't need to have a certain structure:
```bash ```bash
mkdir blog mkdir blog
cd blog cd blog
v init touch blog.v
``` ```
First, let's create a simple hello world website: First, let's create a simple hello world website:
@ -203,11 +203,11 @@ since a DB connection doesn't have to be set up for each request.
// blog.v // blog.v
fn main() { fn main() {
mut app := App{ mut app := App{
db: sqlite.connect(':memory:') or { panic(err) } db: sqlite.connect(':memory:')!
} }
sql app.db { sql app.db {
create table Article create table Article
} }!
first_article := Article{ first_article := Article{
title: 'Hello, world!' title: 'Hello, world!'
@ -222,7 +222,7 @@ fn main() {
sql app.db { sql app.db {
insert first_article into Article insert first_article into Article
insert second_article into Article insert second_article into Article
} }!
vweb.run(app, 8080) vweb.run(app, 8080)
} }
``` ```
@ -242,7 +242,7 @@ struct Article {
pub fn (app &App) find_all_articles() []Article { pub fn (app &App) find_all_articles() []Article {
return sql app.db { return sql app.db {
select from Article select from Article
} } or { panic(err) }
} }
``` ```
@ -289,7 +289,7 @@ For example, if we only wanted to find articles with ids between 100 and 200, we
return sql app.db { return sql app.db {
select from Article where id >= 100 && id <= 200 select from Article where id >= 100 && id <= 200
} } or { panic(err) }
``` ```
Retrieving a single article is very simple: Retrieving a single article is very simple:
@ -299,7 +299,7 @@ Retrieving a single article is very simple:
pub fn (app &App) retrieve_article() ?Article { pub fn (app &App) retrieve_article() ?Article {
return sql app.db { return sql app.db {
select from Article limit 1 select from Article limit 1
} } or { panic(err) }[0]
} }
``` ```
@ -308,9 +308,8 @@ bad queries will always be handled by the developer:
```v ignore ```v ignore
// article.v // article.v
article := app.retrieve_article(10) or { article := app.retrieve_article() or {
app.text('Article not found') return app.text('Article not found')
return
} }
``` ```
@ -349,7 +348,7 @@ pub fn (mut app App) new_article(title string, text string) vweb.Result {
println(article) println(article)
sql app.db { sql app.db {
insert article into Article insert article into Article
} } or { panic(err) }
return app.redirect('/') return app.redirect('/')
} }
``` ```
@ -402,7 +401,7 @@ If one wants to persist data they need to use a file instead of memory SQLite Da
Replace the db setup code with this instead: Replace the db setup code with this instead:
``` ```
db: sqlite.connect('blog.db') or { panic(err) } db: sqlite.connect('blog.db')!
``` ```
As we can see it attempts to open a file in the current directory named `blog.db`. As we can see it attempts to open a file in the current directory named `blog.db`.

View File

@ -865,7 +865,7 @@ pub fn (s string) split_nth(delim string, nth int) []string {
i = 1 i = 1
for ch in s { for ch in s {
if nth > 0 && i >= nth { if nth > 0 && i >= nth {
res << s[i..] res << s[i - 1..]
break break
} }
res << ch.ascii_str() res << ch.ascii_str()
@ -938,7 +938,7 @@ pub fn (s string) rsplit_nth(delim string, nth int) []string {
0 { 0 {
for i >= 0 { for i >= 0 {
if nth > 0 && res.len == nth - 1 { if nth > 0 && res.len == nth - 1 {
res << s[..i] res << s[..i + 1]
break break
} }
res << s[i].ascii_str() res << s[i].ascii_str()

View File

@ -183,6 +183,15 @@ fn test_split_nth() {
assert e.split_nth(',,', 3).len == 3 assert e.split_nth(',,', 3).len == 3
assert e.split_nth(',', -1).len == 12 assert e.split_nth(',', -1).len == 12
assert e.split_nth(',', 3).len == 3 assert e.split_nth(',', 3).len == 3
f := '1:2:3'
assert f.split_nth(':', 2) == ['1', '2:3']
assert f.rsplit_nth(':', 2) == ['3', '1:2']
g := '123'
assert g.split_nth('', 2) == ['1', '23']
assert g.rsplit_nth('', 2) == ['3', '12']
h := ''
assert h.split_nth('', 2) == []
assert h.rsplit_nth('', 2) == []
} }
fn test_rsplit_nth() { fn test_rsplit_nth() {

View File

@ -18,9 +18,9 @@ pub mut:
description string description string
man_description string man_description string
version string version string
pre_execute FnCommandCallback pre_execute FnCommandCallback = unsafe { nil }
execute FnCommandCallback execute FnCommandCallback = unsafe { nil }
post_execute FnCommandCallback post_execute FnCommandCallback = unsafe { nil }
disable_help bool disable_help bool
disable_man bool disable_man bool
disable_version bool disable_version bool

View File

@ -151,17 +151,17 @@ pub fn new() &Digest {
} }
// new512_224 returns a new Digest (implementing hash.Hash) computing the SHA-512/224 checksum. // new512_224 returns a new Digest (implementing hash.Hash) computing the SHA-512/224 checksum.
fn new512_224() &Digest { pub fn new512_224() &Digest {
return new_digest(.sha512_224) return new_digest(.sha512_224)
} }
// new512_256 returns a new Digest (implementing hash.Hash) computing the SHA-512/256 checksum. // new512_256 returns a new Digest (implementing hash.Hash) computing the SHA-512/256 checksum.
fn new512_256() &Digest { pub fn new512_256() &Digest {
return new_digest(.sha512_256) return new_digest(.sha512_256)
} }
// new384 returns a new Digest (implementing hash.Hash) computing the SHA-384 checksum. // new384 returns a new Digest (implementing hash.Hash) computing the SHA-384 checksum.
fn new384() &Digest { pub fn new384() &Digest {
return new_digest(.sha384) return new_digest(.sha384)
} }

View File

@ -4,13 +4,22 @@ module datatypes
[heap] [heap]
struct BloomFilter[T] { struct BloomFilter[T] {
hash_func fn (T) u32 // hash function, input [T] , output u32 // TODO V bug
table_size int // every entry is one-bit, packed into `table` hash_func fn (T) u32 = unsafe { nil } // hash function, input [T] , output u32
num_functions int // 1~16 // hash_func fn (T) u32 = empty_cb // hash function, input [T] , output u32
table_size int // every entry is one-bit, packed into `table`
num_functions int // 1~16
mut: mut:
table []u8 table []u8
} }
/*
TODO maybe allow pointing to generic fns?
fn empty_cb[T](x T) u32 {
panic('empty BloomFilter.hash_func callback')
}
*/
const ( const (
// Salt values(random values). These salts are XORed with the output of the hash function to give multiple unique hashes. // Salt values(random values). These salts are XORed with the output of the hash function to give multiple unique hashes.
salts = [ salts = [

View File

@ -51,3 +51,14 @@ Read this section to learn how to install and connect to PostgreSQL
[*Windows*](https://www.postgresqltutorial.com/install-postgresql); [*Windows*](https://www.postgresqltutorial.com/install-postgresql);
[*Linux*](https://www.postgresqltutorial.com/postgresql-getting-started/install-postgresql-linux); [*Linux*](https://www.postgresqltutorial.com/postgresql-getting-started/install-postgresql-linux);
[*macOS*](https://www.postgresqltutorial.com/postgresql-getting-started/install-postgresql-macos). [*macOS*](https://www.postgresqltutorial.com/postgresql-getting-started/install-postgresql-macos).
## Using Parameterized Queries
Parameterized queries (exec_param, etc.) in V require the use of the following syntax: ($n).
The number following the $ specifies which parameter from the argument array to use.
```v ignore
db.exec_param_many('INSERT INTO users (username, password) VALUES ($1, $2)', ['tom', 'securePassword']) or { panic(err) }
db.exec_param('SELECT * FROM users WHERE username = ($1) limit 1', 'tom') or { panic(err) }
```

View File

@ -251,7 +251,7 @@ pub fn (db DB) exec_one(query string) !Row {
return row return row
} }
// exec_param_many executes a query with the provided parameters // exec_param_many executes a query with the parameters provided as ($1), ($2), ($n)
pub fn (db DB) exec_param_many(query string, params []string) ![]Row { pub fn (db DB) exec_param_many(query string, params []string) ![]Row {
unsafe { unsafe {
mut param_vals := []&char{len: params.len} mut param_vals := []&char{len: params.len}
@ -265,12 +265,12 @@ pub fn (db DB) exec_param_many(query string, params []string) ![]Row {
} }
} }
// exec_param2 executes a query with 1 parameter, and returns either an error on failure, or the full result set on success // exec_param2 executes a query with 1 parameter ($1), and returns either an error on failure, or the full result set on success
pub fn (db DB) exec_param(query string, param string) ![]Row { pub fn (db DB) exec_param(query string, param string) ![]Row {
return db.exec_param_many(query, [param]) return db.exec_param_many(query, [param])
} }
// exec_param2 executes a query with 2 parameters, and returns either an error on failure, or the full result set on success // exec_param2 executes a query with 2 parameters ($1) and ($2), and returns either an error on failure, or the full result set on success
pub fn (db DB) exec_param2(query string, param string, param2 string) ![]Row { pub fn (db DB) exec_param2(query string, param string, param2 string) ![]Row {
return db.exec_param_many(query, [param, param2]) return db.exec_param_many(query, [param, param2])
} }

View File

@ -19,8 +19,8 @@ mut:
struct EventHandler[T] { struct EventHandler[T] {
name T name T
handler EventHandlerFn handler EventHandlerFn = unsafe { nil }
receiver voidptr = unsafe { nil } receiver voidptr = unsafe { nil }
once bool once bool
} }

View File

@ -6,15 +6,15 @@ pub struct C.FONSparams {
flags char flags char
userPtr voidptr userPtr voidptr
// int (*renderCreate)(void* uptr, int width, int height) // int (*renderCreate)(void* uptr, int width, int height)
renderCreate fn (uptr voidptr, width int, height int) int renderCreate fn (uptr voidptr, width int, height int) int = unsafe { nil }
// int (*renderResize)(void* uptr, int width, int height) // int (*renderResize)(void* uptr, int width, int height)
renderResize fn (uptr voidptr, width int, height int) int renderResize fn (uptr voidptr, width int, height int) int = unsafe { nil }
// void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data) // void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data)
renderUpdate fn (uptr voidptr, rect &int, data &u8) renderUpdate fn (uptr voidptr, rect &int, data &u8) = unsafe { nil }
// void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts) // void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts)
renderDraw fn (uptr voidptr, verts &f32, tcoords &f32, colors &u32, nverts int) renderDraw fn (uptr voidptr, verts &f32, tcoords &f32, colors &u32, nverts int) = unsafe { nil }
// void (*renderDelete)(void* uptr) // void (*renderDelete)(void* uptr)
renderDelete fn (uptr voidptr) renderDelete fn (uptr voidptr) = unsafe { nil }
} }
pub struct C.FONSquad { pub struct C.FONSquad {

View File

@ -25,15 +25,25 @@ pub mut:
} }
// create_image creates an `Image` from `file`. // create_image creates an `Image` from `file`.
pub fn (ctx &Context) create_image(file string) !Image { pub fn (mut ctx Context) create_image(file string) !Image {
// println('\ncreate_image("$file")')
if !os.exists(file) { if !os.exists(file) {
return error('image file "${file}" not found') $if android {
image_data := os.read_apk_asset(file)!
mut image := ctx.create_image_from_byte_array(image_data)!
image.path = file
return image
} $else {
return error('image file "${file}" not found')
}
} }
$if macos { $if macos {
if ctx.native_rendering { if ctx.native_rendering {
// return C.darwin_create_image(file) // return C.darwin_create_image(file)
mut img := C.darwin_create_image(file) mut img := C.darwin_create_image(file)
// println('created macos image: $img.path w=$img.width') // println('created macos image: $img.path w=$img.width')
// C.printf('p = %p\n', img.data) // C.printf('p = %p\n', img.data)
img.id = ctx.image_cache.len img.id = ctx.image_cache.len
@ -43,6 +53,7 @@ pub fn (ctx &Context) create_image(file string) !Image {
return img return img
} }
} }
if !gfx.is_valid() { if !gfx.is_valid() {
// Sokol is not initialized yet, add stbi object to a queue/cache // Sokol is not initialized yet, add stbi object to a queue/cache
// ctx.image_queue << file // ctx.image_queue << file

View File

@ -382,7 +382,7 @@ pub fn (u_ Uint256) str() string {
} }
// uint256_from_dec_str creates a new `unsigned.Uint256` from the given string if possible // uint256_from_dec_str creates a new `unsigned.Uint256` from the given string if possible
pub fn uint256_from_dec_str(value string) ?Uint256 { pub fn uint256_from_dec_str(value string) !Uint256 {
mut res := unsigned.uint256_zero mut res := unsigned.uint256_zero
for b_ in value.bytes() { for b_ in value.bytes() {
b := b_ - '0'.bytes()[0] b := b_ - '0'.bytes()[0]

View File

@ -170,7 +170,12 @@ fn C.mbedtls_pk_parse_keyfile(&C.mbedtls_pk_context, &char, &char, voidptr, void
fn C.mbedtls_net_connect(&C.mbedtls_net_context, &u8, &u8, int) int fn C.mbedtls_net_connect(&C.mbedtls_net_context, &u8, &u8, int) int
fn C.mbedtls_ssl_conf_own_cert(&C.mbedtls_ssl_config, &C.mbedtls_x509_crt, &C.mbedtls_pk_context) fn C.mbedtls_net_bind(&C.mbedtls_net_context, voidptr, &u8, int) int
fn C.mbedtls_net_accept(&C.mbedtls_net_context, &C.mbedtls_net_context, voidptr, int, voidptr) int
fn C.mbedtls_ssl_session_reset(&C.mbedtls_ssl_context)
fn C.mbedtls_ssl_conf_authmode(&C.mbedtls_ssl_config, int)
fn C.mbedtls_ssl_conf_own_cert(&C.mbedtls_ssl_config, &C.mbedtls_x509_crt, &C.mbedtls_pk_context) int
fn C.mbedtls_ssl_conf_authmode(&C.mbedtls_ssl_config, int) fn C.mbedtls_ssl_conf_authmode(&C.mbedtls_ssl_config, int)
fn C.mbedtls_ssl_conf_ca_chain(&C.mbedtls_ssl_config, &C.mbedtls_x509_crt, &C.mbedtls_x509_crl) fn C.mbedtls_ssl_conf_ca_chain(&C.mbedtls_ssl_config, &C.mbedtls_x509_crt, &C.mbedtls_x509_crl)
fn C.mbedtls_ssl_conf_rng(&C.mbedtls_ssl_config, voidptr, &C.mbedtls_ctr_drbg_context) fn C.mbedtls_ssl_conf_rng(&C.mbedtls_ssl_config, voidptr, &C.mbedtls_ctr_drbg_context)

View File

@ -45,6 +45,172 @@ mut:
owns_socket bool owns_socket bool
} }
// SSLListener listens on a TCP port and accepts connection secured with TLS
pub struct SSLListener {
saddr string
config SSLConnectConfig
mut:
server_fd C.mbedtls_net_context
ssl C.mbedtls_ssl_context
conf C.mbedtls_ssl_config
certs &SSLCerts = unsafe { nil }
opened bool
// handle int
// duration time.Duration
}
// create a new SSLListener binding to `saddr`
pub fn new_ssl_listener(saddr string, config SSLConnectConfig) !&SSLListener {
mut listener := &SSLListener{
saddr: saddr
config: config
}
listener.init()!
listener.opened = true
return listener
}
// finish the listener and clean up resources
pub fn (mut l SSLListener) shutdown() ! {
$if trace_ssl ? {
eprintln(@METHOD)
}
if unsafe { l.certs != nil } {
C.mbedtls_x509_crt_free(&l.certs.cacert)
C.mbedtls_x509_crt_free(&l.certs.client_cert)
C.mbedtls_pk_free(&l.certs.client_key)
}
C.mbedtls_ssl_free(&l.ssl)
C.mbedtls_ssl_config_free(&l.conf)
if l.opened {
C.mbedtls_net_free(&l.server_fd)
}
}
// internal function to init and bind the listener
fn (mut l SSLListener) init() ! {
$if trace_ssl ? {
eprintln(@METHOD)
}
lhost, lport := net.split_address(l.saddr)!
if l.config.cert == '' || l.config.cert_key == '' {
return error('No certificate or key provided')
}
if l.config.validate && l.config.verify == '' {
return error('No root CA provided')
}
C.mbedtls_net_init(&l.server_fd)
C.mbedtls_ssl_init(&l.ssl)
C.mbedtls_ssl_config_init(&l.conf)
l.certs = &SSLCerts{}
C.mbedtls_x509_crt_init(&l.certs.client_cert)
C.mbedtls_pk_init(&l.certs.client_key)
unsafe {
C.mbedtls_ssl_conf_rng(&l.conf, C.mbedtls_ctr_drbg_random, &mbedtls.ctr_drbg)
}
mut ret := 0
if l.config.in_memory_verification {
if l.config.verify != '' {
ret = C.mbedtls_x509_crt_parse(&l.certs.cacert, l.config.verify.str, l.config.verify.len)
}
if l.config.cert != '' {
ret = C.mbedtls_x509_crt_parse(&l.certs.client_cert, l.config.cert.str, l.config.cert.len)
}
if l.config.cert_key != '' {
unsafe {
ret = C.mbedtls_pk_parse_key(&l.certs.client_key, l.config.cert_key.str,
l.config.cert_key.len, 0, 0, C.mbedtls_ctr_drbg_random, &mbedtls.ctr_drbg)
}
}
} else {
if l.config.verify != '' {
ret = C.mbedtls_x509_crt_parse_file(&l.certs.cacert, &char(l.config.verify.str))
}
ret = C.mbedtls_x509_crt_parse_file(&l.certs.client_cert, &char(l.config.cert.str))
unsafe {
ret = C.mbedtls_pk_parse_keyfile(&l.certs.client_key, &char(l.config.cert_key.str),
0, C.mbedtls_ctr_drbg_random, &mbedtls.ctr_drbg)
}
}
if l.config.validate {
C.mbedtls_ssl_conf_authmode(&l.conf, C.MBEDTLS_SSL_VERIFY_REQUIRED)
}
mut bind_ip := unsafe { nil }
if lhost != '' {
bind_ip = voidptr(lhost.str)
}
bind_port := lport.str()
ret = C.mbedtls_net_bind(&l.server_fd, bind_ip, voidptr(bind_port.str), C.MBEDTLS_NET_PROTO_TCP)
if ret != 0 {
return error_with_code("can't bind to ${l.saddr}", ret)
}
ret = C.mbedtls_ssl_config_defaults(&l.conf, C.MBEDTLS_SSL_IS_SERVER, C.MBEDTLS_SSL_TRANSPORT_STREAM,
C.MBEDTLS_SSL_PRESET_DEFAULT)
if ret != 0 {
return error_with_code("can't to set config defaults", ret)
}
C.mbedtls_ssl_conf_ca_chain(&l.conf, &l.certs.cacert, unsafe { nil })
ret = C.mbedtls_ssl_conf_own_cert(&l.conf, &l.certs.client_cert, &l.certs.client_key)
if ret != 0 {
return error_with_code("can't load certificate", ret)
}
ret = C.mbedtls_ssl_setup(&l.ssl, &l.conf)
if ret != 0 {
return error_with_code("can't setup ssl", ret)
}
}
// accepts a new connection and returns a SSLConn of the connected client
pub fn (mut l SSLListener) accept() !&SSLConn {
mut conn := &SSLConn{
conf: l.conf
config: l.config
opened: true
owns_socket: true
}
// TODO: save the client's IP address somewhere (maybe add a field to SSLConn ?)
mut ret := C.mbedtls_net_accept(&l.server_fd, &conn.server_fd, unsafe { nil }, 0,
unsafe { nil })
if ret != 0 {
return error_with_code("can't accept connection", ret)
}
C.mbedtls_ssl_init(&conn.ssl)
C.mbedtls_ssl_config_init(&conn.conf)
ret = C.mbedtls_ssl_setup(&conn.ssl, &l.conf)
if ret != 0 {
return error_with_code('SSL setup failed', ret)
}
C.mbedtls_ssl_set_bio(&conn.ssl, &conn.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
unsafe { nil })
ret = C.mbedtls_ssl_handshake(&conn.ssl)
for ret != 0 {
if ret != C.MBEDTLS_ERR_SSL_WANT_READ && ret != C.MBEDTLS_ERR_SSL_WANT_WRITE {
return error_with_code('SSL handshake failed', ret)
}
ret = C.mbedtls_ssl_handshake(&conn.ssl)
}
return conn
}
[params] [params]
pub struct SSLConnectConfig { pub struct SSLConnectConfig {
verify string // the path to a rootca.pem file, containing trusted CA certificate(s) verify string // the path to a rootca.pem file, containing trusted CA certificate(s)

View File

@ -284,20 +284,27 @@ mut:
accept_deadline time.Time accept_deadline time.Time
} }
pub fn listen_tcp(family AddrFamily, saddr string) !&TcpListener { [params]
s := new_tcp_socket(family) or { return error('${err.msg()}; could not create new socket') } pub struct ListenOptions {
pub:
dualstack bool = true
backlog int = 128
}
pub fn listen_tcp(family AddrFamily, saddr string, options ListenOptions) !&TcpListener {
mut s := new_tcp_socket(family) or { return error('${err.msg()}; could not create new socket') }
s.set_dualstack(options.dualstack) or {}
addrs := resolve_addrs(saddr, family, .tcp) or { addrs := resolve_addrs(saddr, family, .tcp) or {
return error('${err.msg()}; could not resolve address ${saddr}') return error('${err.msg()}; could not resolve address ${saddr}')
} }
// TODO(logic to pick here) // TODO(logic to pick here)
addr := addrs[0] addr := addrs[0]
// cast to the correct type // cast to the correct type
alen := addr.len() alen := addr.len()
socket_error_message(C.bind(s.handle, voidptr(&addr), alen), 'binding to ${saddr} failed')! socket_error_message(C.bind(s.handle, voidptr(&addr), alen), 'binding to ${saddr} failed')!
socket_error_message(C.listen(s.handle, 128), 'listening on ${saddr} failed')! socket_error_message(C.listen(s.handle, options.backlog), 'listening on ${saddr} with maximum backlog pending queue of ${options.backlog}, failed')!
return &TcpListener{ return &TcpListener{
sock: s sock: s
accept_deadline: no_deadline accept_deadline: no_deadline

View File

@ -150,9 +150,7 @@ pub fn (mut ws Client) listen() ! {
ws.debug_log('got message: ${msg.opcode}') ws.debug_log('got message: ${msg.opcode}')
match msg.opcode { match msg.opcode {
.text_frame { .text_frame {
log_msg = 'read: text' ws.debug_log('read: text')
ws.debug_log(log_msg)
unsafe { log_msg.free() }
ws.send_message_event(msg) ws.send_message_event(msg)
unsafe { msg.free() } unsafe { msg.free() }
} }
@ -184,9 +182,7 @@ pub fn (mut ws Client) listen() ! {
} }
} }
.close { .close {
log_msg = 'read: close' ws.debug_log('read: close')
ws.debug_log(log_msg)
unsafe { log_msg.free() }
defer { defer {
ws.manage_clean_close() ws.manage_clean_close()
} }

View File

@ -13,17 +13,15 @@ pub fn input_password(prompt string) !string {
if termios.tcgetattr(0, mut old_state) != 0 { if termios.tcgetattr(0, mut old_state) != 0 {
return last_error() return last_error()
} }
defer {
termios.tcsetattr(0, C.TCSANOW, mut old_state)
println('')
}
mut new_state := old_state mut new_state := old_state
new_state.disable_echo()
new_state.c_lflag &= termios.invert(C.ECHO) // Disable echoing of characters termios.set_state(0, new_state)
termios.tcsetattr(0, C.TCSANOW, mut new_state)
password := input_opt(prompt) or { return error('Failed to read password') } password := input_opt(prompt) or { return error('Failed to read password') }
termios.set_state(0, old_state)
println('')
return password return password
} }

View File

@ -106,8 +106,6 @@ fn (mut pv Picoev) del(fd int) int {
} }
if pv.update_events(fd, picoev.picoev_del) != 0 { if pv.update_events(fd, picoev.picoev_del) != 0 {
target.loop_id = -1
target.fd = 0
return -1 return -1
} }
@ -149,15 +147,20 @@ fn (mut pv Picoev) set_timeout(fd int, secs int) {
// timeout event // timeout event
[direct_array_access; inline] [direct_array_access; inline]
fn (mut pv Picoev) handle_timeout() { fn (mut pv Picoev) handle_timeout() {
mut to_remove := []int{}
for fd, timeout in pv.timeouts { for fd, timeout in pv.timeouts {
if timeout <= pv.loop.now { if timeout <= pv.loop.now {
target := pv.file_descriptors[fd] to_remove << fd
assert target.loop_id == pv.loop.id
pv.timeouts.delete(fd)
unsafe { target.cb(fd, picoev.picoev_timeout, &pv) }
} }
} }
for fd in to_remove {
target := pv.file_descriptors[fd]
assert target.loop_id == pv.loop.id
pv.timeouts.delete(fd)
unsafe { target.cb(fd, picoev.picoev_timeout, &pv) }
}
} }
// accept_callback accepts a new connection from `listen_fd` and adds it to the loop // accept_callback accepts a new connection from `listen_fd` and adds it to the loop

View File

@ -248,7 +248,7 @@ mut:
// counters for quantifier check (repetitions) // counters for quantifier check (repetitions)
rep int rep int
// validator function pointer // validator function pointer
validator FnValidator validator FnValidator = unsafe { nil }
// groups variables // groups variables
group_neg bool // negation flag for the group, 0 => no negation > 0 => negataion group_neg bool // negation flag for the group, 0 => no negation > 0 => negataion
group_rep int // repetition of the group group_rep int // repetition of the group
@ -376,7 +376,7 @@ fn (mut re RE) reset_src() {
******************************************************************************/ ******************************************************************************/
struct BslsStruct { struct BslsStruct {
ch rune // meta char ch rune // meta char
validator FnValidator // validator function pointer validator FnValidator = unsafe { nil } // validator function pointer
} }
const ( const (
@ -463,7 +463,7 @@ mut:
cc_type int // type of cc token cc_type int // type of cc token
ch0 rune // first char of the interval a-b a in this case ch0 rune // first char of the interval a-b a in this case
ch1 rune // second char of the interval a-b b in this case ch1 rune // second char of the interval a-b b in this case
validator FnValidator // validator function pointer validator FnValidator = unsafe { nil } // validator function pointer
} }
enum CharClass_parse_state { enum CharClass_parse_state {

View File

@ -6,10 +6,21 @@ pub const used_import = 1
#flag -I @VEXEROOT/thirdparty/sokol/util #flag -I @VEXEROOT/thirdparty/sokol/util
#flag freebsd -I /usr/local/include #flag freebsd -I /usr/local/include
#flag darwin -fobjc-arc #flag darwin -fobjc-arc
#flag linux -lX11 -lGL -lXcursor -lXi -lpthread #flag linux -lX11 -lGL -lXcursor -lXi -lpthread
#flag freebsd -L/usr/local/lib -lX11 -lGL -lXcursor -lXi #flag freebsd -L/usr/local/lib -lX11 -lGL -lXcursor -lXi
#flag openbsd -L/usr/X11R6/lib -lX11 -lGL -lXcursor -lXi #flag openbsd -L/usr/X11R6/lib -lX11 -lGL -lXcursor -lXi
#flag windows -lgdi32 #flag windows -lgdi32
// Note that -lm is needed *only* for sokol_gl.h's usage of sqrtf(),
// but without -lm, this fails:
// `v -cc gcc ~/.vmodules/sdl/examples/sdl_opengl_and_sokol/`
// With tcc, this succeeds with or without -lm:
// `v ~/.vmodules/sdl/examples/sdl_opengl_and_sokol/`
$if !tinyc {
#flag linux -lm
}
// METAL // METAL
$if macos { $if macos {
$if darwin_sokol_glcore33 ? { $if darwin_sokol_glcore33 ? {
@ -19,10 +30,6 @@ $if macos {
#flag -framework Metal -framework Cocoa -framework MetalKit -framework QuartzCore #flag -framework Metal -framework Cocoa -framework MetalKit -framework QuartzCore
} }
} }
$if linux {
#flag -D SOKOL_GLCORE33
}
$if ios { $if ios {
#flag -DSOKOL_METAL #flag -DSOKOL_METAL
#flag -framework Foundation -framework Metal -framework MetalKit -framework UIKit #flag -framework Foundation -framework Metal -framework MetalKit -framework UIKit

View File

@ -5,14 +5,14 @@ import sokol.memory
[typedef] [typedef]
pub struct C.sg_allocator { pub struct C.sg_allocator {
pub mut: pub mut:
alloc memory.FnAllocatorAlloc alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr user_data voidptr
} }
[typedef] [typedef]
pub struct C.sg_logger { pub struct C.sg_logger {
pub mut: pub mut:
log_cb memory.FnLogCb log_cb memory.FnLogCb = unsafe { nil }
user_data voidptr user_data voidptr
} }

View File

@ -46,8 +46,8 @@ pub type GLContextDesc = C.sg_gl_context_desc
struct C.sg_metal_context_desc { struct C.sg_metal_context_desc {
device voidptr device voidptr
renderpass_descriptor_cb fn () voidptr renderpass_descriptor_cb fn () voidptr = unsafe { nil }
drawable_cb fn () voidptr drawable_cb fn () voidptr = unsafe { nil }
} }
pub type MetalContextDesc = C.sg_metal_context_desc pub type MetalContextDesc = C.sg_metal_context_desc
@ -55,8 +55,8 @@ pub type MetalContextDesc = C.sg_metal_context_desc
struct C.sg_d3d11_context_desc { struct C.sg_d3d11_context_desc {
device voidptr device voidptr
device_context voidptr device_context voidptr
render_target_view_cb fn () voidptr render_target_view_cb fn () voidptr = unsafe { nil }
depth_stencil_view_cb fn () voidptr depth_stencil_view_cb fn () voidptr = unsafe { nil }
} }
pub type D3D11ContextDesc = C.sg_d3d11_context_desc pub type D3D11ContextDesc = C.sg_d3d11_context_desc

View File

@ -5,14 +5,14 @@ import sokol.memory
[typedef] [typedef]
pub struct C.sapp_allocator { pub struct C.sapp_allocator {
pub mut: pub mut:
alloc memory.FnAllocatorAlloc alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr user_data voidptr
} }
[typedef] [typedef]
pub struct C.sapp_logger { pub struct C.sapp_logger {
pub mut: pub mut:
log_cb memory.FnLogCb log_cb memory.FnLogCb = unsafe { nil }
user_data voidptr user_data voidptr
} }

View File

@ -37,18 +37,19 @@ pub type IconDesc = C.sapp_icon_desc
[typedef] [typedef]
pub struct C.sapp_desc { pub struct C.sapp_desc {
pub: pub:
init_cb fn () // these are the user-provided callbacks without user data // these are the user-provided callbacks without user data
frame_cb fn () init_cb fn () = unsafe { nil }
cleanup_cb fn () frame_cb fn () = unsafe { nil }
event_cb fn (&Event) //&sapp_event) cleanup_cb fn () = unsafe { nil }
fail_cb fn (&u8) event_cb fn (&Event) = unsafe { nil } // &sapp_event
fail_cb fn (&u8) = unsafe { nil }
user_data voidptr // these are the user-provided callbacks with user data user_data voidptr // these are the user-provided callbacks with user data
init_userdata_cb fn (voidptr) init_userdata_cb fn (voidptr) = unsafe { nil }
frame_userdata_cb fn (voidptr) frame_userdata_cb fn (voidptr) = unsafe { nil }
cleanup_userdata_cb fn (voidptr) cleanup_userdata_cb fn (voidptr) = unsafe { nil }
event_userdata_cb fn (&Event, voidptr) event_userdata_cb fn (&Event, voidptr) = unsafe { nil }
fail_userdata_cb fn (&char, voidptr) fail_userdata_cb fn (&char, voidptr) = unsafe { nil }
width int // the preferred width of the window / canvas width int // the preferred width of the window / canvas
height int // the preferred height of the window / canvas height int // the preferred height of the window / canvas

View File

@ -6,8 +6,8 @@ import sokol.memory
[typedef] [typedef]
pub struct C.sfons_allocator_t { pub struct C.sfons_allocator_t {
pub: pub:
alloc memory.FnAllocatorAlloc alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr user_data voidptr
} }

View File

@ -5,14 +5,14 @@ import sokol.memory
[typedef] [typedef]
pub struct C.sgl_allocator_t { pub struct C.sgl_allocator_t {
pub mut: pub mut:
alloc memory.FnAllocatorAlloc alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr user_data voidptr
} }
[typedef] [typedef]
pub struct C.sgl_logger_t { pub struct C.sgl_logger_t {
pub mut: pub mut:
log_cb memory.FnLogCb log_cb memory.FnLogCb = unsafe { nil }
user_data voidptr user_data voidptr
} }

View File

@ -26,7 +26,7 @@ mut:
nxt &Subscription = unsafe { nil } nxt &Subscription = unsafe { nil }
} }
enum Direction { pub enum Direction {
pop pop
push push
} }

View File

@ -24,9 +24,15 @@ mut:
pub type ThreadCB = fn (mut p PoolProcessor, idx int, task_id int) voidptr pub type ThreadCB = fn (mut p PoolProcessor, idx int, task_id int) voidptr
fn empty_cb(mut p PoolProcessor, idx int, task_id int) voidptr {
unsafe {
return nil
}
}
pub struct PoolProcessorConfig { pub struct PoolProcessorConfig {
maxjobs int maxjobs int
callback ThreadCB callback ThreadCB = empty_cb
} }
// new_pool_processor returns a new PoolProcessor instance. // new_pool_processor returns a new PoolProcessor instance.

View File

@ -176,7 +176,7 @@ pub fn (mut sem Semaphore) timed_wait(timeout time.Duration) bool {
C.GetSystemTimeAsFileTime(&ft_start) C.GetSystemTimeAsFileTime(&ft_start)
time_end := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) + time_end := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) +
u64(timeout / (100 * time.nanosecond)) u64(timeout / (100 * time.nanosecond))
mut t_ms := timeout.sys_milliseconds() mut t_ms := u32(timeout.sys_milliseconds())
C.AcquireSRWLockExclusive(&sem.mtx) C.AcquireSRWLockExclusive(&sem.mtx)
mut res := 0 mut res := 0
c = C.atomic_load_u32(&sem.count) c = C.atomic_load_u32(&sem.count)

View File

@ -17,7 +17,8 @@ pub fn get_terminal_size() (int, int) {
} }
// clear clears current terminal screen. // clear clears current terminal screen.
pub fn clear() { pub fn clear() bool {
print('\x1b[2J') print('\x1b[2J')
print('\x1b[H') print('\x1b[H')
return true
} }

View File

@ -97,7 +97,7 @@ pub fn set_tab_title(title string) bool {
// clear clears current terminal screen. // clear clears current terminal screen.
// Implementation taken from https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-2. // Implementation taken from https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-2.
pub fn clear() { pub fn clear() bool {
hconsole := C.GetStdHandle(C.STD_OUTPUT_HANDLE) hconsole := C.GetStdHandle(C.STD_OUTPUT_HANDLE)
mut csbi := C.CONSOLE_SCREEN_BUFFER_INFO{} mut csbi := C.CONSOLE_SCREEN_BUFFER_INFO{}
mut scrollrect := C.SMALL_RECT{} mut scrollrect := C.SMALL_RECT{}
@ -106,7 +106,7 @@ pub fn clear() {
// Get the number of character cells in the current buffer. // Get the number of character cells in the current buffer.
if !C.GetConsoleScreenBufferInfo(hconsole, &csbi) { if !C.GetConsoleScreenBufferInfo(hconsole, &csbi) {
return return false
} }
// Scroll the rectangle of the entire buffer. // Scroll the rectangle of the entire buffer.
scrollrect.Left = 0 scrollrect.Left = 0
@ -130,4 +130,5 @@ pub fn clear() {
csbi.dwCursorPosition.Y = 0 csbi.dwCursorPosition.Y = 0
C.SetConsoleCursorPosition(hconsole, csbi.dwCursorPosition) C.SetConsoleCursorPosition(hconsole, csbi.dwCursorPosition)
return true
} }

View File

@ -41,7 +41,7 @@ fn C.ioctl(fd int, request u64, arg voidptr) int
// for the underlying C.termios structure // for the underlying C.termios structure
[inline] [inline]
pub fn flag(value int) TcFlag { pub fn flag(value int) TcFlag {
return int(value) return TcFlag(value)
} }
// invert is a platform dependant way to bitwise NOT (~) TcFlag // invert is a platform dependant way to bitwise NOT (~) TcFlag
@ -86,3 +86,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -84,3 +84,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -1,12 +1,6 @@
// Copyright (c) 2019-2023 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019-2023 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
//
// TODO Mac version needs to be implemented
// Will serve as more advanced input method
// based on the work of https://github.com/AmokHuginnsson/replxx
//
module termios module termios
// not used but needed for function declarations // not used but needed for function declarations
@ -14,45 +8,68 @@ type TcFlag = int
type Speed = int type Speed = int
type Cc = u8 type Cc = u8
// flag provides a termios flag of the correct size // Termios definitions
// for the underlying C.termios structure
// It is only implemented for Unix like OSes
pub fn flag(value int) TcFlag {
$compile_error('feature not available')
}
// invert is a platform dependant way to bitwise NOT (~) TcFlag
// as its length varies across platforms
// It is only implemented for Unix like OSes
pub fn invert(value TcFlag) TcFlag {
$compile_error('feature not available')
}
// termios definitions
// Linux https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/bits/termios.h // Linux https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/bits/termios.h
// OpenBSD https://github.com/openbsd/src/blob/master/sys/sys/termios.h // OpenBSD https://github.com/openbsd/src/blob/master/sys/sys/termios.h
// FreeBSD https://web.mit.edu/freebsd/head/sys/sys/_termios.h // FreeBSD https://web.mit.edu/freebsd/head/sys/sys/_termios.h
// Solaris https://github.com/omniti-labs/illumos-omnios/blob/master/usr/src/uts/common/sys/termios.h // Solaris https://github.com/omniti-labs/illumos-omnios/blob/master/usr/src/uts/common/sys/termios.h
// DragonFly https://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/sys/_termios.h // DragonFly https://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/sys/_termios.h
// QNX https://github.com/vocho/openqnx/blob/master/trunk/lib/c/public/termios.h // QNX https://github.com/vocho/openqnx/blob/master/trunk/lib/c/public/termios.h
pub struct Termios { pub struct Termios {
pub mut:
c_iflag TcFlag
c_oflag TcFlag
c_cflag TcFlag
c_lflag TcFlag
c_line Cc
c_cc [32]Cc
c_ispeed Speed
c_ospeed Speed
}
// flag provides a termios flag of the correct size
// for the underlying C.termios structure
pub fn flag(value int) TcFlag {
return TcFlag(value)
}
// invert is a platform dependant way to bitwise NOT (~) TcFlag
// as its length varies across platforms
// It is only implemented for Unix like OSes
pub fn invert(value TcFlag) TcFlag {
return TcFlag(~int(value))
} }
// tcgetattr is an unsafe wrapper around C.termios and keeps its semantic // tcgetattr is an unsafe wrapper around C.termios and keeps its semantic
// It is only implemented for Unix like OSes // It is only implemented for Unix like OSes
pub fn tcgetattr(fd int, mut termios_p Termios) int { pub fn tcgetattr(fd int, mut t Termios) int {
$compile_error('feature not available') $compile_warn('termios.tcgetattr is not implemented on the platform')
eprintln('tcgetattr, fd: ${fd}, t: ${t}')
return 0
} }
// tcsetattr is an unsafe wrapper around C.termios and keeps its semantic // tcsetattr is an unsafe wrapper around C.termios and keeps its semantic
// It is only implemented for Unix like OSes // It is only implemented for Unix like OSes
pub fn tcsetattr(fd int, optional_actions int, mut termios_p Termios) int { pub fn tcsetattr(fd int, optional_actions int, mut t Termios) int {
$compile_error('feature not available') eprintln('tcsetattr, fd: ${fd}, optional_actions: ${optional_actions}, t: ${t}')
return 0
} }
// ioctl is an unsafe wrapper around C.ioctl and keeps its semantic // ioctl is an unsafe wrapper around C.ioctl and keeps its semantic
[inline] // It is only implemented for Unix like OSes
pub fn ioctl(fd int, request u64, arg voidptr) int { pub fn ioctl(fd int, request u64, arg voidptr) int {
$compile_error('feature not available') eprintln('ioctl, fd: ${fd}, request: ${request}, arg: ${arg}')
return 0
}
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
eprintln('set_state, fd: ${fd} | new_state: ${new_state}')
return 0
}
// disable_echo disables echoing characters as they are typed
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(8)
} }

View File

@ -84,3 +84,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -84,3 +84,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -41,7 +41,7 @@ fn C.ioctl(fd int, request u64, arg voidptr) int
// for the underlying C.termios structure // for the underlying C.termios structure
[inline] [inline]
pub fn flag(value int) TcFlag { pub fn flag(value int) TcFlag {
return int(value) return TcFlag(value)
} }
// invert is a platform dependant way to bitwise NOT (~) TcFlag // invert is a platform dependant way to bitwise NOT (~) TcFlag
@ -86,3 +86,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -84,3 +84,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -86,3 +86,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -80,3 +80,15 @@ pub fn ioctl(fd int, request u64, arg voidptr) int {
return C.ioctl(fd, request, arg) return C.ioctl(fd, request, arg)
} }
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
mut x := new_state
return tcsetattr(0, C.TCSANOW, mut x)
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
t.c_lflag &= invert(C.ECHO)
}

View File

@ -1,20 +0,0 @@
module termios
fn test_termios() {
mut original_term := Termios{}
tcgetattr(0, mut original_term)
println(original_term)
mut silent_term := original_term
silent_term.c_lflag &= invert(C.ECHO)
tcsetattr(0, C.TCSANOW, mut silent_term)
mut check_term := Termios{}
tcgetattr(0, mut check_term)
assert check_term.c_lflag == silent_term.c_lflag
tcsetattr(0, C.TCSANOW, mut orginal_term)
tcgetattr(0, mut check_term)
assert check_term.c_lflag == orginal_term.c_lflag
}

View File

@ -0,0 +1,35 @@
module termios
fn test_portable() {
assert 123 == int(flag(123))
o := Termios{
c_lflag: flag(0xFFFF)
} // assume c_lflag exists everywhere
// dump( o.c_lflag )
mut n := o
n.c_lflag &= invert(1)
// dump( n.c_lflag )
assert n.c_lflag != o.c_lflag
n.disable_echo() // just assume it exists, and can be called everywhere
assert true
}
[if !windows]
fn test_termios() {
mut original_term := Termios{}
tcgetattr(0, mut original_term)
println(original_term)
mut silent_term := original_term
silent_term.c_lflag &= invert(C.ECHO)
tcsetattr(0, C.TCSANOW, mut silent_term)
mut check_term := Termios{}
tcgetattr(0, mut check_term)
assert check_term.c_lflag == silent_term.c_lflag
tcsetattr(0, C.TCSANOW, mut original_term)
tcgetattr(0, mut check_term)
assert check_term.c_lflag == original_term.c_lflag
}

View File

@ -27,6 +27,15 @@ pub fn invert(value TcFlag) TcFlag {
} }
pub struct Termios { pub struct Termios {
pub mut:
c_iflag TcFlag
c_oflag TcFlag
c_cflag TcFlag
c_lflag TcFlag
c_line Cc
c_cc [32]Cc
c_ispeed Speed
c_ospeed Speed
} }
// tcgetattr is an unsafe wrapper around C.termios and keeps its semantic // tcgetattr is an unsafe wrapper around C.termios and keeps its semantic
@ -47,3 +56,13 @@ pub fn tcsetattr(fd int, optional_actions int, mut termios_p Termios) int {
pub fn ioctl(fd int, request u64, arg voidptr) int { pub fn ioctl(fd int, request u64, arg voidptr) int {
return -1 return -1
} }
// set_state applies the flags in the `new_state` to the descriptor `fd`.
pub fn set_state(fd int, new_state Termios) int {
return -1
}
// disable_echo disables echoing characters as they are typed,
// when that Termios state is later set with termios.set_state(fd,t)
pub fn (mut t Termios) disable_echo() {
}

View File

@ -135,3 +135,15 @@ pub fn (t Time) strftime(fmt string) string {
C.strftime(&buf[0], usize(sizeof(buf)), fmt_c, tm) C.strftime(&buf[0], usize(sizeof(buf)), fmt_c, tm)
return unsafe { cstring_to_vstring(&char(&buf[0])) } return unsafe { cstring_to_vstring(&char(&buf[0])) }
} }
// some *nix system functions (e.g. `C.poll()`, C.epoll_wait()) accept an `int`
// value as *timeout in milliseconds* with the special value `-1` meaning "infinite"
pub fn (d Duration) sys_milliseconds() int {
if d > 2147483647 * millisecond { // treat 2147483647000001 .. C.INT64_MAX as "infinite"
return -1
} else if d <= 0 {
return 0 // treat negative timeouts as 0 - consistent with Unix behaviour
} else {
return int(d / millisecond)
}
}

View File

@ -88,11 +88,11 @@ fn darwin_utc() Time {
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn solaris_now() Time { fn solaris_now() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn solaris_utc() Time { fn solaris_utc() Time {
return Time{} return Time{}
} }

View File

@ -6,21 +6,21 @@ fn sys_mono_now_darwin() u64 {
} }
// darwin_now - dummy fn to compile on all platforms/compilers // darwin_now - dummy fn to compile on all platforms/compilers
pub fn darwin_now() Time { fn darwin_now() Time {
return Time{} return Time{}
} }
// solaris_now - dummy fn to compile on all platforms/compilers // solaris_now - dummy fn to compile on all platforms/compilers
pub fn solaris_now() Time { fn solaris_now() Time {
return Time{} return Time{}
} }
// darwin_utc - dummy fn to compile on all platforms/compilers // darwin_utc - dummy fn to compile on all platforms/compilers
pub fn darwin_utc() Time { fn darwin_utc() Time {
return Time{} return Time{}
} }
// solaris_utc - dummy fn to compile on all platforms/compilers // solaris_utc - dummy fn to compile on all platforms/compilers
pub fn solaris_utc() Time { fn solaris_utc() Time {
return Time{} return Time{}
} }

View File

@ -95,12 +95,12 @@ fn linux_utc() Time {
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn win_now() Time { fn win_now() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn win_utc() Time { fn win_utc() Time {
return Time{} return Time{}
} }
@ -125,15 +125,6 @@ pub fn (d Duration) timespec() C.timespec {
return ts return ts
} }
// zero_timespec returns the calendar time in seconds and nanoseconds of the beginning of the Unix epoch.
pub fn zero_timespec() C.timespec {
ts := C.timespec{
tv_sec: 0
tv_nsec: 0
}
return ts
}
// sleep suspends the execution of the calling thread for a given duration (in nanoseconds). // sleep suspends the execution of the calling thread for a given duration (in nanoseconds).
pub fn sleep(duration Duration) { pub fn sleep(duration Duration) {
mut req := C.timespec{duration / second, duration % second} mut req := C.timespec{duration / second, duration % second}
@ -147,15 +138,3 @@ pub fn sleep(duration Duration) {
} }
} }
} }
// some *nix system functions (e.g. `C.poll()`, C.epoll_wait()) accept an `int`
// value as *timeout in milliseconds* with the special value `-1` meaning "infinite"
pub fn (d Duration) sys_milliseconds() int {
if d > C.INT32_MAX * millisecond { // treat 2147483647000001 .. C.INT64_MAX as "infinite"
return -1
} else if d <= 0 {
return 0 // treat negative timeouts as 0 - consistent with Unix behaviour
} else {
return int(d / millisecond)
}
}

View File

@ -22,11 +22,11 @@ fn solaris_utc() Time {
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn darwin_now() Time { fn darwin_now() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn darwin_utc() Time { fn darwin_utc() Time {
return Time{} return Time{}
} }

View File

@ -171,7 +171,7 @@ fn win_utc() Time {
} }
// unix_time returns Unix time. // unix_time returns Unix time.
pub fn (st SystemTime) unix_time() i64 { fn (st SystemTime) unix_time() i64 {
tt := C.tm{ tt := C.tm{
tm_sec: st.second tm_sec: st.second
tm_min: st.minute tm_min: st.minute
@ -184,32 +184,32 @@ pub fn (st SystemTime) unix_time() i64 {
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn darwin_now() Time { fn darwin_now() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn linux_now() Time { fn linux_now() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn solaris_now() Time { fn solaris_now() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn darwin_utc() Time { fn darwin_utc() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn linux_utc() Time { fn linux_utc() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers // dummy to compile with all compilers
pub fn solaris_utc() Time { fn solaris_utc() Time {
return Time{} return Time{}
} }
@ -223,15 +223,3 @@ pub struct C.timeval {
pub fn sleep(duration Duration) { pub fn sleep(duration Duration) {
C.Sleep(int(duration / millisecond)) C.Sleep(int(duration / millisecond))
} }
// some Windows system functions (e.g. `C.WaitForSingleObject()`) accept an `u32`
// value as *timeout in milliseconds* with the special value `u32(-1)` meaning "infinite"
pub fn (d Duration) sys_milliseconds() u32 {
if d >= u32(-1) * millisecond { // treat 4294967295000000 .. C.INT64_MAX as "infinite"
return u32(-1)
} else if d <= 0 {
return 0 // treat negative timeouts as 0 - consistent with Unix behaviour
} else {
return u32(d / millisecond)
}
}

View File

@ -15,7 +15,7 @@ pub interface Modifier {
pub type InspectorFn = fn (value &ast.Value, data voidptr) ! pub type InspectorFn = fn (value &ast.Value, data voidptr) !
struct Inspector { struct Inspector {
inspector_callback InspectorFn inspector_callback InspectorFn = unsafe { nil }
mut: mut:
data voidptr data voidptr
} }

View File

@ -381,6 +381,7 @@ pub:
language Language language Language
is_union bool is_union bool
attrs []Attr attrs []Attr
pre_comments []Comment
end_comments []Comment end_comments []Comment
embeds []Embed embeds []Embed
pub mut: pub mut:
@ -531,10 +532,11 @@ pub:
method_idx int method_idx int
rec_mut bool // is receiver mutable rec_mut bool // is receiver mutable
rec_share ShareType rec_share ShareType
language Language // V, C, JS language Language // V, C, JS
file_mode Language // whether *the file*, where a function was a '.c.v', '.js.v' etc. file_mode Language // whether *the file*, where a function was a '.c.v', '.js.v' etc.
no_body bool // just a definition `fn C.malloc()` no_body bool // just a definition `fn C.malloc()`
is_builtin bool // this function is defined in builtin/strconv is_builtin bool // this function is defined in builtin/strconv
name_pos token.Pos
body_pos token.Pos // function bodys position body_pos token.Pos // function bodys position
file string file string
generic_names []string generic_names []string
@ -633,7 +635,8 @@ pub:
type_pos token.Pos type_pos token.Pos
is_hidden bool // interface first arg is_hidden bool // interface first arg
pub mut: pub mut:
typ Type typ Type
comments []Comment
} }
pub fn (p &Param) specifier() string { pub fn (p &Param) specifier() string {
@ -1194,6 +1197,7 @@ pub:
val_var string val_var string
is_range bool is_range bool
pos token.Pos pos token.Pos
kv_pos token.Pos
comments []Comment comments []Comment
val_is_mut bool // `for mut val in vals {` means that modifying `val` will modify the array val_is_mut bool // `for mut val in vals {` means that modifying `val` will modify the array
// and the array cannot be indexed inside the loop // and the array cannot be indexed inside the loop

View File

@ -59,7 +59,7 @@ pub fn (node &CallExpr) fkey() string {
} }
// These methods are used only by vfmt, vdoc, and for debugging. // These methods are used only by vfmt, vdoc, and for debugging.
pub fn (node &AnonFn) stringify_anon_decl(t &Table, cur_mod string, m2a map[string]string) string { pub fn (t &Table) stringify_anon_decl(node &AnonFn, cur_mod string, m2a map[string]string) string {
mut f := strings.new_builder(30) mut f := strings.new_builder(30)
f.write_string('fn ') f.write_string('fn ')
if node.inherited_vars.len > 0 { if node.inherited_vars.len > 0 {
@ -79,11 +79,11 @@ pub fn (node &AnonFn) stringify_anon_decl(t &Table, cur_mod string, m2a map[stri
} }
f.write_string('] ') f.write_string('] ')
} }
stringify_fn_after_name(node.decl, mut f, t, cur_mod, m2a) t.stringify_fn_after_name(node.decl, mut f, cur_mod, m2a)
return f.str() return f.str()
} }
pub fn (node &FnDecl) stringify_fn_decl(t &Table, cur_mod string, m2a map[string]string) string { pub fn (t &Table) stringify_fn_decl(node &FnDecl, cur_mod string, m2a map[string]string) string {
mut f := strings.new_builder(30) mut f := strings.new_builder(30)
if node.is_pub { if node.is_pub {
f.write_string('pub ') f.write_string('pub ')
@ -120,11 +120,11 @@ pub fn (node &FnDecl) stringify_fn_decl(t &Table, cur_mod string, m2a map[string
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] { if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] {
f.write_string(' ') f.write_string(' ')
} }
stringify_fn_after_name(node, mut f, t, cur_mod, m2a) t.stringify_fn_after_name(node, mut f, cur_mod, m2a)
return f.str() return f.str()
} }
fn stringify_fn_after_name(node &FnDecl, mut f strings.Builder, t &Table, cur_mod string, m2a map[string]string) { fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_mod string, m2a map[string]string) {
mut add_para_types := true mut add_para_types := true
if node.generic_names.len > 0 { if node.generic_names.len > 0 {
if node.is_method { if node.is_method {
@ -149,27 +149,26 @@ fn stringify_fn_after_name(node &FnDecl, mut f strings.Builder, t &Table, cur_mo
} }
} }
f.write_string('(') f.write_string('(')
for i, arg in node.params { for i, param in node.params {
// skip receiver // skip receiver
// if (node.is_method || node.is_interface) && i == 0 {
if node.is_method && i == 0 { if node.is_method && i == 0 {
continue continue
} }
if arg.is_hidden { if param.is_hidden {
continue continue
} }
is_last_arg := i == node.params.len - 1 is_last_param := i == node.params.len - 1
is_type_only := arg.name == '' is_type_only := param.name == ''
should_add_type := true // is_last_arg || is_type_only || node.params[i + 1].typ != arg.typ || should_add_type := true // is_last_param || is_type_only || node.params[i + 1].typ != param.typ ||
// (node.is_variadic && i == node.params.len - 2) // (node.is_variadic && i == node.params.len - 2)
if arg.is_mut { if param.is_mut {
f.write_string(arg.typ.share().str() + ' ') f.write_string(param.typ.share().str() + ' ')
} }
f.write_string(arg.name) f.write_string(param.name)
arg_sym := t.sym(arg.typ) param_sym := t.sym(param.typ)
if arg_sym.kind == .struct_ && (arg_sym.info as Struct).is_anon { if param_sym.kind == .struct_ && (param_sym.info as Struct).is_anon {
f.write_string(' struct {') f.write_string(' struct {')
struct_ := arg_sym.info as Struct struct_ := param_sym.info as Struct
for field in struct_.fields { for field in struct_.fields {
f.write_string(' ${field.name} ${t.type_to_str(field.typ)}') f.write_string(' ${field.name} ${t.type_to_str(field.typ)}')
if field.has_default_expr { if field.has_default_expr {
@ -181,9 +180,9 @@ fn stringify_fn_after_name(node &FnDecl, mut f strings.Builder, t &Table, cur_mo
} }
f.write_string('}') f.write_string('}')
} else { } else {
mut s := t.type_to_str(arg.typ.clear_flag(.shared_f)) mut s := t.type_to_str(param.typ.clear_flag(.shared_f))
if arg.is_mut { if param.is_mut {
if s.starts_with('&') && ((!arg_sym.is_number() && arg_sym.kind != .bool) if s.starts_with('&') && ((!param_sym.is_number() && param_sym.kind != .bool)
|| node.language != .v) { || node.language != .v) {
s = s[1..] s = s[1..]
} }
@ -194,13 +193,13 @@ fn stringify_fn_after_name(node &FnDecl, mut f strings.Builder, t &Table, cur_mo
if !is_type_only { if !is_type_only {
f.write_string(' ') f.write_string(' ')
} }
if node.is_variadic && is_last_arg { if node.is_variadic && is_last_param {
f.write_string('...') f.write_string('...')
} }
f.write_string(s) f.write_string(s)
} }
} }
if !is_last_arg { if !is_last_param {
f.write_string(', ') f.write_string(', ')
} }
} }
@ -218,7 +217,7 @@ struct StringifyModReplacement {
weight int weight int
} }
fn shorten_full_name_based_on_aliases(input string, m2a map[string]string) string { pub fn shorten_full_name_based_on_aliases(input string, m2a map[string]string) string {
if m2a.len == 0 || -1 == input.index_u8(`.`) { if m2a.len == 0 || -1 == input.index_u8(`.`) {
// a simple typename, like `string` or `[]bool`; no module aliasings apply, // a simple typename, like `string` or `[]bool`; no module aliasings apply,
// (or there just are not any mappings) // (or there just are not any mappings)

View File

@ -10,8 +10,12 @@ mut:
pub type InspectorFn = fn (node &ast.Node, data voidptr) bool pub type InspectorFn = fn (node &ast.Node, data voidptr) bool
fn empty_callback(node &ast.Node, data voidptr) bool {
panic('empty ast.walker')
}
struct Inspector { struct Inspector {
inspector_callback InspectorFn inspector_callback InspectorFn = empty_callback
mut: mut:
data voidptr data voidptr
} }

View File

@ -590,7 +590,7 @@ pub fn (mut b Builder) print_warnings_and_errors() {
for stmt in file.stmts { for stmt in file.stmts {
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
if stmt.name == fn_name { if stmt.name == fn_name {
fheader := stmt.stringify_fn_decl(b.table, 'main', map[string]string{}) fheader := b.table.stringify_fn_decl(&stmt, 'main', map[string]string{})
redefines << FunctionRedefinition{ redefines << FunctionRedefinition{
fpath: file.path fpath: file.path
fline: stmt.pos.line_nr fline: stmt.pos.line_nr

View File

@ -777,7 +777,7 @@ fn (mut b Builder) cc_linux_cross() {
verror(res.output) verror(res.output)
return return
} }
println(out_name + ' has been successfully compiled') println(out_name + ' has been successfully cross compiled for linux.')
} }
fn (mut c Builder) cc_windows_cross() { fn (mut c Builder) cc_windows_cross() {
@ -795,10 +795,9 @@ fn (mut c Builder) cc_windows_cross() {
if !c.pref.out_name.to_lower().ends_with('.exe') { if !c.pref.out_name.to_lower().ends_with('.exe') {
c.pref.out_name += '.exe' c.pref.out_name += '.exe'
} }
c.pref.out_name = os.quoted_path(c.pref.out_name)
mut args := []string{} mut args := []string{}
args << '${c.pref.cflags}' args << '${c.pref.cflags}'
args << '-o ${c.pref.out_name}' args << '-o ${os.quoted_path(c.pref.out_name)}'
args << '-w -L.' args << '-w -L.'
// //
cflags := c.get_os_cflags() cflags := c.get_os_cflags()
@ -839,21 +838,6 @@ fn (mut c Builder) cc_windows_cross() {
} else { } else {
args << cflags.c_options_after_target() args << cflags.c_options_after_target()
} }
/*
winroot := '${pref.default_module_path}/winroot'
if !os.is_dir(winroot) {
winroot_url := 'https://github.com/vlang/v/releases/download/v0.1.10/winroot.zip'
println('"$winroot" not found.')
println('Download it from $winroot_url and save it in ${pref.default_module_path}')
println('Unzip it afterwards.\n')
println('winroot.zip contains all library and header files needed ' + 'to cross-compile for Windows.')
exit(1)
}
mut obj_name := c.out_name
obj_name = obj_name.replace('.exe', '')
obj_name = obj_name.replace('.o.o', '.o')
include := '-I $winroot/include '
*/
if os.user_os() !in ['macos', 'linux', 'termux'] { if os.user_os() !in ['macos', 'linux', 'termux'] {
println(os.user_os()) println(os.user_os())
panic('your platform is not supported yet') panic('your platform is not supported yet')
@ -883,20 +867,7 @@ fn (mut c Builder) cc_windows_cross() {
} }
exit(1) exit(1)
} }
/* println(c.pref.out_name + ' has been successfully cross compiled for windows.')
if c.pref.build_mode != .build_module {
link_cmd := 'lld-link $obj_name $winroot/lib/libcmt.lib ' + '$winroot/lib/libucrt.lib $winroot/lib/kernel32.lib $winroot/lib/libvcruntime.lib ' + '$winroot/lib/uuid.lib'
if c.pref.show_cc {
println(link_cmd)
}
if os.system(link_cmd) != 0 {
println('Cross compilation for Windows failed. Make sure you have lld linker installed.')
exit(1)
}
// os.rm(obj_name)
}
*/
println(c.pref.out_name + ' has been successfully compiled')
} }
fn (mut b Builder) build_thirdparty_obj_files() { fn (mut b Builder) build_thirdparty_obj_files() {

View File

@ -173,7 +173,7 @@ fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
return false return false
} }
} }
if expected.has_flag(.generic) { if expected.has_flag(.generic) && !got.has_flag(.generic) {
return false return false
} }
return true return true

View File

@ -39,46 +39,49 @@ pub const (
pub struct Checker { pub struct Checker {
pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
pub mut: pub mut:
table &ast.Table = unsafe { nil } table &ast.Table = unsafe { nil }
file &ast.File = unsafe { nil } file &ast.File = unsafe { nil }
nr_errors int nr_errors int
nr_warnings int nr_warnings int
nr_notices int nr_notices int
errors []errors.Error errors []errors.Error
warnings []errors.Warning warnings []errors.Warning
notices []errors.Notice notices []errors.Notice
error_lines []int // to avoid printing multiple errors for the same line error_lines []int // to avoid printing multiple errors for the same line
expected_type ast.Type expected_type ast.Type
expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
expected_expr_type ast.Type // if/match is_expr: expected_type expected_expr_type ast.Type // if/match is_expr: expected_type
mod string // current module name mod string // current module name
const_var &ast.ConstField = unsafe { nil } // the current constant, when checking const declarations const_var &ast.ConstField = unsafe { nil } // the current constant, when checking const declarations
const_deps []string const_deps []string
const_names []string const_names []string
global_names []string global_names []string
locked_names []string // vars that are currently locked locked_names []string // vars that are currently locked
rlocked_names []string // vars that are currently read-locked rlocked_names []string // vars that are currently read-locked
in_for_count int // if checker is currently in a for loop in_for_count int // if checker is currently in a for loop
should_abort bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time. should_abort bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
returns bool returns bool
scope_returns bool scope_returns bool
is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
is_just_builtin_mod bool // true only inside 'builtin' is_just_builtin_mod bool // true only inside 'builtin'
is_generated bool // true for `[generated] module xyz` .v files is_generated bool // true for `[generated] module xyz` .v files
inside_unsafe bool // true inside `unsafe {}` blocks inside_unsafe bool // true inside `unsafe {}` blocks
inside_const bool // true inside `const ( ... )` blocks inside_const bool // true inside `const ( ... )` blocks
inside_anon_fn bool // true inside `fn() { ... }()` inside_anon_fn bool // true inside `fn() { ... }()`
inside_ref_lit bool // true inside `a := &something` inside_ref_lit bool // true inside `a := &something`
inside_defer bool // true inside `defer {}` blocks inside_defer bool // true inside `defer {}` blocks
inside_fn_arg bool // `a`, `b` in `a.f(b)` inside_fn_arg bool // `a`, `b` in `a.f(b)`
inside_ct_attr bool // true inside `[if expr]` inside_ct_attr bool // true inside `[if expr]`
inside_x_is_type bool // true inside the Type expression of `if x is Type {` inside_x_is_type bool // true inside the Type expression of `if x is Type {`
inside_comptime_for_field bool inside_comptime_for_field bool
skip_flags bool // should `#flag` and `#include` be skipped inside_generic_struct_init bool
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc cur_struct_generic_types []ast.Type
smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo cur_struct_concrete_types []ast.Type
smartcast_cond_pos token.Pos // match cond skip_flags bool // should `#flag` and `#include` be skipped
ct_cond_stack []ast.Expr fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo
smartcast_cond_pos token.Pos // match cond
ct_cond_stack []ast.Expr
mut: mut:
stmt_level int // the nesting level inside each stmts list; stmt_level int // the nesting level inside each stmts list;
// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1` // .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
@ -2438,11 +2441,19 @@ fn (mut c Checker) stmts_ending_with_expression(mut stmts []ast.Stmt) {
} }
fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type { fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) && c.table.cur_fn != unsafe { nil } { if typ.has_flag(.generic) {
if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names, if c.inside_generic_struct_init {
c.table.cur_concrete_types) generic_names := c.cur_struct_generic_types.map(c.table.sym(it).name)
{ if t_typ := c.table.resolve_generic_to_concrete(typ, generic_names, c.cur_struct_concrete_types) {
return t_typ return t_typ
}
}
if c.table.cur_fn != unsafe { nil } {
if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names,
c.table.cur_concrete_types)
{
return t_typ
}
} }
} }
return typ return typ
@ -3329,7 +3340,9 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
if func := c.table.find_fn(node.name) { if func := c.table.find_fn(node.name) {
if func.generic_names.len > 0 { if func.generic_names.len > 0 {
concrete_types := node.concrete_types.map(c.unwrap_generic(it)) concrete_types := node.concrete_types.map(c.unwrap_generic(it))
c.table.register_fn_concrete_types(func.fkey(), concrete_types) if concrete_types.all(!it.has_flag(.generic)) {
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
}
} }
} }
return info.typ return info.typ
@ -3493,7 +3506,9 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
concrete_types) concrete_types)
{ {
fn_type = typ_ fn_type = typ_
c.table.register_fn_concrete_types(func.fkey(), concrete_types) if concrete_types.all(!it.has_flag(.generic)) {
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
}
} }
} }
node.name = name node.name = name

View File

@ -1654,6 +1654,26 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
} }
node.return_type = left_sym.info.return_type node.return_type = left_sym.info.return_type
return left_sym.info.return_type return left_sym.info.return_type
} else if final_left_sym.info is ast.ArrayFixed && method_name == 'wait' {
elem_sym := c.table.sym(final_left_sym.info.elem_type)
if elem_sym.kind == .thread {
if node.args.len != 0 {
c.error('`.wait()` does not have any arguments', node.args[0].pos)
}
thread_ret_type := c.unwrap_generic(elem_sym.thread_info().return_type)
if thread_ret_type.has_flag(.option) {
c.error('`.wait()` cannot be called for an array when thread functions return options. Iterate over the arrays elements instead and handle each returned option with `or`.',
node.pos)
} else if thread_ret_type.has_flag(.result) {
c.error('`.wait()` cannot be called for an array when thread functions return results. Iterate over the arrays elements instead and handle each returned result with `or`.',
node.pos)
}
node.return_type = c.table.find_or_register_array(thread_ret_type)
return node.return_type
} else {
c.error('`${left_sym.name}` has no method `wait()` (only thread handles and arrays of them have)',
node.left.pos())
}
} else if left_sym.kind == .char && left_type.nr_muls() == 0 && method_name == 'str' { } else if left_sym.kind == .char && left_type.nr_muls() == 0 && method_name == 'str' {
c.error('calling `.str()` on type `char` is not allowed, use its address or cast it to an integer instead', c.error('calling `.str()` on type `char` is not allowed, use its address or cast it to an integer instead',
node.left.pos().extend(node.pos)) node.left.pos().extend(node.pos))
@ -2304,7 +2324,7 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
has_decompose := node.args.filter(it.expr is ast.ArrayDecompose).len > 0 has_decompose := node.args.filter(it.expr is ast.ArrayDecompose).len > 0
if has_decompose { if has_decompose {
// if call(...args) is present // if call(...args) is present
min_required_params = nr_args min_required_params = nr_args - 1
} }
} }
if min_required_params < 0 { if min_required_params < 0 {

View File

@ -70,6 +70,19 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
} }
} }
} }
// Do not allow uninitialized `fn` fields, or force `?fn`
// (allow them in `C.` structs)
if !c.is_builtin_mod && node.language == .v {
sym := c.table.sym(field.typ)
if sym.kind == .function {
if !field.typ.has_flag(.option) && !field.has_default_expr
&& field.attrs.filter(it.name == 'required').len == 0 {
error_msg := 'uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)'
c.note(error_msg, field.pos)
}
}
}
if field.has_default_expr { if field.has_default_expr {
c.expected_type = field.typ c.expected_type = field.typ
field.default_expr_typ = c.expr(mut field.default_expr) field.default_expr_typ = c.expr(mut field.default_expr)
@ -155,6 +168,9 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
true) true)
} }
} }
} else if c.table.final_sym(field.typ).kind == .function
&& field.default_expr_typ.is_pointer() {
continue
} else { } else {
c.error('incompatible initializer for field `${field.name}`: ${err.msg()}', c.error('incompatible initializer for field `${field.name}`: ${err.msg()}',
field.default_expr.pos()) field.default_expr.pos())
@ -315,6 +331,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
} }
} }
struct_sym := c.table.sym(node.typ) struct_sym := c.table.sym(node.typ)
mut old_inside_generic_struct_init := false
mut old_cur_struct_generic_types := []ast.Type{}
mut old_cur_struct_concrete_types := []ast.Type{}
if struct_sym.info is ast.Struct { if struct_sym.info is ast.Struct {
// check if the generic param types have been defined // check if the generic param types have been defined
for ct in struct_sym.info.concrete_types { for ct in struct_sym.info.concrete_types {
@ -352,6 +371,20 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
c.error('a non generic struct `${node.typ_str}` used like a generic struct', c.error('a non generic struct `${node.typ_str}` used like a generic struct',
node.name_pos) node.name_pos)
} }
if struct_sym.info.generic_types.len > 0
&& struct_sym.info.generic_types.len == struct_sym.info.concrete_types.len {
old_inside_generic_struct_init = c.inside_generic_struct_init
old_cur_struct_generic_types = c.cur_struct_generic_types.clone()
old_cur_struct_concrete_types = c.cur_struct_concrete_types.clone()
c.inside_generic_struct_init = true
c.cur_struct_generic_types = struct_sym.info.generic_types.clone()
c.cur_struct_concrete_types = struct_sym.info.concrete_types.clone()
defer {
c.inside_generic_struct_init = old_inside_generic_struct_init
c.cur_struct_generic_types = old_cur_struct_generic_types
c.cur_struct_concrete_types = old_cur_struct_concrete_types
}
}
} else if struct_sym.info is ast.Alias { } else if struct_sym.info is ast.Alias {
parent_sym := c.table.sym(struct_sym.info.parent_type) parent_sym := c.table.sym(struct_sym.info.parent_type)
// e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´ // e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´
@ -613,10 +646,10 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
// and the second part is all fields embedded in the structure // and the second part is all fields embedded in the structure
// If the return value data composition form in `c.table.struct_fields()` is modified, // If the return value data composition form in `c.table.struct_fields()` is modified,
// need to modify here accordingly. // need to modify here accordingly.
fields := c.table.struct_fields(type_sym) mut fields := c.table.struct_fields(type_sym)
mut checked_types := []ast.Type{} mut checked_types := []ast.Type{}
for i, field in fields { for i, mut field in fields {
if field.name in inited_fields { if field.name in inited_fields {
continue continue
} }
@ -628,7 +661,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
} }
if field.has_default_expr { if field.has_default_expr {
if i < info.fields.len && field.default_expr_typ == 0 { if i < info.fields.len && field.default_expr_typ == 0 {
if field.default_expr is ast.StructInit { if mut field.default_expr is ast.StructInit {
idx := c.table.find_type_idx(field.default_expr.typ_str) idx := c.table.find_type_idx(field.default_expr.typ_str)
if idx != 0 { if idx != 0 {
info.fields[i].default_expr_typ = ast.new_type(idx) info.fields[i].default_expr_typ = ast.new_type(idx)
@ -637,6 +670,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
if field.typ.is_any_kind_of_pointer() { if field.typ.is_any_kind_of_pointer() {
info.fields[i].default_expr_typ = field.typ info.fields[i].default_expr_typ = field.typ
} }
} else if field.default_expr is ast.Ident
&& field.default_expr.info is ast.IdentFn {
c.expr(mut field.default_expr)
} else { } else {
if const_field := c.table.global_scope.find_const('${field.default_expr}') { if const_field := c.table.global_scope.find_const('${field.default_expr}') {
info.fields[i].default_expr_typ = const_field.typ info.fields[i].default_expr_typ = const_field.typ

View File

@ -1,5 +1,5 @@
struct St{ struct St {
attr fn() attr fn () = unsafe { nil }
} }
fn (s St) attr() {} fn (s St) attr() {}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/fn_array_decompose_arg_mismatch_err_c.vv:2:6: error: expected 0 arguments, but got 1
1 | fn main() {
2 | foo(...args)
| ~~~~~~~
3 | }
4 |

View File

@ -0,0 +1,6 @@
fn main() {
foo(...args)
}
fn foo() {
}

Some files were not shown because too many files have changed in this diff Show More