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

View File

@ -2,7 +2,6 @@ module benchmark
import time import time
import term import term
/* /*
Example usage of this module: Example usage of this module:
``` ```
@ -26,6 +25,7 @@ println( bmark.total_message('remarks about the benchmark') )
``` ```
*/ */
pub struct Benchmark { pub struct Benchmark {
pub mut: pub mut:
bench_start_time i64 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 { pub fn (b &Benchmark) total_message(msg string) string {
mut tmsg := '$msg \n ok, fail, total = ' + 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}'
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 { if b.verbose {
tmsg = '<=== total time spent $tmsg' 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 { pub fn (b &Benchmark) total_duration() i64 {
return (b.bench_end_time - b.bench_start_time) return (b.bench_end_time - b.bench_start_time)
} }
////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
fn (b &Benchmark) tdiff_in_ms(s string, sticks i64, eticks i64) string { fn (b &Benchmark) tdiff_in_ms(s string, sticks i64, eticks i64) string {
if b.verbose { if b.verbose {
tdiff := (eticks - sticks) tdiff := (eticks - sticks)
@ -112,3 +109,4 @@ fn (b &Benchmark) tdiff_in_ms(s string, sticks i64, eticks i64) string {
fn now() i64 { fn now() i64 {
return time.ticks() return time.ticks()
} }

View File

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

View File

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

View File

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

View File

@ -4,7 +4,6 @@ import (
os os
term term
) )
// //////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////
// NB: The code in this file is organized in layers (between the ///// lines). // NB: The code in this file is organized in layers (between the ///// lines).
// This allows for easier keeping in sync of error/warn functions. // This allows for easier keeping in sync of error/warn functions.
@ -18,6 +17,7 @@ import (
// p.error(msg) // p.error(msg)
// //////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////
fn (p mut Parser) error(s string) { fn (p mut Parser) error(s string) {
// no positioning info, so just assume that the last token was the culprit: // no positioning info, so just assume that the last token was the culprit:
p.error_with_token_index(s, p.token_idx - 1) 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) { fn (p mut Parser) production_error_with_token_index(e string, tokenindex int) {
if p.pref.is_prod { if p.pref.is_prod {
p.error_with_token_index(e, tokenindex) p.error_with_token_index(e, tokenindex)
}else { }
else {
p.warn_with_token_index(e, tokenindex) 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 // 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. // 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') eprintln('${fullpath}:${s.line_nr + 1}:${col}: $final_message')
if s.print_line_on_error && s.nlines > 0 { if s.print_line_on_error && s.nlines > 0 {
context_start_line := imax(0, (s.line_nr - error_context_before)) 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)) 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) line := '${(cline+1):5d}| ' + s.line(cline)
coloredline := if cline == s.line_nr && color_on { term.red(line) } else { line } coloredline := if cline == s.line_nr && color_on { term.red(line) } else { line }
eprintln(coloredline) 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 // The pointerline should have the same spaces/tabs as the offending
// line, so that it prints the ^ character exactly on the *same spot* // line, so that it prints the ^ character exactly on the *same spot*
// where it is needed. That is the reason we can not just // 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 // / 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]
[inline] fn imax(a,b int) int { return if a > b { a } else { b } } fn imax(a, b int) int {
[inline] fn imin(a,b int) int { return if a < b { a } else { b } } 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 { fn (s &Scanner) get_error_filepath() string {
verror_paths_override := os.getenv('VERROR_PATHS') verror_paths_override := os.getenv('VERROR_PATHS')
use_relative_paths := match verror_paths_override { use_relative_paths := match verror_paths_override {
'relative' { true } 'relative'{
'absolute' { false } true
else { s.print_rel_paths_on_error }
} }
'absolute'{
false
}
else {
s.print_rel_paths_on_error}}
if use_relative_paths { if use_relative_paths {
workdir := os.getwd() + os.path_separator workdir := os.getwd() + os.path_separator
if s.file_path.starts_with(workdir) { 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')) { if !p.pref.is_repl && !p.pref.is_test && (p.file_path.contains('v/compiler') || cur_path.contains('v/compiler')) {
println('\n=========================') println('\n=========================')
println('It looks like you are building V. It is being frequently updated every day.') 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('lead to this error.')
println('\nRun `v up`, that will most likely fix it.') 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, 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') println('=========================\n')
} }
if p.pref.is_debug { if p.pref.is_debug {
@ -176,10 +191,7 @@ fn (p mut Parser) print_error_context(){
fn normalized_error(s string) string { fn normalized_error(s string) string {
// Print `[]int` instead of `array_int` in errors // Print `[]int` instead of `array_int` in errors
mut res := s.replace('array_', '[]') mut res := s.replace('array_', '[]').replace('__', '.').replace('Option_', '?').replace('main.', '')
.replace('__', '.')
.replace('Option_', '?')
.replace('main.', '')
if res.contains('_V_MulRet_') { if res.contains('_V_MulRet_') {
res = res.replace('_V_MulRet_', '(').replace('_V_', ', ') res = res.replace('_V_MulRet_', '(').replace('_V_', ', ')
res = res[..res.len - 1] + ')"' 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, // 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 // so that if there is an error found later, v could show a more accurate
// position about where the error initially was. // position about where the error initially was.
// NB: The fields of ScannerPos *should be kept synchronized* with the // NB: The fields of ScannerPos *should be kept synchronized* with the
// corresponding fields in Scanner. // corresponding fields in Scanner.
struct ScannerPos { struct ScannerPos {
mut: mut:
pos int pos int
@ -207,7 +217,11 @@ pub fn (s ScannerPos) str() string {
} }
fn (s &Scanner) get_scanner_pos() ScannerPos { 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) { 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) { fn (p mut Parser) mutable_arg_error(i int, arg Var, f Fn) {
mut dots_example := 'mut $p.lit' mut dots_example := 'mut $p.lit'
if i > 0 { 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 { if i < f.args.len - 1 {
dots_example = dots_example + ',..' dots_example = dots_example + ',..'
} }
p.error('`$arg.name` is a mutable argument, you need to provide `mut`: ' + p.error('`$arg.name` is a mutable argument, you need to provide `mut`: ' + '`$f.name ($dots_example)`')
'`$f.name ($dots_example)`')
} }
const ( const (
warn_match_arrow = '=> is no longer needed in match statements, use\n' + warn_match_arrow = '=> is no longer needed in match statements, use\n' + 'match foo {
'match foo {
1 { bar } 1 { bar }
2 { baz } 2 { baz }
else { ... } else { ... }
}' }'
// make_receiver_mutable = // make_receiver_mutable =
err_used_as_value = 'used as value' 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' 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. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module compiler module compiler
pub fn get_v_options_and_main_command(args []string) ([]string,string) { 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('-') { if !a.starts_with('-') {
potential_commands << a potential_commands << a
continue continue
}else{ }
else {
options << a 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 // potential_commands[0] is always the executable itself, so ignore it
command := if potential_commands.len > 1 { potential_commands[1] } else { '' } command := if potential_commands.len > 1 { potential_commands[1] } else { '' }
return options,command return options,command
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,7 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module compiler module compiler
// TODO replace with comptime code generation. // TODO replace with comptime code generation.
// TODO remove cJSON dependency. // TODO remove cJSON dependency.
// OLD: User decode_User(string js) { // OLD: User decode_User(string js) {
@ -85,28 +83,20 @@ string res = tos2("");
if field.attr == 'skip' { if field.attr == 'skip' {
continue continue
} }
name := if field.attr.starts_with('json:') { name := if field.attr.starts_with('json:') { field.attr[5..] } else { field.name }
field.attr[5..]
} else {
field.name
}
field_type := p.table.find_type(field.typ) field_type := p.table.find_type(field.typ)
_typ := field.typ.replace('*', '') _typ := field.typ.replace('*', '')
enc_name := js_enc_name(_typ) enc_name := js_enc_name(_typ)
if field.attr == 'raw' { if field.attr == 'raw' {
dec += ' res->$field.name = tos2(cJSON_PrintUnformatted(' + dec += ' res->$field.name = tos2(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));\n'
'js_get(root, "$name")));\n' }
else {
} else {
// Now generate decoders for all field types in this struct // Now generate decoders for all field types in this struct
// need to do it here so that these functions are generated first // need to do it here so that these functions are generated first
p.gen_json_for_type(field_type) p.gen_json_for_type(field_type)
dec_name := js_dec_name(_typ) dec_name := js_dec_name(_typ)
if is_js_prim(_typ) { if is_js_prim(_typ) {
dec += ' res->$field.name = $dec_name (js_get(' + dec += ' res->$field.name = $dec_name (js_get(' + 'root, "$name"))'
'root, "$name"))'
} }
else { else {
dec += ' $dec_name (js_get(root, "$name"), & (res->$field.name))' dec += ' $dec_name (js_get(root, "$name"), & (res->$field.name))'
@ -122,10 +112,7 @@ string res = tos2("");
} }
fn is_js_prim(typ string) bool { fn is_js_prim(typ string) bool {
return typ == 'int' || typ == 'string' || return typ == 'int' || typ == 'string' || typ == 'bool' || typ == 'f32' || typ == 'f64' || typ == 'i8' || typ == 'i16' || typ == 'i64' || typ == 'u16' || typ == 'u32' || typ == 'u64'
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 { 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 { if v.pref.is_live {
cgen.genln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;') cgen.genln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;')
} }
} else { }
else {
if v.pref.is_so { if v.pref.is_so {
cgen.genln('HANDLE live_fn_mutex;') cgen.genln('HANDLE live_fn_mutex;')
cgen.genln(' cgen.genln('
@ -45,7 +46,9 @@ void pthread_mutex_unlock(HANDLE *m) {
} }
fn (v &V) generate_hotcode_reloading_main_caller() { 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 // We are in live code reload mode, so start the .so loader in the background
mut cgen := v.cgen mut cgen := v.cgen
cgen.genln('') cgen.genln('')
@ -57,7 +60,8 @@ fn (v &V) generate_hotcode_reloading_main_caller() {
cgen.genln(' load_so(live_library_name);') cgen.genln(' load_so(live_library_name);')
cgen.genln(' pthread_t _thread_so;') cgen.genln(' pthread_t _thread_so;')
cgen.genln(' pthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);') cgen.genln(' pthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);')
} else { }
else {
// windows: // windows:
so_name := file_base + if v.pref.ccompiler == 'msvc' { '.dll' } else { '.so' } so_name := file_base + if v.pref.ccompiler == 'msvc' { '.dll' } else { '.so' }
cgen.genln(' char *live_library_name = "$so_name";') 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() { fn (v &V) generate_hot_reload_code() {
mut cgen := v.cgen mut cgen := v.cgen
// Hot code reloading // Hot code reloading
if v.pref.is_live { if v.pref.is_live {
mut file := os.realpath(v.dir) 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 // Need to build .so file before building the live application
// The live app needs to load this .so file on initialization. // The live app needs to load this .so file on initialization.
mut vexe := os.args[0] mut vexe := os.args[0]
if os.user_os() == 'windows' { if os.user_os() == 'windows' {
vexe = cescaped_path(vexe) vexe = cescaped_path(vexe)
file = cescaped_path(file) file = cescaped_path(file)
} }
mut msvc := '' mut msvc := ''
if v.pref.ccompiler == 'msvc' { if v.pref.ccompiler == 'msvc' {
msvc = '-cc msvc' msvc = '-cc msvc'
} }
so_debug_flag := if v.pref.is_debug { '-g' } else { '' } 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' cmd_compile_shared_library := '$vexe $msvc $so_debug_flag -o $file_base -solive -shared $file'
if v.pref.show_c_cmd { if v.pref.show_c_cmd {
@ -100,7 +100,6 @@ fn (v &V) generate_hot_reload_code() {
diff := time.ticks() - ticks diff := time.ticks() - ticks
println('compiling shared library took $diff ms') println('compiling shared library took $diff ms')
println('=========\n') println('=========\n')
cgen.genln(' cgen.genln('
void lfnmutex_print(char *s){ void lfnmutex_print(char *s){
@ -111,7 +110,6 @@ void lfnmutex_print(char *s){
} }
} }
') ')
if v.os != .windows { if v.os != .windows {
cgen.genln(' cgen.genln('
void* live_lib=0; void* live_lib=0;
@ -153,12 +151,10 @@ int load_so(byteptr path) {
return 0; return 0;
} }
') ')
for so_fn in cgen.so_fns { for so_fn in cgen.so_fns {
cgen.genln('$so_fn = (void *)GetProcAddress(live_lib, "$so_fn"); ') cgen.genln('$so_fn = (void *)GetProcAddress(live_lib, "$so_fn"); ')
} }
} }
cgen.genln('return 1; cgen.genln('return 1;
} }
@ -220,8 +216,8 @@ void reload_so() {
} }
') ')
} }
if v.pref.is_so { if v.pref.is_so {
cgen.genln(' int load_so(byteptr path) { return 0; }') cgen.genln(' int load_so(byteptr path) { return 0; }')
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -251,7 +251,7 @@ fn (p &Parser) gen_fmt() {
return return
} }
//files := ['get_type.v'] //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 } //if !(p.file_name in files) { return }
path := os.tmpdir() + '/' + p.file_name path := os.tmpdir() + '/' + p.file_name
println('generating ${path}') 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. remove [module] Removes an installed module, or ALL installed modules at once, when no module name is given.
' '
) )
/* /*
- To disable automatic formatting: - To disable automatic formatting:
v -nofmt file.v v -nofmt file.v
*/ */

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module compiler module compiler
import os import os
@ -10,7 +9,9 @@ import filepath
pub fn get_vtmp_folder() string { pub fn get_vtmp_folder() string {
vtmp := filepath.join(os.tmpdir(),'v') vtmp := filepath.join(os.tmpdir(),'v')
if !os.is_dir(vtmp) { if !os.is_dir(vtmp) {
os.mkdir(vtmp) or { panic(err) } os.mkdir(vtmp)or{
panic(err)
}
} }
return vtmp return vtmp
} }
@ -19,3 +20,4 @@ pub fn get_vtmp_filename(base_file_name string, postfix string) string {
vtmp := get_vtmp_folder() vtmp := get_vtmp_folder()
return os.realpath(filepath.join(vtmp,os.filename(os.realpath(base_file_name)) + postfix)) 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_args := oargs.join(' ')
tool_command := '"$tool_exe" $tool_args' tool_command := '"$tool_exe" $tool_args'
// println('Launching: "$tool_command" ...') // println('Launching: "$tool_command" ...')
mut tool_should_be_recompiled := false mut tool_should_be_recompiled := false
if !os.exists(tool_exe) { if !os.exists(tool_exe) {
// fresh checkout // fresh checkout
tool_should_be_recompiled = true tool_should_be_recompiled = true
}else{ }
else {
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) { if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) {
// v was recompiled, maybe after v up ... // v was recompiled, maybe after v up ...
// rebuild the tool too just in case // rebuild the tool too just in case
@ -29,14 +29,16 @@ pub fn launch_tool(tname string){
tool_should_be_recompiled = true tool_should_be_recompiled = true
} }
} }
if tool_should_be_recompiled { if tool_should_be_recompiled {
compilation_command := '"$vexe" "$tool_source"' compilation_command := '"$vexe" "$tool_source"'
// println('Compiling $tname with: "$compilation_command"') // 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 { if tool_compilation.exit_code != 0 {
panic('V tool "$tool_source" could not be compiled\n' + tool_compilation.output) panic('V tool "$tool_source" could not be compiled\n' + tool_compilation.output)
} }
} }
exit(os.system(tool_command)) exit(os.system(tool_command))
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,13 +19,13 @@
* *
**********************************************************************/ **********************************************************************/
module strconv module strconv
/********************************************************************** /**********************************************************************
* *
* 96 bit operation utilities * 96 bit operation utilities
* Note: when u128 will be available these function can be refactored * Note: when u128 will be available these function can be refactored
* *
**********************************************************************/ **********************************************************************/
// right logical shift 96 bit // right logical shift 96 bit
fn lsr96(s2 u32, s1 u32, s0 u32) (u32,u32,u32) { fn lsr96(s2 u32, s1 u32, s0 u32) (u32,u32,u32) {
mut r0 := u32(0) 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 * Constants
* *
**********************************************************************/ **********************************************************************/
const ( const (
// //
// f64 constants // f64 constants
@ -96,7 +98,6 @@ const (
DOUBLE_MINUS_ZERO = 0x8000000000000000 DOUBLE_MINUS_ZERO = 0x8000000000000000
DOUBLE_PLUS_INFINITY = 0x7FF0000000000000 DOUBLE_PLUS_INFINITY = 0x7FF0000000000000
DOUBLE_MINUS_INFINITY = 0xFFF0000000000000 DOUBLE_MINUS_INFINITY = 0xFFF0000000000000
// //
// parser state machine states // parser state machine states
// //
@ -110,7 +111,6 @@ const (
FSM_H = 7 FSM_H = 7
FSM_I = 8 FSM_I = 8
FSM_STOP = 9 FSM_STOP = 9
// //
// Possible parser return values. // Possible parser return values.
// //
@ -119,7 +119,6 @@ const (
PARSER_MZERO = 2 // number is negative, module smaller PARSER_MZERO = 2 // number is negative, module smaller
PARSER_PINF = 3 // number is higher than +HUGE_VAL PARSER_PINF = 3 // number is higher than +HUGE_VAL
PARSER_MINF = 4 // number is lower than -HUGE_VAL PARSER_MINF = 4 // number is lower than -HUGE_VAL
// //
// char constants // char constants
// Note: Modify these if working with non-ASCII encoding // Note: Modify these if working with non-ASCII encoding
@ -129,18 +128,15 @@ const (
MINUS = `-` MINUS = `-`
ZERO = `0` ZERO = `0`
NINE = `9` NINE = `9`
TEN = u32(10) TEN = u32(10)
) )
/********************************************************************** /**********************************************************************
* *
* Utility * 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 { fn is_digit(x byte) bool {
return (x >= ZERO && x <= NINE) == true return (x >= ZERO && x <= NINE) == true
} }
@ -177,22 +173,21 @@ pub fn strlong(x f64) string {
* Support struct * Support struct
* *
**********************************************************************/ **********************************************************************/
// The structure is filled by parser, then given to converter. // The structure is filled by parser, then given to converter.
pub struct PrepNumber pub struct PrepNumber {
{
pub mut: pub mut:
negative bool=false // 0 if positive number, 1 if negative negative bool=false // 0 if positive number, 1 if negative
exponent int=0 // power of 10 exponent exponent int=0 // power of 10 exponent
mantissa u64=u64(0) // integer mantissa mantissa u64=u64(0) // integer mantissa
} }
/********************************************************************** /**********************************************************************
* *
* String parser * String parser
* NOTE: #TOFIX need one char after the last char of the number * 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 // parser return a support struct with all the parsing information for the converter
fn parser(s string) (int,PrepNumber) { fn parser(s string) (int,PrepNumber) {
mut state := FSM_A mut state := FSM_A
@ -202,60 +197,62 @@ fn parser(s string ) (int,PrepNumber) {
mut expneg := false mut expneg := false
mut expexp := 0 mut expexp := 0
mut i := 0 mut i := 0
mut pn := PrepNumber{
mut pn := PrepNumber{} }
for state != FSM_STOP { for state != FSM_STOP {
match state { match state {
// skip starting spaces // skip starting spaces
FSM_A { FSM_A {
if is_space(c) == true { if is_space(c) == true {
c = s[i++] c = s[i++]
} else { }
else {
state = FSM_B state = FSM_B
} }
} }
// check for the sign or point // check for the sign or point
FSM_B { FSM_B {
state = FSM_C state = FSM_C
if c == PLUS { if c == PLUS {
c = s[i++] c = s[i++]
} else if c == MINUS { }
else if c == MINUS {
pn.negative = true pn.negative = true
c = s[i++] c = s[i++]
} else if is_digit(c) {} }
else if c == DPOINT {} else if is_digit(c) {
}
else if c == DPOINT {
}
else { else {
state = FSM_STOP state = FSM_STOP
} }
} }
// skip the inital zeros // skip the inital zeros
FSM_C { FSM_C {
if c == ZERO { c = s[i++] } if c == ZERO {
c = s[i++]
}
else if c == DPOINT { else if c == DPOINT {
c = s[i++] c = s[i++]
state = FSM_D state = FSM_D
} else { }
else {
state = FSM_E state = FSM_E
} }
} }
// reading leading zeros in the fractional part of mantissa // reading leading zeros in the fractional part of mantissa
FSM_D { FSM_D {
if c == ZERO { if c == ZERO {
c = s[i++] c = s[i++]
if pn.exponent > -2147483647 { pn.exponent-- } if pn.exponent > -2147483647 {
pn.exponent--
}
} }
else { else {
state = FSM_F state = FSM_F
} }
} }
// reading integer part of mantissa // reading integer part of mantissa
FSM_E { FSM_E {
if is_digit(c) { if is_digit(c) {
@ -264,10 +261,10 @@ fn parser(s string ) (int,PrepNumber) {
pn.mantissa += u64(c - ZERO) pn.mantissa += u64(c - ZERO)
digx++ digx++
} }
else if pn.exponent < 2147483647 { pn.exponent++ } else if pn.exponent < 2147483647 {
pn.exponent++
}
c = s[i++] c = s[i++]
} }
else if c == DPOINT { else if c == DPOINT {
c = s[i++] c = s[i++]
@ -277,7 +274,6 @@ fn parser(s string ) (int,PrepNumber) {
state = FSM_F state = FSM_F
} }
} }
// reading fractional part of mantissa // reading fractional part of mantissa
FSM_F { FSM_F {
if is_digit(c) { if is_digit(c) {
@ -287,7 +283,6 @@ fn parser(s string ) (int,PrepNumber) {
pn.exponent-- pn.exponent--
digx++ digx++
} }
c = s[i++] c = s[i++]
} }
else if is_exp(c) { else if is_exp(c) {
@ -298,18 +293,17 @@ fn parser(s string ) (int,PrepNumber) {
state = FSM_G state = FSM_G
} }
} }
// reading sign of exponent // reading sign of exponent
FSM_G { FSM_G {
if c == PLUS { if c == PLUS {
c = s[i++] c = s[i++]
} else if c == MINUS { }
else if c == MINUS {
expneg = true expneg = true
c = s[i++] c = s[i++]
} }
state = FSM_H state = FSM_H
} }
// skipping leading zeros of exponent // skipping leading zeros of exponent
FSM_H { FSM_H {
if c == ZERO { if c == ZERO {
@ -319,7 +313,6 @@ fn parser(s string ) (int,PrepNumber) {
state = FSM_I state = FSM_I
} }
} }
// reading exponent digits // reading exponent digits
FSM_I { FSM_I {
if is_digit(c) { if is_digit(c) {
@ -327,63 +320,59 @@ fn parser(s string ) (int,PrepNumber) {
expexp *= 10 expexp *= 10
expexp += int(c - ZERO) expexp += int(c - ZERO)
} }
c = s[i++] c = s[i++]
} }
else { else {
state = FSM_STOP state = FSM_STOP
} }
} }
else {
else {} }}
}
// C.printf("len: %d i: %d str: %s \n",s.len,i,s[..i]) // C.printf("len: %d i: %d str: %s \n",s.len,i,s[..i])
if i >= s.len { if i >= s.len {
state = FSM_STOP state = FSM_STOP
} }
} }
if expneg { if expneg {
expexp = -expexp expexp = -expexp
} }
pn.exponent += expexp pn.exponent += expexp
if pn.mantissa == 0 { if pn.mantissa == 0 {
if pn.negative { if pn.negative {
result = PARSER_MZERO result = PARSER_MZERO
} else { }
else {
result = PARSER_PZERO result = PARSER_PZERO
} }
} }
else if (pn.exponent > 309) { else if (pn.exponent > 309) {
if pn.negative { if pn.negative {
result = PARSER_MINF result = PARSER_MINF
} else { }
else {
result = PARSER_PINF result = PARSER_PINF
} }
} }
else if pn.exponent < -328 { else if pn.exponent < -328 {
if pn.negative { if pn.negative {
result = PARSER_MZERO result = PARSER_MZERO
} else { }
else {
result = PARSER_PZERO result = PARSER_PZERO
} }
} }
return result,pn return result,pn
} }
/********************************************************************** /**********************************************************************
* *
* Converter to the bit form of the f64 number * Converter to the bit form of the f64 number
* *
**********************************************************************/ **********************************************************************/
// converter return a u64 with the bit image of the f64 number // converter return a u64 with the bit image of the f64 number
fn converter(pn mut PrepNumber) u64 { fn converter(pn mut PrepNumber) u64 {
mut binexp := 92 mut binexp := 92
mut s2 := u32(0) // 96-bit precision integer mut s2 := u32(0) // 96-bit precision integer
mut s1 := u32(0) mut s1 := u32(0)
mut s0 := 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 r2 := u32(0) // 96-bit precision integer
mut r1 := u32(0) mut r1 := u32(0)
mut r0 := u32(0) mut r0 := u32(0)
mask28 := u32(0xF<<28) mask28 := u32(0xF<<28)
mut result := u64(0) mut result := u64(0)
// working on 3 u32 to have 96 bit precision // working on 3 u32 to have 96 bit precision
s0 = u32(pn.mantissa & u64(0x00000000FFFFFFFF)) s0 = u32(pn.mantissa & u64(0x00000000FFFFFFFF))
s1 = u32(pn.mantissa>>32) s1 = u32(pn.mantissa>>32)
s2 = u32(0) s2 = u32(0)
// so we take the decimal exponent off // so we take the decimal exponent off
for pn.exponent > 0 { for pn.exponent > 0 {
q2,q1,q0 = lsl96(s2, s1, s0) // q = s * 2 q2,q1,q0 = lsl96(s2, s1, s0) // q = s * 2
r2,r1,r0 = lsl96(q2, q1, q0) // r = s * 4 <=> q * 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 = 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 s2,s1,s0 = add96(s2, s1, s0, q2, q1, q0) // s = (s * 8) + (s * 2) <=> s*10
pn.exponent-- pn.exponent--
for (s2 & mask28) != 0 { for (s2 & mask28) != 0 {
q2,q1,q0 = lsr96(s2, s1, s0) q2,q1,q0 = lsr96(s2, s1, s0)
binexp++ binexp++
@ -419,9 +402,7 @@ fn converter(pn mut PrepNumber) u64 {
s1 = q1 s1 = q1
s0 = q0 s0 = q0
} }
} }
for pn.exponent < 0 { for pn.exponent < 0 {
for !((s2 & (u32(1)<<31)) != 0) { for !((s2 & (u32(1)<<31)) != 0) {
q2,q1,q0 = lsl96(s2, s1, s0) q2,q1,q0 = lsl96(s2, s1, s0)
@ -429,9 +410,7 @@ fn converter(pn mut PrepNumber) u64 {
s2 = q2 s2 = q2
s1 = q1 s1 = q1
s0 = q0 s0 = q0
} }
q2 = s2 / TEN q2 = s2 / TEN
r1 = s2 % TEN r1 = s2 % TEN
r2 = (s1>>8) | (r1<<24) r2 = (s1>>8) | (r1<<24)
@ -447,13 +426,9 @@ fn converter(pn mut PrepNumber) u64 {
s2 = q2 s2 = q2
s1 = q1 s1 = q1
s0 = q0 s0 = q0
pn.exponent++ pn.exponent++
} }
// C.printf("mantissa before normalization: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) // 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 // normalization, the 28 bit in s2 must the leftest one in the variable
if s2 != 0 || s1 != 0 || s0 != 0 { if s2 != 0 || s1 != 0 || s0 != 0 {
for (s2 & mask28) == 0 { for (s2 & mask28) == 0 {
@ -464,7 +439,6 @@ fn converter(pn mut PrepNumber) u64 {
s0 = q0 s0 = q0
} }
} }
// rounding if needed // rounding if needed
/* /*
* "round half to even" algorithm * "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 0, round down
* If bit 53 is 1, round up * If bit 53 is 1, round up
*/ */
/* test case 1 complete /* test case 1 complete
s2=0x1FFFFFFF s2=0x1FFFFFFF
s1=0xFFFFFF80 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) // C.printf("mantissa before rounding: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp)
// s1 => 0xFFFFFFxx only F are rapresented // s1 => 0xFFFFFFxx only F are rapresented
nbit := 7 nbit := 7
check_round_bit := u32(1)<<u32(nbit) check_round_bit := u32(1)<<u32(nbit)
check_round_mask := u32(0xFFFFFFFF)<<u32(nbit) check_round_mask := u32(0xFFFFFFFF)<<u32(nbit)
if (s1 & check_round_bit) != 0 { if (s1 & check_round_bit) != 0 {
// C.printf("need round!! cehck mask: %08x\n", s1 & ~check_round_mask ) // C.printf("need round!! cehck mask: %08x\n", s1 & ~check_round_mask )
if (s1 & ~check_round_mask) != 0 { if (s1 & ~check_round_mask) != 0 {
// C.printf("Add 1!\n") // C.printf("Add 1!\n")
s2,s1,s0 = add96(s2, s1, s0, 0, check_round_bit, 0) s2,s1,s0 = add96(s2, s1, s0, 0, check_round_bit, 0)
} else { }
else {
// C.printf("All 0!\n") // C.printf("All 0!\n")
if (s1 & (check_round_bit<<u32(1))) != 0 { if (s1 & (check_round_bit<<u32(1))) != 0 {
// C.printf("Add 1 form -1 bit control!\n") // C.printf("Add 1 form -1 bit control!\n")
@ -518,7 +490,6 @@ fn converter(pn mut PrepNumber) u64 {
s1 = s1 & check_round_mask s1 = s1 & check_round_mask
s0 = u32(0) s0 = u32(0)
} }
// recheck normalization // recheck normalization
if s2 & (mask28<<u32(1)) != 0 { if s2 & (mask28<<u32(1)) != 0 {
// C.printf("Renormalize!!") // C.printf("Renormalize!!")
@ -528,20 +499,17 @@ fn converter(pn mut PrepNumber) u64 {
s1 = q1 s1 = q1
s0 = q0 s0 = q0
} }
// tmp := ( u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8) // 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("mantissa after rounding : %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp)
// C.printf("Tmp result: %016x\n",tmp) // C.printf("Tmp result: %016x\n",tmp)
// end rounding // end rounding
// offset the binary exponent IEEE 754 // offset the binary exponent IEEE 754
binexp += 1023 binexp += 1023
if binexp > 2046 { if binexp > 2046 {
if pn.negative { if pn.negative {
result = DOUBLE_MINUS_INFINITY result = DOUBLE_MINUS_INFINITY
} else { }
else {
result = DOUBLE_PLUS_INFINITY result = DOUBLE_PLUS_INFINITY
} }
} }
@ -553,14 +521,12 @@ fn converter(pn mut PrepNumber) u64 {
else if s2 != 0 { else if s2 != 0 {
mut q := u64(0) mut q := u64(0)
binexs2 := u64(binexp)<<52 binexs2 := u64(binexp)<<52
q = (u64(s2 & ~mask28)<<24) | ((u64(s1) + u64(128))>>8) | binexs2 q = (u64(s2 & ~mask28)<<24) | ((u64(s1) + u64(128))>>8) | binexs2
if pn.negative { if pn.negative {
q |= (u64(1)<<63) q |= (u64(1)<<63)
} }
result = q result = q
} }
return result return result
} }
@ -569,18 +535,17 @@ fn converter(pn mut PrepNumber) u64 {
* Public functions * Public functions
* *
**********************************************************************/ **********************************************************************/
// atof64 return a f64 from a string doing a parsing operation // atof64 return a f64 from a string doing a parsing operation
pub fn atof64(s string) f64 { pub fn atof64(s string) f64 {
mut pn := PrepNumber{} mut pn := PrepNumber{
}
mut res_parsing := 0 mut res_parsing := 0
mut result := f64(0) mut result := f64(0)
result = f64(0.0) result = f64(0.0)
mut res_ptr := *u64(&result) mut res_ptr := *u64(&result)
res_parsing,pn = parser(s + ' ') // TODO: need an extra char for now res_parsing,pn = parser(s + ' ') // TODO: need an extra char for now
// println(pn) // println(pn)
match res_parsing { match res_parsing {
PARSER_OK { PARSER_OK {
*res_ptr = converter(mut pn) *res_ptr = converter(mut pn)
@ -597,7 +562,8 @@ pub fn atof64(s string) f64 {
PARSER_MINF { PARSER_MINF {
*res_ptr = DOUBLE_MINUS_INFINITY *res_ptr = DOUBLE_MINUS_INFINITY
} }
else {} else {
} }}
return result 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. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
// TODO: use optionals, or some way to return default with error. // TODO: use optionals, or some way to return default with error.
module strconv module strconv
const ( const (
// int_size is the size in bits of an int or uint value. // int_size is the size in bits of an int or uint value.
// int_size = 32 << (~u32(0) >> 63) // 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 { 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 bit_size := _bit_size
mut base := _base mut base := _base
if s.len < 1 || !underscore_ok(s) { if s.len < 1 || !underscore_ok(s) {
// return error('parse_uint: syntax error $s') // return error('parse_uint: syntax error $s')
return u64(0) return u64(0)
} }
base0 := base == 0 base0 := base == 0
mut start_index := 0 mut start_index := 0
if 2 <= base && base <= 36 { if 2 <= base && base <= 36 {
// valid base; nothing to do // valid base; nothing to do
} else if base == 0 { }
else if base == 0 {
// Look for octal, hex prefix. // Look for octal, hex prefix.
base = 10 base = 10
if s[0] == `0` { 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++ start_index++
} }
} }
} else { }
else {
// return error('parse_uint: base error $s - $base') // return error('parse_uint: base error $s - $base')
return u64(0) return u64(0)
} }
if bit_size == 0 { if bit_size == 0 {
bit_size = int_size 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 error('parse_uint: bitsize error $s - $bit_size')
return u64(0) return u64(0)
} }
// Cutoff is the smallest number such that cutoff*base > maxUint64. // Cutoff is the smallest number such that cutoff*base > maxUint64.
// Use compile-time constants for common cases. // Use compile-time constants for common cases.
cutoff := max_u64 / u64(base) + u64(1) cutoff := max_u64 / u64(base) + u64(1)
max_val := if bit_size == 64 { max_val := if bit_size == 64 { max_u64 } else { (u64(1)<<u64(bit_size)) - u64(1) }
max_u64
} else {
(u64(1)<<u64(bit_size))-u64(1)
}
mut underscores := false mut underscores := false
mut n := u64(0) mut n := u64(0)
for i in start_index .. s.len { 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 underscores = true
continue continue
} }
else if `0` <= c && c <= `9` { d = c - `0` } else if `0` <= c && c <= `9` {
else if `a` <= cl && cl <= `z` { d = cl - `a` + 10 } d = c - `0`
}
else if `a` <= cl && cl <= `z` {
d = cl - `a` + 10
}
else { else {
if error_on_non_digit { if error_on_non_digit {
// return error('parse_uint: syntax error $s') // return error('parse_uint: syntax error $s')
return u64(0) return u64(0)
} else { }
else {
break 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 { if error_on_high_digit {
// return error('parse_uint: syntax error $s') // return error('parse_uint: syntax error $s')
return u64(0) return u64(0)
} else { }
else {
break 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 { 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 s := _s
mut bit_size := _bit_size mut bit_size := _bit_size
if s.len < 1 { if s.len < 1 {
// return error('parse_int: syntax error $s') // return error('parse_int: syntax error $s')
return i64(0) 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 mut neg := false
if s[0] == `+` { if s[0] == `+` {
s = s[1..] s = s[1..]
} else if s[0] == `-` { }
else if s[0] == `-` {
neg = true neg = true
s = s[1..] s = s[1..]
} }
// Convert unsigned and check range. // Convert unsigned and check range.
// un := parse_uint(s, base, bit_size) or { // un := parse_uint(s, base, bit_size) or {
// return i64(0) // 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 { if un == 0 {
return i64(0) return i64(0)
} }
if bit_size == 0 { if bit_size == 0 {
bit_size = int_size bit_size = int_size
} }
// TODO: check should u64(bit_size-1) be size of int (32)? // TODO: check should u64(bit_size-1) be size of int (32)?
cutoff := u64(1)<<u64(bit_size - 1) cutoff := u64(1)<<u64(bit_size - 1)
if !neg && un >= cutoff { 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 error('parse_int: range error $s0')
return -i64(cutoff) return -i64(cutoff)
} }
return if neg { -i64(un) } else { i64(un) } return if neg { -i64(un) } else { i64(un) }
} }
// parse_int interprets a string s in the given base (0, 2 to 36) and // 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. // 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. // atoi is equivalent to parse_int(s, 10, 0), converted to type int.
pub fn atoi(s string) int { pub fn atoi(s string) int {
if (int_size == 32 && (0 < s.len && s.len < 10)) || if (int_size == 32 && (0 < s.len && s.len < 10)) || (int_size == 64 && (0 < s.len && s.len < 19)) {
(int_size == 64 && (0 < s.len && s.len < 19)) {
// Fast path for small integers that fit int type. // Fast path for small integers that fit int type.
mut start_idx := 0 mut start_idx := 0
if s[0] == `-` || s[0] == `+` { if s[0] == `-` || s[0] == `+` {
@ -210,7 +203,6 @@ pub fn atoi(s string) int {
return 0 return 0
} }
} }
mut n := 0 mut n := 0
for i in start_idx .. s.len { for i in start_idx .. s.len {
ch := s[i] - `0` ch := s[i] - `0`
@ -220,13 +212,10 @@ pub fn atoi(s string) int {
} }
n = n * 10 + int(ch) n = n * 10 + int(ch)
} }
return if s[0] == `-` { -n } else { n } return if s[0] == `-` { -n } else { n }
} }
// Slow path for invalid, big, or underscored integers. // Slow path for invalid, big, or underscored integers.
int64 := parse_int(s, 10, 0) int64 := parse_int(s, 10, 0)
return int(int64) return int(int64)
} }
@ -241,26 +230,21 @@ fn underscore_ok(s string) bool {
// ! for none of the above. // ! for none of the above.
mut saw := `^` mut saw := `^`
mut i := 0 mut i := 0
// Optional sign. // Optional sign.
if s.len >= 1 && (s[0] == `-` || s[0] == `+`) { if s.len >= 1 && (s[0] == `-` || s[0] == `+`) {
i++ i++
} }
// Optional base prefix. // Optional base prefix.
mut hex := false mut hex := false
if s.len-i >= 2 && s[i] == `0` && 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`) {
(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" saw = `0` // base prefix counts as a digit for "underscore as digit separator"
hex = byte_to_lower(s[i + 1]) == `x` hex = byte_to_lower(s[i + 1]) == `x`
i += 2 i += 2
} }
// Number proper. // Number proper.
for ; i < s.len; i++ { for ; i < s.len; i++ {
// Digits are always okay. // Digits are always okay.
if (`0` <= s[i] && s[i] <= `9`) || if (`0` <= s[i] && s[i] <= `9`) || (hex && `a` <= byte_to_lower(s[i]) && byte_to_lower(s[i]) <= `f`) {
(hex && `a` <= byte_to_lower(s[i]) && byte_to_lower(s[i]) <= `f`) {
saw = `0` saw = `0`
continue continue
} }

View File

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

View File

@ -1,7 +1,5 @@
module strings module strings
// #-js // #-js
// use levenshtein distance algorithm to calculate // use levenshtein distance algorithm to calculate
// the distance between between two strings (lower is closer) // the distance between between two strings (lower is closer)
pub fn levenshtein_distance(a, b string) int { 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) 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 { pub fn ok_message(s string) string {
return if can_show_color_on_stdout() { return if can_show_color_on_stdout() { green(s) } else { s }
green( s )
}else{
s
}
} }
pub fn fail_message(s string) string { pub fn fail_message(s string) string {
return if can_show_color_on_stdout() { return if can_show_color_on_stdout() { red(s) } else { s }
red( s )
}else{
s
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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