mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
$exec may now provide a stdin parameter. Deprecated path.append, path.tappend, getcwd, tgetcwd, path.absolute, ls. Deprecated env::get_config_dir, replaced by env::new_get_config_dir. Added path.has_extension, path.new_append, path.temp_append, new_cwd, temp_cwd, path.new_absolute, new_ls, temp_ls. Added dstring.replace Updated win escapes for exec.
This commit is contained in:
9
.github/workflows/main.yml
vendored
9
.github/workflows/main.yml
vendored
@@ -558,15 +558,6 @@ jobs:
|
||||
cd resources/testfragments
|
||||
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
|
||||
|
||||
- name: Install QEMU and Risc-V toolchain
|
||||
run: |
|
||||
sudo apt-get install opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf
|
||||
|
||||
- name: Compile and run Baremetal Risc-V Example
|
||||
run: |
|
||||
cd resources/examples/embedded/riscv-qemu
|
||||
make C3C_PATH=../../../../build/ run
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
|
||||
@@ -7,26 +7,17 @@ import libc, std::hash, std::io, std::os::backtrace;
|
||||
/**
|
||||
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
**/
|
||||
fault IteratorResult
|
||||
{
|
||||
NO_MORE_ELEMENT
|
||||
}
|
||||
fault IteratorResult { NO_MORE_ELEMENT }
|
||||
|
||||
/**
|
||||
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
**/
|
||||
fault SearchResult
|
||||
{
|
||||
MISSING
|
||||
}
|
||||
fault SearchResult { MISSING }
|
||||
|
||||
/**
|
||||
* Use `CastResult` when an attempt at conversion fails.
|
||||
**/
|
||||
fault CastResult
|
||||
{
|
||||
TYPE_MISMATCH
|
||||
}
|
||||
fault CastResult { TYPE_MISMATCH }
|
||||
|
||||
/**
|
||||
* Stores a variable on the stack, then restores it at the end of the
|
||||
|
||||
@@ -48,6 +48,56 @@ fn DString new(String c = "", Allocator allocator = allocator::heap())
|
||||
|
||||
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
|
||||
|
||||
|
||||
fn void DString.replace_char(self, char ch, char replacement)
|
||||
{
|
||||
StringData* data = self.data();
|
||||
foreach (&c : data.chars[:data.len])
|
||||
{
|
||||
if (*c == ch) *c = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
fn void DString.replace(&self, String needle, String replacement)
|
||||
{
|
||||
StringData* data = self.data();
|
||||
usz needle_len = needle.len;
|
||||
if (!data || data.len < needle_len) return;
|
||||
usz replace_len = replacement.len;
|
||||
if (needle_len == 1 && replace_len == 1)
|
||||
{
|
||||
self.replace_char(needle[0], replacement[0]);
|
||||
return;
|
||||
}
|
||||
@pool(data.allocator) {
|
||||
String str = self.tcopy_str();
|
||||
self.clear();
|
||||
usz len = str.len;
|
||||
usz match = 0;
|
||||
foreach (i, c : str)
|
||||
{
|
||||
if (c == needle[match])
|
||||
{
|
||||
match++;
|
||||
if (match == needle_len)
|
||||
{
|
||||
self.append_chars(replacement);
|
||||
match = 0;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (match > 0)
|
||||
{
|
||||
self.append_chars(str[i - match:match]);
|
||||
match = 0;
|
||||
}
|
||||
self.append_char(c);
|
||||
}
|
||||
if (match > 0) self.append_chars(str[^match:match]);
|
||||
};
|
||||
}
|
||||
|
||||
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap())
|
||||
{
|
||||
DString string;
|
||||
|
||||
@@ -16,7 +16,7 @@ fn void! native_rmtree(Path dir)
|
||||
{
|
||||
String name = ((ZString)&entry.name).str_view();
|
||||
if (!name || name == "." || name == "..") continue;
|
||||
Path new_path = dir.tappend(name)!;
|
||||
Path new_path = dir.temp_append(name)!;
|
||||
if (entry.d_type == posix::DT_DIR)
|
||||
{
|
||||
native_rmtree(new_path)!;
|
||||
|
||||
@@ -15,7 +15,9 @@ fault PathResult
|
||||
NO_PARENT,
|
||||
}
|
||||
|
||||
struct Path (Printable)
|
||||
def Path = PathImp;
|
||||
|
||||
struct PathImp (Printable)
|
||||
{
|
||||
String path_string;
|
||||
PathEnv env;
|
||||
@@ -27,7 +29,15 @@ enum PathEnv
|
||||
POSIX
|
||||
}
|
||||
|
||||
fn Path! getcwd(Allocator allocator = allocator::heap())
|
||||
fn Path! new_cwd(Allocator allocator = allocator::heap())
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
return new(os::getcwd(allocator::temp()), allocator);
|
||||
};
|
||||
}
|
||||
|
||||
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
@@ -39,7 +49,8 @@ fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
|
||||
fn bool is_file(Path path) => os::native_is_file(path.str_view());
|
||||
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
|
||||
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
|
||||
fn Path! tgetcwd() => getcwd(allocator::temp()) @inline;
|
||||
fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline;
|
||||
fn Path! tgetcwd() @deprecated("Use temp_cwd()") => new_cwd(allocator::temp()) @inline;
|
||||
fn void! chdir(Path path) => os::native_chdir(path) @inline;
|
||||
fn Path! temp_directory(Allocator allocator = allocator::heap()) => os::native_temp_directory(allocator);
|
||||
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
|
||||
@@ -59,7 +70,17 @@ macro bool is_win32_separator(char c)
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap())
|
||||
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) @deprecated("use new_ls")
|
||||
{
|
||||
return new_ls(dir, no_dirs, no_symlinks, mask, allocator);
|
||||
|
||||
}
|
||||
fn PathList! temp_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
|
||||
{
|
||||
return new_ls(dir, no_dirs, no_symlinks, mask, allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
fn PathList! new_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap())
|
||||
{
|
||||
$if $defined(os::native_ls):
|
||||
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
|
||||
@@ -139,12 +160,17 @@ fn bool Path.equals(self, Path p2)
|
||||
return self.env == p2.env && self.path_string == p2.path_string;
|
||||
}
|
||||
|
||||
fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap()) @deprecated("Use path.new_append(...)")
|
||||
{
|
||||
return self.new_append(filename, allocator) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the string to the current path.
|
||||
*
|
||||
* @param [in] filename
|
||||
**/
|
||||
fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap())
|
||||
fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap())
|
||||
{
|
||||
if (!self.path_string.len) return new(filename, allocator, self.env)!;
|
||||
assert(!is_separator(self.path_string[^1], self.env));
|
||||
@@ -159,7 +185,9 @@ fn Path! Path.append(self, String filename, Allocator allocator = allocator::hea
|
||||
};
|
||||
}
|
||||
|
||||
fn Path! Path.tappend(self, String filename) => self.append(filename, allocator::temp());
|
||||
fn Path! Path.temp_append(self, String filename) => self.new_append(filename, allocator::temp());
|
||||
|
||||
fn Path! Path.tappend(self, String filename) @deprecated("Use path.temp_append(...)") => self.new_append(filename, allocator::temp());
|
||||
|
||||
fn usz Path.start_of_base_name(self) @local
|
||||
{
|
||||
@@ -193,10 +221,15 @@ fn bool! Path.is_absolute(self)
|
||||
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
|
||||
}
|
||||
|
||||
fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecated("Use path.new_absolute()")
|
||||
{
|
||||
return self.new_absolute(allocator) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
|
||||
**/
|
||||
fn Path! Path.absolute(self, Allocator allocator = allocator::heap())
|
||||
fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap())
|
||||
{
|
||||
String path_str = self.str_view();
|
||||
if (!path_str.len) return PathResult.INVALID_PATH?;
|
||||
@@ -220,7 +253,7 @@ fn Path! Path.absolute(self, Allocator allocator = allocator::heap())
|
||||
};
|
||||
$else
|
||||
String cwd = os::getcwd(allocator::temp())!;
|
||||
return Path { cwd, self.env }.append(path_str, allocator)!;
|
||||
return Path { cwd, self.env }.new_append(path_str, allocator)!;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -250,6 +283,22 @@ fn String Path.dirname(self)
|
||||
return path_str[:basename_start - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the path has the given extension, so given the path /foo/bar.c3
|
||||
* this would be true matching the extension "c3"
|
||||
*
|
||||
* @param [in] extension `The extension name (not including the leading '.')`
|
||||
* @require extension.len > 0 : `The extension cannot be empty`
|
||||
* @return `true if the extension matches`
|
||||
**/
|
||||
fn bool Path.has_extension(self, String extension)
|
||||
{
|
||||
String basename = self.basename();
|
||||
if (basename.len <= extension.len) return false;
|
||||
if (basename[^extension.len + 1] != '.') return false;
|
||||
return basename[^extension.len..] == extension;
|
||||
}
|
||||
|
||||
fn String! Path.extension(self)
|
||||
{
|
||||
String basename = self.basename();
|
||||
@@ -470,12 +519,12 @@ fn bool! Path.walk(self, PathWalker w, void* data)
|
||||
const PATH_MAX = 512;
|
||||
@stack_mem(PATH_MAX; Allocator allocator)
|
||||
{
|
||||
Path abs = self.absolute(allocator)!;
|
||||
PathList files = ls(abs, .allocator = allocator)!;
|
||||
Path abs = self.new_absolute(allocator)!;
|
||||
PathList files = new_ls(abs, .allocator = allocator)!;
|
||||
foreach (f : files)
|
||||
{
|
||||
if (f.str_view() == "." || f.str_view() == "..") continue;
|
||||
f = abs.append(f.str_view(), allocator)!;
|
||||
f = abs.new_append(f.str_view(), allocator)!;
|
||||
bool is_directory = is_dir(f);
|
||||
if (w(f, is_directory, data)!) return true;
|
||||
if (is_directory && f.walk(w, data)!) return true;
|
||||
|
||||
@@ -83,10 +83,15 @@ fn String! get_home_dir(Allocator using = allocator::heap())
|
||||
return get_var(home, using);
|
||||
}
|
||||
|
||||
fn Path! get_config_dir(Allocator allocator = allocator::heap()) @deprecated("use new_get_config_dir()")
|
||||
{
|
||||
return new_get_config_dir(allocator) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current user's config directory.
|
||||
**/
|
||||
fn Path! get_config_dir(Allocator allocator = allocator::heap())
|
||||
fn Path! new_get_config_dir(Allocator allocator = allocator::heap())
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
@@ -100,7 +105,7 @@ fn Path! get_config_dir(Allocator allocator = allocator::heap())
|
||||
String s = get_var_temp("XDG_CONFIG_HOME") ?? get_var_temp("HOME")!;
|
||||
const DIR = ".config";
|
||||
$endif
|
||||
return path::temp_new(s).append(DIR, .allocator = allocator);
|
||||
return path::temp_new(s).new_append(DIR, .allocator = allocator);
|
||||
$endif
|
||||
};
|
||||
}
|
||||
@@ -126,11 +131,16 @@ fn bool clear_var(String name)
|
||||
};
|
||||
}
|
||||
|
||||
fn String! executable_path(Allocator allocator = allocator::heap())
|
||||
fn String! executable_path(Allocator allocator = allocator::heap()) @deprecated("use new_executable_path()")
|
||||
{
|
||||
return new_executable_path(allocator) @inline;
|
||||
}
|
||||
|
||||
fn String! new_executable_path(Allocator allocator = allocator::heap())
|
||||
{
|
||||
$if env::DARWIN:
|
||||
return darwin::executable_path(allocator);
|
||||
$else
|
||||
return "<Unsupported>";
|
||||
return SearchResult.MISSING?;
|
||||
$endif
|
||||
}
|
||||
@@ -80,7 +80,7 @@ fn uptr! load_address() @local
|
||||
{
|
||||
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
|
||||
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?;
|
||||
String path = env::executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
|
||||
String path = env::new_executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
|
||||
uint dyld_count = darwin::_dyld_image_count();
|
||||
for (uint i = 0; i < dyld_count; i++)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
- Pointers are rendered with "0x" prefix when passed to '%s'.
|
||||
- Add temp allocator scribble.
|
||||
- Use PIC by default on Linux.
|
||||
- `$exec` may now provide a stdin parameter.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -74,6 +75,10 @@
|
||||
|
||||
- `send` and `recv` added to `libc` for Posix / Win32.
|
||||
- Add support to destroy temp allocators.
|
||||
- Deprecated `path.append`, `path.tappend`, `getcwd`, `tgetcwd`, `path.absolute`, `ls`.
|
||||
- Deprecated `env::get_config_dir`, replaced by `env::new_get_config_dir`.
|
||||
- Added `path.has_extension`, `path.new_append`, `path.temp_append`, `new_cwd`, `temp_cwd`, `path.new_absolute`, `new_ls`, `temp_ls`.
|
||||
- Added `dstring.replace`
|
||||
|
||||
## 0.6.1 Change list
|
||||
|
||||
|
||||
@@ -295,12 +295,12 @@ void resolve_libraries(BuildTarget *build_target)
|
||||
FOREACH(const char *, exec, library->execs)
|
||||
{
|
||||
printf("] Execute '%s' for library '%s':", exec, library->provides);
|
||||
puts(execute_cmd(exec, false));
|
||||
puts(execute_cmd(exec, false, NULL));
|
||||
}
|
||||
FOREACH(const char *, exec, target->execs)
|
||||
{
|
||||
printf("] Execute '%s' for library '%s':", exec, library->provides);
|
||||
puts(execute_cmd(exec, false));
|
||||
puts(execute_cmd(exec, false, NULL));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1058,12 +1058,12 @@ static void execute_scripts(void)
|
||||
if (call.len < 3 || call.ptr[call.len - 3] != '.' || call.ptr[call.len - 2] != 'c' ||
|
||||
call.ptr[call.len - 2] != '3')
|
||||
{
|
||||
(void) execute_cmd(exec, false);
|
||||
(void) execute_cmd(exec, false, NULL);
|
||||
continue;
|
||||
}
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append_len(call.ptr, call.len);
|
||||
(void) compile_and_invoke(scratch_buffer_to_string(), execs.len ? execs.ptr : "");
|
||||
(void) compile_and_invoke(scratch_buffer_to_string(), execs.len ? execs.ptr : "", NULL);
|
||||
}
|
||||
dir_change(old_path);
|
||||
free(old_path);
|
||||
@@ -1247,7 +1247,31 @@ const char *scratch_buffer_interned_as(TokenType* type)
|
||||
fnv1a(scratch_buffer.str, scratch_buffer.len), type);
|
||||
}
|
||||
|
||||
File *compile_and_invoke(const char *file, const char *args)
|
||||
void scratch_buffer_append_native_safe_path(const char *data, int len)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
scratch_buffer_append("\"");
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
char c = data[i];
|
||||
switch (c)
|
||||
{
|
||||
case '/':
|
||||
case '\\':
|
||||
scratch_buffer_append("\\");
|
||||
break;
|
||||
default:
|
||||
scratch_buffer_append_char(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
scratch_buffer_append("\"");
|
||||
#else
|
||||
scratch_buffer_append_len(data, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
File *compile_and_invoke(const char *file, const char *args, const char *stdin_data)
|
||||
{
|
||||
char *name;
|
||||
if (!file_namesplit(compiler_exe_name, &name, NULL))
|
||||
@@ -1255,25 +1279,23 @@ File *compile_and_invoke(const char *file, const char *args)
|
||||
error_exit("Failed to extract file name from '%s'", compiler_exe_name);
|
||||
}
|
||||
const char *compiler_path = file_append_path(find_executable_path(), name);
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(compiler_path);
|
||||
#if (_MSC_VER)
|
||||
const char *output = "__c3exec__.exe";
|
||||
#else
|
||||
|
||||
if (PLATFORM_WINDOWS) scratch_buffer_append_char('"');
|
||||
scratch_buffer_append_native_safe_path(compiler_path, strlen(compiler_path));
|
||||
const char *output = "__c3exec__";
|
||||
#endif
|
||||
scratch_buffer_append(" compile -g0 --single-module=yes");
|
||||
StringSlice slice = slice_from_string(file);
|
||||
while (slice.len > 0)
|
||||
{
|
||||
StringSlice file_name = slice_next_token(&slice, ';');
|
||||
if (!file_name.len) continue;
|
||||
scratch_buffer_append_char(' ');
|
||||
scratch_buffer_append_len(file_name.ptr, file_name.len);
|
||||
scratch_buffer_append(" ");
|
||||
scratch_buffer_append_native_safe_path(file_name.ptr, file_name.len);
|
||||
}
|
||||
scratch_buffer_printf(" -o %s", output);
|
||||
const char *out;
|
||||
if (!execute_cmd_failable(scratch_buffer_to_string(), &out))
|
||||
if (PLATFORM_WINDOWS) scratch_buffer_append_char('"');
|
||||
if (!execute_cmd_failable(scratch_buffer_to_string(), &out, NULL))
|
||||
{
|
||||
error_exit("Failed to compile script '%s'.", file);
|
||||
}
|
||||
@@ -1285,7 +1307,7 @@ File *compile_and_invoke(const char *file, const char *args)
|
||||
scratch_buffer_append(output);
|
||||
scratch_buffer_append(" ");
|
||||
scratch_buffer_append(args);
|
||||
if (!execute_cmd_failable(scratch_buffer_to_string(), &out))
|
||||
if (!execute_cmd_failable(scratch_buffer_to_string(), &out, stdin_data))
|
||||
{
|
||||
error_exit("Error invoking script '%s' with arguments %s.", file, args);
|
||||
}
|
||||
|
||||
@@ -348,6 +348,7 @@ typedef struct
|
||||
{
|
||||
Expr *filename;
|
||||
Expr **args;
|
||||
Expr *stdin_string;
|
||||
} ExecDecl;
|
||||
|
||||
typedef struct
|
||||
@@ -2251,7 +2252,7 @@ File *source_file_load(const char *filename, bool *already_loaded, const char **
|
||||
File *source_file_generate(const char *filename);
|
||||
File *source_file_text_load(const char *filename, const char *content);
|
||||
|
||||
File *compile_and_invoke(const char *file, const char *args);
|
||||
File *compile_and_invoke(const char *file, const char *args, const char *stdin_data);
|
||||
void compiler_parse(void);
|
||||
void emit_json(void);
|
||||
|
||||
|
||||
@@ -2649,12 +2649,22 @@ static Decl *parse_exec(ParseContext *c)
|
||||
advance_and_verify(c, TOKEN_CT_EXEC);
|
||||
CONSUME_OR_RET(TOKEN_LPAREN, poisoned_decl);
|
||||
ASSIGN_EXPR_OR_RET(decl->exec_decl.filename, parse_constant_expr(c), poisoned_decl);
|
||||
// We might just have `$exec("foo")`
|
||||
if (try_consume(c, TOKEN_RPAREN)) goto END;
|
||||
// Get the `,`
|
||||
CONSUME_OR_RET(TOKEN_COMMA, poisoned_decl);
|
||||
CONSUME_OR_RET(TOKEN_LBRACE, poisoned_decl);
|
||||
while (try_consume(c, TOKEN_COMMA))
|
||||
{
|
||||
ASSIGN_EXPR_OR_RET(Expr *expr, parse_constant_expr(c), poisoned_decl);
|
||||
vec_add(decl->exec_decl.args, expr);
|
||||
}
|
||||
CONSUME_OR_RET(TOKEN_RBRACE, poisoned_decl);
|
||||
if (try_consume(c, TOKEN_RPAREN)) goto END;
|
||||
CONSUME_OR_RET(TOKEN_COMMA, poisoned_decl);
|
||||
ASSIGN_EXPR_OR_RET(decl->exec_decl.stdin_string, parse_constant_expr(c), poisoned_decl);
|
||||
CONSUME_OR_RET(TOKEN_RPAREN, poisoned_decl);
|
||||
END:
|
||||
if (!parse_attributes_for_global(c, decl)) return poisoned_decl;
|
||||
CONSUME_EOS_OR_RET(poisoned_decl);
|
||||
return decl;
|
||||
|
||||
@@ -184,6 +184,46 @@ static Decl **sema_load_include(CompilationUnit *unit, Decl *decl)
|
||||
return parse_include_file(file, unit);
|
||||
}
|
||||
|
||||
static bool exec_arg_append_to_scratch(Expr *arg)
|
||||
{
|
||||
assert(expr_is_const(arg));
|
||||
switch (arg->const_expr.const_kind)
|
||||
{
|
||||
case CONST_FLOAT:
|
||||
scratch_buffer_append_double(arg->const_expr.fxx.f);
|
||||
return true;
|
||||
case CONST_INTEGER:
|
||||
scratch_buffer_append(int_to_str(arg->const_expr.ixx, 10, false));
|
||||
return true;
|
||||
case CONST_BOOL:
|
||||
scratch_buffer_append(arg->const_expr.b ? "true" : "false");
|
||||
return true;
|
||||
case CONST_ENUM:
|
||||
case CONST_ERR:
|
||||
scratch_buffer_append(arg->const_expr.enum_err_val->name);
|
||||
return true;
|
||||
case CONST_TYPEID:
|
||||
if (!arg->const_expr.typeid->name)
|
||||
{
|
||||
RETURN_PRINT_ERROR_AT(false, arg, "The type '%s' has no trivial name.",
|
||||
type_quoted_error_string(arg->const_expr.typeid));
|
||||
}
|
||||
scratch_buffer_append(arg->const_expr.typeid->name);
|
||||
return true;
|
||||
case CONST_STRING:
|
||||
scratch_buffer_append(arg->const_expr.bytes.ptr);
|
||||
return true;
|
||||
case CONST_POINTER:
|
||||
scratch_buffer_append_unsigned_int(arg->const_expr.ptr);
|
||||
return true;
|
||||
case CONST_BYTES:
|
||||
case CONST_INITIALIZER:
|
||||
case CONST_UNTYPED_LIST:
|
||||
case CONST_MEMBER:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static Decl **sema_run_exec(CompilationUnit *unit, Decl *decl)
|
||||
{
|
||||
@@ -203,12 +243,20 @@ static Decl **sema_run_exec(CompilationUnit *unit, Decl *decl)
|
||||
Expr *filename = decl->exec_decl.filename;
|
||||
bool success = sema_analyse_ct_expr(&context, filename);
|
||||
FOREACH(Expr *, arg, decl->exec_decl.args) success &= sema_analyse_ct_expr(&context, arg);
|
||||
Expr *stdin_expr = decl->exec_decl.stdin_string;
|
||||
if (stdin_expr) success &= sema_analyse_ct_expr(&context, stdin_expr);
|
||||
sema_context_destroy(&context);
|
||||
if (!success) return NULL;
|
||||
if (!expr_is_const_string(filename))
|
||||
{
|
||||
RETURN_PRINT_ERROR_AT(NULL, filename, "A filename was expected as the first argument to '$exec'.");
|
||||
}
|
||||
const char *stdin_string = NULL;
|
||||
if (stdin_expr)
|
||||
{
|
||||
if (!expr_is_const_string(stdin_expr)) RETURN_PRINT_ERROR_AT(NULL, stdin_expr, "Expected the stdin parameter to be a compile time string.");
|
||||
stdin_string = stdin_expr->const_expr.bytes.ptr;
|
||||
}
|
||||
scratch_buffer_clear();
|
||||
const char *file_str = filename->const_expr.bytes.ptr;
|
||||
bool c3_script = str_has_suffix(file_str, ".c3");
|
||||
@@ -221,43 +269,10 @@ static Decl **sema_run_exec(CompilationUnit *unit, Decl *decl)
|
||||
{
|
||||
if (i) scratch_buffer_append(" ");
|
||||
assert(expr_is_const(arg));
|
||||
switch (arg->const_expr.const_kind)
|
||||
if (!exec_arg_append_to_scratch(arg))
|
||||
{
|
||||
case CONST_FLOAT:
|
||||
scratch_buffer_append_double(arg->const_expr.fxx.f);
|
||||
continue;
|
||||
case CONST_INTEGER:
|
||||
scratch_buffer_append(int_to_str(arg->const_expr.ixx, 10, false));
|
||||
continue;
|
||||
case CONST_BOOL:
|
||||
scratch_buffer_append(arg->const_expr.b ? "true" : "false");
|
||||
continue;
|
||||
case CONST_ENUM:
|
||||
case CONST_ERR:
|
||||
scratch_buffer_append(arg->const_expr.enum_err_val->name);
|
||||
continue;
|
||||
case CONST_TYPEID:
|
||||
if (!arg->const_expr.typeid->name)
|
||||
{
|
||||
RETURN_PRINT_ERROR_AT(NULL, arg, "The type '%s' has no trivial name.",
|
||||
type_quoted_error_string(arg->const_expr.typeid));
|
||||
RETURN_PRINT_ERROR_AT(NULL, arg, "Bytes, initializers and member references may not be used as arguments.");
|
||||
}
|
||||
scratch_buffer_append(arg->const_expr.typeid->name);
|
||||
continue;
|
||||
case CONST_STRING:
|
||||
scratch_buffer_append(arg->const_expr.bytes.ptr);
|
||||
continue;
|
||||
case CONST_POINTER:
|
||||
scratch_buffer_append_unsigned_int(arg->const_expr.ptr);
|
||||
continue;
|
||||
case CONST_BYTES:
|
||||
case CONST_INITIALIZER:
|
||||
case CONST_UNTYPED_LIST:
|
||||
case CONST_MEMBER:
|
||||
RETURN_PRINT_ERROR_AT(NULL, arg,
|
||||
"Bytes, initializers and member references may not be used as arguments.");
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
File *file;
|
||||
// TODO fix Win32
|
||||
@@ -273,11 +288,11 @@ static Decl **sema_run_exec(CompilationUnit *unit, Decl *decl)
|
||||
}
|
||||
if (c3_script)
|
||||
{
|
||||
file = compile_and_invoke(file_str, scratch_buffer_copy());
|
||||
file = compile_and_invoke(file_str, scratch_buffer_copy(), stdin_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *output = execute_cmd(scratch_buffer_to_string(), false);
|
||||
const char *output = execute_cmd(scratch_buffer_to_string(), false, stdin_string);
|
||||
file = source_file_text_load(scratch_buffer_to_string(), output);
|
||||
}
|
||||
if (old_path)
|
||||
|
||||
@@ -505,7 +505,7 @@ void file_delete_all_files_in_dir_with_suffix(const char *path, const char *suff
|
||||
#else
|
||||
const char *cmd = "rm -f %s/*%s";
|
||||
#endif
|
||||
execute_cmd(str_printf(cmd, path, suffix), true);
|
||||
execute_cmd(str_printf(cmd, path, suffix), true, NULL);
|
||||
}
|
||||
|
||||
#if (_MSC_VER)
|
||||
@@ -591,64 +591,41 @@ void file_add_wildcard_files(const char ***files, const char *path, bool recursi
|
||||
#endif
|
||||
|
||||
#define BUFSIZE 1024
|
||||
const char *execute_cmd(const char *cmd, bool ignore_failure)
|
||||
const char *execute_cmd(const char *cmd, bool ignore_failure, const char *stdin_string)
|
||||
{
|
||||
char buffer[BUFSIZE];
|
||||
char *output = "";
|
||||
FILE *process = NULL;
|
||||
#if (_MSC_VER)
|
||||
if (!(process = _wpopen(win_utf8to16(cmd), L"r")))
|
||||
{
|
||||
if (ignore_failure) return "";
|
||||
error_exit("Failed to open a pipe for command '%s'.", cmd);
|
||||
}
|
||||
#else
|
||||
if (!(process = popen(cmd, "r")))
|
||||
{
|
||||
if (ignore_failure) return "";
|
||||
error_exit("Failed to open a pipe for command '%s'.", cmd);
|
||||
}
|
||||
#endif
|
||||
while (fgets(buffer, BUFSIZE - 1, process))
|
||||
{
|
||||
output = str_cat(output, buffer);
|
||||
}
|
||||
#if PLATFORM_WINDOWS
|
||||
int err = _pclose(process);
|
||||
#else
|
||||
int err = pclose(process);
|
||||
#endif
|
||||
if (err)
|
||||
const char *result = NULL;
|
||||
bool success = execute_cmd_failable(cmd, &result, stdin_string);
|
||||
if (!success)
|
||||
{
|
||||
if (ignore_failure) return "";
|
||||
error_exit("Failed to execute '%s'.", cmd);
|
||||
}
|
||||
|
||||
while (output[0] != 0)
|
||||
{
|
||||
switch (output[0])
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
output++;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return str_trim(output);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool execute_cmd_failable(const char *cmd, const char **result)
|
||||
bool execute_cmd_failable(const char *cmd, const char **result, const char *stdin_string)
|
||||
{
|
||||
char buffer[BUFSIZE];
|
||||
char *output = "";
|
||||
FILE *process = NULL;
|
||||
FILE *stdin_file = NULL;
|
||||
if (stdin_string)
|
||||
{
|
||||
cmd = strdup(cmd);
|
||||
scratch_buffer_clear();
|
||||
#if (_MSC_VER)
|
||||
if (!(process = _wpopen(win_utf8to16(cmd), L"r"))) return false;
|
||||
scratch_buffer_printf("%s < __c3temp.bin", cmd);
|
||||
#else
|
||||
scratch_buffer_printf("cat __c3temp.bin | %s", cmd);
|
||||
#endif
|
||||
free((char*)cmd);
|
||||
cmd = scratch_buffer_to_string();
|
||||
FILE *f = fopen("__c3temp.bin", "w");
|
||||
fputs(stdin_string, f);
|
||||
fclose(f);
|
||||
}
|
||||
#if (_MSC_VER)
|
||||
if (!(process = _wpopen(win_utf8to16(cmd), L"rb"))) return false;
|
||||
#else
|
||||
if (!(process = popen(cmd, "r"))) return false;
|
||||
#endif
|
||||
@@ -661,6 +638,10 @@ bool execute_cmd_failable(const char *cmd, const char **result)
|
||||
#else
|
||||
int err = pclose(process);
|
||||
#endif
|
||||
if (stdin_string)
|
||||
{
|
||||
file_delete_file("__c3temp.bin");
|
||||
}
|
||||
if (err) return false;
|
||||
|
||||
while (output[0] != 0)
|
||||
|
||||
@@ -39,7 +39,7 @@ static char *find_visual_studio(void)
|
||||
const char *install_path = NULL;
|
||||
|
||||
// Call vswhere.exe
|
||||
if (!execute_cmd_failable(scratch_buffer_to_string(), &install_path))
|
||||
if (!execute_cmd_failable(scratch_buffer_to_string(), &install_path, NULL))
|
||||
{
|
||||
error_exit("Failed to find vswhere.exe to detect MSVC.");
|
||||
}
|
||||
|
||||
@@ -87,8 +87,9 @@ bool file_has_suffix_in_list(const char *file_name, int name_len, const char **s
|
||||
void file_add_wildcard_files(const char ***files, const char *path, bool recursive, const char **suffix_list, int suffix_count);
|
||||
const char *file_append_path(const char *path, const char *name);
|
||||
|
||||
const char *execute_cmd(const char *cmd, bool ignore_failure);
|
||||
bool execute_cmd_failable(const char *cmd, const char **result);
|
||||
const char *execute_cmd(const char *cmd, bool ignore_failure, const char *stdin_string);
|
||||
|
||||
bool execute_cmd_failable(const char *cmd, const char **result, const char *stdin_string);
|
||||
void *cmalloc(size_t size);
|
||||
void *ccalloc(size_t size, size_t elements);
|
||||
void memory_init(size_t max_mem);
|
||||
|
||||
@@ -2,6 +2,14 @@ module std::core::dstring2 @test;
|
||||
|
||||
const TEST_STRING = "hello world";
|
||||
|
||||
fn void test_replace()
|
||||
{
|
||||
DString hello = dstring::new("Hello world where are you? Are you here too?");
|
||||
defer hello.free();
|
||||
hello.replace("u", "ooo");
|
||||
assert(hello.str_view() == "Hello world where are yoooo? Are yoooo here too?");
|
||||
}
|
||||
|
||||
fn void test_delete()
|
||||
{
|
||||
{
|
||||
|
||||
@@ -5,8 +5,8 @@ fn void! test_dot()
|
||||
Path p = path::new(".")!;
|
||||
assert(@catch(p.parent()));
|
||||
// It must be possible to form the absolute version.
|
||||
Path p2 = p.absolute()!;
|
||||
p2 = p.append("/hello/world")!;
|
||||
Path p2 = p.new_absolute()!;
|
||||
p2 = p.new_append("/hello/world")!;
|
||||
if (p2.env == POSIX)
|
||||
{
|
||||
assert(p2.str_view() == "hello/world");
|
||||
@@ -239,6 +239,36 @@ fn void! test_extension()
|
||||
|
||||
}
|
||||
|
||||
fn void! test_has_extension()
|
||||
{
|
||||
assert(!path::new(`C:\temp\foo.bar\README`, .path_env = PathEnv.WIN32)!.has_extension(`bar\README`));
|
||||
|
||||
assert(path::new_windows("file.txt")!.has_extension("txt"));
|
||||
assert(path::new_posix("file.txt")!.has_extension("txt"));
|
||||
|
||||
assert(path::new_windows("a/b/file.txt")!.has_extension("txt"));
|
||||
assert(path::new_posix("a/b/file.txt")!.has_extension("txt"));
|
||||
|
||||
assert(path::new_windows("a\\b\\file.txt")!.has_extension("txt"));
|
||||
|
||||
assert(path::new_windows("a.b/file.txt")!.has_extension("txt"));
|
||||
assert(path::new_posix("a.b/file.txt")!.has_extension("txt"));
|
||||
assert(path::new_windows("a.b/file.txt")!.has_extension("txt"));
|
||||
assert(path::new_posix("a.b/file.txt")!.has_extension("txt"));
|
||||
|
||||
assert(path::new_windows("a.b\\file.txt")!.has_extension("txt"));
|
||||
|
||||
assert(path::new_windows("domain.dot.com")!.has_extension("com"));
|
||||
assert(path::new_posix("domain.dot.com")!.has_extension("com"));
|
||||
|
||||
assert(path::new_windows("image.jpeg")!.has_extension("jpeg"));
|
||||
assert(path::new_posix("image.jpeg")!.has_extension("jpeg"));
|
||||
|
||||
assert(path::new_windows("../filename.ext")!.has_extension("ext"));
|
||||
assert(path::new_posix("../filename.ext")!.has_extension("ext"));
|
||||
|
||||
}
|
||||
|
||||
fn void! test_basename()
|
||||
{
|
||||
assert(path::new_windows("file.txt").basename()! == "file.txt");
|
||||
@@ -335,7 +365,7 @@ fn void! test_path_absolute()
|
||||
$if env::WIN32:
|
||||
assert(path::new_windows(`C:\abs`).absolute()!.str_view() == `C:\abs`);
|
||||
$else
|
||||
assert(path::new_posix("/").absolute()!.str_view() == "/");
|
||||
assert(path::new_posix(".").absolute()!.str_view() == path::getcwd(allocator::temp())!!.str_view());
|
||||
assert(path::new_posix("/").new_absolute()!.str_view() == "/");
|
||||
assert(path::new_posix(".").new_absolute()!.str_view() == path::temp_cwd()!!.str_view());
|
||||
$endif
|
||||
}
|
||||
Reference in New Issue
Block a user