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

23
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 (
@ -9,20 +8,19 @@ import (
benchmark benchmark
os os
filepath filepath
//time // time
) )
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() {
//t := time.ticks() // t := time.ticks()
//defer { println(time.ticks() - t) } // defer { println(time.ticks() - t) }
args := compiler.env_vflags_and_os_args() args := compiler.env_vflags_and_os_args()
options, command := compiler.get_v_options_and_main_command( args ) options,command := compiler.get_v_options_and_main_command(args)
// external tool // external tool
if command in simple_tools { if command in simple_tools {
compiler.launch_tool('v' + command) compiler.launch_tool('v' + command)
@ -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' {
@ -109,7 +109,7 @@ fn v_command(command string, args []string) {
os.chdir(vdir) os.chdir(vdir)
mod := args.last() mod := args.last()
os.system('$vexe build module vlib$os.path_separator' + args.last()) os.system('$vexe build module vlib$os.path_separator' + args.last())
txt := os.read_file(filepath.join(compiler.v_modules_path, 'vlib', '${mod}.vh')) or { txt := os.read_file(filepath.join(compiler.v_modules_path,'vlib','${mod}.vh'))or{
panic(err) panic(err)
} }
println(txt) println(txt)
@ -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,19 +25,20 @@ 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
bench_end_time i64 bench_end_time i64
step_start_time i64 step_start_time i64
step_end_time i64 step_end_time i64
ntotal int ntotal int
nok int nok int
nfail int nfail int
verbose bool verbose bool
} }
pub fn new_benchmark() Benchmark{ pub fn new_benchmark() Benchmark {
return Benchmark{ return Benchmark{
bench_start_time: benchmark.now() bench_start_time: benchmark.now()
verbose: true verbose: true
@ -67,14 +67,14 @@ pub fn (b mut Benchmark) ok() {
pub fn (b mut Benchmark) fail_many(n int) { pub fn (b mut Benchmark) fail_many(n int) {
b.step_end_time = benchmark.now() b.step_end_time = benchmark.now()
b.ntotal+=n b.ntotal += n
b.nfail+=n b.nfail += n
} }
pub fn (b mut Benchmark) ok_many(n int) { pub fn (b mut Benchmark) ok_many(n int) {
b.step_end_time = benchmark.now() b.step_end_time = benchmark.now()
b.ntotal+=n b.ntotal += n
b.nok+=n b.nok += n
} }
pub fn (b mut Benchmark) neither_fail_nor_ok() { pub fn (b mut Benchmark) neither_fail_nor_ok() {
@ -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,13 +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 compiler module compiler
fn (p mut Parser) inline_asm() { fn (p mut Parser) inline_asm() {
if !p.inside_unsafe { if !p.inside_unsafe {
p.error('asm() needs to be run inside `unsafe {}`') p.error('asm() needs to be run inside `unsafe {}`')
} }
p.next() p.next()
p.check(.lcbr) p.check(.lcbr)
s := p.check_string() s := p.check_string()
@ -15,7 +14,7 @@ fn (p mut Parser) inline_asm() {
for p.tok == .str { for p.tok == .str {
p.genln('"$p.lit"') p.genln('"$p.lit"')
p.next() p.next()
} }
for p.tok == .colon { for p.tok == .colon {
p.next() p.next()
arg := p.check_string() arg := p.check_string()
@ -25,11 +24,12 @@ fn (p mut Parser) inline_asm() {
var_name := p.check_name() var_name := p.check_name()
if !p.known_var(var_name) { if !p.known_var(var_name) {
p.error('unknown variable `$var_name`') p.error('unknown variable `$var_name`')
} }
p.check(.rpar) p.check(.rpar)
p.genln('($var_name)') p.genln('($var_name)')
} }
} }
p.genln(');') p.genln(');')
p.check(.rcbr) p.check(.rcbr)
} }

View File

@ -1,13 +1,11 @@
// 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
os string // eg. windows | darwin | linux os string // eg. windows | darwin | linux
name string // eg. -I name string // eg. -I
@ -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
} }
} }
@ -48,12 +44,12 @@ fn (v &V) get_rest_of_module_cflags(c &CFlag) []CFlag {
// format flag // format flag
fn (cf &CFlag) format() string { fn (cf &CFlag) format() string {
mut value := cf.value mut value := cf.value
if cf.name in ['-l', '-Wa', '-Wl', '-Wp'] && value.len>0 { if cf.name in ['-l', '-Wa', '-Wl', '-Wp'] && value.len > 0 {
return '${cf.name}${value}'.trim_space() return '${cf.name}${value}'.trim_space()
} }
// convert to absolute path // convert to absolute path
if cf.name == '-I' || cf.name == '-L' || value.ends_with('.o') { if cf.name == '-I' || cf.name == '-L' || value.ends_with('.o') {
value = '"'+os.realpath(value)+'"' value = '"' + os.realpath(value) + '"'
} }
return '$cf.name $value'.trim_space() return '$cf.name $value'.trim_space()
} }
@ -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()
} }
@ -93,7 +86,7 @@ fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
mut value := '' mut value := ''
if flag[0] == `-` { if flag[0] == `-` {
for f in allowed_flags { for f in allowed_flags {
i := 1+f.len i := 1 + f.len
if i <= flag.len && f == flag[1..i] { if i <= flag.len && f == flag[1..i] {
name = flag[..i].trim_space() name = flag[..i].trim_space()
flag = flag[i..].trim_space() flag = flag[i..].trim_space()
@ -101,20 +94,24 @@ 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 {
i := index+2+f.len i := index + 2 + f.len
if i <= flag.len && f == flag[index+2..i] { if i <= flag.len && f == flag[index + 2..i] {
value = flag[..index+1].trim_space() value = flag[..index + 1].trim_space()
flag = flag[index+1..].trim_space() flag = flag[index + 1..].trim_space()
has_next = true has_next = true
break break
} }
} }
if has_next { break } if has_next {
index = flag.index_after(' -', index+1) break
}
index = flag.index_after(' -', index + 1)
} }
if index == -1 { if index == -1 {
value = flag.trim_space() value = flag.trim_space()
@ -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) {
@ -139,13 +136,18 @@ fn (table mut Table) parse_cflag(cflag string, mod string) ?bool {
return true return true
} }
//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
mut args:=[]string mut args := []string
for flag in cflags { for flag in cflags {
if flag.name != '-l' { if flag.name != '-l' {
args << flag.format() args << flag.format()
@ -156,7 +158,7 @@ fn (cflags []CFlag) c_options_before_target() string {
fn (cflags []CFlag) c_options_after_target() string { fn (cflags []CFlag) c_options_after_target() string {
// -l flags (libs) // -l flags (libs)
mut args:=[]string mut args := []string
for flag in cflags { for flag in cflags {
if flag.name == '-l' { if flag.name == '-l' {
args << flag.format() args << flag.format()
@ -166,7 +168,7 @@ fn (cflags []CFlag) c_options_after_target() string {
} }
fn (cflags []CFlag) c_options_without_object_files() string { fn (cflags []CFlag) c_options_without_object_files() string {
mut args:=[]string mut args := []string
for flag in cflags { for flag in cflags {
if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { if flag.value.ends_with('.o') || flag.value.ends_with('.obj') {
continue continue
@ -177,7 +179,7 @@ fn (cflags []CFlag) c_options_without_object_files() string {
} }
fn (cflags []CFlag) c_options_only_object_files() string { fn (cflags []CFlag) c_options_only_object_files() string {
mut args:=[]string mut args := []string
for flag in cflags { for flag in cflags {
if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { if flag.value.ends_with('.o') || flag.value.ends_with('.obj') {
args << flag.format() args << flag.format()
@ -185,3 +187,4 @@ fn (cflags []CFlag) c_options_only_object_files() string {
} }
return args.join(' ') return args.join(' ')
} }

View File

@ -356,19 +356,19 @@ fn platform_postfix_to_ifdefguard(name string) string {
'.v'{ '.v'{
'' ''
} // no guard needed } // no guard needed
'_win.v','_windows.v'{ '_win.v', '_windows.v'{
'#ifdef _WIN32' '#ifdef _WIN32'
} }
'_nix.v'{ '_nix.v'{
'#ifndef _WIN32' '#ifndef _WIN32'
} }
'_lin.v','_linux.v'{ '_lin.v', '_linux.v'{
'#ifdef __linux__' '#ifdef __linux__'
} }
'_mac.v','_darwin.v'{ '_mac.v', '_darwin.v'{
'#ifdef __APPLE__' '#ifdef __APPLE__'
} }
'_bsd.v','_freebsd.v '{ '_bsd.v', '_freebsd.v '{
'#ifdef __FreeBSD__' '#ifdef __FreeBSD__'
} }
'_solaris.v'{ '_solaris.v'{

View File

@ -1,8 +1,7 @@
module compiler module compiler
const ( const (
c_common_macros = '
c_common_macros = '
#define EMPTY_STRUCT_DECLARATION #define EMPTY_STRUCT_DECLARATION
#define EMPTY_STRUCT_INITIALIZATION 0 #define EMPTY_STRUCT_INITIALIZATION 0
@ -23,8 +22,7 @@ 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
#include <stdio.h> // TODO remove all these includes, define all function signatures and types manually #include <stdio.h> // TODO remove all these includes, define all function signatures and types manually
@ -91,7 +89,7 @@ c_headers = '
#include <sys/wait.h> // os__wait uses wait on nix #include <sys/wait.h> // os__wait uses wait on nix
#endif #endif
$c_common_macros $c_common_macros
#ifdef _WIN32 #ifdef _WIN32
#define WINVER 0x0600 #define WINVER 0x0600
@ -146,8 +144,7 @@ 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() {}
var array_byte = function() {} var array_byte = function() {}
@ -170,9 +167,7 @@ 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
//#include <stdint.h> // int64_t etc //#include <stdint.h> // int64_t etc
@ -211,8 +206,7 @@ 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,49 +4,50 @@ 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.
// The functions in each of the layers, call the functions from the layers *below*. // The functions in each of the layers, call the functions from the layers *below*.
// The functions in each of the layers, also have more details about the warn/error situation, // The functions in each of the layers, also have more details about the warn/error situation,
// so they can display more informative message, so please call the lowest level variant you can. // so they can display more informative message, so please call the lowest level variant you can.
////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////
// TLDR: If you have a token index, call: // TLDR: If you have a token index, call:
// p.error_with_token_index(msg, token_index) // p.error_with_token_index(msg, token_index)
// ... not just : // ... not just :
// 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)
} }
fn (p mut Parser) warn(s string) { fn (p mut Parser) warn(s string) {
p.warn_with_token_index(s, p.token_idx-1 ) p.warn_with_token_index(s, p.token_idx - 1)
} }
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 { }
p.warn_with_token_index( e, tokenindex ) else {
p.warn_with_token_index(e, tokenindex)
} }
} }
fn (p mut Parser) error_with_token_index(s string, tokenindex int) { fn (p mut Parser) error_with_token_index(s string, tokenindex int) {
p.error_with_position(s, p.scanner.get_scanner_pos_of_token( p.tokens[ tokenindex ] ) ) p.error_with_position(s, p.scanner.get_scanner_pos_of_token(p.tokens[tokenindex]))
} }
fn (p mut Parser) warn_with_token_index(s string, tokenindex int) { fn (p mut Parser) warn_with_token_index(s string, tokenindex int) {
p.warn_with_position(s, p.scanner.get_scanner_pos_of_token( p.tokens[ tokenindex ] ) ) p.warn_with_position(s, p.scanner.get_scanner_pos_of_token(p.tokens[tokenindex]))
} }
fn (p mut Parser) error_with_position(s string, sp ScannerPos) { fn (p mut Parser) error_with_position(s string, sp ScannerPos) {
p.print_error_context() p.print_error_context()
e := normalized_error( s ) e := normalized_error(s)
p.scanner.goto_scanner_position( sp ) p.scanner.goto_scanner_position(sp)
p.scanner.error_with_col(e, sp.pos - sp.last_nl_pos) p.scanner.error_with_col(e, sp.pos - sp.last_nl_pos)
} }
@ -56,10 +57,10 @@ fn (p mut Parser) warn_with_position(s string, sp ScannerPos) {
} }
// on a warning, restore the scanner state after printing the warning: // on a warning, restore the scanner state after printing the warning:
cpos := p.scanner.get_scanner_pos() cpos := p.scanner.get_scanner_pos()
e := normalized_error( s ) e := normalized_error(s)
p.scanner.goto_scanner_position( sp ) p.scanner.goto_scanner_position(sp)
p.scanner.warn_with_col(e, sp.pos - sp.last_nl_pos) p.scanner.warn_with_col(e, sp.pos - sp.last_nl_pos)
p.scanner.goto_scanner_position( cpos ) p.scanner.goto_scanner_position(cpos)
} }
fn (s &Scanner) error(msg string) { fn (s &Scanner) error(msg string) {
@ -73,14 +74,14 @@ fn (s &Scanner) warn(msg string) {
fn (s &Scanner) warn_with_col(msg string, col int) { fn (s &Scanner) warn_with_col(msg string, col int) {
fullpath := s.get_error_filepath() fullpath := s.get_error_filepath()
color_on := s.is_color_output_on() color_on := s.is_color_output_on()
final_message := if color_on { term.bold(term.bright_blue( msg )) } else { msg } final_message := if color_on { term.bold(term.bright_blue(msg)) } else { msg }
eprintln('warning: ${fullpath}:${s.line_nr+1}:${col}: $final_message') eprintln('warning: ${fullpath}:${s.line_nr+1}:${col}: $final_message')
} }
fn (s &Scanner) error_with_col(msg string, col int) { fn (s &Scanner) error_with_col(msg string, col int) {
fullpath := s.get_error_filepath() fullpath := s.get_error_filepath()
color_on := s.is_color_output_on() color_on := s.is_color_output_on()
final_message := if color_on { term.red( term.bold( msg ) ) } else { msg } final_message := if color_on { term.red(term.bold(msg)) } else { msg }
// The filepath:line:col: format is the default C compiler // The filepath:line:col: format is the default C compiler
// error output format. It allows editors and IDE's like // error output format. It allows editors and IDE's like
// emacs to quickly find the errors in the output // emacs to quickly find the errors in the output
@ -88,65 +89,79 @@ 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))
for cline := context_start_line; cline < context_end_line; cline++ { for cline := context_start_line; cline < context_end_line; cline++ {
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
// use strings.repeat(` `, col) to form it. // use strings.repeat(` `, col) to form it.
mut pointerline := []string mut pointerline := []string
for i , c in line { for i, c in line {
if i < col { if i < col {
x := if c.is_space() { c } else { ` ` } x := if c.is_space() { c } else { ` ` }
pointerline << x.str() pointerline << x.str()
continue continue
} }
pointerline << if color_on { term.bold( term.blue('^') ) } else { '^' } pointerline << if color_on { term.bold(term.blue('^')) } else { '^' }
break break
} }
eprintln( ' ' + pointerline.join('') ) eprintln(' ' + pointerline.join(''))
} }
} }
exit(1) exit(1)
} }
////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////
// / 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
}
/// Misc error helper functions, can be called by any of the functions above [inline]
fn imax(a, b int) int {
return if a > b { a } else { b }
}
[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 imin(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 }
}
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) {
return s.file_path.replace( workdir, '') return s.file_path.replace(workdir, '')
} }
return s.file_path return s.file_path
} }
return os.realpath( s.file_path ) return os.realpath(s.file_path)
} }
fn (s &Scanner) is_color_output_on() bool { fn (s &Scanner) is_color_output_on() bool {
return s.print_colored_error && term.can_show_color_on_stderr() return s.print_colored_error && term.can_show_color_on_stderr()
} }
fn (p mut Parser) print_error_context(){ fn (p mut Parser) print_error_context() {
// Dump all vars and types for debugging // Dump all vars and types for debugging
if p.pref.is_debug { if p.pref.is_debug {
// os.write_to_file('/var/tmp/lang.types', '')//pes(p.table.types)) // os.write_to_file('/var/tmp/lang.types', '')//pes(p.table.types))
@ -158,14 +173,14 @@ fn (p mut Parser) print_error_context(){
p.cgen.save() p.cgen.save()
// V up hint // V up hint
cur_path := os.getwd() cur_path := os.getwd()
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,30 +191,25 @@ 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] + ')"'
} }
return res return res
} }
////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////
// 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
line_nr int line_nr int
last_nl_pos int last_nl_pos int
} }
pub fn (s ScannerPos) str() string { pub fn (s ScannerPos) str() string {
@ -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) {
@ -217,7 +231,7 @@ fn (s mut Scanner) goto_scanner_position(scp ScannerPos) {
} }
fn (s &Scanner) get_last_nl_from_pos(_pos int) int { fn (s &Scanner) get_last_nl_from_pos(_pos int) int {
pos := if _pos >= s.text.len { s.text.len-1 } else { _pos } pos := if _pos >= s.text.len { s.text.len - 1 } else { _pos }
for i := pos; i >= 0; i-- { for i := pos; i >= 0; i-- {
if s.text[i] == `\n` { if s.text[i] == `\n` {
return i return i
@ -234,33 +248,27 @@ 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 {
dots_example = '.., ' + dots_example dots_example = '.., ' + dots_example
} }
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' + err_modify_bitfield = 'to modify a bitfield flag use the methods: set, clear, toggle. and to check for flag use: has'
'for example: `(a && b) || c` instead of `a && b || c`'
err_modify_bitfield = 'to modify a bitfield flag use the methods: set, clear, toggle. and to check for flag use: has'
) )

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // 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,36 +1,35 @@
// 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 {
mut: mut:
name string name string
deps []string deps []string
} }
struct DepGraph { struct DepGraph {
//pub: // pub:
mut: mut:
acyclic bool acyclic bool
nodes []DepGraphNode nodes []DepGraphNode
} }
struct DepSet { struct DepSet {
mut: mut:
items []string items []string
} }
pub fn(dset mut DepSet) add(item string) { pub fn (dset mut DepSet) add(item string) {
dset.items << item dset.items << item
} }
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
@ -39,7 +38,7 @@ pub fn(dset &DepSet) diff(otherset DepSet) DepSet {
return diff return diff
} }
pub fn(dset &DepSet) size() int { pub fn (dset &DepSet) size() int {
return dset.items.len return dset.items.len
} }
@ -49,37 +48,34 @@ 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()
} }
} }
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,25 +84,22 @@ 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
} }
pub fn(graph &DepGraph) last_node() DepGraphNode { pub fn (graph &DepGraph) last_node() DepGraphNode {
return graph.nodes[graph.nodes.len-1] return graph.nodes[graph.nodes.len - 1]
} }
pub fn(graph &DepGraph) display() string { pub fn (graph &DepGraph) display() 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 {
@ -116,7 +109,7 @@ pub fn(graph &DepGraph) display() string {
return out return out
} }
pub fn(graph &DepGraph) display_cycles() string { pub fn (graph &DepGraph) display_cycles() string {
mut node_names := map[string]DepGraphNode mut node_names := map[string]DepGraphNode
for node in graph.nodes { for node in graph.nodes {
node_names[node.name] = node node_names[node.name] = node
@ -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) {
@ -12,7 +11,7 @@ fn (p mut Parser) enum_decl(no_name bool) {
} }
p.check(.key_enum) p.check(.key_enum)
p.fspace() p.fspace()
mut enum_name := p.check_name() mut enum_name := p.check_name()
is_c := enum_name == 'C' && p.tok == .dot is_c := enum_name == 'C' && p.tok == .dot
if is_c { if is_c {
p.check(.dot) p.check(.dot)
@ -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)
@ -62,7 +62,7 @@ fn (p mut Parser) enum_decl(no_name bool) {
if is_flag && fields.len > 32 { if is_flag && fields.len > 32 {
p.error('when an enum is used as bit field, it must have a max of 32 fields') p.error('when an enum is used as bit field, it must have a max of 32 fields')
} }
mut T := Type { mut T := Type{
name: enum_name name: enum_name
mod: p.mod mod: p.mod
parent: 'int' parent: 'int'
@ -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

@ -718,7 +718,7 @@ fn (p mut Parser) factor() string {
// p.fgen('$sizeof_typ)') // p.fgen('$sizeof_typ)')
return 'int' return 'int'
} }
.amp,.dot,.mul { .amp, .dot, .mul {
// (dot is for enum vals: `.green`) // (dot is for enum vals: `.green`)
return p.name_expr() return p.name_expr()
} }
@ -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() {
@ -11,7 +10,7 @@ fn (p mut Parser) for_st() {
if p.tok != .lcbr { if p.tok != .lcbr {
p.fspace() p.fspace()
} }
//debug := p.scanner.file_path.contains('r_draw') // debug := p.scanner.file_path.contains('r_draw')
p.open_scope() p.open_scope()
mut label := 0 mut label := 0
mut to := 0 mut to := 0
@ -70,18 +69,19 @@ fn (p mut Parser) for_st() {
p.check(.key_in) p.check(.key_in)
p.fspace() p.fspace()
tmp := p.get_tmp() tmp := p.get_tmp()
mut typ, expr := p.tmp_expr() mut typ,expr := p.tmp_expr()
is_arr := typ.starts_with('array_') is_arr := typ.starts_with('array_')
is_map := typ.starts_with('map_') is_map := typ.starts_with('map_')
is_str := typ == 'string' is_str := typ == 'string'
is_variadic_arg := typ.starts_with('varg_') is_variadic_arg := typ.starts_with('varg_')
if !is_arr && !is_str && !is_map && !is_variadic_arg { if !is_arr && !is_str && !is_map && !is_variadic_arg {
p.error('cannot range over type `$typ`') p.error('cannot range over type `$typ`')
} }
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;')
} }
} }
@ -92,7 +92,7 @@ fn (p mut Parser) for_st() {
p.gen_for_varg_header(i, expr, typ, val) p.gen_for_varg_header(i, expr, typ, val)
} }
else if is_arr { else if is_arr {
typ = typ[6..].replace('_ptr','*') typ = typ[6..].replace('_ptr', '*')
p.gen_for_header(i, tmp, typ, val) p.gen_for_header(i, tmp, typ, val)
} }
else if is_map { else if is_map {
@ -109,7 +109,7 @@ fn (p mut Parser) for_st() {
if p.known_var(i) { if p.known_var(i) {
p.error('redefinition of `$i`') p.error('redefinition of `$i`')
} }
p.register_var(Var { p.register_var(Var{
name: i name: i
typ: i_var_type typ: i_var_type
is_mut: true is_mut: true
@ -120,7 +120,7 @@ fn (p mut Parser) for_st() {
if p.known_var(val) { if p.known_var(val) {
p.error('redefinition of `$val`') p.error('redefinition of `$val`')
} }
p.register_var(Var { p.register_var(Var{
name: val name: val
typ: typ typ: typ
ptr: typ.contains('*') ptr: typ.contains('*')
@ -134,9 +134,9 @@ fn (p mut Parser) for_st() {
p.check(.key_in) p.check(.key_in)
p.fspace() p.fspace()
tmp := p.get_tmp() tmp := p.get_tmp()
mut typ, expr := p.tmp_expr() mut typ,expr := p.tmp_expr()
is_range := p.tok == .dotdot is_range := p.tok == .dotdot
is_variadic_arg := typ.starts_with('varg_') is_variadic_arg := typ.starts_with('varg_')
mut range_end := '' mut range_end := ''
if is_range { if is_range {
p.check_types(typ, 'int') p.check_types(typ, 'int')
@ -144,14 +144,13 @@ fn (p mut Parser) for_st() {
if p.pref.x64 { if p.pref.x64 {
to = p.lit.int() to = p.lit.int()
} }
range_typ, range_expr := p.tmp_expr() range_typ,range_expr := p.tmp_expr()
p.check_types(range_typ, 'int') p.check_types(range_typ, 'int')
range_end = range_expr range_end = range_expr
if p.pref.x64 { if p.pref.x64 {
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;')
} }
} }
@ -177,7 +178,7 @@ fn (p mut Parser) for_st() {
p.gen_for_range_header(i, range_end, tmp, typ, val) p.gen_for_range_header(i, range_end, tmp, typ, val)
} }
else if is_arr { else if is_arr {
typ = typ[6..].replace('_ptr','*')// all after `array_` typ = typ[6..].replace('_ptr', '*') // all after `array_`
p.gen_for_header(i, tmp, typ, val) p.gen_for_header(i, tmp, typ, val)
} }
else if is_str { else if is_str {
@ -194,7 +195,7 @@ fn (p mut Parser) for_st() {
if p.known_var(val) { if p.known_var(val) {
p.error('redefinition of `$val`') p.error('redefinition of `$val`')
} }
p.register_var(Var { p.register_var(Var{
name: val name: val
typ: typ typ: typ
ptr: typ.contains('*') ptr: typ.contains('*')
@ -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) {
@ -27,7 +25,7 @@ fn (p mut Parser) gen_json_for_type(typ Type) {
} }
// println('gen_json_for_type( $typ.name )') // println('gen_json_for_type( $typ.name )')
// Register decoder fn // Register decoder fn
mut dec_fn := Fn { mut dec_fn := Fn{
mod: p.mod mod: p.mod
typ: 'Option_$typ.name' typ: 'Option_$typ.name'
name: js_dec_name(t) name: js_dec_name(t)
@ -38,19 +36,19 @@ fn (p mut Parser) gen_json_for_type(typ Type) {
} }
// decode_TYPE funcs receive an actual cJSON* object to decode // decode_TYPE funcs receive an actual cJSON* object to decode
// cJSON_Parse(str) call is added by the compiler // cJSON_Parse(str) call is added by the compiler
arg := Var { arg := Var{
typ: 'cJSON*' typ: 'cJSON*'
} }
dec_fn.args << arg dec_fn.args << arg
p.table.register_fn(dec_fn) p.table.register_fn(dec_fn)
// Register encoder fn // Register encoder fn
mut enc_fn := Fn { mut enc_fn := Fn{
mod: p.mod mod: p.mod
typ: 'cJSON*' typ: 'cJSON*'
name: js_enc_name(t) name: js_enc_name(t)
} }
// encode_TYPE funcs receive an object to encode // encode_TYPE funcs receive an object to encode
enc_arg := Var { enc_arg := Var{
typ: t typ: t
} }
enc_fn.args << enc_arg enc_fn.args << enc_arg
@ -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))'
@ -116,16 +106,13 @@ string res = tos2("");
enc += ' cJSON_AddItemToObject(o, "$name",$enc_name (val.$field.name)); \n' enc += ' cJSON_AddItemToObject(o, "$name",$enc_name (val.$field.name)); \n'
} }
// cJSON_delete // cJSON_delete
//p.cgen.fns << '$dec return opt_ok(res); \n}' // p.cgen.fns << '$dec return opt_ok(res); \n}'
p.cgen.fns << '$dec return opt_ok(res, sizeof(*res)); \n}' p.cgen.fns << '$dec return opt_ok(res, sizeof(*res)); \n}'
p.cgen.fns << '/*enc start*/ $enc return o;}' p.cgen.fns << '/*enc start*/ $enc return o;}'
} }
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

@ -7,10 +7,10 @@ fn (v &V) generate_hotcode_reloading_compiler_flags() []string {
mut a := []string mut a := []string
if v.pref.is_live || v.pref.is_so { if v.pref.is_live || v.pref.is_so {
// See 'man dlopen', and test running a GUI program compiled with -live // See 'man dlopen', and test running a GUI program compiled with -live
if (v.os == .linux || os.user_os() == 'linux'){ if (v.os == .linux || os.user_os() == 'linux') {
a << '-rdynamic' a << '-rdynamic'
} }
if (v.os == .mac || os.user_os() == 'mac'){ if (v.os == .mac || os.user_os() == 'mac') {
a << '-flat_namespace' a << '-flat_namespace'
} }
} }
@ -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,9 +60,10 @@ 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";')
cgen.genln(' live_fn_mutex = CreateMutexA(0, 0, 0);') cgen.genln(' live_fn_mutex = CreateMutexA(0, 0, 0);')
cgen.genln(' load_so(live_library_name);') cgen.genln(' load_so(live_library_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;
} }
@ -218,10 +214,10 @@ void reload_so() {
time__sleep_ms(100); time__sleep_ms(100);
} }
} }
' ) ')
} }
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; }')
} }
} }

File diff suppressed because it is too large Load Diff

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,28 +8,27 @@ 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
consts strings.Builder consts strings.Builder
fns strings.Builder fns strings.Builder
types strings.Builder types strings.Builder
tokens []Token tokens []Token
} }
// `mod` == "vlib/os" // `mod` == "vlib/os"
fn generate_vh(mod string) { fn generate_vh(mod string) {
println('\n\n\n\nGenerating a V header file for module `$mod`') println('\n\n\n\nGenerating a V header file for module `$mod`')
vexe := vexe_path() vexe := vexe_path()
full_mod_path := filepath.join(os.dir(vexe), mod) full_mod_path := filepath.join(os.dir(vexe),mod)
dir := if mod.starts_with('vlib') { '$compiler.v_modules_path${os.path_separator}$mod' } else { mod } dir := if mod.starts_with('vlib') { '$compiler.v_modules_path${os.path_separator}$mod' } else { mod }
path := dir + '.vh' path := dir + '.vh'
pdir := dir.all_before_last(os.path_separator) pdir := dir.all_before_last(os.path_separator)
@ -38,32 +36,28 @@ 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')
// Consts // Consts
println(full_mod_path) println(full_mod_path)
vfiles := os.walk_ext(full_mod_path, '.v') vfiles := os.walk_ext(full_mod_path, '.v')
//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') && // println('f:')
!it.ends_with('_lin.v') && // println(filtered)
!it.contains('${os.path_separator}examples') &&
!it.contains('_js.v') &&
!it.contains('_bare.v') &&
!it.contains('${os.path_separator}js')) // TODO merge once filter allows it
//println('f:')
//println(filtered)
mut v := new_v(['foo.v']) mut v := new_v(['foo.v'])
//v.pref.generating_vh = true // v.pref.generating_vh = true
mut g := VhGen{ mut g := VhGen{
consts : strings.new_builder(1000) consts: strings.new_builder(1000)
fns : strings.new_builder(1000) fns: strings.new_builder(1000)
types : strings.new_builder(1000) types: strings.new_builder(1000)
} }
for file in filtered { for file in filtered {
mut p := v.new_parser_from_file(file) mut p := v.new_parser_from_file(file)
@ -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 := result := g.types.str() + g.consts.str() + g.fns.str().replace('\n\n\n', '\n').replace('\n\n', '\n')
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()
} }
@ -97,23 +95,21 @@ fn (g mut VhGen) generate_fn() {
if g.i >= g.tokens.len - 2 { if g.i >= g.tokens.len - 2 {
return return
} }
mut next := g.tokens[g.i+1] mut next := g.tokens[g.i + 1]
if g.i > 0 && g.tokens[g.i-1].tok != .key_pub { if g.i > 0 && g.tokens[g.i - 1].tok != .key_pub {
// 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
} }
//out.write('pub ') // out.write('pub ')
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
g.fns.write(' ') g.fns.write(' ')
} }
@ -121,22 +117,22 @@ fn (g mut VhGen) generate_fn() {
tok = g.tokens[g.i] tok = g.tokens[g.i]
} }
g.fns.writeln('') g.fns.writeln('')
//g.i-- // g.i--
} }
fn (g mut VhGen) generate_alias() { fn (g mut VhGen) generate_alias() {
mut tok := g.tokens[g.i] mut tok := g.tokens[g.i]
for g.i < g.tokens.len-1 { for g.i < g.tokens.len - 1 {
g.types.write(tok.str()) g.types.write(tok.str())
g.types.write(' ') g.types.write(' ')
if tok.line_nr != g.tokens[g.i+1].line_nr { if tok.line_nr != g.tokens[g.i + 1].line_nr {
break break
} }
g.i++ g.i++
tok = g.tokens[g.i] tok = g.tokens[g.i]
} }
g.types.writeln('\n') g.types.writeln('\n')
//g.i-- // g.i--
} }
fn (g mut VhGen) generate_const() { fn (g mut VhGen) generate_const() {
@ -144,29 +140,30 @@ fn (g mut VhGen) generate_const() {
for g.i < g.tokens.len && tok.tok != .rpar { for g.i < g.tokens.len && tok.tok != .rpar {
g.consts.write(tok.str()) g.consts.write(tok.str())
g.consts.write(' ') g.consts.write(' ')
if g.tokens[g.i+2].tok == .assign { if g.tokens[g.i + 2].tok == .assign {
g.consts.write('\n\t') g.consts.write('\n\t')
} }
g.i++ g.i++
tok = g.tokens[g.i] tok = g.tokens[g.i]
} }
g.consts.writeln('\n)') g.consts.writeln('\n)')
//g.i-- // g.i--
} }
fn (g mut VhGen) generate_type() { fn (g mut VhGen) generate_type() {
//old := g.i // old := g.i
mut tok := g.tokens[g.i] mut tok := g.tokens[g.i]
for g.i < g.tokens.len && tok.tok != .rcbr { for g.i < g.tokens.len && tok.tok != .rcbr {
g.types.write(tok.str()) g.types.write(tok.str())
g.types.write(' ') g.types.write(' ')
if g.tokens[g.i+1].line_nr != g.tokens[g.i].line_nr { if g.tokens[g.i + 1].line_nr != g.tokens[g.i].line_nr {
g.types.write('\n\t') g.types.write('\n\t')
} }
g.i++ g.i++
tok = g.tokens[g.i] tok = g.tokens[g.i]
} }
g.types.writeln('\n}') g.types.writeln('\n}')
//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,15 +9,13 @@ 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:
imports map[string]string // alias => module imports map[string]string // alias => module
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 {
@ -26,7 +23,7 @@ fn (table &Table) qualify_module(mod string, file_path string) string {
if m.contains('.') && m.contains(mod) { if m.contains('.') && m.contains(mod) {
m_parts := m.split('.') m_parts := m.split('.')
m_path := m_parts.join(os.path_separator) m_path := m_parts.join(os.path_separator)
if mod == m_parts[m_parts.len-1] && file_path.contains(m_path) { if mod == m_parts[m_parts.len - 1] && file_path.contains(m_path) {
return m return m
} }
} }
@ -36,7 +33,7 @@ fn (table &Table) qualify_module(mod string, file_path string) string {
fn new_import_table() ImportTable { fn new_import_table() ImportTable {
return ImportTable{ return ImportTable{
imports: map[string]string imports: map[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('.')
@ -121,7 +120,7 @@ pub fn (v &V) resolve_deps() &DepGraph {
} }
// graph of all imported modules // graph of all imported modules
pub fn(v &V) import_graph() &DepGraph { pub fn (v &V) import_graph() &DepGraph {
mut graph := new_dep_graph() mut graph := new_dep_graph()
for p in v.parsers { for p in v.parsers {
mut deps := []string mut deps := []string
@ -134,7 +133,7 @@ pub fn(v &V) import_graph() &DepGraph {
} }
// get ordered imports (module speficic dag method) // get ordered imports (module speficic dag method)
pub fn(graph &DepGraph) imports() []string { pub fn (graph &DepGraph) imports() []string {
mut mods := []string mut mods := []string
for node in graph.nodes { for node in graph.nodes {
mods << node.name mods << node.name
@ -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,33 +150,30 @@ 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:
// 0) V test files are very commonly located right inside the folder of the // 0) V test files are very commonly located right inside the folder of the
// 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.
// 3) search in vlib/ // 3) search in vlib/
// 4.1) search in -vpath (if given) // 4.1) search in -vpath (if given)
// 4.2) search in ~/.vmodules/ (i.e. modules installed with vpm) (no -vpath) // 4.2) search in ~/.vmodules/ (i.e. modules installed with vpm) (no -vpath)
v.module_lookup_paths = [] v.module_lookup_paths = []
if v.pref.is_test { if v.pref.is_test {
v.module_lookup_paths << os.basedir(v.compiled_dir) // pdir of _test.v v.module_lookup_paths << os.basedir(v.compiled_dir) // pdir of _test.v
} }
v.module_lookup_paths << v.compiled_dir v.module_lookup_paths << v.compiled_dir
v.module_lookup_paths << filepath.join(v.compiled_dir, 'modules') v.module_lookup_paths << filepath.join(v.compiled_dir,'modules')
v.module_lookup_paths << v.pref.vlib_path v.module_lookup_paths << v.pref.vlib_path
v.module_lookup_paths << mlookup_path v.module_lookup_paths << mlookup_path
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')
} }
@ -185,20 +182,27 @@ fn (v mut V) set_module_lookup_paths(){
fn (v &V) find_module_path(mod string) ?string { 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 {
return try_path println(' << found $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,27 +3,22 @@ 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
ucrt_include_path string
um_include_path string vs_include_path string
ucrt_include_path string
vs_include_path string
shared_include_path string shared_include_path string
} }
// 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)
} }
} }
@ -71,11 +57,10 @@ 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,45 +68,38 @@ 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')
} }
// Try and find win10 kit // Try and find win10 kit
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'
@ -132,8 +110,8 @@ fn find_windows_kit_root(host_arch string) ?WindowsKit {
struct VsInstallation { struct VsInstallation {
include_path string include_path string
lib_path string lib_path string
exe_path string exe_path string
} }
fn find_vs(vswhere_dir string, host_arch string) ?VsInstallation { fn find_vs(vswhere_dir string, host_arch string) ?VsInstallation {
@ -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,45 +150,33 @@ 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 { wk := find_windows_kit_root(host_arch)or{
'%ProgramFiles(x86)%'
}
host_arch := if processor_architecture == 'x86' {
'X86'
} else {
'X64'
}
wk := find_windows_kit_root(host_arch) or {
return error('Unable to find windows sdk') 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
ucrt_lib_path: wk.ucrt_lib_path
um_lib_path: wk.um_lib_path, vs_lib_path: vs.lib_path
ucrt_lib_path: wk.ucrt_lib_path, um_include_path: wk.um_include_path
vs_lib_path: vs.lib_path, ucrt_include_path: wk.ucrt_include_path
vs_include_path: vs.include_path
um_include_path: wk.um_include_path, shared_include_path: wk.shared_include_path
ucrt_include_path: wk.ucrt_include_path,
vs_include_path: vs.include_path,
shared_include_path: wk.shared_include_path,
} }
} } $else {
$else {
verror('Cannot find msvc on this OS') verror('Cannot find msvc on this OS')
return error('msvc not found') return error('msvc not found')
} }
} }
pub fn (v mut V) cc_msvc() { pub fn (v mut V) cc_msvc() {
r := find_msvc() or { r := find_msvc()or{
// TODO: code reuse // TODO: code reuse
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)
@ -231,41 +184,35 @@ 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
a << '/c' a << '/c'
@ -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,10 +279,8 @@ 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')
return return
@ -371,51 +290,44 @@ 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"'
//NB: the quotes above ARE balanced. // NB: the quotes above ARE balanced.
println('thirdparty cmd line: $cmd') println('thirdparty cmd line: $cmd')
res := os.exec(cmd) or { res := os.exec(cmd)or{
println('msvc: failed thirdparty object build cmd: $cmd') println('msvc: failed thirdparty object build cmd: $cmd')
verror(err) verror(err)
return return
@ -428,12 +340,11 @@ 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
inc_paths []string inc_paths []string
lib_paths []string lib_paths []string
other_flags []string other_flags []string
} }
@ -441,9 +352,9 @@ fn (cflags []CFlag) msvc_string_flags() MsvcStringFlags {
mut real_libs := []string mut real_libs := []string
mut inc_paths := []string mut inc_paths := []string
mut lib_paths := []string mut lib_paths := []string
mut other_flags := []string mut other_flags := []string
for flag in cflags { for flag in cflags {
//println('fl: $flag.name | flag arg: $flag.value') // println('fl: $flag.name | flag arg: $flag.value')
// We need to see if the flag contains -l // We need to see if the flag contains -l
// -l isnt recognised and these libs will be passed straight to the linker // -l isnt recognised and these libs will be passed straight to the linker
// by the compiler // by the compiler
@ -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`
@ -13,12 +12,13 @@ fn (p mut Parser) in_optimization(typ string, ph int) {
// Get `a` expr value (can be a string literal, not a variable) // Get `a` expr value (can be a string literal, not a variable)
expr := p.cgen.cur_line[ph..] expr := p.cgen.cur_line[ph..]
is_str := typ == 'string' is_str := typ == 'string'
//println('!! $p.expr_var.name => $name ($typ)') // println('!! $p.expr_var.name => $name ($typ)')
for p.tok != .rsbr && p.tok != .eof { for p.tok != .rsbr && p.tok != .eof {
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
@ -474,7 +472,7 @@ fn (p mut Parser) parse(pass Pass) {
.key_const { .key_const {
p.const_decl() p.const_decl()
} }
.key_struct,.key_union,.key_interface { .key_struct, .key_union, .key_interface {
p.struct_decl() p.struct_decl()
} }
.key_enum { .key_enum {
@ -498,7 +496,7 @@ fn (p mut Parser) parse(pass Pass) {
// or a struct definition // or a struct definition
p.attribute() p.attribute()
} }
.key_struct,.key_interface,.key_union,.lsbr { .key_struct, .key_interface, .key_union, .lsbr {
p.struct_decl() p.struct_decl()
} }
.key_const { .key_const {
@ -982,7 +980,7 @@ fn (p mut Parser) get_type() string {
// Register anon fn type // Register anon fn type
fn_typ := Type{ fn_typ := Type{
name: f.typ_str() // 'fn (int, int) string' name: f.typ_str() // 'fn (int, int) string'
mod: p.mod mod: p.mod
func: f func: f
} }
@ -1364,7 +1362,7 @@ fn (p mut Parser) statement(add_semi bool) string {
.key_match { .key_match {
p.match_statement(false) p.match_statement(false)
} }
.key_mut,.key_static { .key_mut, .key_static {
p.var_decl() p.var_decl()
} }
.key_return { .key_return {

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
@ -10,37 +9,41 @@ fn sql_params2params_gen(sql_params []string, sql_types []string, qprefix string
mut params_gen := '' mut params_gen := ''
for i, mparam in sql_params { for i, mparam in sql_params {
param := mparam.trim_space() param := mparam.trim_space()
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')
} }
} }
} }
//println('>>>>>>>> params_gen') // println('>>>>>>>> params_gen')
//println( params_gen ) // println( params_gen )
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,
// because we can have many queries in the _same_ scope. // because we can have many queries in the _same_ scope.
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)
@ -56,7 +59,7 @@ fn (p mut Parser) select_query(fn_ph int) string {
if typ.name == '' { if typ.name == '' {
p.error('unknown type `$table_name`') p.error('unknown type `$table_name`')
} }
//fields := typ.fields.filter(typ == 'string' || typ == 'int') // fields := typ.fields.filter(typ == 'string' || typ == 'int')
// get only string and int fields // get only string and int fields
mut fields := []Var mut fields := []Var
for i, field in typ.fields { for i, field in typ.fields {
@ -86,20 +89,24 @@ fn (p mut Parser) select_query(fn_ph int) string {
q += ' from ' q += ' from '
} }
for field in fields { for field in fields {
//println('registering sql field var $field.name') // println('registering sql field var $field.name')
if !(field.typ in ['string', 'int', 'bool']) { if !(field.typ in ['string', 'int', 'bool']) {
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
if p.tok == .name && p.lit == 'where' { if p.tok == .name && p.lit == 'where' {
p.next() p.next()
p.is_sql = true p.is_sql = true
_, expr := p.tmp_expr() _,expr := p.tmp_expr()
p.is_sql = false p.is_sql = false
q += ' where ' + expr q += ' where ' + expr
} }
@ -108,7 +115,7 @@ fn (p mut Parser) select_query(fn_ph int) string {
if p.tok == .name && p.lit == 'limit' { if p.tok == .name && p.lit == 'limit' {
p.next() p.next()
p.is_sql = true p.is_sql = true
_, limit := p.tmp_expr() _,limit := p.tmp_expr()
p.is_sql = false p.is_sql = false
q += ' limit ' + limit q += ' limit ' + limit
// `limit 1` means we are getting `?User`, not `[]User` // `limit 1` means we are getting `?User`, not `[]User`
@ -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,12 +141,11 @@ 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 {
mut params_gen := sql_params2params_gen( p.sql_params, p.sql_types, qprefix ) mut params_gen := sql_params2params_gen(p.sql_params, p.sql_types, qprefix)
p.cgen.insert_before(' p.cgen.insert_before('
char* ${qprefix}params[$p.sql_i]; char* ${qprefix}params[$p.sql_i];
@ -164,7 +170,7 @@ ${obj_gen.str()}
// Array // Array
else { else {
q += ' order by id' q += ' order by id'
params_gen := sql_params2params_gen( p.sql_params, p.sql_types, qprefix ) params_gen := sql_params2params_gen(p.sql_params, p.sql_types, qprefix)
p.cgen.insert_before('char* ${qprefix}params[$p.sql_i]; p.cgen.insert_before('char* ${qprefix}params[$p.sql_i];
$params_gen $params_gen
@ -181,17 +187,18 @@ 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 {
@ -219,9 +228,9 @@ fn (p mut Parser) insert_query(fn_ph int) {
p.error('V orm: `id int` must be the first field in `$var.typ`') p.error('V orm: `id int` must be the first field in `$var.typ`')
} }
table_name := var.typ table_name := var.typ
mut sfields := '' // 'name, city, country' mut sfields := '' // 'name, city, country'
mut params := '' // params[0] = 'bob'; params[1] = 'Vienna'; mut params := '' // params[0] = 'bob'; params[1] = 'Vienna';
mut vals := '' // $1, $2, $3... mut vals := '' // $1, $2, $3...
mut nr_vals := 0 mut nr_vals := 0
for i, field in fields { for i, field in fields {
if field.name == 'id' { if field.name == 'id' {
@ -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,11 +286,16 @@ 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
set_typ, expr := p.tmp_expr() set_typ,expr := p.tmp_expr()
p.is_sql = false p.is_sql = false
// TODO this hack should not be necessary // TODO this hack should not be necessary
if set_typ == 'bool' { if set_typ == 'bool' {
@ -289,21 +305,20 @@ fn (p mut Parser) update_query(fn_ph int) {
else { else {
q += 'false' q += 'false'
} }
} else { }
else {
q += expr q += expr
} }
// where // where
if p.tok == .name && p.lit == 'where' { if p.tok == .name && p.lit == 'where' {
p.next() p.next()
p.is_sql = true p.is_sql = true
_, wexpr := p.tmp_expr() _,wexpr := p.tmp_expr()
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( ')
println('update q="$q"') println('update q="$q"')
p.genln('.conn, "$q", $nr_vals, 0, params, 0, 0, 0)') p.genln('.conn, "$q", $nr_vals, 0, params, 0, 0, 0)')

View File

@ -87,7 +87,7 @@ struct ScanRes {
lit string lit string
} }
fn scan_res(tok TokenKind,lit string) ScanRes { fn scan_res(tok TokenKind, lit string) ScanRes {
return ScanRes{ return ScanRes{
tok,lit} tok,lit}
} }
@ -271,7 +271,7 @@ fn (s mut Scanner) scan() ScanRes {
name := s.ident_name() name := s.ident_name()
// tmp hack to detect . in ${} // tmp hack to detect . in ${}
// Check if not .eof to prevent panic // Check if not .eof to prevent panic
next_char := if s.pos + 1 < s.text.len {s.text[s.pos + 1]}else {`\0`} next_char := if s.pos + 1 < s.text.len { s.text[s.pos + 1] } else { `\0` }
if is_key(name) { if is_key(name) {
return scan_res(key_to_token(name), '') return scan_res(key_to_token(name), '')
} }
@ -306,7 +306,7 @@ fn (s mut Scanner) scan() ScanRes {
if c == `)` && s.inter_start { if c == `)` && s.inter_start {
s.inter_end = true s.inter_end = true
s.inter_start = false s.inter_start = false
next_char := if s.pos + 1 < s.text.len {s.text[s.pos + 1]}else {`\0`} next_char := if s.pos + 1 < s.text.len { s.text[s.pos + 1] } else { `\0` }
if next_char == s.quote { if next_char == s.quote {
s.inside_string = false s.inside_string = false
} }
@ -360,7 +360,7 @@ fn (s mut Scanner) scan() ScanRes {
`?` { `?` {
return scan_res(.question, '') return scan_res(.question, '')
} }
single_quote,double_quote { single_quote, double_quote {
return scan_res(.str, s.ident_string()) return scan_res(.str, s.ident_string())
} }
`\`` { `\`` {
@ -659,7 +659,7 @@ fn (s &Scanner) current_column() int {
return s.pos - s.last_nl_pos return s.pos - s.last_nl_pos
} }
fn (s Scanner) count_symbol_before(p int,sym byte) int { fn (s Scanner) count_symbol_before(p int, sym byte) int {
mut count := 0 mut count := 0
for i := p; i >= 0; i-- { for i := p; i >= 0; i-- {
if s.text[i] != sym { if s.text[i] != sym {
@ -775,10 +775,10 @@ fn (s mut Scanner) ident_char() string {
return '`' return '`'
} }
// Escapes a `'` character // Escapes a `'` character
return if c == "\'" {'\\' + c}else {c} return if c == "\'" { '\\' + c } else { c }
} }
fn (s &Scanner) expect(want string,start_pos int) bool { fn (s &Scanner) expect(want string, start_pos int) bool {
end_pos := start_pos + want.len end_pos := start_pos + want.len
if start_pos < 0 || start_pos >= s.text.len { if start_pos < 0 || start_pos >= s.text.len {
return false return false
@ -841,7 +841,7 @@ fn (s mut Scanner) inc_line_number() {
fn (s Scanner) line(n int) string { fn (s Scanner) line(n int) string {
mut res := '' mut res := ''
if n >= 0 && n < s.line_ends.len { if n >= 0 && n < s.line_ends.len {
nline_start := if n == 0 {0}else {s.line_ends[n - 1]} nline_start := if n == 0 { 0 } else { s.line_ends[n - 1] }
nline_end := s.line_ends[n] nline_end := s.line_ends[n]
if nline_start <= nline_end { if nline_start <= nline_end {
res = s.text[nline_start..nline_end] res = s.text[nline_start..nline_end]
@ -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

@ -12,7 +12,7 @@ fn (p mut Parser) string_expr() {
str := p.lit str := p.lit
// No ${}, just return a simple string // No ${}, just return a simple string
if p.peek() != .str_dollar || is_raw { if p.peek() != .str_dollar || is_raw {
f := if is_raw {cescaped_path(str)}else {format_str(str)} f := if is_raw { cescaped_path(str) } else { format_str(str) }
// `C.puts('hi')` => `puts("hi");` // `C.puts('hi')` => `puts("hi");`
/* /*
Calling a C function sometimes requires a call to a string method Calling a C function sometimes requires a call to a string method
@ -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 {
@ -67,18 +67,18 @@ fn (p mut Parser) struct_decl() {
} }
mut typ := p.table.find_type(name) mut typ := p.table.find_type(name)
if p.pass == .decl && p.table.known_type_fast(typ) { if p.pass == .decl && p.table.known_type_fast(typ) {
//if name in reserved_type_param_names { // if name in reserved_type_param_names {
//p.error('name `$name` is reserved for type parameters') // p.error('name `$name` is reserved for type parameters')
//} else { // } else {
p.error('type `$name` redeclared') p.error('type `$name` redeclared')
//} // }
} }
if is_objc { if is_objc {
// Forward declaration of an Objective-C interface with `@class` :) // Forward declaration of an Objective-C interface with `@class` :)
p.gen_typedef('@class $name;') p.gen_typedef('@class $name;')
} }
else if !is_c { else if !is_c {
kind := if is_union {'union'} else {'struct'} kind := if is_union { 'union' } else { 'struct' }
p.gen_typedef('typedef $kind $name $name;') p.gen_typedef('typedef $kind $name $name;')
} }
// Register the type // Register the type
@ -96,7 +96,7 @@ fn (p mut Parser) struct_decl() {
p.table.rewrite_type(typ) p.table.rewrite_type(typ)
} }
else { else {
typ = Type { typ = Type{
name: name name: name
mod: p.mod mod: p.mod
is_c: is_c is_c: is_c
@ -114,14 +114,14 @@ fn (p mut Parser) struct_decl() {
p.check(.lcbr) p.check(.lcbr)
// Struct fields // Struct fields
mut access_mod := AccessMod.private mut access_mod := AccessMod.private
//mut is_pub_field := false // mut is_pub_field := false
//mut is_mut := false // mut is_mut := false
mut names := []string// to avoid dup names TODO alloc perf mut names := []string // to avoid dup names TODO alloc perf
mut fmt_max_len := p.table.max_field_len[name] mut fmt_max_len := p.table.max_field_len[name]
//println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass') // println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
if !is_ph && p.first_pass() { if !is_ph && p.first_pass() {
p.table.register_type(typ) p.table.register_type(typ)
//println('registering 1 nrfields=$typ.fields.len') // println('registering 1 nrfields=$typ.fields.len')
} }
mut did_gen_something := false mut did_gen_something := false
mut used := []AccessMod mut used := []AccessMod
@ -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 {
@ -203,7 +204,7 @@ fn (p mut Parser) struct_decl() {
continue continue
} }
// `pub` access mod // `pub` access mod
//access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private} // access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
p.fspace() p.fspace()
tt := p.get_type2() tt := p.get_type2()
field_type := tt.name field_type := tt.name
@ -222,11 +223,11 @@ fn (p mut Parser) struct_decl() {
// `a int = 4` // `a int = 4`
if p.tok == .assign { if p.tok == .assign {
p.next() p.next()
def_val_type, expr := p.tmp_expr() def_val_type,expr := p.tmp_expr()
if def_val_type != field_type { if def_val_type != field_type {
p.error('expected `$field_type` but got `$def_val_type`') p.error('expected `$field_type` but got `$def_val_type`')
} }
//println('pass=$p.pass $typ.name ADDING field=$field_name "$def_val_type" "$expr"') // println('pass=$p.pass $typ.name ADDING field=$field_name "$def_val_type" "$expr"')
if !p.first_pass() { if !p.first_pass() {
p.table.add_default_val(i, typ.name, expr) p.table.add_default_val(i, typ.name, expr)
} }
@ -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,24 +261,22 @@ 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
} }
if p.scanner.is_fmt && p.pass == .decl { if p.scanner.is_fmt && p.pass == .decl {
p.table.max_field_len[typ.name] = fmt_max_len p.table.max_field_len[typ.name] = fmt_max_len
} }
//p.fgen_require_nl() // p.fgen_require_nl()
p.check(.rcbr) p.check(.rcbr)
if !is_c && !did_gen_something && p.first_pass() { if !is_c && !did_gen_something && p.first_pass() {
p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private) p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private)
} }
p.fgen_nl() p.fgen_nl()
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
@ -292,7 +296,7 @@ fn (p mut Parser) struct_init(typ string) string {
peek := p.peek() peek := p.peek()
if peek == .colon || p.tok == .rcbr { if peek == .colon || p.tok == .rcbr {
for p.tok != .rcbr { for p.tok != .rcbr {
field := if typ != 'Option' { p.table.var_cgen_name( p.check_name() ) } else { p.check_name() } field := if typ != 'Option' { p.table.var_cgen_name(p.check_name()) } else { p.check_name() }
if !p.first_pass() && !t.has_field(field) { if !p.first_pass() && !t.has_field(field) {
p.error('`$t.name` has no field `$field`') p.error('`$t.name` has no field `$field`')
} }
@ -312,7 +316,7 @@ fn (p mut Parser) struct_init(typ string) string {
p.check(.colon) p.check(.colon)
p.fspace() p.fspace()
p.expected_type = f.typ p.expected_type = f.typ
p.check_types(p.bool_expression(), f.typ) p.check_types(p.bool_expression(), f.typ)
if p.tok == .comma { if p.tok == .comma {
p.next() p.next()
p.fremove_last() p.fremove_last()
@ -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

@ -875,13 +875,13 @@ fn (p mut Parser) typ_to_fmt(typ string, level int) string {
'ustring' { 'ustring' {
return '%.*s' return '%.*s'
} }
'byte','bool','int','char','byte','i16','i8' { 'byte', 'bool', 'int', 'char', 'byte', 'i16', 'i8' {
return '%d' return '%d'
} }
'u16','u32' { 'u16', 'u32' {
return '%u' return '%u'
} }
'f64','f32' { 'f64', 'f32' {
return '%f' return '%f'
} }
'i64' { 'i64' {
@ -890,7 +890,7 @@ fn (p mut Parser) typ_to_fmt(typ string, level int) string {
'u64' { 'u64' {
return '%llu' return '%llu'
} }
'byte*','byteptr' { 'byte*', 'byteptr' {
return '%s' return '%s'
} }
// case 'array_string': return '%s' // case 'array_string': return '%s'

View File

@ -1,25 +1,23 @@
// 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 {
tok TokenKind // the token number/enum; for quick comparisons tok TokenKind // the token number/enum; for quick comparisons
lit string // literal representation of the token lit string // literal representation of the token
line_nr int // the line number in the source where the token occured line_nr int // the line number in the source where the token occured
name_idx int // name table index for O(1) lookup name_idx int // name table index for O(1) lookup
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
number // 123 number // 123
str // 'foo' str // 'foo'
str_inter // 'name=$user.name' str_inter // 'name=$user.name'
chartoken // `A` chartoken // `A`
plus plus
minus minus
mul mul
@ -44,7 +42,7 @@ enum TokenKind {
str_dollar str_dollar
left_shift left_shift
righ_shift righ_shift
//at // @ // at // @
assign // = assign // =
decl_assign // := decl_assign // :=
plus_assign // += plus_assign // +=
@ -102,7 +100,7 @@ enum TokenKind {
key_import_const key_import_const
key_in key_in
key_interface key_interface
//key_it // key_it
key_match key_match
key_module key_module
key_mut key_mut
@ -114,7 +112,7 @@ enum TokenKind {
key_switch key_switch
key_true key_true
key_type key_type
//typeof // typeof
key_orelse key_orelse
key_union key_union
key_pub key_pub
@ -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++ {
@ -163,7 +163,7 @@ fn build_token_str() []string {
s[TokenKind.dotdot] = '..' s[TokenKind.dotdot] = '..'
s[TokenKind.ellipsis] = '...' s[TokenKind.ellipsis] = '...'
s[TokenKind.comma] = ',' s[TokenKind.comma] = ','
//s[TokenKind.at] = '@' // s[TokenKind.at] = '@'
s[TokenKind.semicolon] = ';' s[TokenKind.semicolon] = ';'
s[TokenKind.colon] = ':' s[TokenKind.colon] = ':'
s[TokenKind.arrow] = '=>' s[TokenKind.arrow] = '=>'
@ -202,7 +202,7 @@ fn build_token_str() []string {
s[TokenKind.key_assert] = 'assert' s[TokenKind.key_assert] = 'assert'
s[TokenKind.key_struct] = 'struct' s[TokenKind.key_struct] = 'struct'
s[TokenKind.key_if] = 'if' s[TokenKind.key_if] = 'if'
//s[TokenKind.key_it] = 'it' // s[TokenKind.key_it] = 'it'
s[TokenKind.key_else] = 'else' s[TokenKind.key_else] = 'else'
s[TokenKind.key_asm] = 'asm' s[TokenKind.key_asm] = 'asm'
s[TokenKind.key_return] = 'return' s[TokenKind.key_return] = 'return'
@ -223,7 +223,7 @@ fn build_token_str() []string {
s[TokenKind.key_import] = 'import' s[TokenKind.key_import] = 'import'
s[TokenKind.key_embed] = 'embed' s[TokenKind.key_embed] = 'embed'
s[TokenKind.key_unsafe] = 'unsafe' s[TokenKind.key_unsafe] = 'unsafe'
//Tokens[key_typeof] = 'typeof' // Tokens[key_typeof] = 'typeof'
s[TokenKind.key_enum] = 'enum' s[TokenKind.key_enum] = 'enum'
s[TokenKind.key_interface] = 'interface' s[TokenKind.key_interface] = 'interface'
s[TokenKind.key_pub] = 'pub' s[TokenKind.key_pub] = 'pub'
@ -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
@ -9,13 +8,16 @@ 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
} }
pub fn get_vtmp_filename(base_file_name string, postfix string) string { 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

@ -2,41 +2,43 @@ module compiler
import os import os
pub fn launch_tool(tname string){ pub fn launch_tool(tname string) {
vexe := vexe_path() vexe := vexe_path()
vroot := os.dir(vexe) vroot := os.dir(vexe)
mut oargs := os.args mut oargs := os.args
oargs[0] = '"$vexe"' // make it more explicit oargs[0] = '"$vexe"' // make it more explicit
tool_exe := os.realpath('$vroot/tools/$tname') tool_exe := os.realpath('$vroot/tools/$tname')
tool_source := os.realpath('$vroot/tools/${tname}.v') tool_source := os.realpath('$vroot/tools/${tname}.v')
////////////////////////////////////////////////////// // ////////////////////////////////////////////////////
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{ }
if os.file_last_mod_unix( tool_exe ) <= os.file_last_mod_unix( vexe ) { else {
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
tool_should_be_recompiled = true tool_should_be_recompiled = true
} }
if os.file_last_mod_unix( tool_exe ) <= os.file_last_mod_unix( tool_source ) { if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(tool_source) {
// the user changed the source code of the tool // the user changed the source code of the tool
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,14 +28,13 @@ 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
g.buf << elfdata2lsb // data encoding g.buf << elfdata2lsb // data encoding
g.buf << ev_current // file version g.buf << ev_current // file version
g.buf << 1//elf_osabi g.buf << 1 // elf_osabi
g.write64(0)//et_rel) // et_rel for .o g.write64(0) // et_rel) // et_rel for .o
g.write16(2) // e_type g.write16(2) // e_type
g.write16(e_machine) // g.write16(e_machine) //
g.write32(ev_current) // e_version g.write32(ev_current) // e_version
@ -70,7 +63,7 @@ pub fn (g mut Gen) generate_elf_header() {
// user code starts here at // user code starts here at
// address: 00070 and a half // address: 00070 and a half
g.code_start_pos = g.buf.len g.code_start_pos = g.buf.len
g.call(0)// call main function, it's not guaranteed to be the first g.call(0) // call main function, it's not guaranteed to be the first
} }
pub fn (g mut Gen) generate_elf_footer() { pub fn (g mut Gen) generate_elf_footer() {
@ -84,19 +77,21 @@ pub fn (g mut Gen) generate_elf_footer() {
g.write64_at(segment_start + g.buf.len, int(g.str_pos[i])) g.write64_at(segment_start + g.buf.len, int(g.str_pos[i]))
g.write_string(s) g.write_string(s)
g.write8(6) g.write8(6)
} }
// Now we know the file size, set it // Now we know the file size, set it
file_size := g.buf.len file_size := g.buf.len
g.write64_at(file_size, g.file_size_pos) // set file size 64 bit value g.write64_at(file_size, g.file_size_pos) // set file size 64 bit value
g.write64_at(file_size, g.file_size_pos+8) g.write64_at(file_size, g.file_size_pos + 8)
// call main function, it's not guaranteed to be the first // call main function, it's not guaranteed to be the first
// we generated call(0) ("e8 0") // we generated call(0) ("e8 0")
// no need to replace "0" with a relative address of the main function // no need to replace "0" with a relative address of the main function
// +1 is for "e8" // +1 is for "e8"
// -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,26 +9,27 @@ 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
typ SectionType typ SectionType
flags i64 flags i64
data voidptr data voidptr
is_saa bool is_saa bool
datalen i64 datalen i64
link int link int
info int info int
align i64 align i64
entsize i64 entsize i64
} }
fn (g mut Gen) section_header(c SectionConfig) { fn (g mut Gen) section_header(c SectionConfig) {
g.write32(g.sect_header_name_pos) g.write32(g.sect_header_name_pos)
@ -38,15 +37,14 @@ fn (g mut Gen) section_header(c SectionConfig) {
g.write32(int(c.typ)) g.write32(int(c.typ))
g.write64(c.flags) g.write64(c.flags)
g.write64(0) // sh_addr g.write64(0) // sh_addr
g.write64(g.offset)// offset g.write64(g.offset) // offset
g.offset += c.datalen+1 g.offset += c.datalen + 1
g.write64(c.datalen) g.write64(c.datalen)
g.write32(c.link) g.write32(c.link)
g.write32(c.info) g.write32(c.info)
g.write64(c.align) g.write64(c.align)
g.write64(c.entsize) g.write64(c.entsize)
} }
fn genobj() { fn genobj() {
/* /*
@ -158,4 +156,4 @@ fn genobj() {
}) })
*/ */
} }

View File

@ -1,22 +1,21 @@
// 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 {
out_name string out_name string
mut: mut:
buf []byte buf []byte
sect_header_name_pos int sect_header_name_pos int
offset i64 offset i64
str_pos []i64 str_pos []i64
strings []string // TODO use a map and don't duplicate strings strings []string // TODO use a map and don't duplicate strings
file_size_pos i64 file_size_pos i64
main_fn_addr i64 main_fn_addr i64
code_start_pos i64 // location of the start of the assembly instructions code_start_pos i64 // location of the start of the assembly instructions
fn_addr map[string]i64 fn_addr map[string]i64
//string_addr map[string]i64 // string_addr map[string]i64
} }
enum Register { enum Register {
@ -39,7 +38,7 @@ enum Size {
pub fn new_gen(out_name string) &Gen { pub fn new_gen(out_name string) &Gen {
return &Gen{ return &Gen{
sect_header_name_pos : 0 sect_header_name_pos: 0
buf: [] buf: []
out_name: out_name out_name: out_name
} }
@ -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)
@ -58,39 +56,39 @@ fn (g mut Gen) write8(n int) {
fn (g mut Gen) write16(n int) { fn (g mut Gen) write16(n int) {
// write 2 bytes // write 2 bytes
g.buf << byte(n) g.buf << byte(n)
g.buf << byte(n >> 8) g.buf << byte(n>>8)
} }
fn (g mut Gen) write32(n int) { fn (g mut Gen) write32(n int) {
// write 4 bytes // write 4 bytes
g.buf << byte(n) g.buf << byte(n)
g.buf << byte(n >> 8) g.buf << byte(n>>8)
g.buf << byte(n >> 16) g.buf << byte(n>>16)
g.buf << byte(n >> 24) g.buf << byte(n>>24)
} }
fn (g mut Gen) write64(n i64) { fn (g mut Gen) write64(n i64) {
// write 8 bytes // write 8 bytes
g.buf << byte(n) g.buf << byte(n)
g.buf << byte(n >> 8) g.buf << byte(n>>8)
g.buf << byte(n >> 16) g.buf << byte(n>>16)
g.buf << byte(n >> 24) g.buf << byte(n>>24)
g.buf << byte(n >> 32) g.buf << byte(n>>32)
g.buf << byte(n >> 40) g.buf << byte(n>>40)
g.buf << byte(n >> 48) g.buf << byte(n>>48)
g.buf << byte(n >> 56) g.buf << byte(n>>56)
} }
fn (g mut Gen) write64_at(n i64, at i64) { fn (g mut Gen) write64_at(n i64, at i64) {
// write 8 bytes // write 8 bytes
g.buf[at] = byte(n) g.buf[at] = byte(n)
g.buf[at+1] = byte(n >> 8) g.buf[at + 1] = byte(n>>8)
g.buf[at+2] = byte(n >> 16) g.buf[at + 2] = byte(n>>16)
g.buf[at+3] = byte(n >> 24) g.buf[at + 3] = byte(n>>24)
g.buf[at+4] = byte(n >> 32) g.buf[at + 4] = byte(n>>32)
g.buf[at+5] = byte(n >> 40) g.buf[at + 5] = byte(n>>40)
g.buf[at+6] = byte(n >> 48) g.buf[at + 6] = byte(n>>48)
g.buf[at+7] = byte(n >> 56) g.buf[at + 7] = byte(n>>56)
} }
fn (g mut Gen) write_string(s string) { fn (g mut Gen) write_string(s string) {
@ -102,48 +100,61 @@ 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
// (`addr` is absolute address) // (`addr` is absolute address)
offset := 0xff - int(abs(addr - g.buf.len))-1 offset := 0xff - int(abs(addr - g.buf.len)) - 1
g.write8(0x7e) g.write8(0x7e)
g.write8(offset) g.write8(offset)
} }
fn (g mut Gen) jl(addr i64) { fn (g mut Gen) jl(addr i64) {
offset := 0xff - int(abs(addr - g.buf.len))-1 offset := 0xff - int(abs(addr - g.buf.len)) - 1
g.write8(0x7c) g.write8(0x7c)
g.write8(offset) g.write8(offset)
} }
fn (g &Gen) abs_to_rel_addr(addr i64) int { fn (g &Gen) abs_to_rel_addr(addr i64) int {
return int(abs(addr - g.buf.len))-1 return int(abs(addr - g.buf.len)) - 1
} }
fn (g mut Gen) jmp (addr i64) { fn (g mut Gen) jmp(addr i64) {
offset := 0xff - g.abs_to_rel_addr(addr) offset := 0xff - g.abs_to_rel_addr(addr)
g.write8(0xe9) g.write8(0xe9)
g.write8(offset) g.write8(offset)
@ -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)
@ -198,13 +209,13 @@ pub fn (g mut Gen) save_main_fn_addr() {
pub fn (g mut Gen) gen_print(s string) { pub fn (g mut Gen) gen_print(s string) {
g.strings << s + '\n' g.strings << s + '\n'
//g.string_addr[s] = str_pos // g.string_addr[s] = str_pos
g.mov(.eax, 1) g.mov(.eax, 1)
g.mov(.edi, 1) g.mov(.edi, 1)
str_pos := g.buf.len + 2 str_pos := g.buf.len + 2
g.str_pos << str_pos g.str_pos << str_pos
g.mov64(.rsi, 0) //segment_start + 0x9f) // str pos // PLACEHOLDER g.mov64(.rsi, 0) // segment_start + 0x9f) // str pos // PLACEHOLDER
g.mov(.edx, s.len+1) // len g.mov(.edx, s.len + 1) // len
g.syscall() g.syscall()
} }
@ -217,22 +228,26 @@ 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)
} }
.r12 { .r12 {
g.write8(0x41) g.write8(0x41)
g.write8(0xbc) // r11 is 0xbb etc g.write8(0xbc) // r11 is 0xbb etc
} }
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

@ -1,10 +1,11 @@
module filepath 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 {
@ -17,16 +18,19 @@ pub fn ext(path string) string {
pub fn is_abs(path string) bool { pub fn is_abs(path string) bool {
$if windows { $if windows {
return path[0] == `/` || // incase we're in MingGW bash return path[0] == `/` || // incase we're in MingGW bash
(path[0].is_letter() && path[1] == `:`) (path[0].is_letter() && path[1] == `:`)
} }
return path[0] == `/` return path[0] == `/`
} }
// pass directories as parameters, returns path as string // pass directories as parameters, returns path as string
// TODO use []string.join once ...string becomes "[]string" // TODO use []string.join once ...string becomes "[]string"
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 {
return result.join( os.path_separator ) result << d
}
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,13 +23,14 @@ 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
) )
pub struct File { pub struct File {
cfile voidptr // Using void* instead of FILE* cfile voidptr // Using void* instead of FILE*
mut: mut:
opened bool opened bool
} }
@ -43,30 +41,33 @@ struct FileInfo {
} }
struct C.stat { struct C.stat {
st_size int st_size int
st_mode u32 st_mode u32
st_mtime int st_mtime int
} }
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
sa_sigaction int sa_sigaction int
sa_flags int sa_flags int
} }
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)
@ -81,7 +82,7 @@ pub fn (f mut File) read_bytes(size int) []byte {
// read_bytes_at reads an amount of bytes at the given position in the file // read_bytes_at reads an amount of bytes at the given position in the file
pub fn (f mut File) read_bytes_at(size, pos int) []byte { pub fn (f mut File) read_bytes_at(size, pos int) []byte {
mut arr := [`0`].repeat(size) mut arr := [`0`].repeat(size)
C.fseek(f.cfile, pos, C.SEEK_SET) C.fseek(f.cfile, pos, C.SEEK_SET)
nreadbytes := C.fread(arr.data, 1, size, f.cfile) nreadbytes := C.fread(arr.data, 1, size, f.cfile)
C.fseek(f.cfile, 0, C.SEEK_SET) C.fseek(f.cfile, 0, C.SEEK_SET)
@ -96,8 +97,7 @@ 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)
return res[0..nr_read_elements * fsize] return res[0..nr_read_elements * fsize]
@ -118,12 +118,13 @@ pub fn read_file(path string) ?string {
C.fread(str, fsize, 1, fp) C.fread(str, fsize, 1, fp)
C.fclose(fp) C.fclose(fp)
str[fsize] = 0 str[fsize] = 0
return string(str, fsize) return string(str,fsize)
} }
// 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 {
@ -161,19 +161,15 @@ pub fn cp(old, new string) ?bool {
} }
} }
pub fn cp_r(osource_path, odest_path string, overwrite bool) ?bool{ 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,20 +178,26 @@ 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)
panic(err) panic(err)
} }
@ -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)
@ -257,7 +259,7 @@ pub fn read_lines(path string) ?[]string {
} }
fn read_ulines(path string) ?[]ustring { fn read_ulines(path string) ?[]ustring {
lines := read_lines(path) or { lines := read_lines(path)or{
return err return err
} }
// mut ulines := new_array(0, lines.len, sizeof(ustring)) // mut ulines := new_array(0, lines.len, sizeof(ustring))
@ -270,16 +272,17 @@ 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'
file = File { file = File{
cfile: C._wfopen(wpath, mode.to_wide()) cfile: C._wfopen(wpath, mode.to_wide())
} }
} $else { } $else {
cpath := path.str cpath := path.str
file = File { file = File{
cfile: C.fopen(charptr(cpath), 'rb') cfile: C.fopen(charptr(cpath), 'rb')
} }
} }
@ -292,16 +295,17 @@ 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'
file = File { file = File{
cfile: C._wfopen(wpath, mode.to_wide()) cfile: C._wfopen(wpath, mode.to_wide())
} }
} $else { } $else {
cpath := path.str cpath := path.str
file = File { file = File{
cfile: C.fopen(charptr(cpath), 'wb') cfile: C.fopen(charptr(cpath), 'wb')
} }
} }
@ -313,16 +317,17 @@ 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'
file = File { file = File{
cfile: C._wfopen(wpath, mode.to_wide()) cfile: C._wfopen(wpath, mode.to_wide())
} }
} $else { } $else {
cpath := path.str cpath := path.str
file = File { file = File{
cfile: C.fopen(charptr(cpath), 'ab') cfile: C.fopen(charptr(cpath), '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')
} }
@ -388,29 +398,28 @@ 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 ){ }
ret = C.WTERMSIG( waitret ) else if C.WIFSIGNALED(waitret) {
ret = C.WTERMSIG(waitret)
is_signaled = true is_signaled = true
} }
return ret , is_signaled return ret,is_signaled
} }
} }
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
} }
} }
@ -418,16 +427,15 @@ fn vpclose(f voidptr) int {
pub struct Result { pub struct Result {
pub: pub:
exit_code int exit_code int
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') {
// TODO remove panic // TODO remove panic
//panic(';, &&, || and \\n are not allowed in shell commands') // panic(';, &&, || and \\n are not allowed in shell commands')
//} // }
mut ret := 0 mut ret := 0
$if windows { $if windows {
// overcome bug in system & _wsystem (cmd) when first char is quote `"` // overcome bug in system & _wsystem (cmd) when first char is quote `"`
@ -439,11 +447,10 @@ 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 {
println('Terminated by signal ${ret:2d} (' + sigint_to_signal_name(pret) + ')' ) println('Terminated by signal ${ret:2d} (' + sigint_to_signal_name(pret) + ')')
} }
ret = pret ret = pret
} }
@ -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'} 2 {
6 {return 'SIGABRT'} return 'SIGINT'
8 {return 'SIGFPE'} }
9 {return 'SIGKILL'} 3 {
11 {return 'SIGSEGV'} return 'SIGQUIT'
13 {return 'SIGPIPE'} }
14 {return 'SIGALRM'} 4 {
15 {return 'SIGTERM'} return 'SIGILL'
else { } }
} 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'} 31, 12, 17 {
17,19,23{ return 'SIGSTOP'} return 'SIGUSR2'
18,20,24{ return 'SIGTSTP'} }
21,21,26{ return 'SIGTTIN'} 20, 17, 18 {
22,22,27{ return 'SIGTTOU'} return 'SIGCHLD'
/////////////////////////////// }
5{ return 'SIGTRAP'} 19, 18, 25 {
7{ return 'SIGBUS' } return 'SIGCONT'
else {} }
} 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'
} }
@ -500,7 +549,7 @@ pub fn getenv(key string) string {
return '' return ''
} }
// NB: C.getenv *requires* that the result be copied. // NB: C.getenv *requires* that the result be copied.
return cstring_to_vstring( byteptr(s) ) return cstring_to_vstring(byteptr(s))
} }
} }
@ -531,7 +580,7 @@ pub fn exists(path string) bool {
p := path.replace('/', '\\') p := path.replace('/', '\\')
return C._waccess(p.to_wide(), 0) != -1 return C._waccess(p.to_wide(), 0) != -1
} $else { } $else {
return C.access(path.str, 0 ) != -1 return C.access(path.str, 0) != -1
} }
} }
@ -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,12 +636,11 @@ 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
} }
return path[..pos ] // NB: *without* terminating / return path[..pos] // NB: *without* terminating /
} }
pub fn filename(path string) string { pub fn filename(path string) string {
@ -608,53 +649,54 @@ pub fn filename(path string) string {
// get_line returns a one-line string from stdin // get_line returns a one-line string from stdin
pub fn get_line() string { 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')
} }
} }
// get_raw_line returns a one-line string from stdin along with '\n' if there is any // get_raw_line returns a one-line string from stdin along with '\n' if there is any
pub fn get_raw_line() string { pub fn get_raw_line() string {
$if windows { $if windows {
max_line_chars := 256 max_line_chars := 256
buf := malloc(max_line_chars*2) buf := malloc(max_line_chars * 2)
if is_atty(0) > 0 { if is_atty(0) > 0 {
h_input := C.GetStdHandle(STD_INPUT_HANDLE) h_input := C.GetStdHandle(STD_INPUT_HANDLE)
mut nr_chars := u32(0) mut nr_chars := u32(0)
C.ReadConsole(h_input, buf, max_line_chars * 2, voidptr(&nr_chars), 0) C.ReadConsole(h_input, buf, max_line_chars * 2, voidptr(&nr_chars), 0)
return string_from_wide2(&u16(buf), int(nr_chars)) return string_from_wide2(&u16(buf), int(nr_chars))
} }
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 '' return string_from_wide2(&u16(buf), len)
} $else { }
max := size_t(256) return ''
buf := charptr(malloc(int(max))) } $else {
nr_chars := C.getline(&buf, &max, stdin) max := size_t(256)
if nr_chars == 0 { buf := charptr(malloc(int(max)))
return '' nr_chars := C.getline(&buf, &max, stdin)
} if nr_chars == 0 {
return string(byteptr(buf), nr_chars) return ''
} }
return string(byteptr(buf),nr_chars)
}
} }
pub fn get_lines() []string { pub fn get_lines() []string {
mut line := '' mut line := ''
mut inputstr := []string mut inputstr := []string
for { for {
line = get_line() line = get_line()
if(line.len <= 0) { if (line.len <= 0) {
break break
} }
line = line.trim_space() line = line.trim_space()
inputstr << line inputstr << line
} }
return inputstr return inputstr
} }
pub fn get_lines_joined() string { pub fn get_lines_joined() string {
@ -694,7 +736,7 @@ pub fn user_os() string {
$if dragonfly { $if dragonfly {
return 'dragonfly' return 'dragonfly'
} }
$if android{ $if android {
return 'android' return 'android'
} }
$if solaris { $if solaris {
@ -726,7 +768,7 @@ pub fn home_dir() string {
// write_file writes `text` data to a file in `path`. // write_file writes `text` data to a file in `path`.
pub fn write_file(path, text string) { pub fn write_file(path, text string) {
mut f := os.create(path) or { mut f := os.create(path)or{
return return
} }
f.write(text) f.write(text)
@ -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 {
@ -774,15 +819,15 @@ pub fn executable() string {
} }
$if windows { $if windows {
max := 512 max := 512
mut result := &u16(calloc(max*2)) // MAX_PATH * sizeof(wchar_t) mut result := &u16(calloc(max * 2)) // MAX_PATH * sizeof(wchar_t)
len := C.GetModuleFileName( 0, result, max ) len := C.GetModuleFileName(0, result, max)
return string_from_wide2(result, len) return string_from_wide2(result, len)
} }
$if macos { $if macos {
mut result := calloc(MAX_PATH) mut result := calloc(MAX_PATH)
pid := C.getpid() pid := C.getpid()
ret := proc_pidpath (pid, result, MAX_PATH) ret := proc_pidpath(pid, result, MAX_PATH)
if ret <= 0 { if ret <= 0 {
eprintln('os.executable() failed at calling proc_pidpath with pid: $pid . proc_pidpath returned $ret ') eprintln('os.executable() failed at calling proc_pidpath with pid: $pid . proc_pidpath returned $ret ')
return os.args[0] return os.args[0]
} }
@ -790,7 +835,7 @@ pub fn executable() string {
} }
$if freebsd { $if freebsd {
mut result := calloc(MAX_PATH) mut result := calloc(MAX_PATH)
mib := [1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1] mib := [1/* CTL_KERN */, 14/* KERN_PROC */, 12/* KERN_PROC_PATHNAME */, -1]
size := MAX_PATH size := MAX_PATH
C.sysctl(mib.data, 4, result, &size, 0, 0) C.sysctl(mib.data, 4, result, &size, 0, 0)
return string(result) return string(result)
@ -806,21 +851,21 @@ pub fn executable() string {
} }
$if netbsd { $if netbsd {
mut result := calloc(MAX_PATH) mut result := calloc(MAX_PATH)
count := int(C.readlink('/proc/curproc/exe', result, MAX_PATH )) count := int(C.readlink('/proc/curproc/exe', result, MAX_PATH))
if count < 0 { if count < 0 {
eprintln('os.executable() failed at reading /proc/curproc/exe to get exe path') eprintln('os.executable() failed at reading /proc/curproc/exe to get exe path')
return os.args[0] return os.args[0]
} }
return string(result, count) return string(result,count)
} }
$if dragonfly { $if dragonfly {
mut result := calloc(MAX_PATH) mut result := calloc(MAX_PATH)
count := int(C.readlink('/proc/curproc/file', result, MAX_PATH )) count := int(C.readlink('/proc/curproc/file', result, MAX_PATH))
if count < 0 { if count < 0 {
eprintln('os.executable() failed at reading /proc/curproc/file to get exe path') eprintln('os.executable() failed at reading /proc/curproc/file to get exe path')
return os.args[0] return os.args[0]
} }
return string(result, count) return string(result,count)
} }
return os.args[0] return os.args[0]
} }
@ -828,9 +873,8 @@ pub fn executable() string {
[deprecated] [deprecated]
pub fn dir_exists(path string) bool { 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 {
$else { statbuf := C.stat{
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)
} }
} }
@ -881,13 +925,12 @@ pub fn chdir(path string) {
pub fn getwd() string { pub fn getwd() string {
$if windows { $if windows {
max := 512 // MAX_PATH * sizeof(wchar_t) max := 512 // MAX_PATH * sizeof(wchar_t)
buf := &u16(calloc(max*2)) buf := &u16(calloc(max * 2))
if C._wgetcwd(buf, max) == 0 { if C._wgetcwd(buf, max) == 0 {
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 ''
@ -899,7 +942,7 @@ pub fn getwd() string {
// Returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved. // Returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved.
// See http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html // See http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
// Also https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html // Also https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
// and https://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html // and https://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html
// NB: this particular rabbit hole is *deep* ... // NB: this particular rabbit hole is *deep* ...
pub fn realpath(fpath string) string { pub fn realpath(fpath string) string {
mut fullpath := calloc(MAX_PATH) mut fullpath := calloc(MAX_PATH)
@ -923,9 +966,11 @@ 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 {
if file.starts_with('.') { if file.starts_with('.') {
continue continue
@ -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,15 +1040,15 @@ 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);
return attr.st_mtime return attr.st_mtime
//# 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)
} }
@ -1009,7 +1058,7 @@ pub fn flush_stdout() {
} }
pub fn print_backtrace() { pub fn print_backtrace() {
/* /*
# void *buffer[100]; # void *buffer[100];
nptrs := 0 nptrs := 0
# nptrs = backtrace(buffer, 100); # nptrs = backtrace(buffer, 100);
@ -1023,24 +1072,30 @@ 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)
}
} }
} }
} }
pub fn join(base string, dirs ...string) string { pub fn join(base string, dirs ...string) string {
println('use filepath.join') println('use filepath.join')
return filepath.join(base, dirs) return filepath.join(base,dirs)
} }
// tmpdir returns the path to a folder, that is suitable for storing temporary files // tmpdir returns the path to a folder, that is suitable for storing temporary files
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,18 +1114,22 @@ 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)
} }
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?
@ -32,7 +30,7 @@ pub fn ls(path string) ?[]string {
return error('ls() couldnt open dir "$path"') return error('ls() couldnt open dir "$path"')
} }
mut ent := &C.dirent(0) mut ent := &C.dirent(0)
//mut ent := &C.dirent{!} // mut ent := &C.dirent{!}
for { for {
ent = C.readdir(dir) ent = C.readdir(dir)
if isnil(ent) { if isnil(ent) {
@ -63,8 +61,10 @@ 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 == '.' {
apath := os.realpath( path ) return true
}
apath := os.realpath(path)
r := C.mkdir(apath.str, 511) r := C.mkdir(apath.str, 511)
if r == -1 { if r == -1 {
return error(get_error_msg(C.errno)) return error(get_error_msg(C.errno))
@ -74,9 +74,9 @@ pub fn mkdir(path string) ?bool {
// exec starts the specified command, waits for it to complete, and returns its output. // exec starts the specified command, waits for it to complete, and returns its output.
pub fn exec(cmd string) ?Result { pub fn exec(cmd string) ?Result {
//if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') { // if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') {
//return error(';, &&, || and \\n are not allowed in shell commands') // return error(';, &&, || and \\n are not allowed in shell commands')
//} // }
pcmd := '$cmd 2>&1' pcmd := '$cmd 2>&1'
f := vpopen(pcmd) f := vpopen(pcmd)
if isnil(f) { if isnil(f) {
@ -89,11 +89,12 @@ pub fn exec(cmd string) ?Result {
} }
res = res.trim_space() res = res.trim_space()
exit_code := vpclose(f) exit_code := vpclose(f)
//if exit_code != 0 { // if exit_code != 0 {
//return error(res) // return error(res)
//} // }
return Result { return Result{
output: res output: res
exit_code: exit_code exit_code: exit_code
} }
} }

View File

@ -1,48 +1,58 @@
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()
rng.state += initstate rng.state += initstate
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)
// Uniformity guarantees that loop below will terminate. In practice, it // Uniformity guarantees that loop below will terminate. In practice, it
// should usually terminate quickly; on average (assuming all bounds are // should usually terminate quickly; on average (assuming all bounds are
// equally likely), 82.25% of the time, we can expect it to require just // equally likely), 82.25% of the time, we can expect it to require just
@ -51,8 +61,9 @@ pub fn new_pcg32(initstate u64, initseq u64) Pcg32 {
for { for {
r := rng.next() r := rng.next()
if r >= threshold { if r >= threshold {
return ( r % bound ) return (r % bound)
} }
} }
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,11 +19,15 @@ 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
ns := ( *rs * 1103515245 + 12345 ) ns := (*rs * 1103515245 + 12345)
*rs = ns *rs = ns
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,38 +19,38 @@
* *
**********************************************************************/ **********************************************************************/
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)
mut r1 := u32(0) mut r1 := u32(0)
mut r2 := u32(0) mut r2 := u32(0)
r0 = (s0 >> 1) | ((s1 & u32(1)) << 31) r0 = (s0>>1) | ((s1 & u32(1))<<31)
r1 = (s1 >> 1) | ((s2 & u32(1)) << 31) r1 = (s1>>1) | ((s2 & u32(1))<<31)
r2 = s2 >> 1 r2 = s2>>1
return r2,r1,r0 return r2,r1,r0
} }
// left logical shift 96 bit // left logical shift 96 bit
fn lsl96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) { fn lsl96(s2 u32, s1 u32, s0 u32) (u32,u32,u32) {
mut r0 := u32(0) mut r0 := u32(0)
mut r1 := u32(0) mut r1 := u32(0)
mut r2 := u32(0) mut r2 := u32(0)
r2 = (s2 << 1) | ((s1 & (u32(1) << 31)) >> 31) r2 = (s2<<1) | ((s1 & (u32(1)<<31))>>31)
r1 = (s1 << 1) | ((s0 & (u32(1) << 31)) >> 31) r1 = (s1<<1) | ((s0 & (u32(1)<<31))>>31)
r0 = s0 << 1 r0 = s0<<1
return r2,r1,r0 return r2,r1,r0
} }
// sum on 96 bit // sum on 96 bit
fn add96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) { fn add96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32,u32,u32) {
mut w := u64(0) mut w := u64(0)
mut r0 := u32(0) mut r0 := u32(0)
mut r1 := u32(0) mut r1 := u32(0)
mut r2 := u32(0) mut r2 := u32(0)
@ -66,8 +66,8 @@ fn add96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) {
} }
// subtraction on 96 bit // subtraction on 96 bit
fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) { fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32,u32,u32) {
mut w := u64(0) mut w := u64(0)
mut r0 := u32(0) mut r0 := u32(0)
mut r1 := u32(0) mut r1 := u32(0)
mut r2 := u32(0) mut r2 := u32(0)
@ -87,62 +87,58 @@ fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) {
* Constants * Constants
* *
**********************************************************************/ **********************************************************************/
const (
//
// f64 constants
//
DIGITS = 18
DOUBLE_PLUS_ZERO = u64(0x0000000000000000)
DOUBLE_MINUS_ZERO = 0x8000000000000000
DOUBLE_PLUS_INFINITY = 0x7FF0000000000000
DOUBLE_MINUS_INFINITY = 0xFFF0000000000000
const (
//
// f64 constants
//
DIGITS = 18
DOUBLE_PLUS_ZERO = u64(0x0000000000000000)
DOUBLE_MINUS_ZERO = 0x8000000000000000
DOUBLE_PLUS_INFINITY = 0x7FF0000000000000
DOUBLE_MINUS_INFINITY = 0xFFF0000000000000
// //
// parser state machine states // parser state machine states
// //
FSM_A = 0 FSM_A = 0
FSM_B = 1 FSM_B = 1
FSM_C = 2 FSM_C = 2
FSM_D = 3 FSM_D = 3
FSM_E = 4 FSM_E = 4
FSM_F = 5 FSM_F = 5
FSM_G = 6 FSM_G = 6
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.
// //
PARSER_OK = 0 // parser finished OK PARSER_OK = 0 // parser finished OK
PARSER_PZERO = 1 // no digits or number is smaller than +-2^-1022 PARSER_PZERO = 1 // no digits or number is smaller than +-2^-1022
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
// //
DPOINT =`.` DPOINT = `.`
PLUS =`+` PLUS = `+`
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
} }
fn is_space(x byte) bool { fn is_space(x byte) bool {
@ -177,97 +173,98 @@ 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
mut digx := 0 mut digx := 0
mut c := ` ` // initial value for kicking off the state machine mut c := ` ` // initial value for kicking off the state machine
mut result := PARSER_OK mut result := PARSER_OK
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 { }
state = FSM_B else {
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) {
if digx < DIGITS { if digx < DIGITS {
pn.mantissa *= 10 pn.mantissa *= 10
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,144 +320,125 @@ 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])
if i >= s.len {
//C.printf("len: %d i: %d str: %s \n",s.len,i,s[..i])
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) mut q2 := u32(0) // 96-bit precision integer
mut q2:=u32(0) // 96-bit precision integer mut q1 := u32(0)
mut q1:=u32(0) mut q0 := u32(0)
mut q0:=u32(0) 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++
s2 = q2 s2 = q2
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)
binexp-- binexp--
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)
q1 = r2 / TEN q1 = r2 / TEN
r1 = r2 % TEN r1 = r2 % TEN
r2 = ((s1 & u32(0xFF)) << 16) | (s0 >> 16) | (r1 << 24) r2 = ((s1 & u32(0xFF))<<16) | (s0>>16) | (r1<<24)
r0 = r2 / TEN r0 = r2 / TEN
r1 = r2 % TEN r1 = r2 % TEN
q1 = (q1 << 8) | ((r0 & u32(0x00FF0000)) >> 16) q1 = (q1<<8) | ((r0 & u32(0x00FF0000))>>16)
q0 = r0 << 16 q0 = r0<<16
r2 = (s0 & u32(0xFFFF)) | (r1 << 16) r2 = (s0 & u32(0xFFFF)) | (r1<<16)
q0 |= r2 / TEN q0 |= r2 / TEN
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 {
q2, q1, q0 = lsl96(s2, s1, s0) q2,q1,q0 = lsl96(s2, s1, s0)
binexp-- binexp--
s2 = q2 s2 = q2
s1 = q1 s1 = q1
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
@ -496,52 +469,47 @@ fn converter(pn mut PrepNumber) u64 {
s0=0x0 s0=0x0
*/ */
//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 { }
//C.printf("All 0!\n") else {
if (s1 & (check_round_bit<<u32(1) )) != 0 { // C.printf("All 0!\n")
//C.printf("Add 1 form -1 bit control!\n") if (s1 & (check_round_bit<<u32(1))) != 0 {
s2,s1,s0 = add96(s2,s1,s0, 0,check_round_bit,0) // C.printf("Add 1 form -1 bit control!\n")
s2,s1,s0 = add96(s2, s1, s0, 0, check_round_bit, 0)
} }
} }
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!!")
q2, q1, q0 = lsr96(s2, s1, s0) q2,q1,q0 = lsr96(s2, s1, s0)
binexp-- binexp--
s2 = q2 s2 = q2
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
} }
} }
@ -552,15 +520,13 @@ 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,21 +535,20 @@ 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)
} }
PARSER_PZERO { PARSER_PZERO {
*res_ptr = DOUBLE_PLUS_ZERO *res_ptr = DOUBLE_PLUS_ZERO
@ -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,18 +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.
// 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) // max_u64 = u64(u64(1 << 63) - 1)
// max_u64 = u64(u64(1 << 63) - 1)
int_size = 32 int_size = 32
max_u64 = u64(C.UINT64_MAX) // use this until we add support max_u64 = u64(C.UINT64_MAX) // use this until we add support
) )
fn byte_to_lower(c byte) byte { fn byte_to_lower(c byte) byte {
@ -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` {
@ -51,39 +47,34 @@ pub fn common_parse_uint(s string, _base int, _bit_size int, error_on_non_digit
start_index += 2 start_index += 2
} }
// manage leading zeros in decimal base's numbers // manage leading zeros in decimal base's numbers
else if s.len >=2 && ( s[1] >= `0` && s[1] <= `9`) { else if s.len >= 2 && (s[1] >= `0` && s[1] <= `9`) {
base = 10 base = 10
start_index ++ start_index++
} }
else { else {
base = 8 base = 8
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 {
c := s[i] c := s[i]
cl := byte_to_lower(c) cl := byte_to_lower(c)
mut d := byte(0) mut d := byte(0)
@ -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,37 +146,35 @@ 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)
// } // }
un := common_parse_uint(s, base, bit_size, error_on_non_digit, error_on_high_digit) un := common_parse_uint(s, base, bit_size, error_on_non_digit, error_on_high_digit)
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 {
// return error('parse_int: range error $s0') // return error('parse_int: range error $s0')
return i64(cutoff - u64(1)) return i64(cutoff - u64(1))
} }
if neg && un > cutoff { if neg && un > cutoff {
// 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,34 +193,29 @@ 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] == `+` {
start_idx++ start_idx++
if s.len-start_idx < 1 { if s.len - start_idx < 1 {
// return 0, &NumError{fnAtoi, s0, ErrSyntax} // return 0, &NumError{fnAtoi, s0, ErrSyntax}
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`
if ch > 9 { if ch > 9 {
// return 0, &NumError{fnAtoi, s0, ErrSyntax} // return 0, &NumError{fnAtoi, s0, ErrSyntax}
return 0 return 0
} }
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,19 +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 strings module strings
pub struct Builder { pub struct Builder {
mut: mut:
buf []byte buf []byte
pub mut: pub mut:
len int len int
initial_size int = 1 initial_size int=1
} }
pub fn new_builder(initial_size int) Builder { pub fn new_builder(initial_size int) Builder {
return Builder { return Builder{
buf: make(0, initial_size, 1) buf: make(0, initial_size, 1)
initial_size: initial_size initial_size: initial_size
} }
@ -31,30 +30,33 @@ pub fn (b mut Builder) write_b(data byte) {
pub fn (b mut Builder) write(s string) { pub fn (b mut Builder) write(s string) {
b.buf.push_many(s.str, s.len) b.buf.push_many(s.str, s.len)
//for c in s { // for c in s {
//b.buf << c // b.buf << c
//} // }
//b.buf << []byte(s) // TODO // b.buf << []byte(s) // TODO
b.len += s.len b.len += s.len
} }
pub fn (b mut Builder) writeln(s string) { pub fn (b mut Builder) writeln(s string) {
//for c in s { // for c in s {
//b.buf << c // b.buf << c
//} // }
b.buf.push_many(s.str, s.len) b.buf.push_many(s.str, s.len)
//b.buf << []byte(s) // TODO // b.buf << []byte(s) // TODO
b.buf << `\n` b.buf << `\n`
b.len += s.len + 1 b.len += s.len + 1
} }
pub fn (b mut Builder) str() string { pub fn (b mut Builder) str() string {
b.buf << `\0` b.buf << `\0`
return string(b.buf, b.len) return string(b.buf,b.len)
} }
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

@ -6,6 +6,6 @@ pub fn repeat(c byte, n int) string {
} }
mut arr := [c].repeat(n + 1) mut arr := [c].repeat(n + 1)
arr[n] = `\0` arr[n] = `\0`
return string(arr, n) return string(arr,n)
} }

View File

@ -18,20 +18,12 @@ 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,24 +1,20 @@
// 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
pub fn set_cursor_position(x int,y int) { pub fn set_cursor_position(x int, y int) {
print('\x1b[$y;$x'+'H') print('\x1b[$y;$x' + 'H')
} }
// n is number of cells // n is number of cells
@ -26,24 +22,24 @@ pub fn set_cursor_position(x int,y int) {
// direction: B is down / South // direction: B is down / South
// direction: C is forward / East // direction: C is forward / East
// direction: D is backward / West // direction: D is backward / West
pub fn move(n int,direction string) { pub fn move(n int, direction string) {
print('\x1b[$n$direction') print('\x1b[$n$direction')
} }
pub fn cursor_up(n int) { pub fn cursor_up(n int) {
move(n,'A') move(n, 'A')
} }
pub fn cursor_down(n int) { pub fn cursor_down(n int) {
move(n,'B') move(n, 'B')
} }
pub fn cursor_forward(n int) { pub fn cursor_forward(n int) {
move(n,'C') move(n, 'C')
} }
pub fn cursor_back(n int) { pub fn cursor_back(n int) {
move(n,'D') move(n, 'D')
} }
// type: 0 -> current cursor position to end of the screen // type: 0 -> current cursor position to end of the screen
@ -51,27 +47,23 @@ pub fn cursor_back(n int) {
// type: 2 -> clears entire screen // type: 2 -> clears entire screen
// type: 3 -> clears entire screen and also delete scrollback buffer // type: 3 -> clears entire screen and also delete scrollback buffer
pub fn erase_display(t string) { 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')
} }
// type: 0 -> current cursor position to end of the line // type: 0 -> current cursor position to end of the line
@ -79,32 +71,28 @@ pub fn erase_del_clear()
// type: 2 -> clears entire line // type: 2 -> clears entire line
// Note: Cursor position does not change // Note: Cursor position does not change
pub fn erase_line(t string) { 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,49 +1,29 @@
// 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.
// Must be 1 mod 400, and times before it will not compute correctly,
// The unsigned zero year for internal calculations. // but otherwise can be changed at will.
// Must be 1 mod 400, and times before it will not compute correctly, absolute_zero_year = i64(-292277022399)
// but otherwise can be changed at will. seconds_per_minute = 60
absolute_zero_year = i64(-292277022399) seconds_per_hour = 60 * seconds_per_minute
seconds_per_day = 24 * seconds_per_hour
seconds_per_minute = 60 seconds_per_week = 7 * seconds_per_day
seconds_per_hour = 60 * seconds_per_minute days_per_400_years = 365 * 400 + 97
seconds_per_day = 24 * seconds_per_hour days_per_100_years = 365 * 100 + 24
seconds_per_week = 7 * seconds_per_day days_per_4_years = 365 * 4 + 1
days_per_400_years = 365*400 + 97 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_per_100_years = 365*100 + 24
days_per_4_years = 365*4 + 1
days_before = [
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
]
) )
#include <time.h> #include <time.h>
pub struct Time { pub struct Time {
pub: pub:
year int year int
@ -56,38 +36,41 @@ pub:
} }
pub enum FormatTime { pub enum FormatTime {
hhmm12 hhmm12
hhmm24 hhmm24
hhmmss12 hhmmss12
hhmmss24 hhmmss24
no_time no_time
} }
pub enum FormatDate { pub enum FormatDate {
ddmmyy ddmmyy
ddmmyyyy ddmmyyyy
mmddyy mmddyy
mmddyyyy mmddyyyy
mmmd mmmd
mmmdd mmmdd
mmmddyyyy mmmddyyyy
no_date no_date
yyyymmdd yyyymmdd
} }
pub enum FormatDelimiter { pub enum FormatDelimiter {
dot dot
hyphen hyphen
slash slash
space space
} }
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,82 +97,81 @@ 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.
// Cut it back down to 3 by subtracting n>>2. // Cut it back down to 3 by subtracting n>>2.
n = d / days_per_100_years n = d / days_per_100_years
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
// by subtracting n>>2. // by subtracting n>>2.
n = d / 365 n = d / 365
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
mut begin := 0 mut begin := 0
end := (days_before[month+1]) end := (days_before[month + 1])
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{
year:year year: year
month: month month: month
day:day day: day
hour:hour hour: hour
minute: minute minute: minute
second: second second: second
uni: abs uni: abs
@ -196,7 +179,7 @@ pub fn unix(abs int) Time {
} }
pub fn convert_ctime(t tm) Time { pub fn convert_ctime(t tm) Time {
return Time { return Time{
year: t.tm_year + 1900 year: t.tm_year + 1900
month: t.tm_mon + 1 month: t.tm_mon + 1
day: t.tm_mday day: t.tm_mday
@ -208,24 +191,23 @@ pub fn convert_ctime(t tm) Time {
} }
// format_ss returns a string for t in a given format YYYY-MM-DD HH:MM:SS in // format_ss returns a string for t in a given format YYYY-MM-DD HH:MM:SS in
// 24h notation // 24h notation
// @param // @param
// @return string // @return string
// @example 1980-07-11 21:23:42 // @example 1980-07-11 21:23:42
pub fn (t Time) format_ss() string { pub fn (t Time) format_ss() string {
return t.get_fmt_str(.hyphen, .hhmmss24, .yyyymmdd) return t.get_fmt_str(.hyphen, .hhmmss24, .yyyymmdd)
} }
// format_ss returns a string for t in a given format YYYY-MM-DD HH:MM in 24h // format_ss returns a string for t in a given format YYYY-MM-DD HH:MM in 24h
// notation // notation
// @param // @param
// @return string // @return string
// @example 1980-07-11 21:23 // @example 1980-07-11 21:23
pub fn (t Time) format() string { 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]
@ -234,7 +216,7 @@ pub fn (t Time) smonth() string {
// hhmm returns a string for t in the given format HH:MM in 24h notation // hhmm returns a string for t in the given format HH:MM in 24h notation
// @example 21:04 // @example 21:04
pub fn (t Time) hhmm() string { pub fn (t Time) hhmm() string {
return t.get_fmt_time_str(.hhmm24) return t.get_fmt_time_str(.hhmm24)
} }
/* /*
@ -245,27 +227,27 @@ fn (t Time) hhmm_tmp() string {
// hhmm12 returns a string for t in the given format HH:MM in 12h notation // hhmm12 returns a string for t in the given format HH:MM in 12h notation
pub fn (t Time) hhmm12() string { pub fn (t Time) hhmm12() string {
return t.get_fmt_time_str(.hhmm12) return t.get_fmt_time_str(.hhmm12)
} }
// hhmmss returns a string for t in the given format HH:MM:SS in 24h notation // hhmmss returns a string for t in the given format HH:MM:SS in 24h notation
pub fn (t Time) hhmmss() string { pub fn (t Time) hhmmss() string {
return t.get_fmt_time_str(.hhmmss24) return t.get_fmt_time_str(.hhmmss24)
} }
// ymmdd returns a string for t in the given format YYYY-MM-DD // ymmdd returns a string for t in the given format YYYY-MM-DD
pub fn (t Time) ymmdd() string { pub fn (t Time) ymmdd() string {
return t.get_fmt_date_str(.hyphen, .yyyymmdd) return t.get_fmt_date_str(.hyphen, .yyyymmdd)
} }
// ddmmy returns a string for t in the given format DD.MM.YYYY // ddmmy returns a string for t in the given format DD.MM.YYYY
pub fn (t Time) ddmmy() string { pub fn (t Time) ddmmy() string {
return t.get_fmt_date_str(.dot, .ddmmyyyy) return t.get_fmt_date_str(.dot, .ddmmyyyy)
} }
// md returns a string for t in the given format MMM D // md returns a string for t in the given format MMM D
pub fn (t Time) md() string { pub fn (t Time) md() string {
return t.get_fmt_date_str(.space, .mmmd) return t.get_fmt_date_str(.space, .mmmd)
} }
pub fn (t Time) clean() string { pub fn (t Time) clean() string {
@ -275,7 +257,7 @@ pub fn (t Time) clean() string {
// } // }
// Today // Today
if t.month == nowe.month && t.year == nowe.year && t.day == nowe.day { if t.month == nowe.month && t.year == nowe.year && t.day == nowe.day {
return t.get_fmt_time_str(.hhmm24) return t.get_fmt_time_str(.hhmm24)
} }
// This week // This week
// if time.Since(t) < 24*7*time.Hour { // if time.Since(t) < 24*7*time.Hour {
@ -283,7 +265,7 @@ pub fn (t Time) clean() string {
// } // }
// This year // This year
if t.year == nowe.year { if t.year == nowe.year {
return t.get_fmt_str(.space, .hhmm24, .mmmd) return t.get_fmt_str(.space, .hhmm24, .mmmd)
} }
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
@ -296,7 +278,7 @@ pub fn (t Time) clean12() string {
// } // }
// Today // Today
if t.month == nowe.month && t.year == nowe.year && t.day == nowe.day { if t.month == nowe.month && t.year == nowe.year && t.day == nowe.day {
return t.get_fmt_time_str(.hhmm12) return t.get_fmt_time_str(.hhmm12)
} }
// This week // This week
// if time.Since(t) < 24*7*time.Hour { // if time.Since(t) < 24*7*time.Hour {
@ -304,12 +286,11 @@ pub fn (t Time) clean12() string {
// } // }
// This year // This year
if t.year == nowe.year { if t.year == nowe.year {
return t.get_fmt_str(.space, .hhmm12, .mmmd) return t.get_fmt_str(.space, .hhmm12, .mmmd)
} }
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"')
@ -329,7 +310,7 @@ pub fn parse(s string) Time {
minute := hms[1] minute := hms[1]
second := hms[2] second := hms[2]
// ////////// // //////////
return new_time(Time { return new_time(Time{
year: ymd[0].int() year: ymd[0].int()
month: ymd[1].int() month: ymd[1].int()
day: ymd[2].int() day: ymd[2].int()
@ -340,20 +321,23 @@ 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 {
if t.uni != 0 { if t.uni != 0 {
return t.uni return t.uni
} }
tt := C.tm{ tt := C.tm{
tm_sec : t.second tm_sec: t.second
tm_min : t.minute tm_min: t.minute
tm_hour : t.hour tm_hour: t.hour
tm_mday : t.day tm_mday: t.day
tm_mon : t.month-1 tm_mon: t.month - 1
tm_year : t.year - 1900 tm_year: t.year - 1900
} }
return C.mktime(&tt) return C.mktime(&tt)
} }
@ -406,7 +390,7 @@ pub fn day_of_week(y, m, d int) int {
if (m < 3) { if (m < 3) {
sy = sy - 1 sy = sy - 1
} }
return ( sy + sy/4 - sy/100 + sy/400 + t[m-1] + d - 1) % 7 + 1 return (sy + sy / 4 - sy / 100 + sy / 400 + t[m - 1] + d - 1) % 7 + 1
} }
pub fn (t Time) day_of_week() int { pub fn (t Time) day_of_week() int {
@ -419,8 +403,8 @@ pub fn (t Time) weekday_str() string {
return days_string[i * 3..(i + 1) * 3] return days_string[i * 3..(i + 1) * 3]
} }
pub struct C.timeval { pub struct C.timeval {
tv_sec u64 tv_sec u64
tv_usec u64 tv_usec u64
} }
@ -428,50 +412,47 @@ pub struct C.timeval {
pub fn ticks() i64 { pub fn ticks() i64 {
$if windows { $if windows {
return C.GetTickCount() return C.GetTickCount()
} } $else {
$else { ts := C.timeval{
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)
} }
} }
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) }
}
} }
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)
} }
} }
// Determine whether a year is a leap year. // Determine whether a year is a leap year.
pub fn is_leap_year(year int) bool { pub fn is_leap_year(year int) bool {
return (year%4 == 0) && (year%100 != 0 || year%400 == 0) return (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)
} }
// Returns number of days in month // Returns number of days in month
@ -479,8 +460,8 @@ pub fn days_in_month(month, year int) ?int {
if month > 12 || month < 1 { if month > 12 || month < 1 {
return error('Invalid month: $month') return error('Invalid month: $month')
} }
extra := if month == 2 && is_leap_year(year) {1} else {0} extra := if month == 2 && is_leap_year(year) { 1 } else { 0 }
res := month_days[month-1] + extra res := month_days[month - 1] + extra
return res return res
} }
@ -489,31 +470,26 @@ pub fn days_in_month(month, year int) ?int {
// @return string // @return string
// @example 21:23:42 // @example 21:23:42
pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string { 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.' return match fmt_time {
} else { .hhmm12{
'a.m.' '$hour:${t.minute:02d} $tp'
} }
.hhmm24{
hour := if t.hour > 12 { '${t.hour:02d}:${t.minute:02d}'
t.hour - 12 }
} else if t.hour == 0 { .hhmmss12{
12 '$hour:${t.minute:02d}:${t.second:02d} $tp'
} else { }
t.hour .hhmmss24{
} '${t.hour:02d}:${t.minute:02d}:${t.second:02d}'
}
return match fmt_time { else {
.hhmm12 { '$hour:${t.minute:02d} $tp' } '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
@ -521,30 +497,52 @@ pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string {
// @return string // @return string
// @example 11.07.1980 // @example 11.07.1980
pub fn (t Time) get_fmt_date_str(fmt_dlmtr FormatDelimiter, fmt_date FormatDate) string { pub fn (t Time) get_fmt_date_str(fmt_dlmtr FormatDelimiter, fmt_date FormatDate) string {
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 {
.ddmmyy{
return match fmt_date { '${t.day:02d}|${t.month:02d}|$year'
.ddmmyy { '${t.day:02d}|${t.month:02d}|$year' } }
.ddmmyyyy { '${t.day:02d}|${t.month:02d}|${t.year}' } .ddmmyyyy{
.mmddyy { '${t.month:02d}|${t.day:02d}|$year' } '${t.day:02d}|${t.month:02d}|${t.year}'
.mmddyyyy { '${t.month:02d}|${t.day:02d}|${t.year}' } }
.mmmd { '$month|${t.day}' } .mmddyy{
.mmmdd { '$month|${t.day:02d}' } '${t.month:02d}|${t.day:02d}|$year'
.mmmddyyyy { '$month|${t.day:02d}|${t.year}' } }
.yyyymmdd { '${t.year}|${t.month:02d}|${t.day:02d}' } .mmddyyyy{
else { 'unknown enumeration $fmt_date' } '${t.month:02d}|${t.day:02d}|${t.year}'
}.replace('|', match fmt_dlmtr { }
.dot { '.' } .mmmd{
.hyphen { '-' } '$month|${t.day}'
.slash { '/' } }
.space { ' ' } .mmmdd{
else { 'unknown enumeration $fmt_dlmtr' } '$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
@ -552,21 +550,23 @@ pub fn (t Time) get_fmt_date_str(fmt_dlmtr FormatDelimiter, fmt_date FormatDate)
// @return string // @return string
// @example 11.07.1980 21:23:42 // @example 11.07.1980 21:23:42
pub fn (t Time) get_fmt_str(fmt_dlmtr FormatDelimiter, fmt_time FormatTime, fmt_date FormatDate) string { pub fn (t Time) get_fmt_str(fmt_dlmtr FormatDelimiter, fmt_time FormatTime, fmt_date FormatDate) string {
if fmt_date == .no_date { if fmt_date == .no_date {
if fmt_time == .no_time { if fmt_time == .no_time {
// 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 { }
return t.get_fmt_time_str(fmt_time) else {
} return t.get_fmt_time_str(fmt_time)
} else { }
if fmt_time != .no_time { }
return t.get_fmt_date_str(fmt_dlmtr, fmt_date) else {
+ ' ' if fmt_time != .no_time {
+ t.get_fmt_time_str(fmt_time) return t.get_fmt_date_str(fmt_dlmtr, fmt_date) + ' ' + t.get_fmt_time_str(fmt_time)
} else { }
return t.get_fmt_date_str(fmt_dlmtr, fmt_date) else {
} return t.get_fmt_date_str(fmt_dlmtr, fmt_date)
} }
}
} }

View File

@ -1,54 +1,54 @@
// 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{
panic('html failed') panic('html failed')
} }
mut header := '' mut header := ''
if os.exists('header.html') { if os.exists('header.html') {
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 {
line := _line.trim_space() line := _line.trim_space()
if line == '<style>' { if line == '<style>' {
in_css = true in_css = true
} }
else if line == '</style>' { else if line == '</style>' {
//in_css = false // in_css = false
} }
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 {
s.writeln('if ' + line[pos+4..] + '{') continue
}
s.writeln('if ' + line[pos + 4..] + '{')
s.writeln(STR_START) s.writeln(STR_START)
} }
else if line.contains('@end') { else if line.contains('@end') {
@ -63,8 +63,10 @@ _ = 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 {
s.writeln('for ' + line[pos+4..] + '{') continue
}
s.writeln('for ' + line[pos + 4..] + '{')
s.writeln(STR_START) s.writeln(STR_START)
} }
else if !in_css && line.contains('.') && line.ends_with('{') { else if !in_css && line.contains('.') && line.ends_with('{') {
@ -76,7 +78,7 @@ _ = header
} }
// HTML, may include `@var` // HTML, may include `@var`
else { else {
s.writeln(line.replace('@', '\x24').replace("'", '"') ) s.writeln(line.replace('@', '\x24').replace("'", '"'))
} }
} }
s.writeln(STR_END) s.writeln(STR_END)