diff --git a/src/build/build_options.c b/src/build/build_options.c index 0c2117b9e..8486f4a71 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -90,6 +90,7 @@ static void usage(void) OUTPUT(" -E - Lex only."); OUTPUT(" -P - Only parse and output the AST as S-expressions."); OUTPUT(" -C - Only lex, parse and check."); + OUTPUT(" - - Read code from standard in."); OUTPUT(" -o - Write output to ."); OUTPUT(" -O0 - Optimizations off."); OUTPUT(" -O1 - Simple optimizations only."); @@ -360,6 +361,9 @@ static void parse_option(BuildOptions *options) const char *argopt; switch (current_arg[1]) { + case '\0': + options->read_stdin = true; + return; case '?': if (match_shortopt("?")) { diff --git a/src/build/build_options.h b/src/build/build_options.h index 234c03f65..45a5d2f15 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -265,6 +265,7 @@ typedef struct BuildOptions_ bool no_stdlib; bool no_libc; bool force_linker; + bool read_stdin; const char *panicfn; const char *cc; const char *build_dir; @@ -347,6 +348,7 @@ typedef struct bool force_linker; bool benchmarking; bool testing; + bool read_stdin; OptimizationLevel optimization_level; SizeOptimizationLevel size_optimization_level; bool single_module; diff --git a/src/build/builder.c b/src/build/builder.c index 98cc6e126..d215657e4 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -283,6 +283,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * void init_default_build_target(BuildTarget *target, BuildOptions *options) { *target = (BuildTarget) { + .read_stdin = options->read_stdin, .type = TARGET_TYPE_EXECUTABLE, .source_dirs = options->files, .name = options->output_name, diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index a030821f7..8928bcf72 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -102,6 +102,11 @@ void compiler_parse(void) global_context_clear_errors(); parse_file(file); } + if (active_target.read_stdin) + { + global_context_clear_errors(); + parse_stdin(); + } exit_compiler(COMPILER_SUCCESS_EXIT); } @@ -663,7 +668,7 @@ void compile() compiler_init_time = bench_mark(); - if (!vec_size(active_target.sources)) error_exit("No files to compile."); + if (!vec_size(active_target.sources) && !active_target.read_stdin) error_exit("No files to compile."); if (active_target.lex_only) { compiler_lex(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index a62fcd210..40fe4c0d7 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -236,6 +236,9 @@ typedef union uint64_t a; } SourceSpan; +extern File stdin_file; +#define stdin_file_id 0xFFFF + static_assert(sizeof(SourceSpan) == 8, "Expected 8 bytes"); typedef struct @@ -2145,6 +2148,7 @@ Decl *module_find_symbol(Module *module, const char *symbol); const char *module_create_object_file_name(Module *module); bool parse_file(File *file); +bool parse_stdin(void); Path *path_create_from_string(const char *string, uint32_t len, SourceSpan span); #define SEMA_ERROR_HERE(...) sema_error_at(c->span, __VA_ARGS__) diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 08436e344..053a6c7ac 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -108,6 +108,57 @@ bool parse_file(File *file) return !global_context.errors_found; } +File stdin_file; + +/** + * Parse stdin + * + * @return true if parsing succeeds. + */ +bool parse_stdin(void) +{ + stdin_file = (File){ + .name = "stdin", + .file_id = stdin_file_id, + .full_path = "", + }; +#define BUF_SIZE 65536 + char buffer[BUF_SIZE]; + size_t capacity = BUF_SIZE; + size_t len = 0; + char *data = buffer; + while (true) + { + int c = getchar(); + if (c == -1) break; + if (len >= capacity - 1) + { + capacity *= 2; + if (buffer == data) + { + data = malloc(capacity); + memcpy(data, buffer, len); + } + else + { + data = realloc(data, capacity); + } + } + data[len++] = c; + } + buffer[len] = 0; + char *stdin_data = MALLOC(len + 1); + memcpy(stdin_data, data, len + 1); + if (data != buffer) free(data); + stdin_file.contents = stdin_data; + CompilationUnit *unit = unit_create(&stdin_file); + ParseContext parse_context = { .unit = unit }; + parse_context.lexer = (Lexer) { .file = &stdin_file, .context = &parse_context }; + lexer_init(&parse_context.lexer); + if (global_context.errors_found) return false; + parse_translation_unit(&parse_context); + return !global_context.errors_found; +} diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 7ffb72b3d..277dfb914 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -242,6 +242,11 @@ void sema_analysis_run(void) if (loaded) continue; if (!parse_file(file)) has_error = true; } + if (active_target.read_stdin) + { + if (!parse_stdin()) has_error = true; + } + if (has_error) exit_compiler(EXIT_FAILURE); compiler_parsing_time = bench_mark(); diff --git a/src/compiler/source_file.c b/src/compiler/source_file.c index 7a40acd8f..0858c4e0f 100644 --- a/src/compiler/source_file.c +++ b/src/compiler/source_file.c @@ -17,6 +17,7 @@ static const size_t LEXER_FILES_START_CAPACITY = 128; File *source_file_by_id(FileId file) { + if (file == stdin_file_id) return &stdin_file; assert(file < vec_size(global_context.loaded_sources)); return global_context.loaded_sources[file]; } diff --git a/src/version.h b/src/version.h index bd3bcb1e8..3afdc1add 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.87" \ No newline at end of file +#define COMPILER_VERSION "0.3.88" \ No newline at end of file