pull/3153/head
Alexander Medvednikov 2019-12-20 00:29:37 +03:00
parent b6fe2ebc0b
commit 6210984c97
54 changed files with 1757 additions and 1993 deletions

13
v.v 100755 → 100644
View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import (
@ -14,8 +13,7 @@ import (
const (
known_commands = ['run', 'build', 'version', 'doc']
simple_tools = ['up', 'create', 'test', 'test-compiler', 'build-tools',
'build-examples', 'build-vbinaries']
simple_tools = ['up', 'create', 'test', 'test-compiler', 'build-tools', 'build-examples', 'build-vbinaries']
)
fn main() {
@ -61,7 +59,8 @@ fn main() {
mut tmark := benchmark.new_benchmark()
if v.pref.x64 {
v.compile_x64()
} else {
}
else {
v.compile()
}
if v.pref.is_stats {
@ -76,7 +75,8 @@ fn main() {
fn v_command(command string, args []string) {
match command {
'', '.', 'run', 'build' { // handled later in vlib/compiler/main.v
'', '.', 'run', 'build' {
// handled later in vlib/compiler/main.v
return
}
'version' {
@ -118,8 +118,7 @@ fn v_command(command string, args []string) {
else {
println('v $command: unknown command')
println('Run "v help" for usage.')
}
}
}}
exit(0)
}

View File

@ -2,7 +2,6 @@ module benchmark
import time
import term
/*
Example usage of this module:
```
@ -26,6 +25,7 @@ println( bmark.total_message('remarks about the benchmark') )
```
*/
pub struct Benchmark {
pub mut:
bench_start_time i64
@ -86,10 +86,7 @@ pub fn (b &Benchmark) step_message(msg string) string {
}
pub fn (b &Benchmark) total_message(msg string) string {
mut tmsg := '$msg \n ok, fail, total = ' +
term.ok_message('${b.nok:5d}') + ', ' +
if b.nfail > 0 { term.fail_message('${b.nfail:5d}') } else { '${b.nfail:5d}' } + ', ' +
'${b.ntotal:5d}'
mut tmsg := '$msg \n ok, fail, total = ' + term.ok_message('${b.nok:5d}') + ', ' + if b.nfail > 0 { term.fail_message('${b.nfail:5d}') } else { '${b.nfail:5d}' } + ', ' + '${b.ntotal:5d}'
if b.verbose {
tmsg = '<=== total time spent $tmsg'
}
@ -99,8 +96,8 @@ pub fn (b &Benchmark) total_message(msg string) string {
pub fn (b &Benchmark) total_duration() i64 {
return (b.bench_end_time - b.bench_start_time)
}
////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
fn (b &Benchmark) tdiff_in_ms(s string, sticks i64, eticks i64) string {
if b.verbose {
tdiff := (eticks - sticks)
@ -112,3 +109,4 @@ fn (b &Benchmark) tdiff_in_ms(s string, sticks i64, eticks i64) string {
fn now() i64 {
return time.ticks()
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
fn (p mut Parser) inline_asm() {
@ -33,3 +32,4 @@ fn (p mut Parser) inline_asm() {
p.genln(');')
p.check(.rcbr)
}

View File

@ -1,11 +1,9 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
import os
// parsed cflag
struct CFlag {
mod string // the module in which the flag was given
@ -22,11 +20,7 @@ pub fn (c &CFlag) str() string {
fn (v &V) get_os_cflags() []CFlag {
mut flags := []CFlag
for flag in v.table.cflags {
if flag.os == ''
|| (flag.os == 'linux' && v.os == .linux)
|| (flag.os == 'darwin' && v.os == .mac)
|| (flag.os == 'freebsd' && v.os == .freebsd)
|| (flag.os == 'windows' && v.os == .windows) {
if flag.os == '' || (flag.os == 'linux' && v.os == .linux) || (flag.os == 'darwin' && v.os == .mac) || (flag.os == 'freebsd' && v.os == .freebsd) || (flag.os == 'windows' && v.os == .windows) {
flags << flag
}
}
@ -38,7 +32,9 @@ fn (v &V) get_rest_of_module_cflags(c &CFlag) []CFlag {
cflags := v.get_os_cflags()
for flag in cflags {
if c.mod == flag.mod {
if c.name == flag.name && c.value == flag.value && c.os == flag.os { continue }
if c.name == flag.name && c.value == flag.value && c.os == flag.os {
continue
}
flags << flag
}
}
@ -71,12 +67,7 @@ fn (table &Table) has_cflag(cflag CFlag) bool {
// parse the flags to (table.cflags) []CFlag
// Note: clean up big time (joe-c)
fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
allowed_flags := [
'framework',
'library',
'Wa', 'Wl', 'Wp',
'I', 'l', 'L',
]
allowed_flags := ['framework', 'library', 'Wa', 'Wl', 'Wp', 'I', 'l', 'L', ]
flag_orig := cflag.trim_space()
mut flag := flag_orig
if flag == '' {
@ -84,7 +75,9 @@ fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
}
mut fos := ''
if flag.starts_with('linux') || flag.starts_with('darwin') || flag.starts_with('freebsd') || flag.starts_with('windows') {
pos := flag.index(' ') or { return none }
pos := flag.index(' ') or {
return none
}
fos = flag[..pos].trim_space()
flag = flag[pos..].trim_space()
}
@ -101,7 +94,9 @@ fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
}
}
}
mut index := flag.index(' -') or { -1 }
mut index := flag.index(' -') or {
-1
}
for index > -1 {
mut has_next := false
for f in allowed_flags {
@ -113,7 +108,9 @@ fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
break
}
}
if has_next { break }
if has_next {
break
}
index = flag.index_after(' -', index + 1)
}
if index == -1 {
@ -124,9 +121,9 @@ fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
return error('bad #flag `$flag_orig`: missing $hint after `$name`')
}
cf := CFlag{
mod: mod,
os: fos,
name: name,
mod: mod
os: fos
name: name
value: value
}
if !table.has_cflag(cf) {
@ -140,8 +137,13 @@ fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
}
// TODO: implement msvc specific c_options_before_target and c_options_after_target ...
fn (cflags []CFlag) c_options_before_target_msvc() string { return '' }
fn (cflags []CFlag) c_options_after_target_msvc() string { return '' }
fn (cflags []CFlag) c_options_before_target_msvc() string {
return ''
}
fn (cflags []CFlag) c_options_after_target_msvc() string {
return ''
}
fn (cflags []CFlag) c_options_before_target() string {
// -I flags, optimization flags and so on
@ -185,3 +187,4 @@ fn (cflags []CFlag) c_options_only_object_files() string {
}
return args.join(' ')
}

View File

@ -1,7 +1,6 @@
module compiler
const (
c_common_macros = '
#define EMPTY_STRUCT_DECLARATION
@ -23,7 +22,6 @@ c_common_macros = '
#define OPTION_CAST(x) (x)
'
c_headers = '
//#include <inttypes.h> // int64_t etc
@ -146,7 +144,6 @@ byteptr g_str_buf;
int load_so(byteptr);
void reload_so();
'
js_headers = '
var array_string = function() {}
@ -170,8 +167,6 @@ var map_string = function() {}
var map_int = function() {}
'
c_builtin_types = '
//#include <inttypes.h> // int64_t etc
@ -211,7 +206,6 @@ typedef map map_string;
#define false 0
#endif
'
bare_c_headers = '
$c_common_macros
@ -223,4 +217,3 @@ void sys_exit (int);
'
)

View File

@ -4,7 +4,6 @@ import (
os
term
)
// ////////////////////////////////////////////////////////////////////////////////////////////////
// NB: The code in this file is organized in layers (between the ///// lines).
// This allows for easier keeping in sync of error/warn functions.
@ -18,6 +17,7 @@ import (
// p.error(msg)
// ////////////////////////////////////////////////////////////////////////////////////////////////
fn (p mut Parser) error(s string) {
// no positioning info, so just assume that the last token was the culprit:
p.error_with_token_index(s, p.token_idx - 1)
@ -30,7 +30,8 @@ fn (p mut Parser) warn(s string) {
fn (p mut Parser) production_error_with_token_index(e string, tokenindex int) {
if p.pref.is_prod {
p.error_with_token_index(e, tokenindex)
}else {
}
else {
p.warn_with_token_index(e, tokenindex)
}
}
@ -88,7 +89,6 @@ fn (s &Scanner) error_with_col(msg string, col int) {
// NB: using only the filename may lead to inability of IDE/editors
// to find the source file, when the IDE has a different working folder than v itself.
eprintln('${fullpath}:${s.line_nr + 1}:${col}: $final_message')
if s.print_line_on_error && s.nlines > 0 {
context_start_line := imax(0, (s.line_nr - error_context_before))
context_end_line := imin(s.nlines - 1, (s.line_nr + error_context_after + 1))
@ -96,7 +96,9 @@ fn (s &Scanner) error_with_col(msg string, col int) {
line := '${(cline+1):5d}| ' + s.line(cline)
coloredline := if cline == s.line_nr && color_on { term.red(line) } else { line }
eprintln(coloredline)
if cline != s.line_nr { continue }
if cline != s.line_nr {
continue
}
// The pointerline should have the same spaces/tabs as the offending
// line, so that it prints the ^ character exactly on the *same spot*
// where it is needed. That is the reason we can not just
@ -118,20 +120,33 @@ fn (s &Scanner) error_with_col(msg string, col int) {
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// / Misc error helper functions, can be called by any of the functions above
[inline]
fn (p &Parser) cur_tok_index() int {
return p.token_idx - 1
}
[inline] fn (p &Parser) cur_tok_index() int { return p.token_idx - 1 }
[inline] fn imax(a,b int) int { return if a > b { a } else { b } }
[inline] fn imin(a,b int) int { return if a < b { a } else { b } }
[inline]
fn imax(a, b int) int {
return if a > b { a } else { b }
}
[inline]
fn imin(a, b int) int {
return if a < b { a } else { b }
}
fn (s &Scanner) get_error_filepath() string {
verror_paths_override := os.getenv('VERROR_PATHS')
use_relative_paths := match verror_paths_override {
'relative' { true }
'absolute' { false }
else { s.print_rel_paths_on_error }
'relative'{
true
}
'absolute'{
false
}
else {
s.print_rel_paths_on_error}}
if use_relative_paths {
workdir := os.getwd() + os.path_separator
if s.file_path.starts_with(workdir) {
@ -161,11 +176,11 @@ fn (p mut Parser) print_error_context(){
if !p.pref.is_repl && !p.pref.is_test && (p.file_path.contains('v/compiler') || cur_path.contains('v/compiler')) {
println('\n=========================')
println('It looks like you are building V. It is being frequently updated every day.')
println('If you didn\'t modify V\'s code, most likely there was a change that ')
println("If you didn\'t modify V\'s code, most likely there was a change that ")
println('lead to this error.')
println('\nRun `v up`, that will most likely fix it.')
// println('\nIf this doesn\'t help, re-install V from source or download a precompiled' + ' binary from\nhttps://vlang.io.')
println('\nIf this doesn\'t help, please create a GitHub issue.')
println("\nIf this doesn\'t help, please create a GitHub issue.")
println('=========================\n')
}
if p.pref.is_debug {
@ -176,10 +191,7 @@ fn (p mut Parser) print_error_context(){
fn normalized_error(s string) string {
// Print `[]int` instead of `array_int` in errors
mut res := s.replace('array_', '[]')
.replace('__', '.')
.replace('Option_', '?')
.replace('main.', '')
mut res := s.replace('array_', '[]').replace('__', '.').replace('Option_', '?').replace('main.', '')
if res.contains('_V_MulRet_') {
res = res.replace('_V_MulRet_', '(').replace('_V_', ', ')
res = res[..res.len - 1] + ')"'
@ -188,13 +200,11 @@ fn normalized_error(s string) string {
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// The goal of ScannerPos is to track the current scanning position,
// so that if there is an error found later, v could show a more accurate
// position about where the error initially was.
// NB: The fields of ScannerPos *should be kept synchronized* with the
// corresponding fields in Scanner.
struct ScannerPos {
mut:
pos int
@ -207,7 +217,11 @@ pub fn (s ScannerPos) str() string {
}
fn (s &Scanner) get_scanner_pos() ScannerPos {
return ScannerPos{ pos: s.pos line_nr: s.line_nr last_nl_pos: s.last_nl_pos }
return ScannerPos{
pos: s.pos
line_nr: s.line_nr
last_nl_pos: s.last_nl_pos
}
}
fn (s mut Scanner) goto_scanner_position(scp ScannerPos) {
@ -235,7 +249,6 @@ fn (s &Scanner) get_scanner_pos_of_token(tok &Token) ScannerPos {
}
// /////////////////////////////
fn (p mut Parser) mutable_arg_error(i int, arg Var, f Fn) {
mut dots_example := 'mut $p.lit'
if i > 0 {
@ -244,23 +257,18 @@ fn (p mut Parser) mutable_arg_error(i int, arg Var, f Fn) {
if i < f.args.len - 1 {
dots_example = dots_example + ',..'
}
p.error('`$arg.name` is a mutable argument, you need to provide `mut`: ' +
'`$f.name ($dots_example)`')
p.error('`$arg.name` is a mutable argument, you need to provide `mut`: ' + '`$f.name ($dots_example)`')
}
const (
warn_match_arrow = '=> is no longer needed in match statements, use\n' +
'match foo {
warn_match_arrow = '=> is no longer needed in match statements, use\n' + 'match foo {
1 { bar }
2 { baz }
else { ... }
}'
// make_receiver_mutable =
err_used_as_value = 'used as value'
and_or_error = 'use `()` to make the boolean expression clear\n' +
'for example: `(a && b) || c` instead of `a && b || c`'
and_or_error = 'use `()` to make the boolean expression clear\n' + 'for example: `(a && b) || c` instead of `a && b || c`'
err_modify_bitfield = 'to modify a bitfield flag use the methods: set, clear, toggle. and to check for flag use: has'
)

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
pub fn get_v_options_and_main_command(args []string) ([]string,string) {
@ -12,12 +11,16 @@ pub fn get_v_options_and_main_command(args []string) ([]string,string) {
if !a.starts_with('-') {
potential_commands << a
continue
}else{
}
else {
options << a
if a in ['-o', '-os', '-cc', '-cflags', '-d'] { i++ }
if a in ['-o', '-os', '-cc', '-cflags', '-d'] {
i++
}
}
}
// potential_commands[0] is always the executable itself, so ignore it
command := if potential_commands.len > 1 { potential_commands[1] } else { '' }
return options,command
}

View File

@ -1,10 +1,8 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
// Directed acyclic graph
// this implementation is specifically suited to ordering dependencies
module compiler
struct DepGraphNode {
@ -30,7 +28,8 @@ pub fn(dset mut DepSet) add(item string) {
}
pub fn (dset &DepSet) diff(otherset DepSet) DepSet {
mut diff := DepSet{}
mut diff := DepSet{
}
for item in dset.items {
if !item in otherset.items {
diff.items << item
@ -49,10 +48,9 @@ pub fn new_dep_graph() &DepGraph {
}
}
pub fn (graph mut DepGraph) add(mod string, deps []string) {
graph.nodes << DepGraphNode{
name: mod,
name: mod
deps: deps.clone()
}
}
@ -60,26 +58,24 @@ pub fn(graph mut DepGraph) add(mod string, deps []string) {
pub fn (graph &DepGraph) resolve() &DepGraph {
mut node_names := map[string]DepGraphNode
mut node_deps := map[string]DepSet
for _, node in graph.nodes {
node_names[node.name] = node
mut dep_set := DepSet{}
mut dep_set := DepSet{
}
for _, dep in node.deps {
dep_set.add(dep)
}
node_deps[node.name] = dep_set
}
mut resolved := new_dep_graph()
for node_deps.size != 0 {
mut ready_set := DepSet{}
mut ready_set := DepSet{
}
for name, deps in node_deps {
if deps.size() == 0 {
ready_set.add(name)
}
}
if ready_set.size() == 0 {
mut g := new_dep_graph()
g.acyclic = false
@ -88,17 +84,14 @@ pub fn(graph &DepGraph) resolve() &DepGraph {
}
return g
}
for name in ready_set.items {
node_deps.delete(name)
resolved.nodes << node_names[name]
}
for name, deps in node_deps {
node_deps[name] = deps.diff(ready_set)
}
}
return resolved
}
@ -124,7 +117,9 @@ pub fn(graph &DepGraph) display_cycles() string {
mut out := '\n'
for node in graph.nodes {
for dep in node.deps {
if !(dep in node_names) { continue }
if !(dep in node_names) {
continue
}
dn := node_names[dep]
if node.name in dn.deps {
out += ' * $node.name -> $dep\n'

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
fn (p mut Parser) enum_decl(no_name bool) {
@ -44,7 +43,8 @@ fn (p mut Parser) enum_decl(no_name bool) {
p.next()
val = p.lit.int()
p.next()
}else{
}
else {
p.next()
enum_assign_tidx = p.cur_tok_index()
p.error_with_token_index('only numbers are allowed in enum initializations', enum_assign_tidx)
@ -89,9 +89,9 @@ fn (p mut Parser) check_enum_member_access() {
p.error('enum `$T.name` does not have value `$val`')
}
p.gen(mod_gen_name(T.mod) + '__' + p.expected_type + '_' + val)
} else {
}
else {
p.error('`$T.name` is not an enum')
}
}

View File

@ -822,3 +822,4 @@ fn (p mut Parser) factor() string {
}
// { user | name: 'new name' }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
fn (p mut Parser) for_st() {
@ -81,7 +80,8 @@ fn (p mut Parser) for_st() {
if !is_variadic_arg {
if p.is_js {
p.genln('var $tmp = $expr;')
} else {
}
else {
p.genln('$typ $tmp = $expr;')
}
}
@ -151,7 +151,6 @@ fn (p mut Parser) for_st() {
label = p.x64.gen_loop_start(expr.int())
// to = range_expr.int() // TODO why empty?
}
}
is_arr := typ.contains('array')
is_fixed := typ.starts_with('[')
@ -162,7 +161,9 @@ fn (p mut Parser) for_st() {
if !is_variadic_arg {
if p.is_js {
p.genln('var $tmp = $expr;')
} else if !is_fixed { // Don't copy if it's a fixed array
}
else if !is_fixed {
// Don't copy if it's a fixed array
p.genln('$typ $tmp = $expr;')
}
}
@ -203,7 +204,8 @@ fn (p mut Parser) for_st() {
is_for_var: true
})
}
} else {
}
else {
// `for a < b {`
p.gen('while (')
p.check_types(p.bool_expression(), 'bool')

View File

@ -1,6 +1,4 @@
module compiler
// import filepath
// import compiler.x64

View File

@ -138,6 +138,7 @@ fn (p mut Parser) match_statement(is_expr bool) string {
break
}
p.check(.comma)
p.fspace()
got_comma = true
}
p.gen(')')

View File

@ -1,9 +1,7 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
// TODO replace with comptime code generation.
// TODO remove cJSON dependency.
// OLD: User decode_User(string js) {
@ -85,28 +83,20 @@ string res = tos2("");
if field.attr == 'skip' {
continue
}
name := if field.attr.starts_with('json:') {
field.attr[5..]
} else {
field.name
}
name := if field.attr.starts_with('json:') { field.attr[5..] } else { field.name }
field_type := p.table.find_type(field.typ)
_typ := field.typ.replace('*', '')
enc_name := js_enc_name(_typ)
if field.attr == 'raw' {
dec += ' res->$field.name = tos2(cJSON_PrintUnformatted(' +
'js_get(root, "$name")));\n'
} else {
dec += ' res->$field.name = tos2(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));\n'
}
else {
// Now generate decoders for all field types in this struct
// need to do it here so that these functions are generated first
p.gen_json_for_type(field_type)
dec_name := js_dec_name(_typ)
if is_js_prim(_typ) {
dec += ' res->$field.name = $dec_name (js_get(' +
'root, "$name"))'
dec += ' res->$field.name = $dec_name (js_get(' + 'root, "$name"))'
}
else {
dec += ' $dec_name (js_get(root, "$name"), & (res->$field.name))'
@ -122,10 +112,7 @@ string res = tos2("");
}
fn is_js_prim(typ string) bool {
return typ == 'int' || typ == 'string' ||
typ == 'bool' || typ == 'f32' || typ == 'f64' ||
typ == 'i8' || typ == 'i16' || typ == 'i64' ||
typ == 'u16' || typ == 'u32' || typ == 'u64'
return typ == 'int' || typ == 'string' || typ == 'bool' || typ == 'f32' || typ == 'f64' || typ == 'i8' || typ == 'i16' || typ == 'i64' || typ == 'u16' || typ == 'u32' || typ == 'u64'
}
fn (p mut Parser) decode_array(array_type string) string {

View File

@ -26,7 +26,8 @@ fn (v &V) generate_hotcode_reloading_declarations() {
if v.pref.is_live {
cgen.genln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;')
}
} else {
}
else {
if v.pref.is_so {
cgen.genln('HANDLE live_fn_mutex;')
cgen.genln('
@ -45,7 +46,9 @@ void pthread_mutex_unlock(HANDLE *m) {
}
fn (v &V) generate_hotcode_reloading_main_caller() {
if !v.pref.is_live { return }
if !v.pref.is_live {
return
}
// We are in live code reload mode, so start the .so loader in the background
mut cgen := v.cgen
cgen.genln('')
@ -57,7 +60,8 @@ fn (v &V) generate_hotcode_reloading_main_caller() {
cgen.genln(' load_so(live_library_name);')
cgen.genln(' pthread_t _thread_so;')
cgen.genln(' pthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);')
} else {
}
else {
// windows:
so_name := file_base + if v.pref.ccompiler == 'msvc' { '.dll' } else { '.so' }
cgen.genln(' char *live_library_name = "$so_name";')
@ -70,7 +74,6 @@ fn (v &V) generate_hotcode_reloading_main_caller() {
fn (v &V) generate_hot_reload_code() {
mut cgen := v.cgen
// Hot code reloading
if v.pref.is_live {
mut file := os.realpath(v.dir)
@ -79,17 +82,14 @@ fn (v &V) generate_hot_reload_code() {
// Need to build .so file before building the live application
// The live app needs to load this .so file on initialization.
mut vexe := os.args[0]
if os.user_os() == 'windows' {
vexe = cescaped_path(vexe)
file = cescaped_path(file)
}
mut msvc := ''
if v.pref.ccompiler == 'msvc' {
msvc = '-cc msvc'
}
so_debug_flag := if v.pref.is_debug { '-g' } else { '' }
cmd_compile_shared_library := '$vexe $msvc $so_debug_flag -o $file_base -solive -shared $file'
if v.pref.show_c_cmd {
@ -100,7 +100,6 @@ fn (v &V) generate_hot_reload_code() {
diff := time.ticks() - ticks
println('compiling shared library took $diff ms')
println('=========\n')
cgen.genln('
void lfnmutex_print(char *s){
@ -111,7 +110,6 @@ void lfnmutex_print(char *s){
}
}
')
if v.os != .windows {
cgen.genln('
void* live_lib=0;
@ -153,12 +151,10 @@ int load_so(byteptr path) {
return 0;
}
')
for so_fn in cgen.so_fns {
cgen.genln('$so_fn = (void *)GetProcAddress(live_lib, "$so_fn"); ')
}
}
cgen.genln('return 1;
}
@ -220,8 +216,8 @@ void reload_so() {
}
')
}
if v.pref.is_so {
cgen.genln(' int load_so(byteptr path) { return 0; }')
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
import (
@ -25,8 +24,7 @@ enum BuildMode {
}
const (
supported_platforms = ['windows', 'mac', 'macos', 'linux', 'freebsd',
'openbsd', 'netbsd', 'dragonfly', 'android', 'js', 'solaris', 'haiku']
supported_platforms = ['windows', 'mac', 'macos', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'android', 'js', 'solaris', 'haiku']
)
enum OS {
@ -98,14 +96,12 @@ pub mut:
is_run bool
show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c
sanitize bool // use Clang's new "-fsanitize" option
is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly).
is_keep_c bool // -keep_c , tell v to leave the generated .tmp.c alone (since by default v will delete them after c backend finishes)
// NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files,
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
is_cache bool // turns on v usage of the module cache to speed up compilation.
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers)
cflags string // Additional options which will be passed to the C compiler.
@ -126,7 +122,6 @@ pub mut:
enable_globals bool // allow __global for low level code
// is_fmt bool
is_bare bool
user_mod_path string // `v -user_mod_path /Users/user/modules` adds a new lookup path for imported modules
vlib_path string
vpath string
@ -188,7 +183,6 @@ pub fn (v mut V) parse(file string, pass Pass) int {
return pidx
}
pub fn (v mut V) compile() {
// Emily: Stop people on linux from being able to build with msvc
if os.user_os() != 'windows' && v.pref.ccompiler == 'msvc' {
@ -217,6 +211,7 @@ pub fn (v mut V) compile() {
}
}
*/
// First pass (declarations)
for file in v.files {
v.parse(file, .decl)
@ -236,7 +231,6 @@ pub fn (v mut V) compile() {
if v.os == .js {
cgen.genln('#define _VJS (1) ')
}
v_hash := vhash()
$if js {
cgen.genln('const V_COMMIT_HASH = "$v_hash";\n')
@ -245,7 +239,6 @@ pub fn (v mut V) compile() {
cgen.genln('#define V_COMMIT_HASH "$v_hash"')
cgen.genln('#endif')
}
q := cgen.nogen // TODO hack
cgen.nogen = false
$if js {
@ -253,15 +246,15 @@ pub fn (v mut V) compile() {
} $else {
if !v.pref.is_bare {
cgen.genln('#include <inttypes.h>') // int64_t etc
} else {
}
else {
cgen.genln('#include <stdint.h>')
}
cgen.genln(c_builtin_types)
if !v.pref.is_bare {
cgen.genln(c_headers)
} else {
}
else {
cgen.genln(bare_c_headers)
}
}
@ -308,7 +301,6 @@ pub fn (v mut V) compile() {
// new vfmt is not ready yet
// }
}
// add parser generated V code (str() methods etc)
mut vgen_parser := v.new_parser_from_string(v.vgen_buf.str())
// free the string builder which held the generated methods
@ -362,10 +354,8 @@ pub fn (v mut V) compile() {
pub fn (v mut V) compile_x64() {
$if !linux {
println('v -x64 can only generate Linux binaries for now')
println('You are not on a Linux system, so you will not ' +
'be able to run the resulting executable')
println('You are not on a Linux system, so you will not ' + 'be able to run the resulting executable')
}
v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare'))
v.files << v.dir
v.x64.generate_elf_header()
@ -379,7 +369,9 @@ pub fn (v mut V) compile_x64() {
}
fn (v mut V) generate_init() {
$if js { return }
$if js {
return
}
if v.pref.build_mode == .build_module {
nogen := v.cgen.nogen
v.cgen.nogen = false
@ -406,7 +398,6 @@ fn (v mut V) generate_init() {
}
}
consts_init_body := v.cgen.consts_init.join_lines()
if v.pref.is_bare {
// vlib can't have init_consts()
v.cgen.genln('
@ -418,7 +409,6 @@ fn (v mut V) generate_init() {
}
')
}
if !v.pref.is_bare {
// vlib can't have `init_consts()`
v.cgen.genln('void init() {
@ -473,8 +463,9 @@ string _STR_TMP(const char *fmt, ...) {
pub fn (v mut V) generate_main() {
mut cgen := v.cgen
$if js { return }
$if js {
return
}
if v.pref.is_vlines {
// After this point, the v files are compiled.
// The rest is auto generated code, which will not have
@ -485,7 +476,6 @@ pub fn (v mut V) generate_main() {
cgen.lines << '#line $lines_so_far "${cescaped_path(os.realpath(cgen.out_path))}"'
cgen.genln('')
}
// Make sure the main function exists
// Obviously we don't need it in libraries
if v.pref.build_mode != .build_module {
@ -510,19 +500,23 @@ pub fn (v mut V) generate_main() {
if test_fn_names.len == 0 {
verror('test files need to have at least one test function')
}
// Generate a C `main`, which calls every single test function
v.gen_main_start(false)
if v.pref.is_stats {
cgen.genln('BenchedTests bt = main__start_testing();')
}
for tfname in test_fn_names {
if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$tfname"));') }
cgen.genln('$tfname ();')
if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_end(&bt);') }
if v.pref.is_stats {
cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$tfname"));')
}
cgen.genln('$tfname ();')
if v.pref.is_stats {
cgen.genln('BenchedTests_testing_step_end(&bt);')
}
}
if v.pref.is_stats {
cgen.genln('BenchedTests_end_testing(&bt);')
}
if v.pref.is_stats { cgen.genln('BenchedTests_end_testing(&bt);') }
v.gen_main_end('return g_test_fails > 0')
}
else if v.table.main_exists() {
@ -542,15 +536,14 @@ pub fn (v mut V) generate_main() {
pub fn (v mut V) gen_main_start(add_os_args bool) {
v.cgen.genln('int main(int argc, char** argv) { ')
v.cgen.genln(' init();')
if add_os_args && 'os' in v.table.imports {
v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);')
}
v.generate_hotcode_reloading_main_caller()
v.cgen.genln('')
}
pub fn (v mut V) gen_main_end(return_statement string) {
v.cgen.genln('')
v.cgen.genln(' $return_statement;')
@ -561,12 +554,7 @@ pub fn final_target_out_name(out_name string) string {
$if windows {
return out_name.replace('/', '\\') + '.exe'
}
return if out_name.starts_with('/') {
out_name
}
else {
'./' + out_name
}
return if out_name.starts_with('/') { out_name } else { './' + out_name }
}
pub fn (v V) run_compiled_executable_and_exit() {
@ -575,18 +563,20 @@ pub fn (v V) run_compiled_executable_and_exit() {
println('============ running $v.out_name ============')
}
mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe', '') + '"'
mut args_after := ' '
for i, a in args {
if i == 0 { continue }
if a.starts_with('-') { continue }
if i == 0 {
continue
}
if a.starts_with('-') {
continue
}
if a in ['run', 'test'] {
args_after += args[i + 2..].join(' ')
break
}
}
cmd += args_after
if v.pref.is_test {
ret := os.system(cmd)
if ret != 0 {
@ -611,10 +601,13 @@ pub fn (v &V) v_files_from_dir(dir string) []string {
println('use `v -o v v.v` instead of `v -o v compiler`')
}
verror("$dir doesn't exist")
} else if !os.is_dir(dir) {
}
else if !os.is_dir(dir) {
verror("$dir isn't a directory")
}
mut files := os.ls(dir) or { panic(err) }
mut files := os.ls(dir)or{
panic(err)
}
if v.pref.is_verbose {
println('v_files_from_dir ("$dir")')
}
@ -664,8 +657,9 @@ pub fn (v mut V) add_v_files_to_compile() {
builtin_files = [builtin_vh]
}
}
if v.pref.is_verbose { v.log('v.add_v_files_to_compile > builtin_files: $builtin_files') }
if v.pref.is_verbose {
v.log('v.add_v_files_to_compile > builtin_files: $builtin_files')
}
// Parse builtin imports
for file in builtin_files {
// add builtins first
@ -723,8 +717,12 @@ pub fn (v mut V) add_v_files_to_compile() {
}
// add remaining main files last
for p in v.parsers {
if p.mod != 'main' { continue }
if p.is_vgen { continue }
if p.mod != 'main' {
continue
}
if p.is_vgen {
continue
}
v.files << p.file_path
}
}
@ -747,27 +745,25 @@ pub fn (v &V) get_user_files() []string {
// Need to store user files separately, because they have to be added after
// libs, but we dont know which libs need to be added yet
mut user_files := []string
preludes_path := filepath.join(v.pref.vlib_path,'compiler','preludes')
if v.pref.is_live {
user_files << filepath.join(preludes_path,'live_main.v')
}
if v.pref.is_solive {
user_files << filepath.join(preludes_path,'live_shared.v')
}
if v.pref.is_test {
user_files << filepath.join(preludes_path,'tests_assertions.v')
}
if v.pref.is_test && v.pref.is_stats {
user_files << filepath.join(preludes_path,'tests_with_stats.v')
}
is_test := dir.ends_with('_test.v')
mut is_internal_module_test := false
if is_test {
tcontent := os.read_file( dir ) or { panic('$dir does not exist') }
tcontent := os.read_file(dir)or{
panic('$dir does not exist')
}
if tcontent.contains('module ') && !tcontent.contains('module main') {
is_internal_module_test = true
}
@ -782,15 +778,18 @@ pub fn (v &V) get_user_files() []string {
user_files << single_test_v_file
dir = os.basedir(single_test_v_file)
}
if dir.ends_with('.v') || dir.ends_with('.vsh') {
single_v_file := dir
// Just compile one file and get parent dir
user_files << single_v_file
if v.pref.is_verbose { v.log('> just compile one file: "${single_v_file}"') }
if v.pref.is_verbose {
v.log('> just compile one file: "${single_v_file}"')
}
}
else {
if v.pref.is_verbose { v.log('> add all .v files from directory "${dir}" ...') }
if v.pref.is_verbose {
v.log('> add all .v files from directory "${dir}" ...')
}
// Add .v files from the directory being compiled
files := v.v_files_from_dir(dir)
for file in files {
@ -823,26 +822,23 @@ pub fn (v mut V) parse_lib_imports() {
mut done_imports := []string
for i in 0 .. v.parsers.len {
for _, mod in v.parsers[i].import_table.imports {
if mod in done_imports { continue }
if mod in done_imports {
continue
}
import_path := v.find_module_path(mod) or {
v.parsers[i].error_with_token_index(
'cannot import module "$mod" (not found)',
v.parsers[i].import_table.get_import_tok_idx(mod))
v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_table.get_import_tok_idx(mod))
break
}
vfiles := v.v_files_from_dir(import_path)
if vfiles.len == 0 {
v.parsers[i].error_with_token_index(
'cannot import module "$mod" (no .v files in "$import_path")',
v.parsers[i].import_table.get_import_tok_idx(mod))
v.parsers[i].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', v.parsers[i].import_table.get_import_tok_idx(mod))
}
// Add all imports referenced by these libs
for file in vfiles {
pidx := v.parse(file, .imports)
p_mod := v.parsers[pidx].mod
if p_mod != mod {
v.parsers[pidx].error_with_token_index(
'bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1)
v.parsers[pidx].error_with_token_index('bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1)
}
}
done_imports << mod
@ -870,15 +866,14 @@ pub fn get_param_after(joined_args, arg, def string) string {
pub fn get_cmdline_option(args []string, param string, def string) string {
mut found := false
for arg in args {
if found {
return arg
} else if param == arg {
}
else if param == arg {
found = true
}
}
return def
}
@ -892,28 +887,26 @@ pub fn (v &V) log(s string) {
pub fn new_v(args []string) &V {
// Create modules dirs if they are missing
if !os.is_dir(v_modules_path) {
os.mkdir(v_modules_path) or { panic(err) }
os.mkdir('$v_modules_path${os.path_separator}cache') or { panic(err) }
os.mkdir(v_modules_path)or{
panic(err)
}
os.mkdir('$v_modules_path${os.path_separator}cache')or{
panic(err)
}
}
// optional, custom modules search path
user_mod_path := get_cmdline_option(args, '-user_mod_path', '')
// Location of all vlib files
vroot := os.dir(vexe_path())
vlib_path := get_cmdline_option(args, '-vlib-path', filepath.join(vroot,'vlib'))
vpath := get_cmdline_option(args, '-vpath', v_modules_path)
mut vgen_buf := strings.new_builder(1000)
vgen_buf.writeln('module vgen\nimport strings')
joined_args := args.join(' ')
target_os := get_arg(joined_args, 'os', '')
comptime_define := get_arg(joined_args, 'd', '')
// println('comptimedefine=$comptime_define')
mut out_name := get_arg(joined_args, 'o', 'a.out')
mut dir := args.last()
if 'run' in args {
dir = get_param_after(joined_args, 'run', '')
@ -927,7 +920,6 @@ pub fn new_v(args[]string) &V {
if args.len < 2 {
dir = ''
}
// build mode
mut build_mode := BuildMode.default_mode
mut mod := ''
@ -935,17 +927,7 @@ pub fn new_v(args[]string) &V {
build_mode = .build_module
os.chdir(vroot)
// v build module ~/v/os => os.o
mod_path := if dir.contains('vlib') {
dir.all_after('vlib'+os.path_separator)
}
else if dir.starts_with('.\\') || dir.starts_with('./') {
dir[2..]
}
else if dir.starts_with(os.path_separator) {
dir.all_after(os.path_separator)
} else {
dir
}
mod_path := if dir.contains('vlib') { dir.all_after('vlib' + os.path_separator) } else if dir.starts_with('.\\') || dir.starts_with('./') { dir[2..] } else if dir.starts_with(os.path_separator) { dir.all_after(os.path_separator) } else { dir }
mod = mod_path.replace(os.path_separator, '.')
println('Building module "${mod}" (dir="$dir")...')
// out_name = '$TmpPath/vlib/${base}.o'
@ -961,6 +943,7 @@ pub fn new_v(args[]string) &V {
println('!Cross compiling $out_name')
}
*/
}
is_test := dir.ends_with('_test.v')
is_script := dir.ends_with('.v') || dir.ends_with('.vsh')
@ -976,8 +959,7 @@ pub fn new_v(args[]string) &V {
// optimized.
if out_name == 'v' && os.is_dir('vlib/compiler') {
println('Saving the resulting V executable in `./v2`')
println('Use `v -o v v.v` if you want to replace current '+
'V executable.')
println('Use `v -o v v.v` if you want to replace current ' + 'V executable.')
out_name = 'v2'
}
}
@ -991,7 +973,9 @@ pub fn new_v(args[]string) &V {
d := out_name.all_before_last(os.path_separator)
if !os.is_dir(d) {
println('creating a new directory "$d"')
os.mkdir(d) or { panic(err) }
os.mkdir(d)or{
panic(err)
}
}
}
mut _os := OS.mac
@ -1045,17 +1029,13 @@ pub fn new_v(args[]string) &V {
println('(os.executable=${os.executable()} vlib_path=$vlib_path vexe_path=${vexe_path()}')
exit(1)
}
mut out_name_c := get_vtmp_filename(out_name, '.tmp.c')
cflags := get_cmdline_cflags(args)
rdir := os.realpath(dir)
rdir_name := os.filename(rdir)
if '-bare' in args {
verror('use -freestanding instead of -bare')
}
obfuscate := '-obf' in args
is_repl := '-repl' in args
pref := &Preferences{
@ -1065,7 +1045,6 @@ pub fn new_v(args[]string) &V {
is_solive: '-solive' in args
is_prod: '-prod' in args
is_verbose: '-verbose' in args || '--verbose' in args
is_debug: '-g' in args || '-cg' in args
is_vlines: '-g' in args && !('-cg' in args)
is_keep_c: '-keep_c' in args
@ -1076,6 +1055,7 @@ pub fn new_v(args[]string) &V {
is_live: '-live' in args
sanitize: '-sanitize' in args
// nofmt: '-nofmt' in args
show_c_cmd: '-show_c_cmd' in args
translated: 'translated' in args
is_run: 'run' in args
@ -1094,6 +1074,7 @@ pub fn new_v(args[]string) &V {
building_v: !is_repl && (rdir_name == 'compiler' || rdir_name == 'v.v' || dir.contains('vlib'))
comptime_define: comptime_define
// is_fmt: comptime_define == 'vfmt'
user_mod_path: user_mod_path
vlib_path: vlib_path
vpath: vpath
@ -1132,8 +1113,9 @@ fn non_empty(a []string) []string {
pub fn env_vflags_and_os_args() []string {
vosargs := os.getenv('VOSARGS')
if '' != vosargs { return non_empty(vosargs.split(' ')) }
if '' != vosargs {
return non_empty(vosargs.split(' '))
}
mut args := []string
vflags := os.getenv('VFLAGS')
if '' != vflags {
@ -1142,7 +1124,8 @@ pub fn env_vflags_and_os_args() []string {
if os.args.len > 1 {
args << os.args[1..]
}
} else{
}
else {
args << os.args
}
return non_empty(args)
@ -1173,23 +1156,29 @@ pub fn vfmt(args[]string) {
os.exec('$vroot/tools/vfmt $file') or { panic(err) }
//if !os.exists('
*/
}
pub fn create_symlink() {
$if windows { return }
$if windows {
return
}
vexe := vexe_path()
link_path := '/usr/local/bin/v'
ret := os.system('ln -sf $vexe $link_path')
if ret == 0 {
println('Symlink "$link_path" has been created')
} else {
}
else {
println('Failed to create symlink "$link_path". Try again with sudo.')
}
}
pub fn vexe_path() string {
vexe := os.getenv('VEXE')
if '' != vexe { return vexe }
if '' != vexe {
return vexe
}
real_vexe_path := os.realpath(os.executable())
os.setenv('VEXE', real_vexe_path, true)
return real_vexe_path
@ -1214,30 +1203,54 @@ pub fn cescaped_path(s string) string {
pub fn os_from_string(os string) OS {
match os {
'linux' { return .linux}
'windows' { return .windows}
'mac' { return .mac}
'macos' { return .mac}
'freebsd' { return .freebsd}
'openbsd' { return .openbsd}
'netbsd' { return .netbsd}
'dragonfly' { return .dragonfly}
'js' { return .js}
'solaris' { return .solaris}
'android' { return .android}
'linux' {
return .linux
}
'windows' {
return .windows
}
'mac' {
return .mac
}
'macos' {
return .mac
}
'freebsd' {
return .freebsd
}
'openbsd' {
return .openbsd
}
'netbsd' {
return .netbsd
}
'dragonfly' {
return .dragonfly
}
'js' {
return .js
}
'solaris' {
return .solaris
}
'android' {
return .android
}
'msvc' {
// notice that `-os msvc` became `-cc msvc`
verror('use the flag `-cc msvc` to build using msvc')
}
'haiku' { return .haiku }
else { panic('bad os $os') }
'haiku' {
return .haiku
}
else {
panic('bad os $os')
}}
// println('bad os $os') // todo panic?
return .linux
}
//
pub fn set_vroot_folder(vroot_path string) {
// Preparation for the compiler module:
// VEXE env variable is needed so that compiler.vexe_path()
@ -1253,3 +1266,4 @@ pub fn new_v_compiler_with_args(args []string) &V {
os.setenv('VOSARGS', allargs.join(' '), true)
return new_v(allargs)
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
import (
@ -9,13 +8,13 @@ import (
os
filepath
)
/*
.vh generation logic.
.vh files contain only function signatures, consts, and types.
They are used together with pre-compiled modules.
*/
struct VhGen {
mut:
i int // token index
@ -23,7 +22,6 @@ mut:
fns strings.Builder
types strings.Builder
tokens []Token
}
// `mod` == "vlib/os"
@ -38,8 +36,10 @@ fn generate_vh(mod string) {
os.mkdir_all(pdir)
// os.mkdir(os.realpath(dir)) or { panic(err) }
}
mut out := os.create(path) or { panic(err) }
mod_path := mod.replace("\\", "/")
mut out := os.create(path)or{
panic(err)
}
mod_path := mod.replace('\\', '/')
out.writeln('// $mod_path module header\n')
mod_def := if mod_path.contains('/') { mod_path.all_after('/') } else { mod_path } // "os"
out.writeln('module $mod_def\n')
@ -49,13 +49,7 @@ fn generate_vh(mod string) {
// mut vfiles := os.ls(full_mod_path) or {
// exit(1)
// }
filtered := vfiles.filter(it.ends_with('.v') && !it.ends_with('test.v') &&
!it.ends_with('_windows.v') && !it.ends_with('_win.v') &&
!it.ends_with('_lin.v') &&
!it.contains('${os.path_separator}examples') &&
!it.contains('_js.v') &&
!it.contains('_bare.v') &&
!it.contains('${os.path_separator}js')) // TODO merge once filter allows it
filtered := vfiles.filter(it.ends_with('.v') && !it.ends_with('test.v') && !it.ends_with('_windows.v') && !it.ends_with('_win.v') && !it.ends_with('_lin.v') && !it.contains('${os.path_separator}examples') && !it.contains('_js.v') && !it.contains('_bare.v') && !it.contains('${os.path_separator}js')) // TODO merge once filter allows it
// println('f:')
// println(filtered)
mut v := new_v(['foo.v'])
@ -76,19 +70,23 @@ fn generate_vh(mod string) {
continue
}
match g.tokens[g.i].tok {
.key_fn { g.generate_fn() }
.key_const { g.generate_const() }
.key_struct { g.generate_type() }
.key_type { g.generate_alias() }
else {}
.key_fn {
g.generate_fn()
}
.key_const {
g.generate_const()
}
.key_struct {
g.generate_type()
}
.key_type {
g.generate_alias()
}
else {
}}
}
}
}
result :=
g.types.str() +
g.consts.str() +
g.fns.str().replace('\n\n\n', '\n').replace('\n\n', '\n')
result := g.types.str() + g.consts.str() + g.fns.str().replace('\n\n\n', '\n').replace('\n\n', '\n')
out.writeln(result.replace('[ ] ', '[]').replace('? ', '?'))
out.close()
}
@ -102,7 +100,6 @@ fn (g mut VhGen) generate_fn() {
// Skip private fns
// return ''
}
if next.tok == .name && next.lit == 'C' {
// println('skipping C')
return
@ -111,7 +108,6 @@ fn (g mut VhGen) generate_fn() {
mut tok := g.tokens[g.i]
for g.i < g.tokens.len - 1 && tok.tok != .lcbr {
next = g.tokens[g.i + 1]
g.fns.write(tok.str())
if tok.tok != .lpar && !(next.tok in [.comma, .rpar]) {
// No space after (), [], etc
@ -170,3 +166,4 @@ fn (g mut VhGen) generate_type() {
// g.i = old
// g.i--
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
import os
@ -10,7 +9,6 @@ import filepath
pub const (
v_modules_path = os.home_dir() + '.vmodules'
)
// Holds import information scoped to the parsed file
struct ImportTable {
mut:
@ -18,7 +16,6 @@ mut:
used_imports []string // alias
import_tok_idx map[string]int // module => idx
}
// Once we have a module format we can read from module file instead
// this is not optimal
fn (table &Table) qualify_module(mod string, file_path string) string {
@ -54,7 +51,9 @@ fn (p mut Parser) register_import_alias(alias string, mod string, tok_idx int) {
mod_parts := mod.split('.')
mut internal_mod_parts := []string
for part in mod_parts {
if part == 'internal' { break }
if part == 'internal' {
break
}
internal_mod_parts << part
}
internal_parent := internal_mod_parts.join('.')
@ -142,7 +141,8 @@ pub fn(graph &DepGraph) imports() []string {
return mods
}
[inline] fn (v &V) module_path(mod string) string {
[inline]
fn (v &V) module_path(mod string) string {
// submodule support
return mod.replace('.', os.path_separator)
}
@ -150,7 +150,6 @@ pub fn(graph &DepGraph) imports() []string {
// 'strings' => 'VROOT/vlib/strings'
// 'installed_mod' => '~/.vmodules/installed_mod'
// 'local_mod' => '/path/to/current/dir/local_mod'
fn (v mut V) set_module_lookup_paths() {
mlookup_path := if v.pref.vpath.len > 0 { v.pref.vpath } else { v_modules_path }
// Module search order:
@ -158,7 +157,6 @@ fn (v mut V) set_module_lookup_paths(){
// module, which they test. Adding the parent folder of the module folder
// with the _test.v files, *guarantees* that the tested module can be found
// without needing to set custom options/flags.
// 1) search in the *same* directory, as the compiled final v program source
// (i.e. the . in `v .` or file.v in `v file.v`)
// 2) search in the modules/ in the same directory.
@ -176,7 +174,6 @@ fn (v mut V) set_module_lookup_paths(){
if v.pref.user_mod_path.len > 0 {
v.module_lookup_paths << v.pref.user_mod_path
}
if v.pref.is_verbose {
v.log('v.module_lookup_paths: $v.module_lookup_paths')
}
@ -186,19 +183,26 @@ fn (v &V) find_module_path(mod string) ?string {
mod_path := v.module_path(mod)
for lookup_path in v.module_lookup_paths {
try_path := filepath.join(lookup_path,mod_path)
if v.pref.is_verbose { println(' >> trying to find $mod in $try_path ...') }
if v.pref.is_verbose {
println(' >> trying to find $mod in $try_path ...')
}
if os.is_dir(try_path) {
if v.pref.is_verbose { println(' << found $try_path .') }
if v.pref.is_verbose {
println(' << found $try_path .')
}
return try_path
}
}
return error('module "$mod" not found')
}
[inline] fn mod_gen_name(mod string) string {
[inline]
fn mod_gen_name(mod string) string {
return mod.replace('.', '_dot_')
}
[inline] fn mod_gen_name_rev(mod string) string {
[inline]
fn mod_gen_name_rev(mod string) string {
return mod.replace('_dot_', '.')
}

View File

@ -3,18 +3,14 @@ module compiler
import os
#flag windows -l shell32
#flag windows -l dbghelp
// RegOpenKeyExW etc
#flag windows -l dbghelp // RegOpenKeyExW etc
#flag windows -l advapi32
struct MsvcResult {
full_cl_exe_path string
exe_path string
um_lib_path string
ucrt_lib_path string
vs_lib_path string
um_include_path string
ucrt_include_path string
vs_include_path string
@ -23,7 +19,6 @@ struct MsvcResult {
// Mimics a HKEY
type RegKey voidptr
// Taken from the windows SDK
const (
HKEY_LOCAL_MACHINE = RegKey(0x80000002)
@ -31,39 +26,30 @@ const (
KEY_WOW64_32KEY = (0x0200)
KEY_ENUMERATE_SUB_KEYS = (0x0008)
)
// Given a root key look for one of the subkeys in 'versions' and get the path
fn find_windows_kit_internal(key RegKey, versions []string) ?string {
$if windows {
for version in versions {
required_bytes := 0 // TODO mut
result := C.RegQueryValueEx(key, version.to_wide(), 0, 0, 0, &required_bytes)
length := required_bytes / 2
if result != 0 {
continue
}
alloc_length := (required_bytes + 2)
mut value := &u16(malloc(alloc_length))
if isnil(value) {
continue
}
result2 := C.RegQueryValueEx(key, version.to_wide(), 0, 0, value, &alloc_length)
if result2 != 0 {
continue
}
// We might need to manually null terminate this thing
// So just make sure that we do that
if (value[length - 1] != u16(0)) {
value[length] = u16(0)
}
return string_from_wide(value)
}
}
@ -73,7 +59,6 @@ fn find_windows_kit_internal(key RegKey, versions []string) ?string {
struct WindowsKit {
um_lib_path string
ucrt_lib_path string
um_include_path string
ucrt_include_path string
shared_include_path string
@ -83,11 +68,10 @@ struct WindowsKit {
fn find_windows_kit_root(host_arch string) ?WindowsKit {
$if windows {
root_key := RegKey(0)
rc := C.RegOpenKeyEx(
HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'.to_wide(), 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &root_key)
defer {C.RegCloseKey(root_key)}
rc := C.RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'.to_wide(), 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &root_key)
defer {
C.RegCloseKey(root_key)
}
if rc != 0 {
return error('Unable to open root key')
}
@ -95,33 +79,27 @@ fn find_windows_kit_root(host_arch string) ?WindowsKit {
kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81'])or{
return error('Unable to find a windows kit')
}
kit_lib := kit_root + 'Lib'
// println(kit_lib)
files := os.ls(kit_lib) or { panic(err) }
files := os.ls(kit_lib)or{
panic(err)
}
mut highest_path := ''
mut highest_int := 0
for f in files {
no_dot := f.replace('.', '')
v_int := no_dot.int()
if v_int > highest_int {
highest_int = v_int
highest_path = f
}
}
kit_lib_highest := kit_lib + '\\$highest_path'
kit_include_highest := kit_lib_highest.replace('Lib', 'Include')
// println('$kit_lib_highest $kit_include_highest')
return WindowsKit{
um_lib_path: kit_lib_highest + '\\um\\$host_arch'
ucrt_lib_path: kit_lib_highest + '\\ucrt\\$host_arch'
um_include_path: kit_include_highest + '\\um'
ucrt_include_path: kit_include_highest + '\\ucrt'
shared_include_path: kit_include_highest + '\\shared'
@ -144,40 +122,27 @@ fn find_vs(vswhere_dir string, host_arch string) ?VsInstallation {
// VSWhere is guaranteed to be installed at this location now
// If its not there then end user needs to update their visual studio
// installation!
res := os.exec('"$vswhere_dir\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath')or{
return error(err)
}
// println('res: "$res"')
version := os.read_file('$res.output\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt')or{
println('Unable to find msvc version')
return error('Unable to find vs installation')
}
// println('version: $version')
v := if version.ends_with('\n') {
version[..version.len - 2]
} else {
version
}
v := if version.ends_with('\n') { version[..version.len - 2] } else { version }
lib_path := '$res.output\\VC\\Tools\\MSVC\\$v\\lib\\$host_arch'
include_path := '$res.output\\VC\\Tools\\MSVC\\$v\\include'
if os.exists('$lib_path\\vcruntime.lib') {
p := '$res.output\\VC\\Tools\\MSVC\\$v\\bin\\Host$host_arch\\$host_arch'
// println('$lib_path $include_path')
return VsInstallation{
exe_path: p
lib_path: lib_path
include_path: include_path
}
}
println('Unable to find vs installation (attempted to use lib path "$lib_path")')
return error('Unable to find vs exe folder')
}
@ -185,38 +150,26 @@ fn find_vs(vswhere_dir string, host_arch string) ?VsInstallation {
fn find_msvc() ?MsvcResult {
$if windows {
processor_architecture := os.getenv('PROCESSOR_ARCHITECTURE')
vswhere_dir := if processor_architecture == 'x86' {
'%ProgramFiles%'
} else {
'%ProgramFiles(x86)%'
}
host_arch := if processor_architecture == 'x86' {
'X86'
} else {
'X64'
}
vswhere_dir := if processor_architecture == 'x86' { '%ProgramFiles%' } else { '%ProgramFiles(x86)%' }
host_arch := if processor_architecture == 'x86' { 'X86' } else { 'X64' }
wk := find_windows_kit_root(host_arch)or{
return error('Unable to find windows sdk')
}
vs := find_vs(vswhere_dir, host_arch)or{
return error('Unable to find visual studio')
}
return MsvcResult{
full_cl_exe_path: os.realpath(vs.exe_path + os.path_separator + 'cl.exe')
exe_path: vs.exe_path,
um_lib_path: wk.um_lib_path,
ucrt_lib_path: wk.ucrt_lib_path,
vs_lib_path: vs.lib_path,
um_include_path: wk.um_include_path,
ucrt_include_path: wk.ucrt_include_path,
vs_include_path: vs.include_path,
shared_include_path: wk.shared_include_path,
exe_path: vs.exe_path
um_lib_path: wk.um_lib_path
ucrt_lib_path: wk.ucrt_lib_path
vs_lib_path: vs.lib_path
um_include_path: wk.um_include_path
ucrt_include_path: wk.ucrt_include_path
vs_include_path: vs.include_path
shared_include_path: wk.shared_include_path
}
}
$else {
} $else {
verror('Cannot find msvc on this OS')
return error('msvc not found')
}
@ -231,40 +184,34 @@ pub fn (v mut V) cc_msvc() {
verror('Cannot find MSVC on this OS')
return
}
out_name_obj := os.realpath(v.out_name_c + '.obj')
// Default arguments
// volatile:ms enables atomic volatile (gcc _Atomic)
// -w: no warnings
// 2 unicode defines
// /Fo sets the object file name - needed so we can clean up after ourselves properly
mut a := ['-w', '/we4013', '/volatile:ms', '/Fo"$out_name_obj"']
if v.pref.is_prod {
a << '/O2'
a << '/MD'
a << '/Zi'
a << '/DNDEBUG'
} else {
}
else {
a << '/Zi'
a << '/MDd'
}
if v.pref.is_so {
if !v.out_name.ends_with('.dll') {
v.out_name = v.out_name + '.dll'
}
// Build dll
a << '/LD'
} else if !v.out_name.ends_with('.exe') {
}
else if !v.out_name.ends_with('.exe') {
v.out_name = v.out_name + '.exe'
}
v.out_name = os.realpath(v.out_name)
// alibs := []string // builtin.o os.o http.o etc
if v.pref.build_mode == .build_module {
// Compile only
@ -286,52 +233,30 @@ pub fn (v mut V) cc_msvc() {
}
*/
}
if v.pref.sanitize {
println('Sanitize not supported on msvc.')
}
// The C file we are compiling
// a << '"$TmpPath/$v.out_name_c"'
a << '"' + os.realpath(v.out_name_c) + '"'
// Emily:
// Not all of these are needed (but the compiler should discard them if they are not used)
// these are the defaults used by msbuild and visual studio
mut real_libs := [
'kernel32.lib',
'user32.lib',
'gdi32.lib',
'winspool.lib',
'comdlg32.lib',
'advapi32.lib',
'shell32.lib',
'ole32.lib',
'oleaut32.lib',
'uuid.lib',
'odbc32.lib',
'odbccp32.lib'
]
mut real_libs := ['kernel32.lib', 'user32.lib', 'gdi32.lib', 'winspool.lib', 'comdlg32.lib', 'advapi32.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib', 'uuid.lib', 'odbc32.lib', 'odbccp32.lib']
sflags := v.get_os_cflags().msvc_string_flags()
real_libs << sflags.real_libs
inc_paths := sflags.inc_paths
lib_paths := sflags.lib_paths
other_flags := sflags.other_flags
// Include the base paths
a << '-I "$r.ucrt_include_path"'
a << '-I "$r.vs_include_path"'
a << '-I "$r.um_include_path"'
a << '-I "$r.shared_include_path"'
a << inc_paths
a << other_flags
// Libs are passed to cl.exe which passes them to the linker
a << real_libs.join(' ')
a << '/link'
a << '/NOLOGO'
a << '/OUT:"$v.out_name"'
@ -339,17 +264,13 @@ pub fn (v mut V) cc_msvc() {
a << '/LIBPATH:"$r.um_lib_path"'
a << '/LIBPATH:"$r.vs_lib_path"'
a << '/DEBUG:FULL' // required for prod builds to generate PDB
if v.pref.is_prod {
a << '/INCREMENTAL:NO' // Disable incremental linking
a << '/OPT:REF'
a << '/OPT:ICF'
}
a << lib_paths
args := a.join(' ')
cmd := '"$r.full_cl_exe_path" $args'
// It is hard to see it at first, but the quotes above ARE balanced :-| ...
// Also the double quotes at the start ARE needed.
@ -358,9 +279,7 @@ pub fn (v mut V) cc_msvc() {
println(cmd)
println('==========\n')
}
// println('$cmd')
res := os.exec(cmd)or{
println(err)
verror('msvc error')
@ -371,45 +290,38 @@ pub fn (v mut V) cc_msvc() {
}
// println(res)
// println('C OUTPUT:')
if !v.pref.is_keep_c && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' {
os.rm(v.out_name_c)
}
// Always remove the object file - it is completely unnecessary
os.rm(out_name_obj)
}
fn build_thirdparty_obj_file_with_msvc(path string, moduleflags []CFlag) {
msvc := find_msvc()or{
println('Could not find visual studio')
return
}
// msvc expects .obj not .o
mut obj_path := '${path}bj'
obj_path = os.realpath(obj_path)
if os.exists(obj_path) {
println('$obj_path already build.')
return
}
println('$obj_path not found, building it (with msvc)...')
parent := os.dir(obj_path)
files := os.ls(parent) or { panic(err) }
files := os.ls(parent)or{
panic(err)
}
mut cfiles := ''
for file in files {
if file.ends_with('.c') {
cfiles += '"' + os.realpath(parent + os.path_separator + file) + '" '
}
}
include_string := '-I "$msvc.ucrt_include_path" -I "$msvc.vs_include_path" -I "$msvc.um_include_path" -I "$msvc.shared_include_path"'
// println('cfiles: $cfiles')
btarget := moduleflags.c_options_before_target_msvc()
atarget := moduleflags.c_options_after_target_msvc()
cmd := '"$msvc.full_cl_exe_path" /volatile:ms /Zi /DNDEBUG $include_string /c $btarget $cfiles $atarget /Fo"$obj_path"'
@ -428,7 +340,6 @@ fn build_thirdparty_obj_file_with_msvc(path string, moduleflags []CFlag) {
println(res.output)
}
struct MsvcStringFlags {
mut:
real_libs []string
@ -476,11 +387,11 @@ fn (cflags []CFlag) msvc_string_flags() MsvcStringFlags {
other_flags << flag.value
}
}
mut lpaths := []string
for l in lib_paths {
lpaths << '/LIBPATH:"' + os.realpath(l) + '"'
}
return MsvcStringFlags{ real_libs, inc_paths, lpaths, other_flags }
return MsvcStringFlags{
real_libs,inc_paths,lpaths,other_flags}
}

View File

@ -1,5 +1,4 @@
module compiler
// `a in [1,2,3]` => `a == 1 || a == 2 || a == 3`
// avoid allocation
// `typ` is the type of `a`
@ -18,7 +17,8 @@ fn (p mut Parser) in_optimization(typ string, ph int) {
if i > 0 {
if is_str {
p.gen(' || string_eq($expr, ')
} else {
}
else {
p.gen(' || $expr == ')
}
}
@ -26,7 +26,8 @@ fn (p mut Parser) in_optimization(typ string, ph int) {
if is_str {
p.cgen.set_placeholder(ph, ' (string_eq(')
p.gen(', ')
} else {
}
else {
p.cgen.set_placeholder(ph, ' (')
p.gen(' ==')
}
@ -44,3 +45,4 @@ fn (p mut Parser) in_optimization(typ string, ph int) {
p.gen(')')
p.check(.rsbr)
}

View File

@ -3,8 +3,6 @@
// that can be found in the LICENSE file.
module compiler
import (
os
strings

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
import strings
@ -13,16 +12,20 @@ fn sql_params2params_gen(sql_params []string, sql_types []string, qprefix string
paramtype := sql_types[i]
if param[0].is_digit() {
params_gen += '${qprefix}params[$i] = int_str($param).str;\n'
}else if param[0] == `\'` {
sparam := param.trim('\'')
}
else if param[0] == `\'` {
sparam := param.trim("\'")
params_gen += '${qprefix}params[$i] = "$sparam";\n'
} else {
}
else {
// A variable like q.nr_orders
if paramtype == 'int' {
params_gen += '${qprefix}params[$i] = int_str( $param ).str;\n'
}else if paramtype == 'string' {
}
else if paramtype == 'string' {
params_gen += '${qprefix}params[$i] = ${param}.str;\n'
}else{
}
else {
verror('orm: only int and string variable types are supported in queries')
}
}
@ -32,7 +35,6 @@ fn sql_params2params_gen(sql_params []string, sql_types []string, qprefix string
return params_gen
}
// `db.select from User where id == 1 && nr_bookings > 0`
fn (p mut Parser) select_query(fn_ph int) string {
// NB: qprefix and { p.sql_i, p.sql_params, p.sql_types } SHOULD be reset for each query,
@ -40,7 +42,8 @@ fn (p mut Parser) select_query(fn_ph int) string {
qprefix := p.get_tmp().replace('tmp', 'sql') + '_'
p.sql_i = 0
p.sql_params = []
if false {}
if false {
}
p.sql_types = []
mut q := 'select '
p.check(.key_select)
@ -91,8 +94,12 @@ fn (p mut Parser) select_query(fn_ph int) string {
println('orm: skipping $field.name')
continue
}
p.register_var({ field | is_mut: true, is_used:true, is_changed:true })
p.register_var({
field |
is_mut:true,
is_used:true,
is_changed:true
})
}
q += table_name + 's'
// `where` statement
@ -118,11 +125,11 @@ fn (p mut Parser) select_query(fn_ph int) string {
}
println('sql query="$q"')
p.cgen.insert_before('// DEBUG_SQL prefix: $qprefix | fn_ph: $fn_ph | query: "$q" ')
if n == 'count' {
p.cgen.set_placeholder(fn_ph, 'pg__DB_q_int(')
p.gen(', tos2("$q"))')
} else {
}
else {
// Build an object, assign each field.
tmp := p.get_tmp()
mut obj_gen := strings.new_builder(300)
@ -134,8 +141,7 @@ fn (p mut Parser) select_query(fn_ph int) string {
else if field.typ == 'bool' {
cast = 'string_bool'
}
obj_gen.writeln('${qprefix}${tmp}.$field.name = ' +
'${cast}(*(string*)array_get(${qprefix}row.vals, $i));')
obj_gen.writeln('${qprefix}${tmp}.$field.name = ' + '${cast}(*(string*)array_get(${qprefix}row.vals, $i));')
}
// One object
if query_one {
@ -182,16 +188,17 @@ for (int i = 0; i < ${qprefix}rows.len; i++) {
')
p.cgen.resetln('${qprefix}arr_$tmp')
}
}
if n == 'count' {
return 'int'
} else if query_one {
}
else if query_one {
opt_type := 'Option_$table_name'
p.cgen.typedefs << 'typedef Option $opt_type;'
p.table.register_builtin(opt_type)
return opt_type
} else {
}
else {
p.register_array('array_$table_name')
return 'array_$table_name'
}
@ -203,7 +210,9 @@ fn (p mut Parser) insert_query(fn_ph int) {
p.check(.lpar)
var_name := p.check_name()
p.check(.rpar)
var := p.find_var(var_name) or { return }
var := p.find_var(var_name) or {
return
}
typ := p.table.find_type(var.typ)
mut fields := []Var
for i, field in typ.fields {
@ -233,9 +242,11 @@ fn (p mut Parser) insert_query(fn_ph int) {
params += 'params[${i-1}] = '
if field.typ == 'string' {
params += '$var_name . $field.name .str;\n'
} else if field.typ == 'int' {
}
else if field.typ == 'int' {
params += 'int_str($var_name . $field.name).str;\n'
} else {
}
else {
p.error('V ORM: unsupported type `$field.typ`')
}
if i < fields.len - 1 {
@ -275,7 +286,12 @@ fn (p mut Parser) update_query(fn_ph int) {
println('orm: skipping $f.name')
continue
}
p.register_var({ f | is_mut: true, is_used:true, is_changed:true })
p.register_var({
f |
is_mut:true,
is_used:true,
is_changed:true
})
}
mut q := 'update ${typ.name}s set $field='
p.is_sql = true
@ -289,7 +305,8 @@ fn (p mut Parser) update_query(fn_ph int) {
else {
q += 'false'
}
} else {
}
else {
q += expr
}
// where
@ -300,8 +317,6 @@ fn (p mut Parser) update_query(fn_ph int) {
p.is_sql = false
q += ' where ' + wexpr
}
nr_vals := 0
p.cgen.insert_before('char* params[$nr_vals];') // + params)
p.cgen.set_placeholder(fn_ph, 'PQexecParams( ')

View File

@ -889,3 +889,4 @@ fn (s &Scanner) validate_var_name(name string) {
s.error('bad variable name `$name`\n' + 'looks like you have a multi-word name without separating them with `_`' + '\nfor example, use `registration_date` instead of `registrationdate` ')
}
}

View File

@ -152,3 +152,4 @@ fn (p mut Parser) string_expr() {
p.gen('_STR($format$args)')
}
}

View File

@ -1,14 +1,14 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
import (
strings
)
// also unions and interfaces
fn (p mut Parser) struct_decl() {
is_pub := p.tok == .key_pub
if is_pub {
@ -136,7 +136,8 @@ fn (p mut Parser) struct_decl() {
p.fspace()
new_access_mod = .public_mut
p.next() // skip `mut`
} else {
}
else {
new_access_mod = .public
}
if new_access_mod in used {
@ -241,12 +242,15 @@ fn (p mut Parser) struct_decl() {
p.check(.colon)
mut val := ''
match p.tok {
.name { val = p.check_name() }
.str { val = p.check_string() }
.name {
val = p.check_name()
}
.str {
val = p.check_string()
}
else {
p.error('attribute value should be either name or string')
}
}
}}
attr += ':' + val
}
p.check(.rsbr)
@ -257,8 +261,7 @@ fn (p mut Parser) struct_decl() {
did_gen_something = true
is_mut := access_mod in [.private_mut, .public_mut, .global]
if p.first_pass() {
p.table.add_field(typ.name, field_name, field_type, is_mut,
attr, access_mod)
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
}
p.fgen_nl() // newline between struct fields
}
@ -274,7 +277,6 @@ fn (p mut Parser) struct_decl() {
p.fgen_nl()
// p.fgenln('//kek')
}
// `User{ foo: bar }`
fn (p mut Parser) struct_init(typ string) string {
p.is_struct_init = true
@ -282,7 +284,9 @@ fn (p mut Parser) struct_init(typ string) string {
if !t.is_public && t.mod != p.mod {
p.warn('type `$t.name` is private')
}
if p.gen_struct_init(typ, t) { return typ }
if p.gen_struct_init(typ, t) {
return typ
}
ptr := typ.contains('*')
mut did_gen_something := false
// Loop thru all struct init keys and assign values
@ -330,18 +334,15 @@ fn (p mut Parser) struct_init(typ string) string {
}
// Zero values: init all fields (ints to 0, strings to '' etc)
for i, field in t.fields {
sanitized_name := if typ != 'Option' {
p.table.var_cgen_name( field.name )
} else {
field.name
}
sanitized_name := if typ != 'Option' { p.table.var_cgen_name(field.name) } else { field.name }
// println('### field.name')
// Skip if this field has already been assigned to
if sanitized_name in inited_fields {
continue
}
field_typ := field.typ
if !p.builtin_mod && field_typ.ends_with('*') && p.mod != 'os' { //&&
if !p.builtin_mod && field_typ.ends_with('*') && p.mod != 'os' {
// &&
p.warn('reference field `${typ}.${field.name}` must be initialized')
}
// init map fields
@ -357,11 +358,7 @@ fn (p mut Parser) struct_init(typ string) string {
}
// Did the user provide a default value for this struct field?
// Use it. Otherwise zero it.
def_val := if t.default_vals.len > i && t.default_vals[i] != '' {
t.default_vals[i]
} else {
type_default(field_typ)
}
def_val := if t.default_vals.len > i && t.default_vals[i] != '' { t.default_vals[i] } else { type_default(field_typ) }
if def_val != '' && def_val != '{0}' {
p.gen_struct_field_init(sanitized_name)
p.gen(def_val)
@ -419,4 +416,3 @@ fn (p mut Parser) struct_init(typ string) string {
return typ
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
struct Token {
@ -12,7 +11,6 @@ struct Token {
pos int // the position of the token in scanner text
}
enum TokenKind {
eof
name // user
@ -125,6 +123,8 @@ enum TokenKind {
// build_keys genereates a map with keywords' string values:
// Keywords['return'] == .key_return
fn build_keys() map[string]int {
mut res := map[string]int
for t := int(TokenKind.keyword_beg) + 1; t < int(TokenKind.keyword_end); t++ {
@ -262,19 +262,11 @@ pub fn (t TokenKind) str() string {
}
fn (t TokenKind) is_decl() bool {
return t in [.key_enum, .key_interface, .key_fn,
.key_struct ,.key_type, .key_const, .key_import_const, .key_pub, .eof]
return t in [.key_enum, .key_interface, .key_fn, .key_struct, .key_type, .key_const, .key_import_const, .key_pub, .eof]
}
const (
AssignTokens = [
TokenKind.assign, .plus_assign, .minus_assign,
.mult_assign, .div_assign, .xor_assign,
.mod_assign,
.or_assign, .and_assign, .righ_shift_assign,
.left_shift_assign
]
AssignTokens = [TokenKind.assign, .plus_assign, .minus_assign, .mult_assign, .div_assign, .xor_assign, .mod_assign, .or_assign, .and_assign, .righ_shift_assign, .left_shift_assign]
)
fn (t TokenKind) is_assign() bool {
@ -293,7 +285,6 @@ fn (t []TokenKind) contains(val TokenKind) bool {
pub fn (t Token) str() string {
if t.tok == .number {
return t.lit
}
if t.tok == .chartoken {
return '`$t.lit`'

View File

@ -251,7 +251,7 @@ fn (p &Parser) gen_fmt() {
return
}
//files := ['get_type.v']
if !p.file_path.contains('vlib/builtin') {return}
if p.file_path.contains('vfmt') {return}
//if !(p.file_name in files) { return }
path := os.tmpdir() + '/' + p.file_name
println('generating ${path}')

View File

@ -84,9 +84,8 @@ V package management commands:
remove [module] Removes an installed module, or ALL installed modules at once, when no module name is given.
'
)
/*
- To disable automatic formatting:
v -nofmt file.v
*/

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
import os
@ -10,7 +9,9 @@ import filepath
pub fn get_vtmp_folder() string {
vtmp := filepath.join(os.tmpdir(),'v')
if !os.is_dir(vtmp) {
os.mkdir(vtmp) or { panic(err) }
os.mkdir(vtmp)or{
panic(err)
}
}
return vtmp
}
@ -19,3 +20,4 @@ pub fn get_vtmp_filename(base_file_name string, postfix string) string {
vtmp := get_vtmp_folder()
return os.realpath(filepath.join(vtmp,os.filename(os.realpath(base_file_name)) + postfix))
}

View File

@ -13,12 +13,12 @@ pub fn launch_tool(tname string){
tool_args := oargs.join(' ')
tool_command := '"$tool_exe" $tool_args'
// println('Launching: "$tool_command" ...')
mut tool_should_be_recompiled := false
if !os.exists(tool_exe) {
// fresh checkout
tool_should_be_recompiled = true
}else{
}
else {
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) {
// v was recompiled, maybe after v up ...
// rebuild the tool too just in case
@ -29,14 +29,16 @@ pub fn launch_tool(tname string){
tool_should_be_recompiled = true
}
}
if tool_should_be_recompiled {
compilation_command := '"$vexe" "$tool_source"'
// println('Compiling $tname with: "$compilation_command"')
tool_compilation := os.exec(compilation_command) or { panic(err) }
tool_compilation := os.exec(compilation_command)or{
panic(err)
}
if tool_compilation.exit_code != 0 {
panic('V tool "$tool_source" could not be compiled\n' + tool_compilation.output)
}
}
exit(os.system(tool_command))
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module x64
import os
@ -14,19 +13,14 @@ const (
ei_class = 4
elfclass64 = 2
elfdata2lsb = 1
ev_current = 1
elf_osabi = 0
// ELF file types
et_rel = 1
et_exec = 2
et_dyn = 3
e_machine = 0x3e
shn_xindex = 0xffff
sht_null = 0
)
@ -34,7 +28,6 @@ const (
segment_start = 0x400000
)
pub fn (g mut Gen) generate_elf_header() {
g.buf << [byte(mag0), mag1, mag2, mag3]
g.buf << elfclass64 // file class
@ -96,7 +89,9 @@ pub fn (g mut Gen) generate_elf_footer() {
// -5 is for "e8 00 00 00 00"
g.write64_at(int(g.main_fn_addr - g.code_start_pos) - 5, g.code_start_pos + 1)
// Create the binary
mut f := os.create(g.out_name) or { panic(err) }
mut f := os.create(g.out_name)or{
panic(err)
}
os.chmod(g.out_name, 0775)
f.write_bytes(g.buf.data, g.buf.len)
f.close()

View File

@ -1,9 +1,7 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module x64
/*
This file is unused right now, since binaries without sections
are generated.
@ -11,13 +9,14 @@ are generated.
But it will be necessary once we have dynamic linking.
*/
enum SectionType {
null = 0
progbits = 1
symtab = 2
strtab = 3
rela = 4
}
null
=0progbits
=1symtab
=2strtab
=3rela
=4}
struct SectionConfig {
name string
@ -47,7 +46,6 @@ fn (g mut Gen) section_header(c SectionConfig) {
g.write64(c.entsize)
}
fn genobj() {
/*
// SHN_UNDEF

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module x64
pub struct Gen {
@ -49,7 +48,6 @@ pub fn (g &Gen) pos() i64 {
return g.buf.len
}
fn (g mut Gen) write8(n int) {
// write 1 byte
g.buf << byte(n)
@ -102,28 +100,41 @@ fn (g mut Gen) write_string(s string) {
fn (g mut Gen) inc(reg Register) {
g.write16(0xff49)
match reg {
.r12 { g.write8(0xc4) }
else { panic('unhandled inc $reg') }
.r12 {
g.write8(0xc4)
}
else {
panic('unhandled inc $reg')
}}
}
fn (g mut Gen) cmp(reg Register, size Size, val i64) {
g.write8(0x49)
// Second byte depends on the size of the value
match size {
._8 { g.write8(0x83) }
._32 { g.write8(0x81) }
else { panic('unhandled cmp') }
._8 {
g.write8(0x83)
}
._32 {
g.write8(0x81)
}
else {
panic('unhandled cmp')
}}
// Third byte depends on the register being compared to
match reg {
.r12 { g.write8(0xfc) }
else { panic('unhandled cmp') }
.r12 {
g.write8(0xfc)
}
else {
panic('unhandled cmp')
}}
g.write8(int(val))
}
fn abs(a i64) i64 { return if a < 0 { -a } else { a } }
fn abs(a i64) i64 {
return if a < 0 { -a } else { a }
}
fn (g mut Gen) jle(addr i64) {
// Calculate the relative offset to jump to
@ -155,15 +166,15 @@ fn (g mut Gen) mov64(reg Register, val i64) {
g.write8(0x48)
g.write8(0xbe)
}
else { println('unhandled mov $reg') }
}
else {
println('unhandled mov $reg')
}}
g.write64(val)
}
fn (g mut Gen) call(addr int) {
// rel := g.abs_to_rel_addr(addr)
// rel := 0xffffffff - int(abs(addr - g.buf.len))-1
println('call addr=$addr rel_addr=$addr pos=$g.buf.len')
g.write8(0xe8)
g.write32(addr)
@ -217,9 +228,15 @@ pub fn (g mut Gen) gen_exit() {
fn (g mut Gen) mov(reg Register, val int) {
match reg {
.eax { g.write8(0xb8) }
.edi { g.write8(0xbf) }
.edx { g.write8(0xba) }
.eax {
g.write8(0xb8)
}
.edi {
g.write8(0xbf)
}
.edx {
g.write8(0xba)
}
.rsi {
g.write8(0x48)
g.write8(0xbe)
@ -230,9 +247,7 @@ fn (g mut Gen) mov(reg Register, val int) {
}
else {
panic('unhandled mov $reg')
}
}
}}
g.write32(val)
}
@ -251,4 +266,3 @@ pub fn (g mut Gen) call_fn(name string) {
println('call $name $addr')
}

View File

@ -3,8 +3,9 @@ module filepath
import (
os
)
// return the extension in the file `path`
pub fn ext(path string) string {
pos := path.last_index_byte(`.`)
if pos != -1 {
@ -27,6 +28,9 @@ pub fn is_abs(path string) bool {
pub fn join(base string, dirs ...string) string {
mut result := []string
result << base.trim_right('\\/')
for d in dirs { result << d }
for d in dirs {
result << d
}
return result.join(os.path_separator)
}

View File

@ -1,10 +1,6 @@
module os
// (Must be realized in Syscall) (Must be specified)
// File modes.
const (
O_RDONLY = 1 // open the file read-only.
O_WRONLY = 2 // open the file write-only.

View File

@ -1,15 +1,12 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module os
import filepath
#include <sys/stat.h>
//#include <signal.h>
#include <sys/stat.h> // #include <signal.h>
#include <errno.h>
/*
struct dirent {
d_ino int
@ -26,6 +23,7 @@ struct C.dirent {
fn C.readdir(voidptr) C.dirent
pub const (
args = []string
MAX_PATH = 4096
@ -49,14 +47,11 @@ struct C.stat {
}
struct C.DIR {
}
// struct C.dirent {
// d_name byteptr
// }
struct C.sigaction {
mut:
sa_mask int
@ -65,8 +60,14 @@ mut:
}
fn C.getline(voidptr, voidptr, voidptr) int
fn C.ftell(fp voidptr) int
fn C.getenv(byteptr) &char
fn C.sigaction(int, voidptr, int)
@ -96,7 +97,6 @@ pub fn read_bytes(path string) ?[]byte {
C.fseek(fp, 0, C.SEEK_END)
fsize := C.ftell(fp)
C.rewind(fp)
mut res := [`0`].repeat(fsize)
nr_read_elements := C.fread(res.data, fsize, 1, fp)
C.fclose(fp)
@ -123,7 +123,8 @@ pub fn read_file(path string) ?string {
// file_size returns the size of the file located in `path`.
pub fn file_size(path string) int {
mut s := C.stat{}
mut s := C.stat{
}
$if windows {
C._wstat(path.to_wide(), voidptr(&s))
} $else {
@ -141,18 +142,17 @@ pub fn mv(old, new string) {
}
fn C.CopyFile(&u32, &u32, int) int
// TODO implement actual cp for linux
pub fn cp(old, new string) ?bool {
$if windows {
_old := old.replace('/', '\\')
_new := new.replace('/', '\\')
C.CopyFile(_old.to_wide(), _new.to_wide(), false)
result := C.GetLastError()
if result == 0 {
return true
} else {
}
else {
return error_with_code('failed to copy $old to $new', int(result))
}
} $else {
@ -165,15 +165,11 @@ pub fn cp_r(osource_path, odest_path string, overwrite bool) ?bool{
source_path := os.realpath(osource_path)
dest_path := os.realpath(odest_path)
if !os.exists(source_path) {
return error('Source path doesn\'t exist')
return error("Source path doesn\'t exist")
}
// single file copy
if !os.is_dir(source_path) {
adjasted_path := if os.is_dir(dest_path) {
filepath.join(dest_path, os.filename(source_path))
} else {
dest_path
}
adjasted_path := if os.is_dir(dest_path) { filepath.join(dest_path,os.filename(source_path)) } else { dest_path }
if os.exists(adjasted_path) {
if overwrite {
os.rm(adjasted_path)
@ -182,18 +178,24 @@ pub fn cp_r(osource_path, odest_path string, overwrite bool) ?bool{
return error('Destination file path already exist')
}
}
os.cp(source_path, adjasted_path) or { return error(err) }
os.cp(source_path, adjasted_path)or{
return error(err)
}
return true
}
if !os.is_dir(dest_path) {
return error('Destination path is not a valid directory')
}
files := os.ls(source_path) or { return error(err) }
files := os.ls(source_path)or{
return error(err)
}
for file in files {
sp := filepath.join(source_path,file)
dp := filepath.join(dest_path,file)
if os.is_dir(sp) {
os.mkdir(dp) or { panic(err) }
os.mkdir(dp)or{
panic(err)
}
}
cp_r(sp, dp, overwrite)or{
os.rmdir(dp)
@ -206,7 +208,9 @@ pub fn cp_r(osource_path, odest_path string, overwrite bool) ?bool{
// mv_by_cp first copies the source file, and if it is copied successfully, deletes the source file.
// mv_by_cp may be used when you are not sure that the source and target are on the same mount/partition.
pub fn mv_by_cp(source string, target string) ?bool {
os.cp(source, target) or { return error(err) }
os.cp(source, target)or{
return error(err)
}
os.rm(source)
return true
}
@ -224,13 +228,11 @@ pub fn read_lines(path string) ?[]string {
mut res := []string
mut buf_len := 1024
mut buf := malloc(buf_len)
mode := 'rb'
mut fp := vfopen(path, mode)
if isnil(fp) {
return error('read_lines() failed to open file "$path"')
}
mut buf_index := 0
for C.fgets(buf + buf_index, buf_len - buf_index, fp) != 0 {
len := vstrlen(buf)
@ -270,7 +272,8 @@ fn read_ulines(path string) ?[]ustring {
}
pub fn open(path string) ?File {
mut file := File{}
mut file := File{
}
$if windows {
wpath := path.to_wide()
mode := 'rb'
@ -292,7 +295,8 @@ pub fn open(path string) ?File {
// create creates a file at a specified location and returns a writable `File` object.
pub fn create(path string) ?File {
mut file := File{}
mut file := File{
}
$if windows {
wpath := path.replace('/', '\\').to_wide()
mode := 'wb'
@ -313,7 +317,8 @@ pub fn create(path string) ?File {
}
pub fn open_append(path string) ?File {
mut file := File{}
mut file := File{
}
$if windows {
wpath := path.replace('/', '\\').to_wide()
mode := 'ab'
@ -337,7 +342,6 @@ pub fn (f mut File) write(s string) {
C.fputs(s.str, f.cfile)
// C.fwrite(s.str, 1, s.len, f.cfile)
}
// convert any value to []byte (LittleEndian) and write it
// for example if we have write(7, 4), "07 00 00 00" gets written
// write(0x1234, 2) => "34 12"
@ -352,7 +356,9 @@ pub fn (f mut File) write_bytes_at(data voidptr, size, pos int) {
}
pub fn (f mut File) writeln(s string) {
if !f.opened { return }
if !f.opened {
return
}
// C.fwrite(s.str, 1, s.len, f.cfile)
// ss := s.clone()
// TODO perf
@ -362,25 +368,29 @@ pub fn (f mut File) writeln(s string) {
}
pub fn (f mut File) flush() {
if !f.opened { return }
if !f.opened {
return
}
C.fflush(f.cfile)
}
pub fn (f mut File) close() {
if !f.opened { return }
if !f.opened {
return
}
f.opened = false
C.fflush(f.cfile)
C.fclose(f.cfile)
}
// system starts the specified command, waits for it to complete, and returns its code.
fn vpopen(path string) voidptr {//*C.FILE {
fn vpopen(path string) voidptr {
// *C.FILE {
$if windows {
mode := 'rb'
wpath := path.to_wide()
return C._wpopen(wpath, mode.to_wide())
}
$else {
} $else {
cpath := path.str
return C.popen(cpath, 'r')
}
@ -389,15 +399,15 @@ fn vpopen(path string) voidptr {//*C.FILE {
fn posix_wait4_to_exit_status(waitret int) (int,bool) {
$if windows {
return waitret,false
}
$else {
} $else {
mut ret := 0
mut is_signaled := true
// (see man system, man 2 waitpid: C macro WEXITSTATUS section)
if C.WIFEXITED(waitret) {
ret = C.WEXITSTATUS(waitret)
is_signaled = false
} else if C.WIFSIGNALED( waitret ){
}
else if C.WIFSIGNALED(waitret) {
ret = C.WTERMSIG(waitret)
is_signaled = true
}
@ -408,8 +418,7 @@ fn posix_wait4_to_exit_status(waitret int) (int,bool) {
fn vpclose(f voidptr) int {
$if windows {
return C._pclose(f)
}
$else {
} $else {
ret,_ := posix_wait4_to_exit_status(C.pclose(f))
return ret
}
@ -421,7 +430,6 @@ pub:
output string
// stderr string // TODO
}
// `system` works like `exec()`, but only returns a return code.
pub fn system(cmd string) int {
// if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') {
@ -439,7 +447,6 @@ pub fn system(cmd string) int {
if ret == -1 {
print_c_errno()
}
$if !windows {
pret,is_signaled := posix_wait4_to_exit_status(ret)
if is_signaled {
@ -453,35 +460,77 @@ pub fn system(cmd string) int {
pub fn sigint_to_signal_name(si int) string {
// POSIX signals:
match si {
1 {return 'SIGHUP'}
2 {return 'SIGINT'}
3 {return 'SIGQUIT'}
4 {return 'SIGILL'}
6 {return 'SIGABRT'}
8 {return 'SIGFPE'}
9 {return 'SIGKILL'}
11 {return 'SIGSEGV'}
13 {return 'SIGPIPE'}
14 {return 'SIGALRM'}
15 {return 'SIGTERM'}
else { }
1 {
return 'SIGHUP'
}
2 {
return 'SIGINT'
}
3 {
return 'SIGQUIT'
}
4 {
return 'SIGILL'
}
6 {
return 'SIGABRT'
}
8 {
return 'SIGFPE'
}
9 {
return 'SIGKILL'
}
11 {
return 'SIGSEGV'
}
13 {
return 'SIGPIPE'
}
14 {
return 'SIGALRM'
}
15 {
return 'SIGTERM'
}
else {
}}
$if linux {
// From `man 7 signal` on linux:
match si {
30,10,16{ return 'SIGUSR1'}
31,12,17{ return 'SIGUSR2'}
20,17,18{ return 'SIGCHLD'}
19,18,25{ return 'SIGCONT'}
17,19,23{ return 'SIGSTOP'}
18,20,24{ return 'SIGTSTP'}
21,21,26{ return 'SIGTTIN'}
22,22,27{ return 'SIGTTOU'}
///////////////////////////////
5{ return 'SIGTRAP'}
7{ return 'SIGBUS' }
else {}
30, 10, 16 {
return 'SIGUSR1'
}
31, 12, 17 {
return 'SIGUSR2'
}
20, 17, 18 {
return 'SIGCHLD'
}
19, 18, 25 {
return 'SIGCONT'
}
17, 19, 23 {
return 'SIGSTOP'
}
18, 20, 24 {
return 'SIGTSTP'
}
21, 21, 26 {
return 'SIGTTIN'
}
22, 22, 27 {
return 'SIGTTOU'
}
// /////////////////////////////
5 {
return 'SIGTRAP'
}
7 {
return 'SIGBUS'
}
else {
}}
}
return 'unknown'
}
@ -544,30 +593,24 @@ pub fn file_exists(_path string) bool {
pub fn rm(path string) {
$if windows {
C._wremove(path.to_wide())
}
$else {
} $else {
C.remove(path.str)
}
// C.unlink(path.cstr())
}
// rmdir removes a specified directory.
pub fn rmdir(path string) {
$if !windows {
C.rmdir(path.str)
}
$else {
} $else {
C.RemoveDirectory(path.to_wide())
}
}
fn print_c_errno() {
// C.printf('errno=%d err="%s"\n', C.errno, C.strerror(C.errno))
}
pub fn ext(path string) string {
pos := path.last_index('.') or {
return ''
@ -575,7 +618,6 @@ pub fn ext(path string) string {
return path[pos..]
}
// dir returns all but the last element of path, typically the path's directory.
pub fn dir(path string) string {
if path == '.' {
@ -594,7 +636,6 @@ fn path_sans_ext(path string) string {
return path[..pos]
}
pub fn basedir(path string) string {
pos := path.last_index(path_separator) or {
return path
@ -611,8 +652,7 @@ pub fn get_line() string {
str := get_raw_line()
$if windows {
return str.trim_right('\r\n')
}
$else {
} $else {
return str.trim_right('\n')
}
}
@ -630,7 +670,9 @@ pub fn get_raw_line() string {
}
res := C.fgetws(&u16(buf), max_line_chars, C.stdin)
len := C.wcslen(&u16(buf))
if !isnil(res) { return string_from_wide2( &u16(buf), len ) }
if !isnil(res) {
return string_from_wide2(&u16(buf), len)
}
return ''
} $else {
max := size_t(256)
@ -746,7 +788,8 @@ pub fn on_segfault(f voidptr) {
return
}
$if macos {
mut sa := C.sigaction{}
mut sa := C.sigaction{
}
C.memset(&sa, 0, sizeof(sigaction))
C.sigemptyset(&sa.sa_mask)
sa.sa_sigaction = f
@ -756,10 +799,12 @@ pub fn on_segfault(f voidptr) {
}
fn C.getpid() int
fn C.proc_pidpath(int, byteptr, int) int
fn C.readlink() int
// executable returns the path name of the executable that started the current
// process.
pub fn executable() string {
@ -830,7 +875,6 @@ pub fn dir_exists(path string) bool {
panic('use os.is_dir()')
// return false
}
// is_dir returns a boolean indicating whether the given path is a directory.
pub fn is_dir(path string) bool {
$if windows {
@ -843,9 +887,9 @@ pub fn is_dir(path string) bool {
return true
}
return false
} $else {
statbuf := C.stat{
}
$else {
statbuf := C.stat{}
if C.stat(path.str, &statbuf) != 0 {
return false
}
@ -859,7 +903,8 @@ pub fn is_link(path string) bool {
$if windows {
return false // TODO
} $else {
statbuf := C.stat{}
statbuf := C.stat{
}
if C.lstat(path.str, &statbuf) != 0 {
return false
}
@ -871,8 +916,7 @@ pub fn is_link(path string) bool {
pub fn chdir(path string) {
$if windows {
C._wchdir(path.to_wide())
}
$else {
} $else {
C.chdir(path.str)
}
}
@ -886,8 +930,7 @@ pub fn getwd() string {
return ''
}
return string_from_wide(buf)
}
$else {
} $else {
buf := calloc(512)
if C.getcwd(buf, 512) == 0 {
return ''
@ -923,7 +966,9 @@ pub fn walk_ext(path, ext string) []string {
if !os.is_dir(path) {
return []
}
mut files := os.ls(path) or { panic(err) }
mut files := os.ls(path)or{
panic(err)
}
mut res := []string
separator := if path.ends_with(path_separator) { '' } else { path_separator }
for i, file in files {
@ -947,7 +992,9 @@ pub fn walk(path string, fnc fn(path string)) {
if !os.is_dir(path) {
return
}
mut files := os.ls(path) or { panic(err) }
mut files := os.ls(path)or{
panic(err)
}
for file in files {
p := path + os.path_separator + file
if os.is_dir(p) {
@ -964,10 +1011,12 @@ pub fn signal(signum int, handler voidptr) {
C.signal(signum, handler)
}
fn C.fork() int
fn C.wait() int
pub fn fork() int {
mut pid := -1
$if !windows {
@ -991,7 +1040,8 @@ pub fn wait() int {
}
pub fn file_last_mod_unix(path string) int {
attr := C.stat{}
attr := C.stat{
}
// # struct stat attr;
C.stat(path.str, &attr)
// # stat(path.str, &attr);
@ -999,7 +1049,6 @@ pub fn file_last_mod_unix(path string) int {
// # return attr.st_mtime ;
}
pub fn log(s string) {
println('os.log: ' + s)
}
@ -1023,7 +1072,9 @@ pub fn mkdir_all(path string) {
for subdir in path.split(os.path_separator) {
p += subdir + os.path_separator
if !os.is_dir(p) {
os.mkdir(p) or { panic(err) }
os.mkdir(p)or{
panic(err)
}
}
}
}
@ -1037,10 +1088,14 @@ pub fn join(base string, dirs ...string) string {
pub fn tmpdir() string {
mut path := os.getenv('TMPDIR')
$if linux {
if path == '' { path = '/tmp' }
if path == '' {
path = '/tmp'
}
}
$if freebsd {
if path == '' { path = '/tmp' }
if path == '' {
path = '/tmp'
}
}
$if macos {
/*
@ -1049,7 +1104,9 @@ pub fn tmpdir() string {
path = C.NSTemporaryDirectory()
}
*/
if path == '' { path = '/tmp' }
if path == '' {
path = '/tmp'
}
}
$if windows {
if path == '' {
@ -1057,14 +1114,17 @@ pub fn tmpdir() string {
// https://doc.qt.io/qt-5/qdir.html#tempPath
// https://github.com/qt/qtbase/blob/e164d61ca8263fc4b46fdd916e1ea77c7dd2b735/src/corelib/io/qfilesystemengine_win.cpp#L1275
path = os.getenv('TEMP')
if path == '' { path = os.getenv('TMP') }
if path == '' { path = 'C:/tmp' }
if path == '' {
path = os.getenv('TMP')
}
if path == '' {
path = 'C:/tmp'
}
}
}
return path
}
pub fn chmod(path string, mode int) {
C.chmod(path.str, mode)
}
@ -1072,3 +1132,4 @@ pub fn chmod(path string, mode int) {
pub const (
wd_at_startup = getwd()
)

View File

@ -2,7 +2,6 @@ module os
#include <dirent.h>
#include <unistd.h>
pub const (
path_separator = '/'
)
@ -15,7 +14,6 @@ pub fn init_os_args(argc int, argv &byteptr) []string {
return args
}
// get_error_msg return error code representation in string.
pub fn get_error_msg(code int) string {
ptr_text := C.strerror(code) // voidptr?
@ -63,7 +61,9 @@ pub fn is_dir(path string) bool {
// mkdir creates a new directory with the specified path.
pub fn mkdir(path string) ?bool {
if path == '.' { return true }
if path == '.' {
return true
}
apath := os.realpath(path)
r := C.mkdir(apath.str, 511)
if r == -1 {
@ -97,3 +97,4 @@ pub fn exec(cmd string) ?Result {
exit_code: exit_code
}
}

View File

@ -1,21 +1,23 @@
module rand
// Ported from http://www.pcg-random.org/download.html
// and https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c
pub struct Pcg32 {
mut:
state u64
inc u64
}
/**
* new_pcg32 - a Pcg32 PRNG generator
* @param initstate - the initial state of the PRNG.
* @param initseq - the stream/step of the PRNG.
* @return a new Pcg32 PRNG instance
*/
pub fn new_pcg32(initstate u64, initseq u64) Pcg32 {
mut rng := Pcg32{}
mut rng := Pcg32{
}
rng.state = u64(0)
rng.inc = (initseq<<u64(1)) | u64(1)
rng.next()
@ -23,23 +25,31 @@ pub fn new_pcg32(initstate u64, initseq u64) Pcg32 {
rng.next()
return rng
}
/**
* Pcg32.next - update the PRNG state and get back the next random number
* @return the generated pseudo random number
*/
[inline] pub fn (rng mut Pcg32) next() u32 {
[inline]
pub fn (rng mut Pcg32) next() u32 {
oldstate := rng.state
rng.state = oldstate * (6364136223846793005) + rng.inc
xorshifted := u32(((oldstate>>u64(18)) ^ oldstate)>>u64(27))
rot := u32(oldstate>>u64(59))
return ((xorshifted>>rot) | (xorshifted<<((-rot) & u32(31))))
}
/**
* Pcg32.bounded_next - update the PRNG state. Get the next number < bound
* @param bound - the returned random number will be < bound
* @return the generated pseudo random number
*/
[inline] pub fn (rng mut Pcg32) bounded_next(bound u32) u32 {
[inline]
pub fn (rng mut Pcg32) bounded_next(bound u32) u32 {
// To avoid bias, we need to make the range of the RNG a multiple of
// bound, which we do by dropping output less than a threshold.
threshold := (-bound % bound)
@ -56,3 +66,4 @@ pub fn new_pcg32(initstate u64, initseq u64) Pcg32 {
}
return u32(0)
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module rand
pub fn seed(s int) {
@ -13,7 +12,6 @@ pub fn next(max int) int {
}
fn C.rand() int
/**
* rand_r - reentrant pseudo-random number generator
*
@ -21,6 +19,9 @@ fn C.rand() int
*
* @return a value between 0 and C.RAND_MAX (inclusive)
*/
pub fn rand_r(seed &int) int {
unsafe{
mut rs := seed
@ -29,3 +30,4 @@ pub fn rand_r(seed &int) int {
return ns & 0x7fffffff
}
}

View File

@ -1,36 +1,46 @@
module rand
// Ported from http://xoshiro.di.unimi.it/splitmix64.c
struct Splitmix64 {
mut:
state u64
}
/**
* new_splitmix64 - a Splitmix64 PRNG generator
* @param seed the initial seed of the PRNG.
* @return a new Splitmix64 PRNG instance
*/
pub fn new_splitmix64(seed u64) Splitmix64 {
return Splitmix64{ seed }
return Splitmix64{
seed}
}
/**
* Splitmix64.next - update the PRNG state and get back the next random number
* @return the generated pseudo random number
*/
[inline] pub fn (rng mut Splitmix64) next() u64 {
[inline]
pub fn (rng mut Splitmix64) next() u64 {
rng.state += (0x9e3779b97f4a7c15)
mut z := rng.state
z = (z ^ ((z>>u64(30)))) * (0xbf58476d1ce4e5b9)
z = (z ^ ((z>>u64(27)))) * (0x94d049bb133111eb)
return z ^ (z>>(31))
}
/**
* Splitmix64.bounded_next - Get the next random number < bound
* @param bound - the returned random number will be < bound
* @return the generated pseudo random number
*/
[inline] pub fn (rng mut Splitmix64) bounded_next(bound u64) u64 {
[inline]
pub fn (rng mut Splitmix64) bounded_next(bound u64) u64 {
threshold := -bound % bound
for {
r := rng.next()
@ -40,3 +50,4 @@ pub fn new_splitmix64(seed u64) Splitmix64 {
}
return u64(0)
}

View File

@ -19,13 +19,13 @@
*
**********************************************************************/
module strconv
/**********************************************************************
*
* 96 bit operation utilities
* Note: when u128 will be available these function can be refactored
*
**********************************************************************/
// right logical shift 96 bit
fn lsr96(s2 u32, s1 u32, s0 u32) (u32,u32,u32) {
mut r0 := u32(0)
@ -87,6 +87,8 @@ fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) {
* Constants
*
**********************************************************************/
const (
//
// f64 constants
@ -96,7 +98,6 @@ const (
DOUBLE_MINUS_ZERO = 0x8000000000000000
DOUBLE_PLUS_INFINITY = 0x7FF0000000000000
DOUBLE_MINUS_INFINITY = 0xFFF0000000000000
//
// parser state machine states
//
@ -110,7 +111,6 @@ const (
FSM_H = 7
FSM_I = 8
FSM_STOP = 9
//
// Possible parser return values.
//
@ -119,7 +119,6 @@ const (
PARSER_MZERO = 2 // number is negative, module smaller
PARSER_PINF = 3 // number is higher than +HUGE_VAL
PARSER_MINF = 4 // number is lower than -HUGE_VAL
//
// char constants
// Note: Modify these if working with non-ASCII encoding
@ -129,18 +128,15 @@ const (
MINUS = `-`
ZERO = `0`
NINE = `9`
TEN = u32(10)
)
/**********************************************************************
*
* Utility
*
**********************************************************************/
// NOTE: Modify these if working with non-ASCII encoding
// NOTE: Modify these if working with non-ASCII encoding
fn is_digit(x byte) bool {
return (x >= ZERO && x <= NINE) == true
}
@ -177,22 +173,21 @@ pub fn strlong(x f64) string {
* Support struct
*
**********************************************************************/
// The structure is filled by parser, then given to converter.
pub struct PrepNumber
{
pub struct PrepNumber {
pub mut:
negative bool=false // 0 if positive number, 1 if negative
exponent int=0 // power of 10 exponent
mantissa u64=u64(0) // integer mantissa
}
/**********************************************************************
*
* String parser
* NOTE: #TOFIX need one char after the last char of the number
*
**********************************************************************/
// parser return a support struct with all the parsing information for the converter
fn parser(s string) (int,PrepNumber) {
mut state := FSM_A
@ -202,60 +197,62 @@ fn parser(s string ) (int,PrepNumber) {
mut expneg := false
mut expexp := 0
mut i := 0
mut pn := PrepNumber{}
mut pn := PrepNumber{
}
for state != FSM_STOP {
match state {
// skip starting spaces
FSM_A {
if is_space(c) == true {
c = s[i++]
} else {
}
else {
state = FSM_B
}
}
// check for the sign or point
FSM_B {
state = FSM_C
if c == PLUS {
c = s[i++]
} else if c == MINUS {
}
else if c == MINUS {
pn.negative = true
c = s[i++]
} else if is_digit(c) {}
else if c == DPOINT {}
}
else if is_digit(c) {
}
else if c == DPOINT {
}
else {
state = FSM_STOP
}
}
// skip the inital zeros
FSM_C {
if c == ZERO { c = s[i++] }
if c == ZERO {
c = s[i++]
}
else if c == DPOINT {
c = s[i++]
state = FSM_D
} else {
}
else {
state = FSM_E
}
}
// reading leading zeros in the fractional part of mantissa
FSM_D {
if c == ZERO {
c = s[i++]
if pn.exponent > -2147483647 { pn.exponent-- }
if pn.exponent > -2147483647 {
pn.exponent--
}
}
else {
state = FSM_F
}
}
// reading integer part of mantissa
FSM_E {
if is_digit(c) {
@ -264,10 +261,10 @@ fn parser(s string ) (int,PrepNumber) {
pn.mantissa += u64(c - ZERO)
digx++
}
else if pn.exponent < 2147483647 { pn.exponent++ }
else if pn.exponent < 2147483647 {
pn.exponent++
}
c = s[i++]
}
else if c == DPOINT {
c = s[i++]
@ -277,7 +274,6 @@ fn parser(s string ) (int,PrepNumber) {
state = FSM_F
}
}
// reading fractional part of mantissa
FSM_F {
if is_digit(c) {
@ -287,7 +283,6 @@ fn parser(s string ) (int,PrepNumber) {
pn.exponent--
digx++
}
c = s[i++]
}
else if is_exp(c) {
@ -298,18 +293,17 @@ fn parser(s string ) (int,PrepNumber) {
state = FSM_G
}
}
// reading sign of exponent
FSM_G {
if c == PLUS {
c = s[i++]
} else if c == MINUS {
}
else if c == MINUS {
expneg = true
c = s[i++]
}
state = FSM_H
}
// skipping leading zeros of exponent
FSM_H {
if c == ZERO {
@ -319,7 +313,6 @@ fn parser(s string ) (int,PrepNumber) {
state = FSM_I
}
}
// reading exponent digits
FSM_I {
if is_digit(c) {
@ -327,63 +320,59 @@ fn parser(s string ) (int,PrepNumber) {
expexp *= 10
expexp += int(c - ZERO)
}
c = s[i++]
}
else {
state = FSM_STOP
}
}
else {}
}
else {
}}
// C.printf("len: %d i: %d str: %s \n",s.len,i,s[..i])
if i >= s.len {
state = FSM_STOP
}
}
if expneg {
expexp = -expexp
}
pn.exponent += expexp
if pn.mantissa == 0 {
if pn.negative {
result = PARSER_MZERO
} else {
}
else {
result = PARSER_PZERO
}
}
else if (pn.exponent > 309) {
if pn.negative {
result = PARSER_MINF
} else {
}
else {
result = PARSER_PINF
}
}
else if pn.exponent < -328 {
if pn.negative {
result = PARSER_MZERO
} else {
}
else {
result = PARSER_PZERO
}
}
return result,pn
}
/**********************************************************************
*
* Converter to the bit form of the f64 number
*
**********************************************************************/
// converter return a u64 with the bit image of the f64 number
fn converter(pn mut PrepNumber) u64 {
mut binexp := 92
mut s2 := u32(0) // 96-bit precision integer
mut s1 := u32(0)
mut s0 := u32(0)
@ -393,25 +382,19 @@ fn converter(pn mut PrepNumber) u64 {
mut r2 := u32(0) // 96-bit precision integer
mut r1 := u32(0)
mut r0 := u32(0)
mask28 := u32(0xF<<28)
mut result := u64(0)
// working on 3 u32 to have 96 bit precision
s0 = u32(pn.mantissa & u64(0x00000000FFFFFFFF))
s1 = u32(pn.mantissa>>32)
s2 = u32(0)
// so we take the decimal exponent off
for pn.exponent > 0 {
q2,q1,q0 = lsl96(s2, s1, s0) // q = s * 2
r2,r1,r0 = lsl96(q2, q1, q0) // r = s * 4 <=> q * 2
s2,s1,s0 = lsl96(r2, r1, r0) // s = s * 8 <=> r * 2
s2,s1,s0 = add96(s2, s1, s0, q2, q1, q0) // s = (s * 8) + (s * 2) <=> s*10
pn.exponent--
for (s2 & mask28) != 0 {
q2,q1,q0 = lsr96(s2, s1, s0)
binexp++
@ -419,9 +402,7 @@ fn converter(pn mut PrepNumber) u64 {
s1 = q1
s0 = q0
}
}
for pn.exponent < 0 {
for !((s2 & (u32(1)<<31)) != 0) {
q2,q1,q0 = lsl96(s2, s1, s0)
@ -429,9 +410,7 @@ fn converter(pn mut PrepNumber) u64 {
s2 = q2
s1 = q1
s0 = q0
}
q2 = s2 / TEN
r1 = s2 % TEN
r2 = (s1>>8) | (r1<<24)
@ -447,13 +426,9 @@ fn converter(pn mut PrepNumber) u64 {
s2 = q2
s1 = q1
s0 = q0
pn.exponent++
}
// C.printf("mantissa before normalization: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp)
// normalization, the 28 bit in s2 must the leftest one in the variable
if s2 != 0 || s1 != 0 || s0 != 0 {
for (s2 & mask28) == 0 {
@ -464,7 +439,6 @@ fn converter(pn mut PrepNumber) u64 {
s0 = q0
}
}
// rounding if needed
/*
* "round half to even" algorithm
@ -477,7 +451,6 @@ fn converter(pn mut PrepNumber) u64 {
* If bit 53 is 0, round down
* If bit 53 is 1, round up
*/
/* test case 1 complete
s2=0x1FFFFFFF
s1=0xFFFFFF80
@ -497,18 +470,17 @@ fn converter(pn mut PrepNumber) u64 {
*/
// C.printf("mantissa before rounding: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp)
// s1 => 0xFFFFFFxx only F are rapresented
nbit := 7
check_round_bit := u32(1)<<u32(nbit)
check_round_mask := u32(0xFFFFFFFF)<<u32(nbit)
if (s1 & check_round_bit) != 0 {
// C.printf("need round!! cehck mask: %08x\n", s1 & ~check_round_mask )
if (s1 & ~check_round_mask) != 0 {
// C.printf("Add 1!\n")
s2,s1,s0 = add96(s2, s1, s0, 0, check_round_bit, 0)
} else {
}
else {
// C.printf("All 0!\n")
if (s1 & (check_round_bit<<u32(1))) != 0 {
// C.printf("Add 1 form -1 bit control!\n")
@ -518,7 +490,6 @@ fn converter(pn mut PrepNumber) u64 {
s1 = s1 & check_round_mask
s0 = u32(0)
}
// recheck normalization
if s2 & (mask28<<u32(1)) != 0 {
// C.printf("Renormalize!!")
@ -528,20 +499,17 @@ fn converter(pn mut PrepNumber) u64 {
s1 = q1
s0 = q0
}
// tmp := ( u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8)
// C.printf("mantissa after rounding : %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp)
// C.printf("Tmp result: %016x\n",tmp)
// end rounding
// offset the binary exponent IEEE 754
binexp += 1023
if binexp > 2046 {
if pn.negative {
result = DOUBLE_MINUS_INFINITY
} else {
}
else {
result = DOUBLE_PLUS_INFINITY
}
}
@ -553,14 +521,12 @@ fn converter(pn mut PrepNumber) u64 {
else if s2 != 0 {
mut q := u64(0)
binexs2 := u64(binexp)<<52
q = (u64(s2 & ~mask28)<<24) | ((u64(s1) + u64(128))>>8) | binexs2
if pn.negative {
q |= (u64(1)<<63)
}
result = q
}
return result
}
@ -569,18 +535,17 @@ fn converter(pn mut PrepNumber) u64 {
* Public functions
*
**********************************************************************/
// atof64 return a f64 from a string doing a parsing operation
pub fn atof64(s string) f64 {
mut pn := PrepNumber{}
mut pn := PrepNumber{
}
mut res_parsing := 0
mut result := f64(0)
result = f64(0.0)
mut res_ptr := *u64(&result)
res_parsing,pn = parser(s + ' ') // TODO: need an extra char for now
// println(pn)
match res_parsing {
PARSER_OK {
*res_ptr = converter(mut pn)
@ -597,7 +562,8 @@ pub fn atof64(s string) f64 {
PARSER_MINF {
*res_ptr = DOUBLE_MINUS_INFINITY
}
else {}
}
else {
}}
return result
}

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,9 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
// TODO: use optionals, or some way to return default with error.
module strconv
const (
// int_size is the size in bits of an int or uint value.
// int_size = 32 << (~u32(0) >> 63)
@ -24,17 +21,16 @@ fn byte_to_lower(c byte) byte {
pub fn common_parse_uint(s string, _base int, _bit_size int, error_on_non_digit bool, error_on_high_digit bool) u64 {
mut bit_size := _bit_size
mut base := _base
if s.len < 1 || !underscore_ok(s) {
// return error('parse_uint: syntax error $s')
return u64(0)
}
base0 := base == 0
mut start_index := 0
if 2 <= base && base <= 36 {
// valid base; nothing to do
} else if base == 0 {
}
else if base == 0 {
// Look for octal, hex prefix.
base = 10
if s[0] == `0` {
@ -60,27 +56,22 @@ pub fn common_parse_uint(s string, _base int, _bit_size int, error_on_non_digit
start_index++
}
}
} else {
}
else {
// return error('parse_uint: base error $s - $base')
return u64(0)
}
if bit_size == 0 {
bit_size = int_size
} else if bit_size < 0 || bit_size > 64 {
}
else if bit_size < 0 || bit_size > 64 {
// return error('parse_uint: bitsize error $s - $bit_size')
return u64(0)
}
// Cutoff is the smallest number such that cutoff*base > maxUint64.
// Use compile-time constants for common cases.
cutoff := max_u64 / u64(base) + u64(1)
max_val := if bit_size == 64 {
max_u64
} else {
(u64(1)<<u64(bit_size))-u64(1)
}
max_val := if bit_size == 64 { max_u64 } else { (u64(1)<<u64(bit_size)) - u64(1) }
mut underscores := false
mut n := u64(0)
for i in start_index .. s.len {
@ -92,13 +83,18 @@ pub fn common_parse_uint(s string, _base int, _bit_size int, error_on_non_digit
underscores = true
continue
}
else if `0` <= c && c <= `9` { d = c - `0` }
else if `a` <= cl && cl <= `z` { d = cl - `a` + 10 }
else if `0` <= c && c <= `9` {
d = c - `0`
}
else if `a` <= cl && cl <= `z` {
d = cl - `a` + 10
}
else {
if error_on_non_digit {
// return error('parse_uint: syntax error $s')
return u64(0)
} else {
}
else {
break
}
}
@ -106,7 +102,8 @@ pub fn common_parse_uint(s string, _base int, _bit_size int, error_on_non_digit
if error_on_high_digit {
// return error('parse_uint: syntax error $s')
return u64(0)
} else {
}
else {
break
}
}
@ -141,7 +138,6 @@ pub fn parse_uint(s string, _base int, _bit_size int) u64 {
pub fn common_parse_int(_s string, base int, _bit_size int, error_on_non_digit bool, error_on_high_digit bool) i64 {
mut s := _s
mut bit_size := _bit_size
if s.len < 1 {
// return error('parse_int: syntax error $s')
return i64(0)
@ -150,11 +146,11 @@ pub fn common_parse_int(_s string, base int, _bit_size int, error_on_non_digit b
mut neg := false
if s[0] == `+` {
s = s[1..]
} else if s[0] == `-` {
}
else if s[0] == `-` {
neg = true
s = s[1..]
}
// Convert unsigned and check range.
// un := parse_uint(s, base, bit_size) or {
// return i64(0)
@ -163,11 +159,9 @@ pub fn common_parse_int(_s string, base int, _bit_size int, error_on_non_digit b
if un == 0 {
return i64(0)
}
if bit_size == 0 {
bit_size = int_size
}
// TODO: check should u64(bit_size-1) be size of int (32)?
cutoff := u64(1)<<u64(bit_size - 1)
if !neg && un >= cutoff {
@ -178,9 +172,9 @@ pub fn common_parse_int(_s string, base int, _bit_size int, error_on_non_digit b
// return error('parse_int: range error $s0')
return -i64(cutoff)
}
return if neg { -i64(un) } else { i64(un) }
}
// parse_int interprets a string s in the given base (0, 2 to 36) and
// bit size (0 to 64) and returns the corresponding value i.
//
@ -199,8 +193,7 @@ pub fn parse_int(_s string, base int, _bit_size int) i64 {
// atoi is equivalent to parse_int(s, 10, 0), converted to type int.
pub fn atoi(s string) int {
if (int_size == 32 && (0 < s.len && s.len < 10)) ||
(int_size == 64 && (0 < s.len && s.len < 19)) {
if (int_size == 32 && (0 < s.len && s.len < 10)) || (int_size == 64 && (0 < s.len && s.len < 19)) {
// Fast path for small integers that fit int type.
mut start_idx := 0
if s[0] == `-` || s[0] == `+` {
@ -210,7 +203,6 @@ pub fn atoi(s string) int {
return 0
}
}
mut n := 0
for i in start_idx .. s.len {
ch := s[i] - `0`
@ -220,13 +212,10 @@ pub fn atoi(s string) int {
}
n = n * 10 + int(ch)
}
return if s[0] == `-` { -n } else { n }
}
// Slow path for invalid, big, or underscored integers.
int64 := parse_int(s, 10, 0)
return int(int64)
}
@ -241,26 +230,21 @@ fn underscore_ok(s string) bool {
// ! for none of the above.
mut saw := `^`
mut i := 0
// Optional sign.
if s.len >= 1 && (s[0] == `-` || s[0] == `+`) {
i++
}
// Optional base prefix.
mut hex := false
if s.len-i >= 2 && s[i] == `0` &&
(byte_to_lower(s[i+1]) == `b` || byte_to_lower(s[i+1]) == `o` || byte_to_lower(s[i+1]) == `x`) {
if s.len - i >= 2 && s[i] == `0` && (byte_to_lower(s[i + 1]) == `b` || byte_to_lower(s[i + 1]) == `o` || byte_to_lower(s[i + 1]) == `x`) {
saw = `0` // base prefix counts as a digit for "underscore as digit separator"
hex = byte_to_lower(s[i + 1]) == `x`
i += 2
}
// Number proper.
for ; i < s.len; i++ {
// Digits are always okay.
if (`0` <= s[i] && s[i] <= `9`) ||
(hex && `a` <= byte_to_lower(s[i]) && byte_to_lower(s[i]) <= `f`) {
if (`0` <= s[i] && s[i] <= `9`) || (hex && `a` <= byte_to_lower(s[i]) && byte_to_lower(s[i]) <= `f`) {
saw = `0`
continue
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module strings
pub struct Builder {
@ -54,7 +53,10 @@ pub fn (b mut Builder) str() string {
}
pub fn (b mut Builder) free() {
unsafe{ free(b.buf.data) }
unsafe{
free(b.buf.data)
}
b.buf = make(0, b.initial_size, 1)
b.len = 0
}

View File

@ -1,7 +1,5 @@
module strings
// #-js
// use levenshtein distance algorithm to calculate
// the distance between between two strings (lower is closer)
pub fn levenshtein_distance(a, b string) int {
@ -66,3 +64,4 @@ pub fn dice_coefficient(s1, s2 string) f32 {
}
return (2.0 * intersection_size) / (f32(a.len) + f32(b.len) - 2)
}

View File

@ -19,19 +19,11 @@ fn supports_escape_sequences(fd int) bool {
}
// ////////////////////////////////////////////
pub fn ok_message(s string) string {
return if can_show_color_on_stdout() {
green( s )
}else{
s
}
return if can_show_color_on_stdout() { green(s) } else { s }
}
pub fn fail_message(s string) string {
return if can_show_color_on_stdout() {
red( s )
}else{
s
}
return if can_show_color_on_stdout() { red(s) } else { s }
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module term
pub fn format(msg, open, close string) string {
@ -21,19 +20,11 @@ pub fn bg_rgb(r, g, b int, msg string) string {
}
pub fn hex(hex int, msg string) string {
return format_rgb(
hex >> 16,
hex >> 8 & 0xFF,
hex & 0xFF,
msg, '38', '39')
return format_rgb(hex>>16, hex>>8 & 0xFF, hex & 0xFF, msg, '38', '39')
}
pub fn bg_hex(hex int, msg string) string {
return format_rgb(
hex >> 16,
hex >> 8 & 0xFF,
hex & 0xFF,
msg, '48', '49')
return format_rgb(hex>>16, hex>>8 & 0xFF, hex & 0xFF, msg, '48', '49')
}
pub fn bg_black(msg string) string {
@ -199,3 +190,4 @@ pub fn yellow(msg string) string {
pub fn bright_yellow(msg string) string {
return format(msg, '93', '39')
}

View File

@ -1,6 +1,5 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module term

View File

@ -1,19 +1,15 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module term
// Sources for ANSI Control Sequences
// https://github.com/RajeshPatkarInstitute/Panim
// https://www.gnu.org/software/screen/manual/html_node/Control-Sequences.html
// https://en.wikipedia.org/wiki/ANSI_escape_code
// Support for Windows
// https://en.wikipedia.org/wiki/ANSI.SYS
// #include <windows.h>
// C.SetConsoleMode(ENABLE_VIRTUAL_TERMINAL_INPUT)
// Setting cursor to the given position
// x is the x coordinate
// y is the y coordinate
@ -54,23 +50,19 @@ pub fn erase_display(t string) {
print('\x1b[' + t + 'J')
}
pub fn erase_toend()
{
pub fn erase_toend() {
erase_display('0')
}
pub fn erase_tobeg()
{
pub fn erase_tobeg() {
erase_display('1')
}
pub fn erase_clear()
{
pub fn erase_clear() {
erase_display('2')
}
pub fn erase_del_clear()
{
pub fn erase_del_clear() {
erase_display('3')
}
@ -82,29 +74,25 @@ pub fn erase_line(t string) {
print('\x1b[' + t + 'K')
}
pub fn erase_line_toend()
{
pub fn erase_line_toend() {
erase_line('0')
}
pub fn erase_line_tobeg()
{
pub fn erase_line_tobeg() {
erase_line('1')
}
pub fn erase_line_clear()
{
pub fn erase_line_clear() {
erase_line('2')
}
// Will make cursor appear if not visible
pub fn show_cursor()
{
pub fn show_cursor() {
print('\x1b[?25h')
}
// Will make cursor invisible
pub fn hide_cursor()
{
pub fn hide_cursor() {
print('\x1b[?25l')
}

View File

@ -1,22 +1,18 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module time
import rand
const (
days_string = 'MonTueWedThuFriSatSun'
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
months_string = 'JanFebMarAprMayJunJulAugSepOctNovDec'
// The unsigned zero year for internal calculations.
// Must be 1 mod 400, and times before it will not compute correctly,
// but otherwise can be changed at will.
absolute_zero_year = i64(-292277022399)
seconds_per_minute = 60
seconds_per_hour = 60 * seconds_per_minute
seconds_per_day = 24 * seconds_per_hour
@ -24,26 +20,10 @@ const (
days_per_400_years = 365 * 400 + 97
days_per_100_years = 365 * 100 + 24
days_per_4_years = 365 * 4 + 1
days_before = [
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
]
days_before = [0, 31, 31 + 28, 31 + 28 + 31, 31 + 28 + 31 + 30, 31 + 28 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, ]
)
#include <time.h>
pub struct Time {
pub:
year int
@ -84,10 +64,13 @@ pub enum FormatDelimiter {
fn C.localtime(int) &C.tm
fn remove_me_when_c_bug_is_fixed() { // TODO
fn remove_me_when_c_bug_is_fixed() {
// TODO
}
pub struct C.time_t {}
pub struct C.time_t {
}
struct C.tm {
tm_year int
@ -100,6 +83,7 @@ struct C.tm {
fn C.time(int) C.time_t
pub fn now() Time {
t := C.time(0)
mut now := &C.tm(0)
@ -113,18 +97,15 @@ pub fn random() Time {
return time.unix(rand_unix)
}
// Based on Go's time package.
// Copyright 2009 The Go Authors.
pub fn unix(abs int) Time {
// Split into time and day.
mut d := abs / seconds_per_day
// Account for 400 year cycles.
mut n := d / days_per_400_years
mut y := 400 * n
d -= days_per_400_years * n
// Cut off 100-year cycles.
// The last cycle has one extra leap year, so on the last day
// of that year, day / days_per_100_years will be 4 instead of 3.
@ -133,14 +114,12 @@ pub fn unix(abs int) Time {
n -= n>>2
y += 100 * n
d -= days_per_100_years * n
// Cut off 4-year cycles.
// The last cycle has a missing leap year, which does not
// affect the computation.
n = d / days_per_4_years
y += 4 * n
d -= days_per_4_years * n
// Cut off years within a 4-year cycle.
// The last year is a leap year, so on the last day of that year,
// day / 365 will be 4 instead of 3. Cut it back down to 3
@ -149,27 +128,31 @@ pub fn unix(abs int) Time {
n -= n>>2
y += n
d -= 365 * n
yday := d
mut day := yday
year := abs / int(3.154e+7) + 1970 // int(i64(y) + absolute_zero_year)
hour := (abs % seconds_per_day) / seconds_per_hour
minute := (abs % seconds_per_hour) / seconds_per_minute
second := (abs % seconds_per_minute)
if is_leap_year(year) {
// Leap year
if day > 31 + 29 - 1 {
// After leap day; pretend it wasn't there.
day--
} else if day == 31+29-1 {
}
else if day == 31 + 29 - 1 {
// Leap day.
day = 29
return Time{year:year, month:2, day:day, hour:hour, minute: minute, second: second}
return Time{
year: year
month: 2
day: day
hour: hour
minute: minute
second: second
}
}
}
// Estimate month on assumption that every month has 31 days.
// The estimate may be too low by at most one month, so adjust.
mut month := day / 31
@ -178,10 +161,10 @@ pub fn unix(abs int) Time {
if day >= end {
month++
begin = end
} else {
}
else {
begin = (days_before[month])
}
month++ // because January is 1
day = day - begin + 1
return Time{
@ -225,7 +208,6 @@ pub fn (t Time) format() string {
return t.get_fmt_str(.hyphen, .hhmm24, .yyyymmdd)
}
pub fn (t Time) smonth() string {
i := t.month - 1
return months_string[i * 3..(i + 1) * 3]
@ -309,7 +291,6 @@ pub fn (t Time) clean12() string {
return t.format()
// return fmt.Sprintf("%4d/%02d/%02d", t.Year(), t.Month(), t.Day()) + " " + hm
}
// `parse` parses time in the following format: "2018-01-27 12:48:34"
pub fn parse(s string) Time {
// println('parse="$s"')
@ -340,7 +321,10 @@ pub fn parse(s string) Time {
}
pub fn new_time(t Time) Time {
return{t | uni: t.calc_unix()}
return {
t |
uni:t.calc_unix()
}
}
pub fn (t &Time) calc_unix() int {
@ -428,25 +412,24 @@ pub struct C.timeval {
pub fn ticks() i64 {
$if windows {
return C.GetTickCount()
} $else {
ts := C.timeval{
}
$else {
ts := C.timeval{}
C.gettimeofday(&ts, 0)
return i64(ts.tv_sec * u64(1000) + (ts.tv_usec / u64(1000)))
}
/*
t := i64(C.mach_absolute_time())
# Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t );
# return (double)(* (uint64_t *) &elapsedNano) / 1000000;
*/
}
pub fn sleep(seconds int) {
$if windows {
C.Sleep(seconds * 1000)
}
$else {
} $else {
C.sleep(seconds)
}
}
@ -454,8 +437,7 @@ pub fn sleep(seconds int) {
pub fn usleep(n int) {
$if windows {
// C._usleep(n)
}
$else {
} $else {
C.usleep(n)
}
}
@ -463,8 +445,7 @@ $else {
pub fn sleep_ms(n int) {
$if windows {
C.Sleep(n)
}
$else {
} $else {
C.usleep(n * 1000)
}
}
@ -492,28 +473,23 @@ pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string {
if fmt_time == .no_time {
return ''
}
tp := if t.hour > 11 {
'p.m.'
} else {
'a.m.'
}
hour := if t.hour > 12 {
t.hour - 12
} else if t.hour == 0 {
12
} else {
t.hour
}
tp := if t.hour > 11 { 'p.m.' } else { 'a.m.' }
hour := if t.hour > 12 { t.hour - 12 } else if t.hour == 0 { 12 } else { t.hour }
return match fmt_time {
.hhmm12 { '$hour:${t.minute:02d} $tp' }
.hhmm24 { '${t.hour:02d}:${t.minute:02d}' }
.hhmmss12 { '$hour:${t.minute:02d}:${t.second:02d} $tp' }
.hhmmss24 { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' }
else { 'unknown enumeration $fmt_time' }
.hhmm12{
'$hour:${t.minute:02d} $tp'
}
.hhmm24{
'${t.hour:02d}:${t.minute:02d}'
}
.hhmmss12{
'$hour:${t.minute:02d}:${t.second:02d} $tp'
}
.hhmmss24{
'${t.hour:02d}:${t.minute:02d}:${t.second:02d}'
}
else {
'unknown enumeration $fmt_time'}}
}
// get_fmt_date_str returns a string for t in a given date format
@ -524,27 +500,49 @@ pub fn (t Time) get_fmt_date_str(fmt_dlmtr FormatDelimiter, fmt_date FormatDate)
if fmt_date == .no_date {
return ''
}
month := '${t.smonth()}'
year := t.year.str()[2..]
return match fmt_date {
.ddmmyy { '${t.day:02d}|${t.month:02d}|$year' }
.ddmmyyyy { '${t.day:02d}|${t.month:02d}|${t.year}' }
.mmddyy { '${t.month:02d}|${t.day:02d}|$year' }
.mmddyyyy { '${t.month:02d}|${t.day:02d}|${t.year}' }
.mmmd { '$month|${t.day}' }
.mmmdd { '$month|${t.day:02d}' }
.mmmddyyyy { '$month|${t.day:02d}|${t.year}' }
.yyyymmdd { '${t.year}|${t.month:02d}|${t.day:02d}' }
else { 'unknown enumeration $fmt_date' }
}.replace('|', match fmt_dlmtr {
.dot { '.' }
.hyphen { '-' }
.slash { '/' }
.space { ' ' }
else { 'unknown enumeration $fmt_dlmtr' }
})
.ddmmyy{
'${t.day:02d}|${t.month:02d}|$year'
}
.ddmmyyyy{
'${t.day:02d}|${t.month:02d}|${t.year}'
}
.mmddyy{
'${t.month:02d}|${t.day:02d}|$year'
}
.mmddyyyy{
'${t.month:02d}|${t.day:02d}|${t.year}'
}
.mmmd{
'$month|${t.day}'
}
.mmmdd{
'$month|${t.day:02d}'
}
.mmmddyyyy{
'$month|${t.day:02d}|${t.year}'
}
.yyyymmdd{
'${t.year}|${t.month:02d}|${t.day:02d}'
}
else {
'unknown enumeration $fmt_date'}}.replace('|', match fmt_dlmtr {
.dot{
'.'
}
.hyphen{
'-'
}
.slash{
'/'
}
.space{
' '
}
else {
'unknown enumeration $fmt_dlmtr'}})
}
// get_fmt_str returns a string for t in a given format for time and date
@ -557,16 +555,18 @@ pub fn (t Time) get_fmt_str(fmt_dlmtr FormatDelimiter, fmt_time FormatTime, fmt_
// saving one function call although it's checked in
// t.get_fmt_time_str(fmt_time) in the beginning
return ''
} else {
}
else {
return t.get_fmt_time_str(fmt_time)
}
} else {
}
else {
if fmt_time != .no_time {
return t.get_fmt_date_str(fmt_dlmtr, fmt_date)
+ ' '
+ t.get_fmt_time_str(fmt_time)
} else {
return t.get_fmt_date_str(fmt_dlmtr, fmt_date) + ' ' + t.get_fmt_time_str(fmt_time)
}
else {
return t.get_fmt_date_str(fmt_dlmtr, fmt_date)
}
}
}

View File

@ -1,18 +1,16 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module tmpl
import os
import strings
const (
STR_START = 'sb.write(\''
STR_END = '\' ) '
STR_START = "sb.write(\'"
STR_END = "\' ) "
)
pub fn compile_template(path string) string {
// lines := os.read_lines(path)
mut html := os.read_file(path)or{
@ -23,18 +21,18 @@ pub fn compile_template(path string) string {
h := os.read_file('header.html')or{
panic('html failed')
}
header = h.replace('\'', '"')
header = h.replace("\'", '"')
html = header + html
}
lines := html.split_into_lines()
mut s := strings.new_builder(1000)
// base := path.all_after('/').replace('.html', '')
s.writeln('
s.writeln("
mut sb := strings.new_builder(${lines.len * 30})
header := \' \' // TODO remove
_ = header
//footer := \'footer\'
')
")
s.writeln(STR_START)
mut in_css := true // false
for _line in lines {
@ -47,7 +45,9 @@ _ = header
}
if line.contains('@if ') {
s.writeln(STR_END)
pos := line.index('@if') or { continue }
pos := line.index('@if') or {
continue
}
s.writeln('if ' + line[pos + 4..] + '{')
s.writeln(STR_START)
}
@ -63,7 +63,9 @@ _ = header
}
else if line.contains('@for') {
s.writeln(STR_END)
pos := line.index('@for') or { continue }
pos := line.index('@for') or {
continue
}
s.writeln('for ' + line[pos + 4..] + '{')
s.writeln(STR_START)
}