js: `os` now compiles to the JS backend, more builtins & minor codegen fixes (#11302)
							parent
							
								
									f257a23313
								
							
						
					
					
						commit
						109d5d5847
					
				|  | @ -32,12 +32,38 @@ struct Option { | |||
| 	err   Error | ||||
| } | ||||
| 
 | ||||
| // IError holds information about an error instance
 | ||||
| pub interface IError { | ||||
| 	msg string | ||||
| 	code int | ||||
| } | ||||
| 
 | ||||
| // Error is the default implementation of IError, that is returned by e.g. `error()`
 | ||||
| pub struct Error { | ||||
| pub: | ||||
| 	msg  string | ||||
| 	code int | ||||
| } | ||||
| 
 | ||||
| const none__ = IError(&None__{}) | ||||
| 
 | ||||
| struct None__ { | ||||
| 	msg  string | ||||
| 	code int | ||||
| } | ||||
| 
 | ||||
| fn (_ None__) str() string { | ||||
| 	return 'none' | ||||
| } | ||||
| 
 | ||||
| pub fn (err IError) str() string { | ||||
| 	return match err { | ||||
| 		None__ { 'none' } | ||||
| 		Error { err.msg } | ||||
| 		else { '$err.type_name(): $err.msg' } | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (o Option) str() string { | ||||
| 	if o.state == 0 { | ||||
| 		return 'Option{ ok }' | ||||
|  | @ -48,21 +74,28 @@ pub fn (o Option) str() string { | |||
| 	return 'Option{ error: "$o.err" }' | ||||
| } | ||||
| 
 | ||||
| pub fn error(s string) Option { | ||||
| 	return Option{ | ||||
| 		state: 2 | ||||
| 		err: Error{ | ||||
| 			msg: s | ||||
| 		} | ||||
| [if trace_error ?] | ||||
| fn trace_error(x string) { | ||||
| 	eprintln('> ${@FN} | $x') | ||||
| } | ||||
| 
 | ||||
| // error returns a default error instance containing the error given in `message`.
 | ||||
| // Example: `if ouch { return error('an error occurred') }`
 | ||||
| [inline] | ||||
| pub fn error(message string) IError { | ||||
| 	trace_error(message) | ||||
| 	return &Error{ | ||||
| 		msg: message | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn error_with_code(s string, code int) Option { | ||||
| 	return Option{ | ||||
| 		state: 2 | ||||
| 		err: Error{ | ||||
| 			msg: s | ||||
| 			code: code | ||||
| 		} | ||||
| // error_with_code returns a default error instance containing the given `message` and error `code`.
 | ||||
| // `if ouch { return error_with_code('an error occurred', 1) }`
 | ||||
| [inline] | ||||
| pub fn error_with_code(message string, code int) IError { | ||||
| 	// trace_error('$message | code: $code')
 | ||||
| 	return &Error{ | ||||
| 		msg: message | ||||
| 		code: code | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -2,7 +2,15 @@ module builtin | |||
| 
 | ||||
| pub fn (b byte) is_space() bool { | ||||
| 	mut result := false | ||||
| 	#result = /^\s*$/.test(String.fromCharCode(b)) | ||||
| 	#result.val = /^\s*$/.test(String.fromCharCode(b)) | ||||
| 
 | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| pub fn (c byte) is_letter() bool { | ||||
| 	result := false | ||||
| 
 | ||||
| 	#result.val = (c.val >= `a`.charCodeAt() && c.val <= `z`.charCodeAt()) || (c.val >= `A`.charCodeAt() && c.val <= `Z`.charCodeAt()) | ||||
| 
 | ||||
| 	return result | ||||
| } | ||||
|  |  | |||
|  | @ -541,3 +541,168 @@ pub fn (s string) split_nth(delim string, nth int) []string { | |||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct RepIndex { | ||||
| 	idx     int | ||||
| 	val_idx int | ||||
| } | ||||
| 
 | ||||
| // replace_each replaces all occurences of the string pairs given in `vals`.
 | ||||
| // Example: assert 'ABCD'.replace_each(['B','C/','C','D','D','C']) == 'AC/DC'
 | ||||
| [direct_array_access] | ||||
| pub fn (s string) replace_each(vals []string) string { | ||||
| 	if s.len == 0 || vals.len == 0 { | ||||
| 		return s.clone() | ||||
| 	} | ||||
| 
 | ||||
| 	if vals.len % 2 != 0 { | ||||
| 		eprintln('string.replace_each(): odd number of strings') | ||||
| 		return s.clone() | ||||
| 	} | ||||
| 
 | ||||
| 	// `rep` - string to replace
 | ||||
| 	// `with_` - string to replace with_
 | ||||
| 	// Remember positions of all rep strings, and calculate the length
 | ||||
| 	// of the new string to do just one allocation.
 | ||||
| 
 | ||||
| 	mut idxs := []RepIndex{} | ||||
| 	mut idx := 0 | ||||
| 	s_ := s.clone() | ||||
| 	#function setCharAt(str,index,chr) { | ||||
| 	#if(index > str.length-1) return str; | ||||
| 	#return str.substring(0,index) + chr + str.substring(index+1); | ||||
| 	#} | ||||
| 
 | ||||
| 	for rep_i := 0; rep_i < vals.len; rep_i += 2 { | ||||
| 		rep := vals[rep_i] | ||||
| 		mut with_ := vals[rep_i + 1] | ||||
| 		with_ = with_ | ||||
| 		for { | ||||
| 			idx = s_.index_after(rep, idx) | ||||
| 			if idx == -1 { | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
| 			for i in 0 .. rep.len { | ||||
| 				mut j_ := i | ||||
| 				j_ = j_ | ||||
| 				#s_.str = setCharAt(s_.str,idx + i, String.fromCharCode(127)) | ||||
| 			} | ||||
| 
 | ||||
| 			idxs << RepIndex{ | ||||
| 				idx: idx | ||||
| 				val_idx: rep_i | ||||
| 			} | ||||
| 			idx += rep.len | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if idxs.len == 0 { | ||||
| 		return s.clone() | ||||
| 	} | ||||
| 
 | ||||
| 	idxs.sort(a.idx < b.idx) | ||||
| 
 | ||||
| 	mut b := '' | ||||
| 	mut idx_pos := 0 | ||||
| 	mut cur_idx := idxs[idx_pos] | ||||
| 	mut b_i := 0 | ||||
| 	for i := 0; i < s.len; i++ { | ||||
| 		if i == cur_idx.idx { | ||||
| 			rep := vals[cur_idx.val_idx] | ||||
| 			with_ := vals[cur_idx.val_idx + 1] | ||||
| 			for j in 0 .. with_.len { | ||||
| 				mut j_ := j | ||||
| 				j_ = j_ | ||||
| 				#b.str = setCharAt(b.str,b_i, with_.str[j]) | ||||
| 				//#b.str[b_i] = with_.str[j]
 | ||||
| 				b_i++ | ||||
| 			} | ||||
| 			i += rep.len - 1 | ||||
| 			idx_pos++ | ||||
| 			if idx_pos < idxs.len { | ||||
| 				cur_idx = idxs[idx_pos] | ||||
| 			} | ||||
| 		} else { | ||||
| 			#b.str = setCharAt(b.str,b_i,s.str[i]) //b.str[b_i] = s.str[i] | ||||
| 			b_i++ | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // last_index returns the position of the last occurence of the input string.
 | ||||
| fn (s string) last_index_(p string) int { | ||||
| 	if p.len > s.len || p.len == 0 { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	mut i := s.len - p.len | ||||
| 	for i >= 0 { | ||||
| 		mut j := 0 | ||||
| 		for j < p.len && s[i + j] == p[j] { | ||||
| 			j++ | ||||
| 		} | ||||
| 		if j == p.len { | ||||
| 			return i | ||||
| 		} | ||||
| 		i-- | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
| 
 | ||||
| // last_index returns the position of the last occurence of the input string.
 | ||||
| pub fn (s string) last_index(p string) ?int { | ||||
| 	idx := s.last_index_(p) | ||||
| 	if idx == -1 { | ||||
| 		return none | ||||
| 	} | ||||
| 	return idx | ||||
| } | ||||
| 
 | ||||
| pub fn (s string) trim_space() string { | ||||
| 	res := '' | ||||
| 	#res.str = s.str.trim() | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn (s string) index_after(p string, start int) int { | ||||
| 	if p.len > s.len { | ||||
| 		return -1 | ||||
| 	} | ||||
| 
 | ||||
| 	mut strt := start | ||||
| 	if start < 0 { | ||||
| 		strt = 0 | ||||
| 	} | ||||
| 	if start >= s.len { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	mut i := strt | ||||
| 
 | ||||
| 	for i < s.len { | ||||
| 		mut j := 0 | ||||
| 		mut ii := i | ||||
| 		for j < p.len && s[ii] == p[j] { | ||||
| 			j++ | ||||
| 			ii++ | ||||
| 		} | ||||
| 
 | ||||
| 		if j == p.len { | ||||
| 			return i | ||||
| 		} | ||||
| 		i++ | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
| 
 | ||||
| pub fn (s string) split_into_lines() []string { | ||||
| 	mut res := []string{} | ||||
| 	#let i = 0 | ||||
| 	#s.str.split('\n').forEach((str) => { | ||||
| 	#res.arr[i] = new string(str); | ||||
| 	#}) | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
|  |  | |||
|  | @ -359,6 +359,7 @@ pub fn (s string) replace_each(vals []string) string { | |||
| 		with := vals[rep_i + 1] | ||||
| 		for { | ||||
| 			idx = s_.index_after(rep, idx) | ||||
| 
 | ||||
| 			if idx == -1 { | ||||
| 				break | ||||
| 			} | ||||
|  | @ -374,6 +375,7 @@ pub fn (s string) replace_each(vals []string) string { | |||
| 				idx: idx | ||||
| 				val_idx: rep_i | ||||
| 			} | ||||
| 
 | ||||
| 			idx += rep.len | ||||
| 			new_len += with.len - rep.len | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| module os_js | ||||
| module os | ||||
| 
 | ||||
| pub struct File { | ||||
| pub: | ||||
|  | @ -128,3 +128,9 @@ pub fn (mut f File) write_string(s string) ?int { | |||
| 	nbytes := f.write(s.bytes()) ? | ||||
| 	return nbytes | ||||
| } | ||||
| 
 | ||||
| pub fn (mut f File) close() { | ||||
| 	#f.valueOf().fd.close() | ||||
| } | ||||
| 
 | ||||
| pub fn (mut f File) write_full_buffer(s voidptr, buffer_len size_t) ? {} | ||||
|  | @ -0,0 +1,21 @@ | |||
| module os | ||||
| 
 | ||||
| // write_file_array writes the data in `buffer` to a file in `path`.
 | ||||
| pub fn write_file_array(path string, buffer array) ? { | ||||
| 	mut f := create(path) ? | ||||
| 	unsafe { f.write_full_buffer(buffer.data, size_t(buffer.len * buffer.element_size)) ? } | ||||
| 	f.close() | ||||
| } | ||||
| 
 | ||||
| pub fn glob(patterns ...string) ?[]string { | ||||
| 	mut matches := []string{} | ||||
| 	for pattern in patterns { | ||||
| 		native_glob_pattern(pattern, mut matches) ? | ||||
| 	} | ||||
| 	matches.sort() | ||||
| 	return matches | ||||
| } | ||||
| 
 | ||||
| pub const ( | ||||
| 	args = []string{} | ||||
| ) | ||||
|  | @ -4,9 +4,9 @@ module os | |||
| #const $path = require('path'); | ||||
| 
 | ||||
| pub const ( | ||||
| 	args           = []string{} | ||||
| 	path_delimiter = '/' | ||||
| 	path_separator = '/' | ||||
| 	args           = []string{} | ||||
| ) | ||||
| 
 | ||||
| $if js_node { | ||||
|  |  | |||
							
								
								
									
										21
									
								
								vlib/os/os.v
								
								
								
								
							
							
						
						
									
										21
									
								
								vlib/os/os.v
								
								
								
								
							|  | @ -4,7 +4,6 @@ | |||
| module os | ||||
| 
 | ||||
| pub const ( | ||||
| 	args          = []string{} | ||||
| 	max_path_len  = 4096 | ||||
| 	wd_at_startup = getwd() | ||||
| ) | ||||
|  | @ -344,22 +343,15 @@ pub fn write_file(path string, text string) ? { | |||
| 	f.close() | ||||
| } | ||||
| 
 | ||||
| // write_file_array writes the data in `buffer` to a file in `path`.
 | ||||
| pub fn write_file_array(path string, buffer array) ? { | ||||
| 	mut f := create(path) ? | ||||
| 	unsafe { f.write_full_buffer(buffer.data, size_t(buffer.len * buffer.element_size)) ? } | ||||
| 	f.close() | ||||
| } | ||||
| 
 | ||||
| // executable_fallback is used when there is not a more platform specific and accurate implementation.
 | ||||
| // It relies on path manipulation of os.args[0] and os.wd_at_startup, so it may not work properly in
 | ||||
| // all cases, but it should be better, than just using os.args[0] directly.
 | ||||
| fn executable_fallback() string { | ||||
| 	if os.args.len == 0 { | ||||
| 	if args.len == 0 { | ||||
| 		// we are early in the bootstrap, os.args has not been initialized yet :-|
 | ||||
| 		return '' | ||||
| 	} | ||||
| 	mut exepath := os.args[0] | ||||
| 	mut exepath := args[0] | ||||
| 	$if windows { | ||||
| 		if !exepath.contains('.exe') { | ||||
| 			exepath += '.exe' | ||||
|  | @ -639,12 +631,3 @@ pub fn execute_or_exit(cmd string) Result { | |||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn glob(patterns ...string) ?[]string { | ||||
| 	mut matches := []string{} | ||||
| 	for pattern in patterns { | ||||
| 		native_glob_pattern(pattern, mut matches) ? | ||||
| 	} | ||||
| 	matches.sort() | ||||
| 	return matches | ||||
| } | ||||
|  |  | |||
|  | @ -15,22 +15,25 @@ pub fn mkdir(path string) ?bool { | |||
| 
 | ||||
| pub fn is_dir(path string) bool { | ||||
| 	res := false | ||||
| 	#res.val = $fs.existsSync(path,str) && $fs.lstatSync(path.str).isDirectory() | ||||
| 
 | ||||
| 	$if js_node { | ||||
| 		#res.val = $fs.existsSync(path,str) && $fs.lstatSync(path.str).isDirectory() | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn is_link(path string) bool { | ||||
| 	res := false | ||||
| 	#res.val = $fs.existsSync(path.str) && $fs.lstatSync(path.str).isSymbolicLink() | ||||
| 
 | ||||
| 	$if js_node { | ||||
| 		#res.val = $fs.existsSync(path.str) && $fs.lstatSync(path.str).isSymbolicLink() | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn exists(path string) bool { | ||||
| 	res := false | ||||
| 	#res.val = $fs.existsSync(path.str) | ||||
| 
 | ||||
| 	$if js_node { | ||||
| 		#res.val = $fs.existsSync(path.str) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
|  | @ -40,8 +43,85 @@ pub fn ls(path string) ?[]string { | |||
| 	} | ||||
| 
 | ||||
| 	result := []string{} | ||||
| 	#let i = 0 | ||||
| 	#$fs.readdirSync(path.str).forEach((path) => result.arr[i++] = new builtin.string(path)) | ||||
| 
 | ||||
| 	$if js_node { | ||||
| 		#let i = 0 | ||||
| 		#$fs.readdirSync(path.str).forEach((path) => result.arr[i++] = new builtin.string(path)) | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| pub fn get_raw_line() string { | ||||
| 	return '' | ||||
| } | ||||
| 
 | ||||
| pub fn executable() string { | ||||
| 	return '' | ||||
| } | ||||
| 
 | ||||
| pub fn is_executable(path string) bool { | ||||
| 	eprintln('TODO: There is no isExecutable on fs.stats') | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| pub fn rmdir(path string) ? { | ||||
| 	$if js_node { | ||||
| 		err := '' | ||||
| 		#try { | ||||
| 		#$fs.rmdirSync(path.str) | ||||
| 		#return; | ||||
| 		#} catch (e) { | ||||
| 		#err.str = 'Failed to remove "' + path.str + '": ' + e.toString() | ||||
| 		#} | ||||
| 
 | ||||
| 		return error(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn rm(path string) ? { | ||||
| 	$if js_node { | ||||
| 		err := '' | ||||
| 		#try { | ||||
| 		#$fs.rmSync(path.str) | ||||
| 		#return; | ||||
| 		#} catch (e) { | ||||
| 		#err.str = 'Failed to remove "' + path.str + '": ' + e.toString() | ||||
| 		#} | ||||
| 
 | ||||
| 		return error(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn cp(src string, dst string) ? { | ||||
| 	$if js_node { | ||||
| 		err := '' | ||||
| 		#try { | ||||
| 		#$fs.cpSync(src.str,dst.str); | ||||
| 		#return; | ||||
| 		#} catch (e) { | ||||
| 		#err.str = 'failed to copy ' + src.str + ' to ' + dst.str + ': ' + e.toString(); | ||||
| 		#} | ||||
| 
 | ||||
| 		return error(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn read_file(s string) ?string { | ||||
| 	mut err := '' | ||||
| 	err = err | ||||
| 	res := '' | ||||
| 	#try { | ||||
| 	#res.str = $fs.readFileSync(s.str).toString() | ||||
| 	#} catch (e) { | ||||
| 	#err.str = 'Failed to read file: ' + e.toString() | ||||
| 	#return error(err) | ||||
| 	#} | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn getwd() string { | ||||
| 	res := '' | ||||
| 	#res.str = $process.cwd() | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
|  |  | |||
|  | @ -1,31 +0,0 @@ | |||
| module os_js | ||||
| 
 | ||||
| // setenv sets the value of an environment variable with `name` to `value`.
 | ||||
| pub fn setenv(key string, val string, overwrite bool) { | ||||
| 	#if ($process.env[key] && !(overwrite.valueOf())) return; | ||||
| 	#$process.env[key] = val + ''; | ||||
| } | ||||
| 
 | ||||
| // `getenv` returns the value of the environment variable named by the key.
 | ||||
| pub fn getenv(key string) string { | ||||
| 	mut res := '' | ||||
| 	#if ($process.env[key]) res = new builtin.string($process.env[key]) | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| // unsetenv clears an environment variable with `name`.
 | ||||
| pub fn unsetenv(name string) int { | ||||
| 	#$process.env[name] = "" | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| pub fn environ() map[string]string { | ||||
| 	mut res := map[string]string{} | ||||
| 	#for (const key in $process.env) { | ||||
| 	#res.map.set(key,$process.env[key]) | ||||
| 	#} | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
|  | @ -1,95 +0,0 @@ | |||
| module os_js | ||||
| 
 | ||||
| #const $fs = require('fs'); | ||||
| #const $path = require('path'); | ||||
| 
 | ||||
| pub const ( | ||||
| 	args = []string{} | ||||
| ) | ||||
| 
 | ||||
| $if js_node { | ||||
| 	#$process.argv.forEach(function(val,index) { args.arr[index] = new string(val); }) | ||||
| } | ||||
| 
 | ||||
| // real_path 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
 | ||||
| // Also https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
 | ||||
| // and https://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html
 | ||||
| // NB: this particular rabbit hole is *deep* ...
 | ||||
| pub fn real_path(fpath string) string { | ||||
| 	$if js_node { | ||||
| 		mut res := '' | ||||
| 		#res = new string( $fs.realpathSync(fpath)) | ||||
| 
 | ||||
| 		return res | ||||
| 	} $else { | ||||
| 		return fpath | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // flush will flush the stdout buffer.
 | ||||
| pub fn flush() { | ||||
| 	$if js_node { | ||||
| 		#$process.stdout.write('') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // chmod change file access attributes of `path` to `mode`.
 | ||||
| // Octals like `0o600` can be used.
 | ||||
| pub fn chmod(path string, mode int) { | ||||
| 	$if js_node { | ||||
| 		#$fs.chmodSync(''+path,mode.valueOf()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // chown changes the owner and group attributes of `path` to `owner` and `group`.
 | ||||
| // Octals like `0o600` can be used.
 | ||||
| pub fn chown(path string, owner int, group int) { | ||||
| 	$if js_node { | ||||
| 		#$fs.chownSync(''+path,owner.valueOf(),group.valueOf()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn temp_dir() string { | ||||
| 	mut res := '' | ||||
| 	$if js_node { | ||||
| 		#res = new builtin.string($os.tmpdir()) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn home_dir() string { | ||||
| 	mut res := '' | ||||
| 	$if js_node { | ||||
| 		#res = new builtin.string($os.homedir()) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| // join_path returns a path as string from input string parameter(s).
 | ||||
| pub fn join_path(base string, dirs ...string) string { | ||||
| 	mut result := []string{} | ||||
| 	result << base.trim_right('\\/') | ||||
| 	for d in dirs { | ||||
| 		result << d | ||||
| 	} | ||||
| 	mut path_sep := '' | ||||
| 	#path_sep = $path.sep; | ||||
| 
 | ||||
| 	res := result.join(path_sep) | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn execute(cmd string) Result { | ||||
| 	mut exit_code := 0 | ||||
| 	mut stdout := '' | ||||
| 	#let commands = cmd.str.split(' '); | ||||
| 	#let output = $child_process.spawnSync(commands[0],commands.slice(1,commands.length)); | ||||
| 	#exit_code = new builtin.int(output.status) | ||||
| 	#stdout = new builtin.string(output.stdout + '') | ||||
| 
 | ||||
| 	return Result{ | ||||
| 		exit_code: exit_code | ||||
| 		output: stdout | ||||
| 	} | ||||
| } | ||||
|  | @ -1,13 +0,0 @@ | |||
| module os_js | ||||
| 
 | ||||
| pub const ( | ||||
| 	// todo(playX): NodeJS does not seem to have any path limit?
 | ||||
| 	max_path_len = 4096 | ||||
| ) | ||||
| 
 | ||||
| pub struct Result { | ||||
| pub: | ||||
| 	exit_code int | ||||
| 	output    string | ||||
| 	// stderr string // TODO
 | ||||
| } | ||||
|  | @ -1,173 +0,0 @@ | |||
| module os_js | ||||
| 
 | ||||
| #const $child_process = require('child_process') | ||||
| 
 | ||||
| // ProcessState.not_started - the process has not yet started
 | ||||
| // ProcessState.running - the process is currently running
 | ||||
| // ProcessState.stopped - the process was running, but was stopped temporarily
 | ||||
| // ProcessState.exited - the process has finished/exited
 | ||||
| // ProcessState.aborted - the process was terminated by a signal
 | ||||
| // ProcessState.closed - the process resources like opened file descriptors were freed/discarded, final state.
 | ||||
| pub enum ProcessState { | ||||
| 	not_started | ||||
| 	running | ||||
| 	stopped | ||||
| 	exited | ||||
| 	aborted | ||||
| 	closed | ||||
| } | ||||
| 
 | ||||
| // todo(playX): fix reference member access in JS backend
 | ||||
| [heap] | ||||
| pub struct Process { | ||||
| pub: | ||||
| 	filename string | ||||
| pub mut: | ||||
| 	pid    voidptr | ||||
| 	code   int = -1 | ||||
| 	status ProcessState = .not_started | ||||
| 	// the current status of the process
 | ||||
| 	err           string   // if the process fails, contains the reason why
 | ||||
| 	args          []string // the arguments that the command takes
 | ||||
| 	env_is_custom bool     // true, when the environment was customized with .set_environment
 | ||||
| 	env           []string // the environment with which the process was started  (list of 'var=val')
 | ||||
| 	use_stdio_ctl bool     // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp()
 | ||||
| 	use_pgroup    bool     // when true, the process will create a new process group, enabling .signal_pgkill()
 | ||||
| 	stdio_fd      [3]int   // the stdio file descriptors for the child process, used only by the nix implementation
 | ||||
| } | ||||
| 
 | ||||
| // new_process - create a new process descriptor
 | ||||
| // NB: new does NOT start the new process.
 | ||||
| // That is done because you may want to customize it first,
 | ||||
| // by calling different set_ methods on it.
 | ||||
| // In order to start it, call p.run() or p.wait()
 | ||||
| pub fn new_process(filename string) &Process { | ||||
| 	return &Process{ | ||||
| 		filename: filename | ||||
| 		stdio_fd: [-1, -1, -1]! | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // set_args - set the arguments for the new process
 | ||||
| pub fn (mut p Process) set_args(pargs []string) { | ||||
| 	if p.status != .not_started { | ||||
| 		return | ||||
| 	} | ||||
| 	p.args = pargs | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // set_environment - set a custom environment variable mapping for the new process
 | ||||
| pub fn (mut p Process) set_environment(envs map[string]string) { | ||||
| 	if p.status != .not_started { | ||||
| 		return | ||||
| 	} | ||||
| 	p.env_is_custom = true | ||||
| 	p.env = []string{} | ||||
| 	for k, v in envs { | ||||
| 		p.env << '$k=$v' | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| fn (mut p Process) spawn_internal() { | ||||
| 	#p.val.pid = $child_process.spawn( | ||||
| 	#p.val.filename+'', | ||||
| 	#p.val.args.arr.map((x) => x.valueOf() + ''), | ||||
| 	#{ | ||||
| 	#env: (p.val.env_is_custom ? p.val.env : $process.env), | ||||
| 	#}) | ||||
| 	#p.val.pid.on('error', function (err) { builtin.panic('Failed to start subprocess') }) | ||||
| 
 | ||||
| 	p.status = .running | ||||
| 	// todo(playX): stderr,stdin
 | ||||
| 	if p.use_stdio_ctl { | ||||
| 		#p.val.pid.stdout.pipe(process.stdout) | ||||
| 		#p.val.pid.stdin.pipe(process.stdin) | ||||
| 		#p.val.pid.stderr.pipe(process.stderr) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Process) run() { | ||||
| 	if p.status != .not_started { | ||||
| 		return | ||||
| 	} | ||||
| 	p.spawn_internal() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Process) signal_kill() { | ||||
| 	if p.status !in [.running, .stopped] { | ||||
| 		return | ||||
| 	} | ||||
| 	#p.val.pid.kill('SIGKILL'); | ||||
| 
 | ||||
| 	p.status = .aborted | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Process) signal_stop() { | ||||
| 	if p.status !in [.running, .stopped] { | ||||
| 		return | ||||
| 	} | ||||
| 	#p.val.pid.kill('SIGSTOP'); | ||||
| 
 | ||||
| 	p.status = .aborted | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Process) signal_continue() { | ||||
| 	if p.status != .stopped { | ||||
| 		return | ||||
| 	} | ||||
| 	#p.val.pid.kill('SIGCONT'); | ||||
| 
 | ||||
| 	p.status = .running | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Process) wait() { | ||||
| 	if p.status == .not_started { | ||||
| 		p.spawn_internal() | ||||
| 	} | ||||
| 	if p.status !in [.running, .stopped] { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	p.wait_internal() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| fn (mut p Process) wait_internal() { | ||||
| 	#p.val.pid.on('exit', function (code) { console.log(code) }) | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Process) set_redirect_stdio() { | ||||
| 	p.use_stdio_ctl = true | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Process) stdin_write(s string) { | ||||
| 	p.check_redirection_call('stdin_write') | ||||
| 	#p.val.pid.stdin.write(s) | ||||
| } | ||||
| 
 | ||||
| // todo(playX): probably does not work
 | ||||
| 
 | ||||
| // will read from stdout pipe, will only return when EOF (end of file) or data
 | ||||
| // means this will block unless there is data
 | ||||
| pub fn (mut p Process) stdout_slurp() string { | ||||
| 	p.check_redirection_call('stdout_slurp') | ||||
| 	mut res := '' | ||||
| 	#p.val.pid.stdout.on('data', function (data) { res = new builtin.string(data) }) | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| // _check_redirection_call - should be called just by stdxxx methods
 | ||||
| fn (mut p Process) check_redirection_call(fn_name string) { | ||||
| 	if !p.use_stdio_ctl { | ||||
| 		panic('Call p.set_redirect_stdio() before calling p.$fn_name') | ||||
| 	} | ||||
| 	if p.status == .not_started { | ||||
| 		panic('Call p.${fn_name}() after you have called p.run()') | ||||
| 	} | ||||
| } | ||||
|  | @ -338,7 +338,7 @@ fn (mut g JsGen) gen_builtin_type_defs() { | |||
| 				g.gen_builtin_prototype( | ||||
| 					typ_name: typ_name | ||||
| 					default_value: 'new Number(0)' | ||||
| 					constructor: 'this.val = typeof(val) == "string" ? val.charCodeAt() : (val | 0)' | ||||
| 					constructor: 'if (typeof(val) == "string") { this.val = val.charCodeAt() } else if (val instanceof string) { this.val = val.str.charCodeAt(); } else { this.val =  val | 0 }' | ||||
| 					value_of: 'this.val | 0' | ||||
| 					to_string: 'new string(this.val + "")' | ||||
| 					eq: 'this.valueOf() === other.valueOf()' | ||||
|  |  | |||
|  | @ -525,6 +525,111 @@ fn (mut g JsGen) gen_global_decl(node ast.GlobalDecl) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g JsGen) stmt_no_semi(node ast.Stmt) { | ||||
| 	g.stmt_start_pos = g.ns.out.len | ||||
| 	match node { | ||||
| 		ast.EmptyStmt {} | ||||
| 		ast.AsmStmt { | ||||
| 			panic('inline asm is not supported by js') | ||||
| 		} | ||||
| 		ast.AssertStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_assert_stmt(node) | ||||
| 		} | ||||
| 		ast.AssignStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_assign_stmt(node, false) | ||||
| 		} | ||||
| 		ast.Block { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_block(node) | ||||
| 			g.writeln('') | ||||
| 		} | ||||
| 		ast.BranchStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_branch_stmt(node) | ||||
| 		} | ||||
| 		ast.CompFor {} | ||||
| 		ast.ConstDecl { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_const_decl(node) | ||||
| 		} | ||||
| 		ast.DeferStmt { | ||||
| 			g.defer_stmts << node | ||||
| 		} | ||||
| 		ast.EnumDecl { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_enum_decl(node) | ||||
| 			g.writeln('') | ||||
| 		} | ||||
| 		ast.ExprStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_expr_stmt_no_semi(node) | ||||
| 		} | ||||
| 		ast.FnDecl { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.fn_decl = unsafe { &node } | ||||
| 			g.gen_fn_decl(node) | ||||
| 		} | ||||
| 		ast.ForCStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_for_c_stmt(node) | ||||
| 			g.writeln('') | ||||
| 		} | ||||
| 		ast.ForInStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_for_in_stmt(node) | ||||
| 			g.writeln('') | ||||
| 		} | ||||
| 		ast.ForStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_for_stmt(node) | ||||
| 			g.writeln('') | ||||
| 		} | ||||
| 		ast.GlobalDecl { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_global_decl(node) | ||||
| 			g.writeln('') | ||||
| 		} | ||||
| 		ast.GotoLabel { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.writeln('${g.js_name(node.name)}:') | ||||
| 		} | ||||
| 		ast.GotoStmt { | ||||
| 			// skip: JS has no goto
 | ||||
| 		} | ||||
| 		ast.HashStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_hash_stmt(node) | ||||
| 		} | ||||
| 		ast.Import { | ||||
| 			g.ns.imports[node.mod] = node.alias | ||||
| 		} | ||||
| 		ast.InterfaceDecl { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_interface_decl(node) | ||||
| 		} | ||||
| 		ast.Module { | ||||
| 			// skip: namespacing implemented externally
 | ||||
| 		} | ||||
| 		ast.NodeError {} | ||||
| 		ast.Return { | ||||
| 			if g.defer_stmts.len > 0 { | ||||
| 				g.gen_defer_stmts() | ||||
| 			} | ||||
| 			g.gen_return_stmt(node) | ||||
| 		} | ||||
| 		ast.SqlStmt {} | ||||
| 		ast.StructDecl { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_struct_decl(node) | ||||
| 		} | ||||
| 		ast.TypeDecl { | ||||
| 			// skip JS has no typedecl
 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g JsGen) stmt(node ast.Stmt) { | ||||
| 	g.stmt_start_pos = g.ns.out.len | ||||
| 	match node { | ||||
|  | @ -538,7 +643,7 @@ fn (mut g JsGen) stmt(node ast.Stmt) { | |||
| 		} | ||||
| 		ast.AssignStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.gen_assign_stmt(node) | ||||
| 			g.gen_assign_stmt(node, true) | ||||
| 		} | ||||
| 		ast.Block { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
|  | @ -860,7 +965,7 @@ fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) { | |||
| 	g.writeln('}') | ||||
| } | ||||
| 
 | ||||
| fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) { | ||||
| fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { | ||||
| 	if stmt.left.len > stmt.right.len { | ||||
| 		// multi return
 | ||||
| 		g.write('const [') | ||||
|  | @ -874,7 +979,9 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) { | |||
| 		} | ||||
| 		g.write('] = ') | ||||
| 		g.expr(stmt.right[0]) | ||||
| 		g.writeln(';') | ||||
| 		if semicolon { | ||||
| 			g.writeln(';') | ||||
| 		} | ||||
| 	} else { | ||||
| 		// `a := 1` | `a,b := 1,2`
 | ||||
| 		for i, left in stmt.left { | ||||
|  | @ -996,10 +1103,12 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) { | |||
| 					g.cast_stack.delete_last() | ||||
| 				} | ||||
| 			} | ||||
| 			if g.inside_loop { | ||||
| 				g.write('; ') | ||||
| 			} else { | ||||
| 				g.writeln(';') | ||||
| 			if semicolon { | ||||
| 				if g.inside_loop { | ||||
| 					g.write('; ') | ||||
| 				} else { | ||||
| 					g.writeln(';') | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -1072,6 +1181,10 @@ fn (mut g JsGen) gen_expr_stmt(it ast.ExprStmt) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g JsGen) gen_expr_stmt_no_semi(it ast.ExprStmt) { | ||||
| 	g.expr(it.expr) | ||||
| } | ||||
| 
 | ||||
| fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { | ||||
| 	if it.no_body || it.is_method { | ||||
| 		// Struct methods are handled by class generation code.
 | ||||
|  | @ -1211,7 +1324,7 @@ fn (mut g JsGen) gen_for_c_stmt(it ast.ForCStmt) { | |||
| 	} | ||||
| 	g.write('; ') | ||||
| 	if it.has_inc { | ||||
| 		g.stmt(it.inc) | ||||
| 		g.stmt_no_semi(it.inc) | ||||
| 	} | ||||
| 	g.writeln(') {') | ||||
| 	g.stmts(it.stmts) | ||||
|  | @ -1349,6 +1462,7 @@ fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) { | |||
| 	// TODO: interfaces are always `pub`?
 | ||||
| 	name := g.js_name(it.name) | ||||
| 	g.push_pub_var('/** @type $name */\n\t\t$name: undefined') | ||||
| 	g.writeln('function ${g.js_name(it.name)} (arg) { return arg; }') | ||||
| } | ||||
| 
 | ||||
| fn (mut g JsGen) gen_return_stmt(it ast.Return) { | ||||
|  | @ -1629,7 +1743,7 @@ fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool { | |||
| 				if it.or_block.stmts.len > 1 { | ||||
| 					g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) | ||||
| 				} | ||||
| 				g.write('return ') | ||||
| 				// g.write('return ')
 | ||||
| 				g.stmt(it.or_block.stmts.last()) | ||||
| 			} | ||||
| 			.propagate { | ||||
|  | @ -1707,7 +1821,8 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { | |||
| 				if it.or_block.stmts.len > 1 { | ||||
| 					g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) | ||||
| 				} | ||||
| 				g.write('return ') | ||||
| 
 | ||||
| 				//	g.write('return ')
 | ||||
| 				g.stmt(it.or_block.stmts.last()) | ||||
| 			} | ||||
| 			.propagate { | ||||
|  | @ -1928,7 +2043,8 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) { | |||
| 	} | ||||
| 
 | ||||
| 	if node.cond is ast.Ident || node.cond is ast.SelectorExpr || node.cond is ast.IntegerLiteral | ||||
| 		|| node.cond is ast.StringLiteral || node.cond is ast.FloatLiteral { | ||||
| 		|| node.cond is ast.StringLiteral || node.cond is ast.FloatLiteral | ||||
| 		|| node.cond is ast.CallExpr { | ||||
| 		cond_var = CondExpr{node.cond} | ||||
| 	} else { | ||||
| 		s := g.new_tmp_var() | ||||
|  | @ -2577,7 +2693,15 @@ fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) { | |||
| 		} | ||||
| 		g.write('string(') | ||||
| 	} | ||||
| 	g.write("\"$text\"") | ||||
| 	if it.is_raw { | ||||
| 		g.writeln('(function() { let s = String(); ') | ||||
| 		for x in text { | ||||
| 			g.writeln('s += String.fromCharCode($x);') | ||||
| 		} | ||||
| 		g.writeln('return s; })()') | ||||
| 	} else { | ||||
| 		g.write("\"$text\"") | ||||
| 	} | ||||
| 	if true || should_cast { | ||||
| 		g.write(')') | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue