diff --git a/common/file/copy.c2 b/common/file/copy.c2 index 692c50e48..31fc1b226 100644 --- a/common/file/copy.c2 +++ b/common/file/copy.c2 @@ -15,6 +15,8 @@ module file_utils; +import string local; + // NOTE: depends on reader + writer public fn i32 copy_file(const char* source, const char* dest) { // Note: not most optimal yet diff --git a/common/file/file_utils.c2 b/common/file/file_utils.c2 index f37c5ab51..b74add485 100644 --- a/common/file/file_utils.c2 +++ b/common/file/file_utils.c2 @@ -39,19 +39,6 @@ public fn const char* get_extension(const char* s) { return ext ? ext : s; } -// Copy a string with truncation -// return the length of the string if shorter than size -// otherwise truncate the string and return size -// compare the return value with size to detect truncation -fn usize pstrcpy(char* dest, usize size, const char* src) { - usize i; - for (i = 0; i < size; i++) { - if ((dest[i] = src[i]) == '\0') return i; - } - if (size) dest[size - 1] = '\0'; - return size; -} - // Copy a directory name with truncation and append a / if not empty fn usize copy_dirname(char* buf, usize size, const char* dir) { usize pos = pstrcpy(buf, size, dir); diff --git a/examples/Makefile b/examples/Makefile index 58d45d1f5..19ea0251a 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -10,3 +10,7 @@ warnings: clean: rm -rf output + +test: + @echo "---- running tests ----" + @./test.sh diff --git a/examples/base64/base64.c2 b/examples/base64/base64.c2 index d3ab15fee..c5f60dc48 100644 --- a/examples/base64/base64.c2 +++ b/examples/base64/base64.c2 @@ -42,10 +42,10 @@ const u8 FIRST = '+'; const u8 LAST = 'z'; public fn void encode(const u8* in, u32 len, i8* out) { - u32 i = 0; - u32 j = 0; + u32 i = 0; + u32 j = 0; - for (; i< len; ++i) { + for (; i< len; ++i) { switch (i % 3) { case 0: out[j++] = Lut_enc[(in[i] >> 2) & 0x3F]; @@ -58,7 +58,7 @@ public fn void encode(const u8* in, u32 len, i8* out) { out[j++] = Lut_enc[in[i] & 0x3F]; break; } - } + } i -= 1; // move back // check the last and add padding switch (i % 3) { diff --git a/examples/comment_strip/main.c2 b/examples/comment_strip/main.c2 deleted file mode 100644 index e8c2e5231..000000000 --- a/examples/comment_strip/main.c2 +++ /dev/null @@ -1,112 +0,0 @@ -/* Copyright 2022-2026 Bas van den Berg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module test; - -import unistd local; -import stdio local; -import string local; -import libc_fcntl local; -import stdlib local; - -import file_utils; -import utils; - -const char NEWLINE = 0x0A; - -/* - strip all lines startin with '//' - TODO also handle multi-line comments -*/ -fn void strip_comments(const char* input, utils.StringBuffer* out) -{ - const char* cp = input; - const char* linestart = cp; - u32 lines = 0; - u32 outlines = 0; - bool white = true; - bool skip = false; - while (*cp != 0) { - if (*cp == NEWLINE) { - cp++; - if (!skip) { - out.add2(linestart, (u32)(cp - linestart)); - outlines++; - } - linestart = cp; - white = true; - skip = false; - lines++; - } else { - switch (*cp) { - case ' ': - case '\t': - break; - case '/': - if (cp[1] == '/') { - // read until newline - while (*cp != NEWLINE && *cp != 0) cp++; -#if 0 - char[256] line; - u32 len = (u32)(cp - linestart); - memcpy(line, linestart, len); - line[len] = 0; - printf("%d %s", white, line); -#endif - if (white) skip = true; - continue; - } - fallthrough; - default: - white = false; - break; - } - cp++; - } - } - printf("lines %d -> %d (stripped %d lines)\n", lines, outlines, lines - outlines); -} - -public fn i32 main(i32 argc, char** argv) { - if (argc != 2) { - printf("Usage: parser [c-file]\n"); - return 0; - } - file_utils.Reader infile; - infile.open(argv[1]); - - //printf("%s", file.data()); - if (infile.isEmpty()) { - printf("file %s is empty\n", argv[1]); - return 0; - } - - utils.StringBuffer* buffer = utils.StringBuffer.create(infile.size); - - strip_comments(infile.data(), buffer); - infile.close(); - - file_utils.Writer outfile; - const char* outname = "out.c"; - printf("written %s\n", outname); - if (!outfile.write(outname, buffer.data(), buffer.size())) { - printf("error writing file: %s\n", outfile.getError()); - } - - buffer.destroy(); - - return 0; -} - diff --git a/examples/common/color.c2 b/examples/common/color.c2 index 03639678a..90150284b 100644 --- a/examples/common/color.c2 +++ b/examples/common/color.c2 @@ -15,21 +15,21 @@ module color; -public const char[] Black = "\033[0;30m"; -public const char[] Red = "\033[0;31m"; -public const char[] Green = "\033[0;32m"; -public const char[] Yellow = "\033[0;33m"; -public const char[] Blue = "\033[0;34m"; -public const char[] Magenta = "\033[0;35m"; -public const char[] Cyan = "\033[0;36m"; -public const char[] Grey = "\033[0;37m"; -public const char[] Darkgrey = "\033[01;30m"; -public const char[] Bred = "\033[01;31m"; -public const char[] Bgreen = "\033[01;32m"; -public const char[] Byellow = "\033[01;33m"; -public const char[] Bblue = "\033[01;34m"; -public const char[] Bmagenta = "\033[01;35m"; -public const char[] Bcyan = "\033[01;36m"; -public const char[] White = "\033[01;37m"; -public const char[] Normal = "\033[0m"; +public const char[] Black @(unused) = "\033[0;30m"; +public const char[] Red @(unused) = "\033[0;31m"; +public const char[] Green @(unused) = "\033[0;32m"; +public const char[] Yellow @(unused) = "\033[0;33m"; +public const char[] Blue @(unused) = "\033[0;34m"; +public const char[] Magenta @(unused) = "\033[0;35m"; +public const char[] Cyan @(unused) = "\033[0;36m"; +public const char[] Grey @(unused) = "\033[0;37m"; +public const char[] Darkgrey @(unused) = "\033[01;30m"; +public const char[] Bred @(unused) = "\033[01;31m"; +public const char[] Bgreen @(unused) = "\033[01;32m"; +public const char[] Byellow @(unused) = "\033[01;33m"; +public const char[] Bblue @(unused) = "\033[01;34m"; +public const char[] Bmagenta @(unused) = "\033[01;35m"; +public const char[] Bcyan @(unused) = "\033[01;36m"; +public const char[] White @(unused) = "\033[01;37m"; +public const char[] Normal @(unused) = "\033[0m"; diff --git a/examples/common/file/reader.c2 b/examples/common/file/reader.c2 index ea6a63205..7e4fecd32 100644 --- a/examples/common/file/reader.c2 +++ b/examples/common/file/reader.c2 @@ -17,13 +17,12 @@ module file_utils; import libc_fcntl local; import c_errno local; -import sys_stat local; import unistd local; -import stdlib; +import stdio local; +import stdlib local; +import string local; -u8 empty; - -public const i32 Err_not_a_file = 2001; +u8[1] empty; public type Reader struct { void* region; @@ -31,62 +30,114 @@ public type Reader struct { i32 errno; } -public fn bool Reader.open(Reader* file, const char* filename) { +public fn bool Reader.open(Reader* file, const char* filename) @(unused) { file.region = nil; file.size = 0; + file.errno = 0; i32 fd = open(filename, O_RDONLY); if (fd == -1) { - file.errno = *errno2(); + file.errno = errno; return false; } + bool res = file.openHandle(fd); + close(fd); + return res; +} - Stat statbuf; - i32 err = fstat(fd, &statbuf); - if (err) { - file.errno = *errno2(); - return false; - } +public fn bool Reader.openStream(Reader* file, FILE* fp) @(unused) { + char *data = nil; + usize size = 0; - if ((statbuf.st_mode & S_IFMT) != S_IFREG) { - close(fd); - file.errno = Err_not_a_file; - return false; + file.region = empty; + file.size = 0; + file.errno = 0; + + if (fseek(fp, 0, SEEK_END) < 0) { // stream is not seekable + char[4096] buf; + usize nread; + while ((nread = fread(buf, 1, elemsof(buf), fp)) > 0) { + data = realloc(data, size + nread + 1); + memcpy(data + size, buf, nread); + size += nread; + } + } else { + i64 file_length = ftell(fp); + usize length = (usize)file_length; + data = malloc(length + 1); + fseek(fp, 0, SEEK_SET); + size = fread(data, 1, length, fp); + } + if (data) { + data[size] = '\0'; + file.region = data; + file.size = (u32)size; } + return true; +} - file.size = (u32)statbuf.st_size; +public fn bool Reader.openHandle(Reader* file, i32 fd) @(unused) { + char *data = nil; + usize size = 0; - if (file.size == 0) { - file.region = ∅ - } else { - file.region = stdlib.malloc(file.size+1); - isize numread = read(fd, file.region, file.size); - if (numread != file.size) return false; - // 0-terminate - u8* ptr = file.region; - ptr[file.size] = 0; + file.region = empty; + file.size = 0; + file.errno = 0; + + i64 file_length = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + if (file_length < 0) { // file descriptor is not seekable + char[4096] buf; + isize nread; + while ((nread = read(fd, buf, elemsof(buf))) != 0) { + if (nread < 0) { + if (errno == EINTR) + continue; + file.errno = errno; + break; + } + data = realloc(data, size + (usize)nread + 1); + memcpy(data + size, buf, (usize)nread); + size += nread; + } + } else + if (file_length > 0) { + usize length = (usize)file_length; + data = malloc(length + 1); + isize nread; + while (size < length && (nread = read(fd, data + size, length - size)) != 0) { + if (nread < 0) { + if (errno == EINTR) + continue; + file.errno = errno; + break; + } + size += nread; + } + } + if (data) { + data[size] = '\0'; + file.region = data; + file.size = (u32)size; } - close(fd); return true; } -public fn void Reader.close(Reader* file) { - if (file.region) { - stdlib.free(file.region); +public fn void Reader.close(Reader* file) @(unused) { + if (file.region && file.region != empty) { + free(file.region); file.region = nil; } } -public fn bool Reader.isOpen(const Reader* file) { +public fn bool Reader.isOpen(const Reader* file) @(unused) { return file.region != nil; } -public fn const void* Reader.data(Reader* file) { +public fn const void* Reader.data(Reader* file) @(unused) { return file.region; } -public fn bool Reader.isEmpty(const Reader* file) { +public fn bool Reader.isEmpty(const Reader* file) @(unused) { return file.size == 0; } - - diff --git a/examples/common/file/writer.c2 b/examples/common/file/writer.c2 index a41653f76..49a2fdfa7 100644 --- a/examples/common/file/writer.c2 +++ b/examples/common/file/writer.c2 @@ -16,7 +16,6 @@ module file_utils; import stdio local; -import stdlib local; import libc_fcntl local; import string local; import c_errno local; @@ -26,25 +25,25 @@ public type Writer struct { char[256] msg; } -public fn bool Writer.write(Writer* writer, const char* filename, const void* data, u32 len) { +public fn bool Writer.write(Writer* writer, const char* filename, const void* data, u32 len) @(unused) { writer.msg[0] = 0; i32 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660); if (fd == -1) { - sprintf(writer.msg, "open failed: %s", strerror(*errno2())); + snprintf(writer.msg, elemsof(writer.msg), "open failed: %s", strerror(errno)); return false; } i64 written = write(fd, data, len); if (written != len) { - sprintf(writer.msg, "write failed: %s", strerror(*errno2())); + snprintf(writer.msg, elemsof(writer.msg), "write failed: %s", strerror(errno)); return false; } - close(fd); + close(fd); return true; } -public fn const char* Writer.getError(const Writer* writer) { +public fn const char* Writer.getError(const Writer* writer) @(unused) { return writer.msg; } diff --git a/examples/common/logger.c2 b/examples/common/logger.c2 index 771f883b5..2a5d994ae 100644 --- a/examples/common/logger.c2 +++ b/examples/common/logger.c2 @@ -21,9 +21,10 @@ import string local; import stdio local; import stdlib local; import sys_time local; -import unistd; import libc_time; +import utils local; + public type Level enum u8 { Fatal = 0, Error, @@ -32,7 +33,7 @@ public type Level enum u8 { Debug, } -fn const char* level2str(Level lvl) { +fn const char* level2str(Level lvl) @(unused) { switch (lvl) { case Fatal: return "Fatal"; case Error: return "Error"; @@ -43,7 +44,7 @@ fn const char* level2str(Level lvl) { return nil; } -fn u64 current_time() { +fn u64 current_time() @(unused) { Timeval tv; gettimeofday(&tv, nil); u64 tv64 = (u64)tv.tv_sec; @@ -62,11 +63,11 @@ public type Base struct { WriteFunc write; } -fn void log_stdout(const char* data, u32 len) { +fn void log_stdout(const char* data, u32 /*len*/) @(unused) { printf("%s", data); } -public fn void Base.init(Base* base, Level lvl_, bool color_, bool relativeTimes_) +public fn void Base.init(Base* base, Level lvl_, bool color_, bool relativeTimes_) @(unused) { base.defaultLevel = lvl_; base.useColor = color_; @@ -75,7 +76,7 @@ public fn void Base.init(Base* base, Level lvl_, bool color_, bool relativeTimes base.write = log_stdout; } -public fn void Base.destroy(Base* base) +public fn void Base.destroy(Base* /*base*/) @(unused) { } @@ -85,18 +86,18 @@ public type Log struct { char[16] name; } -public fn void Log.init(Log* log, Base* base, const char* name_) { +public fn void Log.init(Log* log, Base* base, const char* name_) @(unused) { log.base = base; log.level = base.defaultLevel; strlcpy(log.name, name_, 16); log.name[15] = 0; } -public fn void Log.setLevel(Log* log, Level lvl) { +public fn void Log.setLevel(Log* log, Level lvl) @(unused) { log.level = lvl; } -public fn void Log.debug(Log* log, const char* format, ...) { +public fn void Log.debug(Log* log, const char* format @(printf_format), ...) @(unused) { if (Level.Debug > log.level) return; va_list args; va_start(args, format); @@ -104,7 +105,7 @@ public fn void Log.debug(Log* log, const char* format, ...) { va_end(args); } -public fn void Log.info(Log* log, const char* format, ...) { +public fn void Log.info(Log* log, const char* format @(printf_format), ...) @(unused) { if (Level.Info > log.level) return; va_list args; va_start(args, format); @@ -112,7 +113,7 @@ public fn void Log.info(Log* log, const char* format, ...) { va_end(args); } -public fn void Log.warn(Log* log, const char* format, ...) { +public fn void Log.warn(Log* log, const char* format @(printf_format), ...) @(unused) { if (Level.Warn > log.level) return; va_list args; va_start(args, format); @@ -120,7 +121,7 @@ public fn void Log.warn(Log* log, const char* format, ...) { va_end(args); } -public fn void Log.error(Log* log, const char* format, ...) { +public fn void Log.error(Log* log, const char* format @(printf_format), ...) @(unused) { if (Level.Error > log.level) return; va_list args; va_start(args, format); @@ -128,7 +129,7 @@ public fn void Log.error(Log* log, const char* format, ...) { va_end(args); } -public fn void Log.fatal(Log* log, const char* format, ...) { +public fn void Log.fatal(Log* log, const char* format @(printf_format), ...) @(unused) { va_list args; va_start(args, format); log.internal(Level.Fatal, color.Red, format, args); @@ -136,32 +137,32 @@ public fn void Log.fatal(Log* log, const char* format, ...) { exit(EXIT_FAILURE); } -fn void Log.internal(Log* log, Level level, const char* color_, const char* format, va_list args) { +fn void Log.internal(Log* log, Level level, const char* color_, const char* format, va_list args) @(unused) { char[256] buffer; - char* cp = buffer; + StringBuffer buf.init(elemsof(buffer), buffer, false); - if (log.base.useColor) cp += sprintf(cp, "%s", color_); - *cp++ = '['; + if (log.base.useColor) buf.add(color_); + buf.putc('['); if (log.base.relativeTimes) { u64 now = current_time(); now -= log.base.startTime; u64 subsec = now % 1000000; - cp += sprintf(cp, "%04d.%06d", now / 1000000, subsec); + buf.printf("%04d.%06d", now / 1000000, subsec); } else { Timeval tv; gettimeofday(&tv, nil); Time now = tv.tv_sec; libc_time.Tm* now2 = libc_time.localtime(&now); - cp += sprintf(cp, "%02d/%02d/%02d %02d:%02d:%02d.%d", - now2.tm_year % 100, now2.tm_mon + 1, now2.tm_mday, - now2.tm_hour, now2.tm_min, now2.tm_sec, tv.tv_usec / 1000); + buf.printf("%02d/%02d/%02d %02d:%02d:%02d.%d", + now2.tm_year % 100, now2.tm_mon + 1, now2.tm_mday, + now2.tm_hour, now2.tm_min, now2.tm_sec, tv.tv_usec / 1000); } - *cp++ = ']'; - cp += sprintf(cp, " %s %s: ", level2str(level), log.name); - cp += vsprintf(cp, format, args); - if (log.base.useColor) cp += sprintf(cp, "%s", color.Normal); - *cp++ = '\n'; - *cp = 0; - log.base.write(buffer, (u32)(cp - buffer)); + buf.putc(']'); + buf.printf(" %s %s: ", level2str(level), log.name); + buf.vprintf(format, args); + if (log.base.useColor) buf.add(color.Normal); + buf.putc('\n'); + log.base.write(buf.data(), buf.size()); + buf.deinit(); } diff --git a/examples/common/string_buffer.c2 b/examples/common/string_buffer.c2 index 1b7e6c45f..3b8de84ab 100644 --- a/examples/common/string_buffer.c2 +++ b/examples/common/string_buffer.c2 @@ -22,78 +22,153 @@ import string local; public type StringBuffer struct { u32 capacity; - u32 size_; - char* data_; + u32 len; + char* data_ptr; + bool own; // data_ptr should be freed + bool grow; // data_ptr should be reallocated + bool truncated @(unused); + bool error @(unused); } -static_assert(16, sizeof(StringBuffer)); +static_assert(24, sizeof(StringBuffer)); -public fn StringBuffer* StringBuffer.create(u32 capacity) +public fn StringBuffer* StringBuffer.init(StringBuffer* buf, + u32 capacity, + char *data = nil, + bool grow = true) @(unused) { - StringBuffer* buf = malloc(sizeof(StringBuffer)); buf.capacity = capacity; - buf.size_ = 0; - buf.data_ = malloc(capacity); + buf.len = 0; + buf.data_ptr = data; + buf.own = false; + buf.grow = grow; + buf.truncated = false; + buf.error = false; + if (!data && capacity) { + buf.data_ptr = malloc(capacity); + buf.own = true; + } return buf; } -public fn void StringBuffer.destroy(StringBuffer* buf) -{ - free(buf.data_); - free(buf); +public fn void StringBuffer.deinit(StringBuffer* buf) @(unused) { + if (buf.own) free(buf.data_ptr); + buf.data_ptr = nil; } -public fn u32 StringBuffer.size(const StringBuffer* buf) -{ - return buf.size_; +public fn StringBuffer* StringBuffer.create(u32 capacity, char *data = nil, bool grow = true) @(unused) { + StringBuffer* buf = malloc(sizeof(StringBuffer)); + return buf.init(capacity, data, grow); } -public fn const char* StringBuffer.data(const StringBuffer* buf) -{ - return buf.data_; +public fn void StringBuffer.destroy(StringBuffer* buf) @(unused) { + if (buf) { + buf.deinit(); + free(buf); + } } -public fn void StringBuffer.clear(StringBuffer* buf) -{ - buf.size_ = 0; +public fn u32 StringBuffer.size(const StringBuffer* buf) @(unused) { + return buf.len; } -public fn void StringBuffer.add(StringBuffer* buf, const char* text) -{ +public fn const char* StringBuffer.data(const StringBuffer* buf) { + return buf.data_ptr; +} + +public fn void StringBuffer.clear(StringBuffer* buf) @(unused) { + buf.len = 0; + buf.truncated = false; +} + +public fn void StringBuffer.add(StringBuffer* buf, const char* text) @(unused) { u32 len = (u32)strlen(text); buf.add2(text, len); } -public fn void StringBuffer.add2(StringBuffer* buf, const char* text, u32 len) -{ - if ((len + 1) >= (buf.capacity - buf.size_)) { - u32 newcap = buf.capacity * 2; - if ((len + 1) >= (newcap - buf.size_)) newcap += buf.size_ + len + 1; - buf.capacity = newcap; - char* data2 = malloc(newcap); - memcpy(data2, buf.data_, buf.size_ + 1); - free(buf.data_); - buf.data_ = data2; +public fn void StringBuffer.putc(StringBuffer* buf, char c) @(unused) { + if (buf.len + 2 > buf.capacity) { + if (!buf.extend(1)) return; + } + buf.data_ptr[buf.len++] = c; + buf.data_ptr[buf.len] = 0; +} + +public fn void StringBuffer.add2(StringBuffer* buf, const char* text, u32 len) @(unused) { + if (buf.len + len + 1 > buf.capacity) { + // truncate if resize fails. + if (!buf.extend(len)) { + u32 size = buf.capacity - buf.len; + if (!size) return; + len = size - 1; + } } - strcpy(&buf.data_[buf.size_], text); - buf.size_ += len; - buf.data_[buf.size_] = 0; + memcpy(&buf.data_ptr[buf.len], text, len); + buf.len += len; + buf.data_ptr[buf.len] = 0; } -public fn void StringBuffer.print(StringBuffer* buf, const char* format, ...) { - // NOTE: no growing +public fn void StringBuffer.printf(StringBuffer* buf, const char* format @(printf_format), ...) @(unused) { va_list args; va_start(args, format); - i32 len = vsprintf(&buf.data_[buf.size_], format, args); - // TODO check size - buf.size_ += len; + buf.vprintf(format, args); va_end(args); } -public fn void StringBuffer.indent(StringBuffer* buf, u32 indent) { - // NOTE: no growing/checking - for (u32 i=0; i= 4096) { + if (!avail) { + buf.truncated = true; + return; + } + dest = &buf.data_ptr[buf.len]; + dest_size = avail; + } + i32 len = vsnprintf(dest, avail, format, args); + if (len < 0) { + buf.error = true; + return; + } + if (len >= (i32)dest_size) { + buf.truncated = true; + len = (i32)dest_size - 1; + } + if (dest == tmp) memcpy(&buf.data_ptr[buf.len], tmp, (u32)len + 1); + buf.len += len; +} + +fn bool StringBuffer.extend(StringBuffer* buf, u32 len) @(unused) { + if (!buf.grow) { + buf.truncated = true; + return false; + } + u32 newcap = buf.capacity * 2; + if ((len + 1) >= (newcap - buf.len)) newcap += buf.len + len + 1; + buf.capacity = newcap; + char* data2 = malloc(newcap); + memcpy(data2, buf.data_ptr, buf.len + 1); + if (buf.own) free(buf.data_ptr); + buf.own = true; + buf.data_ptr = data2; + return true; +} + +public fn void StringBuffer.indent(StringBuffer* buf, u32 indent) @(unused) { + if (buf.len + indent + 1 > buf.capacity) { + // truncate if resize fails. + if (!buf.extend(indent)) { + u32 size = buf.capacity - buf.len; + if (!size) return; + indent = size - 1; + } + } + for (u32 i = 0; i < indent; i++) { + buf.data_ptr[buf.len++] = ' '; } - buf.data_[buf.size_] = 0; + buf.data_ptr[buf.len] = 0; } diff --git a/examples/cstrip/main.c2 b/examples/cstrip/main.c2 new file mode 100644 index 000000000..4d917d46b --- /dev/null +++ b/examples/cstrip/main.c2 @@ -0,0 +1,229 @@ +/* Copyright 2022-2026 Bas van den Berg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module test; + +import ctype local; +import stdio local; +import string local; + +import file_utils; +import utils local; + +const char NEWLINE = 0x0A; + +/* + strip all lines startin with '//' + TODO also handle multi-line comments +*/ +fn void strip_comments(const char* input, StringBuffer* out) +{ + const char* cp = input; + const char* linestart = cp; + u32 lines = 0; + u32 outlines = 0; + u32 comments = 0; + u32 blanks = 0; + bool white = true; + bool skip = false; + char c; + while (*cp) { + switch (c = *cp++) { + case NEWLINE: + lines++; + if (!skip) { + if (blanks) { + // strip trailing whitespace + out.add2(linestart, (u32)(cp - 1 - blanks - linestart)); + linestart = cp - 1; + } + out.add2(linestart, (u32)(cp - linestart)); + outlines++; + } + linestart = cp; + blanks = 0; + white = true; + skip = false; + continue; + case ' ': + case '\t': + blanks++; + continue; + case '\\': + if (*cp == NEWLINE) { + cp++; + lines++; + continue; + } + break; + case '\'': + if (isdigit(*cp) && cp > linestart + 1 && isdigit(cp[-2])) { + // ignore C++ style thousand separator + break; + } + fallthrough; + case '"': + // skip C style character constants and string literals + char sep = c; + while ((c = *cp) != 0 && c != NEWLINE) { + cp++; + if (c == sep) break; + if (c == '\\' && *cp) { + if (*cp == NEWLINE) lines++; + cp++; + } + } + break; + case '`': + // skip C2 style raw strings + u32 count = 1; + while (*cp == '`') { + count++; + cp++; + } + while ((c = *cp) != 0) { + cp++; + if (c == NEWLINE) lines++; + if (c == '`') { + u32 i = 1; + while (i < count && *cp == '`') { + cp++; + i++; + } + if (i == count) break; + } + } + break; + case '/': + if (*cp == '/' || *cp == '*') { + const char* start = cp - 1; + cp = skip_comments(cp, &lines, &comments); + if (!*cp || *cp == NEWLINE) { + start -= blanks; + if (white) { + skip = true; + break; + } + } + out.add2(linestart, (u32)(start - linestart)); + // replace comments with a single space to separate tokens + if (!isspace(*cp) && start > linestart && !blanks) out.putc(' '); + linestart = cp; + } + break; + } + white = false; + blanks = 0; + } + fprintf(stderr, "cstrip: lines %d -> %d", lines, outlines); + if (comments) { + fprintf(stderr, " (stripped %d lines, %d comments)\n", lines - outlines, comments); + } else { + fprintf(stderr, " (file does not contain any comments)\n"); + } +} + +fn const char* skip_comments(const char* cp, u32* linep, u32* comments) { + char c; + for (;;) { + if (*cp == '/') { // single line comment + *comments += 1; + cp++; + while ((c = *cp) != 0 && c != NEWLINE) { + cp++; + if (c == '\\' && *cp == NEWLINE) { + *linep += 1; + cp++; + } + } + return cp; + } + if (*cp == '*') { // multi line comment + *comments += 1; + cp++; + while ((c = *cp) != 0) { + cp++; + if (c == '*' && *cp == '/') { // end of comment + cp++; + break; + } + if (c == NEWLINE) *linep += 1; + } + while (isblank(*cp)) cp++; + } + if (*cp != '/') + break; + cp++; + } + return cp; +} + +public fn i32 main(i32 argc, char** argv) { + if (argc != 2) { + fprintf(stderr, "usage: parser [c-file]\n"); + return 2; + } + const char* filename = argv[1]; + const char* outname = "out.c"; + bool to_stdout = false; + i32 status = 0; + + file_utils.Reader infile; + if (!strcmp(filename, "-")) { + filename = ""; + outname = ""; + to_stdout = true; + infile.openStream(stdin); + } else { + if (!infile.open(filename)) { + fprintf(stderr, "cstrip: cannot load %s: %s\n", + filename, strerror(infile.errno)); + return 1; + } + } + + if (infile.errno) { + fprintf(stderr, "cstrip: error reading %s: %s\n", + filename, strerror(infile.errno)); + infile.close(); + return 1; + } + + if (infile.isEmpty()) { + fprintf(stderr, "cstrip: file %s is empty\n", filename); + infile.close(); + return 0; + } + + StringBuffer* buffer = StringBuffer.create(infile.size + 1); + + strip_comments(infile.data(), buffer); + infile.close(); + + file_utils.Writer outfile; + if (to_stdout) { + fwrite(buffer.data(), 1, buffer.size(), stdout); + } else { + if (outfile.write(outname, buffer.data(), buffer.size())) { + printf("%d bytes written to %s\n", buffer.size(), outname); + } else { + fprintf(stderr, "cstrip: error writing file %s: %s\n", outname, outfile.getError()); + status = 1; + } + } + buffer.destroy(); + return status; +} + diff --git a/examples/dir_walker/main.c2 b/examples/dir_walker/main.c2 index 41f9000a6..205c42d71 100644 --- a/examples/dir_walker/main.c2 +++ b/examples/dir_walker/main.c2 @@ -16,60 +16,51 @@ module mod_main; import stdio local; -import stdlib local; import string local; -import sys_stat local; -import sys_time; import libc_dirent local; import c_errno local; -import unistd; -fn void print_indent(u32 indent) { - for (u32 i=0; i 1 ? argv[1] : ".", 0); +} diff --git a/examples/event/example_socket.c2 b/examples/event/example_socket.c2 index c0671ea23..3f245bf84 100644 --- a/examples/event/example_socket.c2 +++ b/examples/event/example_socket.c2 @@ -120,8 +120,8 @@ public fn Socket* Socket.create(events.Base* base, logger.Base* logbase, const c fatal("opening TCP socket"); } - Sockaddr_in my_addr; - my_addr.sin_family = AF_INET; + Sockaddr_in my_addr; + my_addr.sin_family = AF_INET; my_addr.sin_port = htons(port); my_addr.sin_addr.s_addr = INADDR_ANY; memset(&my_addr.sin_zero, 0, 8); diff --git a/examples/event/main.c2 b/examples/event/main.c2 index 92de6aa69..51de6babe 100644 --- a/examples/event/main.c2 +++ b/examples/event/main.c2 @@ -46,7 +46,7 @@ public fn i32 main() { Sigaction act; memset(&act, 0, sizeof(act)); act.sa_flags = SA_NOCLDSTOP|SA_RESTART; - act.sa_handler = signal_handler; + act.sa_handler = signal_handler; if (sigaction(SIGINT, &act, nil) != 0) fatal("sigaction(SIGINT)"); if (sigaction(SIGTERM, &act, nil) != 0) fatal("sigaction(SIGTERM)"); @@ -70,7 +70,8 @@ public fn i32 main() { } fn void fatal(const char* msg) { - exit(EXIT_FAILURE); + fprintf(stderr, "fatal: %s\n", msg); + exit(EXIT_FAILURE); } fn void signal_handler(i32 num) { diff --git a/examples/file_ops/main.c2 b/examples/file_ops/main.c2 index 2f644ca9b..cfe0409e5 100644 --- a/examples/file_ops/main.c2 +++ b/examples/file_ops/main.c2 @@ -15,19 +15,27 @@ module event_test; -import csignal local; -import string local; -import stdlib local; import stdio local; -import unistd local; +import string local; + import file_utils; public fn i32 main(i32 argc, char** argv) { + if (argc != 2) { + fprintf(stderr, "usage: file_ops [file]\n"); + return 2; + } + + const char* filename = argv[1]; file_utils.Reader file; - file.open(argv[1]); + + if (!file.open(filename)) { + fprintf(stderr, "cannot load '%s': %s\n", filename, strerror(file.errno)); + return 1; + } const char* contents = file.data(); - printf("%s", contents); + fputs(contents, stdout); file.close(); diff --git a/examples/json_parser/json_builder.c2 b/examples/json_parser/json_builder.c2 index 576582762..6e071ac73 100644 --- a/examples/json_parser/json_builder.c2 +++ b/examples/json_parser/json_builder.c2 @@ -15,85 +15,66 @@ module json; -import stdlib local; -import stdio local; - -fn const char* indent(u32 i) { - static const char[] indents = " "; - static const u32 max = 28; - return &indents[max - i*2]; -} - +import utils local; public type Builder struct { - char* buffer; - u32 size; - u32 capacity; + StringBuffer buffer; bool[6] hasEntry; bool[6] inArray; u32 indent; } public fn void Builder.init(Builder* b, u32 capacity) { - b.buffer = malloc(capacity); - b.capacity = capacity; + b.buffer.init(capacity); b.reset(); } public fn void Builder.free(Builder* b) { - free(b.buffer); + b.buffer.deinit(); } public fn void Builder.finalize(Builder* b) { - char* orig = &b.buffer[b.size]; - i32 len = sprintf(orig, "\n"); - b.size += len; + b.buffer.putc('\n'); } public fn const char* Builder.getData(const Builder* b) { - return b.buffer; + return b.buffer.data(); } public fn u32 Builder.getSize(const Builder* b) { - return b.size + 1; // include 0-termination + return b.buffer.size() + 1; // include 0-termination } public fn void Builder.addNumber(Builder* b, const char* key, i64 value) { - char* orig = &b.buffer[b.size]; - char* cp = orig; - if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); + if (b.hasEntry[b.indent]) b.buffer.add(",\n"); + b.buffer.indent(b.indent * 2); if (b.inArray[b.indent]) { - cp += sprintf(cp, "%s%d", indent(b.indent), value); + b.buffer.printf("%d", value); } else { - cp += sprintf(cp, "%s\"%s\": %d", indent(b.indent), key, value); + b.buffer.printf("\"%s\": %d", key, value); } - b.size += (cp - orig); b.hasEntry[b.indent] = true; } public fn void Builder.addValue(Builder* b, const char* key, const char* value) { - char* orig = &b.buffer[b.size]; - char* cp = orig; - if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); + if (b.hasEntry[b.indent]) b.buffer.add(",\n"); + b.buffer.indent(b.indent * 2); if (b.inArray[b.indent]) { - cp += sprintf(cp, "%s\"%s\"", indent(b.indent), value); + b.buffer.printf("\"%s\"", value); } else { - cp += sprintf(cp, "%s\"%s\": \"%s\"", indent(b.indent), key, value); + b.buffer.printf("\"%s\": \"%s\"", key, value); } - b.size += (cp - orig); b.hasEntry[b.indent] = true; } public fn void Builder.addObject(Builder* b, const char* key) { - char* orig = &b.buffer[b.size]; - char* cp = orig; - if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); - if (b.inArray[b.indent]) { - cp += sprintf(cp, "%s{\n", indent(b.indent)); - } else { - cp += sprintf(cp, "%s\"%s\":\n%s{\n", indent(b.indent), key, indent(b.indent)); + if (b.hasEntry[b.indent]) b.buffer.add(",\n"); + b.buffer.indent(b.indent * 2); + if (!b.inArray[b.indent]) { + b.buffer.printf("\"%s\":\n", key); + b.buffer.indent(b.indent * 2); } - b.size += (cp - orig); + b.buffer.printf("{\n"); b.hasEntry[b.indent] = true; b.indent++; b.hasEntry[b.indent] = false; @@ -103,23 +84,19 @@ public fn void Builder.addObject(Builder* b, const char* key) { public fn void Builder.closeObject(Builder* b) { // NOTE: no sanity check b.indent--; - char* orig = &b.buffer[b.size]; - char* cp = orig; - *cp++ = '\n'; - cp += sprintf(cp, "%s}", indent(b.indent)); - b.size += (cp - orig); + b.buffer.putc('\n'); + b.buffer.indent(b.indent * 2); + b.buffer.putc('}'); } public fn void Builder.addArray(Builder* b, const char* key) { - char* orig = &b.buffer[b.size]; - char* cp = orig; - if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); + if (b.hasEntry[b.indent]) b.buffer.add(",\n"); + b.buffer.indent(b.indent * 2); if (key) { - cp += sprintf(cp, "%s\"%s\":\n%s[\n", indent(b.indent), key, indent(b.indent)); - } else { - cp += sprintf(cp, "%s[\n", indent(b.indent)); + b.buffer.printf("\"%s\":\n", key); + b.buffer.indent(b.indent * 2); } - b.size += (cp - orig); + b.buffer.printf("[\n"); b.hasEntry[b.indent] = true; b.indent++; b.inArray[b.indent] = true; @@ -129,18 +106,15 @@ public fn void Builder.addArray(Builder* b, const char* key) { public fn void Builder.closeArray(Builder* b) { b.inArray[b.indent] = false; b.indent--; - char* orig = &b.buffer[b.size]; - char* cp = orig; - *cp++ = '\n'; - cp += sprintf(cp, "%s]", indent(b.indent)); - b.size += (cp - orig); + b.buffer.putc('\n'); + b.buffer.indent(b.indent * 2); + b.buffer.putc(']'); } public fn void Builder.reset(Builder* b) { - b.size = 0; - for (u32 i=0; i startScript(%s%s%s)\n", color.Green, scriptname, color.Normal); + if (unistd.isatty(1)) { + printf(" C> startScript(%s%s%s)\n", color.Green, scriptname, color.Normal); + } else { + printf(" C> startScript(%s)\n", scriptname); + } // TODO check returns lua.getglobal(l, "runScript"); @@ -137,7 +142,11 @@ public fn i32 stopScript(lua.State* l) { return -1; } const char* scriptname = lua.tolstring(l, -1, nil); - printf(" C> stopScript(%s%s%s)\n", color.Red, scriptname, color.Normal); + if (unistd.isatty(1)) { + printf(" C> stopScript(%s%s%s)\n", color.Red, scriptname, color.Normal); + } else { + printf(" C> stopScript(%s)\n", scriptname); + } return 0; } diff --git a/examples/lua/stdout b/examples/lua/stdout new file mode 100644 index 000000000..bfce1ec48 --- /dev/null +++ b/examples/lua/stdout @@ -0,0 +1,26 @@ + C> running function doTick +LUA> doTick() timesCalled 1 + C> running function doTick +LUA> doTick() timesCalled 2 + C> running function doTick +LUA> doTick() timesCalled 3 + C> running function doTick +LUA> doTick() timesCalled 4 + C> running function doTick +LUA> doTick() timesCalled 5 + C> calling LUA timesTen(2) = 20 + C> calling LUA timesTen(3) = 30 + C> running function function1 +LUA> running function1 +LUA> starting script foo + C> startScript(foo) +LUA> running script (foo) +LUA> starting script bar + C> startScript(bar) +LUA> running script (bar) +LUA> starting script 10 + C> startScript(10) +LUA> running script (10) +LUA> part2 + C> stopScript(foo) + C> stopScript(foo) diff --git a/examples/multicast/multicast_receiver.c2 b/examples/multicast/multicast_receiver.c2 index 74e07c013..e33e8137b 100644 --- a/examples/multicast/multicast_receiver.c2 +++ b/examples/multicast/multicast_receiver.c2 @@ -50,7 +50,7 @@ public fn void Receiver.destroy(Receiver* s) { mreq.imr_multiaddr.s_addr = inet_addr(s.ipnr); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(s.fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - fprintf(stderr, "setsockopt(IP_DROP_MEMBERSHIP): %s\n", strerror(*errno2())); + fprintf(stderr, "setsockopt(IP_DROP_MEMBERSHIP): %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -60,13 +60,13 @@ public fn void Receiver.destroy(Receiver* s) { fn void Receiver.open_socket(Receiver* s) { // create what looks like an ordinary UDP socket if ((s.fd = socket(AF_INET, SocketType.SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) { - fprintf(stderr, "error opening socket: %s\n", strerror(*errno2())); + fprintf(stderr, "error opening socket: %s\n", strerror(errno)); exit(EXIT_FAILURE); } i32 reuse = 1; if (setsockopt(s.fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0) { - fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(*errno2())); + fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -77,7 +77,7 @@ fn void Receiver.open_socket(Receiver* s) { addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(s.port); if (bind(s.fd, (Sockaddr*)&addr, sizeof(addr))) { - fprintf(stderr, "bind: %s\n", strerror(*errno2())); + fprintf(stderr, "bind: %s\n", strerror(errno)); exit(EXIT_FAILURE); } s.addr = addr; @@ -87,7 +87,7 @@ fn void Receiver.open_socket(Receiver* s) { mreq.imr_multiaddr.s_addr = inet_addr(s.ipnr); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(s.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(*errno2())); + fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno)); exit(EXIT_FAILURE); } } diff --git a/examples/multicast/multicast_sender.c2 b/examples/multicast/multicast_sender.c2 index 857ed824c..1bf32650d 100644 --- a/examples/multicast/multicast_sender.c2 +++ b/examples/multicast/multicast_sender.c2 @@ -43,19 +43,19 @@ fn void Sender.open_socket(Sender* s) { // create what looks like an ordinary UDP socket //if ((s.fd = socket(AF_INET, SocketType.SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP)) < 0) { if ((s.fd = socket(AF_INET, SocketType.SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) { - fprintf(stderr, "error opening socket: %s\n", strerror(*errno2())); + fprintf(stderr, "error opening socket: %s\n", strerror(errno)); exit(EXIT_FAILURE); } u8 ttl = 1; if (setsockopt(s.fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) != 0) { - fprintf(stderr, "setsockopt(IP_MULTICAST_TTL): %s\n", strerror(*errno2())); + fprintf(stderr, "setsockopt(IP_MULTICAST_TTL): %s\n", strerror(errno)); exit(EXIT_FAILURE); } i32 reuse = 1; if (setsockopt(s.fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0) { - fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(*errno2())); + fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -67,7 +67,7 @@ fn void Sender.open_socket(Sender* s) { local_addr.sin_port = htons(0); // let OS pick port if (bind(s.fd, (Sockaddr*)&local_addr, sizeof(local_addr))) { - fprintf(stderr, "bind: %s\n", strerror(*errno2())); + fprintf(stderr, "bind: %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -80,7 +80,7 @@ fn void Sender.open_socket(Sender* s) { // bind to a local address if (connect(s.fd, (Sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) { - fprintf(stderr, "connect: %s\n", strerror(*errno2())); + fprintf(stderr, "connect: %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -94,7 +94,7 @@ fn void Sender.open_socket(Sender* s) { public fn void Sender.broadcast(Sender* s, const void* data, u32 size) { if (send(s.fd, data, size, 0) < 0) { - fprintf(stderr, "send: %s\n", strerror(*errno2())); + fprintf(stderr, "send: %s\n", strerror(errno)); exit(EXIT_FAILURE); } diff --git a/examples/multicast/receiver.c2 b/examples/multicast/receiver.c2 index ef5a80cfa..e30c92655 100644 --- a/examples/multicast/receiver.c2 +++ b/examples/multicast/receiver.c2 @@ -18,10 +18,6 @@ module test; import events; import multicast_receiver local; -import stdio local; -import string local; -import unistd; - const char[] MULTICAST_IP = "225.0.0.37"; const u16 MULTICAST_PORT = 12345; diff --git a/examples/multicast/sender.c2 b/examples/multicast/sender.c2 index 3ee27a273..93f0392db 100644 --- a/examples/multicast/sender.c2 +++ b/examples/multicast/sender.c2 @@ -33,7 +33,7 @@ public fn i32 main() { while (1) { const char* message = "Hello Multicast World!"; char[64] tmp; - sprintf(tmp, "%s %d", message, nr); + snprintf(tmp, elemsof(tmp), "%s %d", message, nr); nr++; sender.broadcast(tmp, (u32)strlen(tmp)); diff --git a/examples/plugin/plugin_mgr.c2 b/examples/plugin/plugin_mgr.c2 index 0aebca9c7..fd0ca878b 100644 --- a/examples/plugin/plugin_mgr.c2 +++ b/examples/plugin/plugin_mgr.c2 @@ -17,17 +17,15 @@ module plugin_mgr; import other; -import c_errno local; import dlfcn local; import stdio local; -import string local; type LoadFn fn void(i32 arg); public fn i32 main(i32 argc, char** argv) { if (argc != 2) { - printf("Usage: %s [filename]\n", argv[0]); - return 0; + fprintf(stderr, "usage: plugin_mgr [filename]\n"); + return 2; } printf("other.test1 = %p\n", other.test1); diff --git a/examples/pthread/pthread.c2 b/examples/pthread/pthread.c2 index 947edf234..611d3121e 100644 --- a/examples/pthread/pthread.c2 +++ b/examples/pthread/pthread.c2 @@ -24,7 +24,7 @@ bool stop = false; fn void* startThread1(void*) { io.printf("started thread1\n"); while (!stop) { - sleep(1); + usleep(10); io.printf("running1\n"); } io.printf("quit thread1\n"); @@ -34,7 +34,7 @@ fn void* startThread1(void*) { fn void* startThread2(void*) { io.printf("started thread2\n"); while (!stop) { - sleep(1); + usleep(10); io.printf("running2\n"); } io.printf("quit thread2\n"); @@ -54,13 +54,13 @@ public fn i32 main() { return -1; } - sleep(4); + usleep(40); io.printf("stopping threads\n"); stop = true; i32 res; res = pthread.join(threads[0], nil); - res = pthread.join(threads[1], nil); + res |= pthread.join(threads[1], nil); io.printf("done\n"); - return 0; + return res != 0; } diff --git a/examples/recipe.txt b/examples/recipe.txt index 3ed219434..ef3870a9c 100644 --- a/examples/recipe.txt +++ b/examples/recipe.txt @@ -1,49 +1,47 @@ - plugin refs_generator executable base64 - $backend c + $backend c base64/base64.c2 base64/main.c2 end executable cstrip - $warnings no-unused - $backend c + $backend c common/file/reader.c2 common/file/writer.c2 common/string_buffer.c2 - comment_strip/main.c2 + cstrip/main.c2 end executable event $warnings no-unused - $backend c + $backend c common/color.c2 common/logger.c2 + common/string_buffer.c2 event/events.c2 event/example_socket.c2 event/main.c2 end executable file_ops - $warnings no-unused - $backend c + $backend c common/file/reader.c2 file_ops/main.c2 end executable inline_asm -# $warnings no-unused - $backend c + $backend c inline_asm/main.c2 end executable json_parser $warnings no-unused - $backend c + $backend c common/file/reader.c2 common/file/writer.c2 + common/string_buffer.c2 json_parser/json_parser.c2 json_parser/json_data.c2 json_parser/json_builder.c2 @@ -52,30 +50,28 @@ executable json_parser end executable list - $warnings no-unused - $backend c + $backend c common/color.c2 list/list.c2 list/main.c2 end executable log - $warnings no-unused - $backend c + $backend c common/logger.c2 + common/string_buffer.c2 common/color.c2 logger/main.c2 end executable jump - $warnings no-unused - $backend c + $backend c longjmp/jump.c2 end # Disabled because location of lua.a/lua.so needs to be specified #executable lua_test -# $warnings no-unused +# $warnings no-unused-parameter # $backend c # $use lua dynamic # common/color.c2 @@ -84,26 +80,24 @@ end #end executable signal_test - $warnings no-unused - $backend c + $backend c signals/main.c2 end executable socket - $backend c + $backend c socket/server.c2 end executable string - $warnings no-unused - $backend c + $backend c common/string_buffer.c2 string_buffer/main.c2 end executable toml_parser $warnings no-unused - $backend c + $backend c # $config DEBUG_NODES common/file/reader.c2 toml_parser/toml_tokenizer.c2 @@ -113,7 +107,7 @@ end executable yaml_parser $warnings no-unused - $backend c + $backend c # $config YAML_PRINT_TOKENS common/file/reader.c2 yaml_parser/yaml_parser.c2 @@ -126,41 +120,39 @@ end executable xml_parser $warnings no-unused - $backend c + $backend c common/color.c2 common/file/reader.c2 -# file_utils.c2 xml_parser/xml_parser.c2 xml_parser/xml_structure.c2 xml_parser/main.c2 end executable sudoku - $warnings no-unused - $backend c + $backend c common/color.c2 sudoku/sudoku.c2 sudoku/boards.c2 end lib plugin1 dynamic - $warnings no-unused - $backend c + $warnings no-unused-public + $backend c $export plugin_main plugin/other.c2 plugin/plugin1.c2 end lib plugin2 dynamic - $warnings no-unused - $backend c + $warnings no-unused-public + $backend c $export plugin_main plugin/plugin2.c2 end executable plugin_mgr - $warnings no-unused - $backend c + $warnings no-unused-public + $backend c $use dl dynamic plugin/other.c2 plugin/plugin_mgr.c2 @@ -168,21 +160,19 @@ end #executable test_lib # $warnings no-unused -# $backend c +# $backend c # $use plugin1 dynamic # plugin/test_lib.c2 #end executable pthread_test - $warnings no-unused - $backend c + $backend c $use pthread dynamic pthread/pthread.c2 end executable load_file - $warnings no-unused $backend c load_file/main.c2 @@ -219,14 +209,13 @@ executable mc_receiver end executable dir_walker - $warnings no-unused $backend c dir_walker/main.c2 end executable terminal - $warnings no-unused + $warnings no-unused-public $backend c terminal/main.c2 diff --git a/examples/signals/main.c2 b/examples/signals/main.c2 index 0db60474c..dde28351f 100644 --- a/examples/signals/main.c2 +++ b/examples/signals/main.c2 @@ -21,12 +21,13 @@ import stdio local; import unistd local; fn void fatal(const char* msg) { - exit(EXIT_FAILURE); + fprintf(stderr, "fatal: %s\n", msg); + exit(EXIT_FAILURE); } fn void signal_handler(i32 num) { - printf("got signal %d\n", num); - exit(EXIT_SUCCESS); + printf("got signal %d\n", num); + exit(EXIT_SUCCESS); } public fn i32 main() { @@ -39,14 +40,14 @@ public fn i32 main() { //if (sigaction(SIGQUIT, &act, nil) != 0) fatal("sigaction(SIGQUIT)"); //if (sigaction(SIGALRM, &act, nil) != 0) fatal("sigaction(SIGALRM)"); - act.sa_handler = signal_handler; + act.sa_handler = signal_handler; if (sigaction(SIGINT, &act, nil) != 0) fatal("sigaction(SIGINT)"); if (sigaction(SIGTERM, &act, nil) != 0) fatal("sigaction(SIGTERM)"); printf("press Ctrl-C to stop\n"); - while (1) { - sleep(1); - } + while (1) { + sleep(1); + } //return 0; } diff --git a/examples/string_buffer/main.c2 b/examples/string_buffer/main.c2 index 583ef18fc..07361c9aa 100644 --- a/examples/string_buffer/main.c2 +++ b/examples/string_buffer/main.c2 @@ -16,24 +16,27 @@ module tester; import stdio local; -import utils; +import utils local; -type Callback fn void (utils.StringBuffer* buf, const char* text); +//type Callback fn void (StringBuffer* buf, const char* text); public fn i32 main() { - utils.StringBuffer* buf = utils.StringBuffer.create(8); - //utils.StringBuffer* buf = utils.StringBuffer.create(100); + StringBuffer* buf = StringBuffer.create(8); buf.add("hello "); buf.add("this "); buf.add("is "); buf.add("a "); buf.add("test!"); + buf.indent(2); + buf.add2("xxx", 1); + buf.putc('y'); + buf.printf("%d", 123); printf("size = %d\n", buf.size()); printf("data = '%s'\n", buf.data()); - Callback cb = utils.StringBuffer.add; + //Callback cb = StringBuffer.add; buf.clear(); buf.destroy(); diff --git a/examples/sudoku/sudoku.c2 b/examples/sudoku/sudoku.c2 index 3aef2a5b4..7491b7cdd 100644 --- a/examples/sudoku/sudoku.c2 +++ b/examples/sudoku/sudoku.c2 @@ -98,32 +98,28 @@ fn void Board.init(Board* board) { } fn void Board.print(const Board* board, bool showOptions, u8 xx, u8 yy) { - char[2048] buffer; - memset(buffer, 0, sizeof(buffer)); - char* cp = buffer; - cp += sprintf(cp, "-------------------------\n"); - for (u32 y=0; y<9; y++) { - *cp++ = '|'; - for (u32 x=0; x<9; x++) { - u32 idx = 9*y + x; + printf("-------------------------\n"); + for (u32 y = 0; y < 9; y++) { + putchar('|'); + for (u32 x = 0; x < 9; x++) { + u32 idx = 9 * y + x; const Field* f = &board.fields[idx]; if (f.value) { const char* col = color.Green; if (x == xx && y == yy) col = color.Byellow; - cp += sprintf(cp, " %s%d%s", col, f.value, color.Normal); + printf(" %s%d%s", col, f.value, color.Normal); } else { - cp += sprintf(cp, " "); + printf(" "); } if (x % 3 == 2) { - *cp++ = ' '; - *cp++ = '|'; + putchar(' '); + putchar('|'); } } - *cp++ = '\n'; - if (y % 3 == 2) cp += sprintf(cp, "-------------------------\n"); + putchar('\n'); + if (y % 3 == 2) printf("-------------------------\n"); } - *cp = 0; - puts(buffer); + putchar('\n'); if (showOptions && board.todo != 0) board.printOptions(); } @@ -389,7 +385,7 @@ const Puzzle[] Puzzles = { public fn i32 main(i32 argc, char** argv) { if (argc != 2) { - printf("usage: %s [puzzle]\n", argv[0]); + printf("usage: sudoku [puzzle]\n"); for (u32 i=0; i "); @@ -59,8 +56,7 @@ public fn i32 main() { break; } } - reset(); + reset(&old); return 0; } - diff --git a/examples/test.sh b/examples/test.sh new file mode 100755 index 000000000..1a4fe138f --- /dev/null +++ b/examples/test.sh @@ -0,0 +1,305 @@ +#!/bin/bash + +# All temporary files are generated in the output directory +export session="output/session" +export stdout="output/stdout" +export stderr="output/stderr" +export actual="output/actual" +export sorted_in="output/sorted_in" +export sorted_out="output/sorted_out" +export empty="/dev/null" + +rm -f $session + +function run() { + export code="$1" + shift + export target="$1" + export cmd="output/$target/$target" + shift + echo "Compiling $target" >> $session + c2c $target >> $session + echo "Running $cmd $* {" >> $session + time ($cmd $* >$stdout 2>$stderr) 2>> $session + export status="$?" + echo " exit: $status, expected: $code" >> $session + if [ $status != $code ] ; then + echo "$cmd -> $status, expected $code" + fi + echo "}" >> $session + echo "output {" >> $session + cat $stdout >> $session + echo "}" >> $session + echo "stderr {" >> $session + cat $stderr >> $session + echo "}" >> $session +} + +function expect() { + export stream="$1" + export output="$2" + if [ "$output" == "" ] ; then + cat - > $actual + elif [ "$output" == "==" ] ; then + echo "$3" > $actual + else + cat $output > $actual + fi + diff -q $stream $actual + if [ $? != 0 ] ; then + echo "$cmd $stream output differs:" + diff -u $stream $actual + fi +} + +function expect_sorted() { + export stream="$1" + sort -u $stream > $sorted_in + sort -u - > $sorted_out + diff -q $sorted_in $sorted_out + if [ $? != 0 ] ; then + echo "$cmd $stream sorted output differs: {" + cat $stream + echo "}" + fi +} + +run 0 base64 +expect $stderr $empty +expect $stdout < B + -> C + -> D + -> F + B -1 + -> D + C -1 + -> B + -> E + D -1 + -> E + E -1 + F -1 + G -1 +Ordered list +all: + G -1 + A 0 + C 1 + F 1 + B 2 + D 3 + E 4 +EOF + +run 0 load_file +expect $stderr $empty +expect $stdout <" +expect $stdout $empty + +run 2 yaml_parser +expect $stderr == "usage: yaml_parser [yaml-file]" +expect $stdout $empty + +#run 0 lua_test +#expect $stderr $empty +#expect $stdout lua/stdout diff --git a/examples/toml_parser/main.c2 b/examples/toml_parser/main.c2 index 276a613e5..1a9f00f80 100644 --- a/examples/toml_parser/main.c2 +++ b/examples/toml_parser/main.c2 @@ -28,8 +28,8 @@ import toml; public fn i32 main(i32 argc, char** argv) { if (argc != 2) { - printf("Usage: parser [toml-file]\n"); - return 0; + fprintf(stderr, "usage: toml_parser [toml-file]\n"); + return 2; } const char* filename = argv[1]; diff --git a/examples/toml_parser/toml_parser.c2 b/examples/toml_parser/toml_parser.c2 index 36c5fd785..bc3840536 100644 --- a/examples/toml_parser/toml_parser.c2 +++ b/examples/toml_parser/toml_parser.c2 @@ -18,9 +18,10 @@ module toml; import stdio local; import stdlib local; import string local; -import file_utils; import csetjmp local; +import file_utils; + const u32 NamesCacheSize = 8; const u32 MaxNodes = 1024; const u32 MaxNames = 4096; @@ -105,6 +106,7 @@ type Parser struct { Token tok; JmpBufTag jump_err; char* errorMsg; + usize errorMsgSize; Blocks* blocks; Node*[MaxDepth] parents; @@ -113,10 +115,11 @@ type Parser struct { Node* topParent; } -fn bool Parser.parse(Parser* p, const char* input, char* diagMsg, Blocks* blocks) { +fn bool Parser.parse(Parser* p, const char* input, char* diagMsg, usize diagMsgSize, Blocks* blocks) { p.tokenizer.init(input); p.tok.init(); p.errorMsg = diagMsg; + p.errorMsgSize = diagMsgSize; p.errorMsg[0] = 0; p.blocks = blocks; @@ -162,7 +165,7 @@ fn void Parser.parseTopLevel(Parser* p) { p.parseTableArray(); break; default: - sprintf(p.errorMsg, "syntax error %s", p.tok.loc.str()); + snprintf(p.errorMsg, p.errorMsgSize, "syntax error %s", p.tok.loc.str()); longjmp(&p.jump_err, 1); break; } @@ -249,7 +252,7 @@ fn u32 Parser.parseValue(Parser* p) { u32 value = 0; switch (p.tok.kind) { case Word: - sprintf(p.errorMsg, "unexpected word at %s", p.tok.loc.str()); + snprintf(p.errorMsg, p.errorMsgSize, "unexpected word at %s", p.tok.loc.str()); longjmp(&p.jump_err, 1); break; case Text: @@ -368,7 +371,7 @@ fn Token* Parser.nextToken(Parser* p) { fn void Parser.expectAndConsume(Parser* p, TokenKind k) { if (p.tok.isNot(k)) { - sprintf(p.errorMsg, "expected '%s' at %s", token2str(k), p.tok.loc.str()); + snprintf(p.errorMsg, p.errorMsgSize, "expected '%s' at %s", token2str(k), p.tok.loc.str()); longjmp(&p.jump_err, 1); } p.consumeToken(); @@ -376,7 +379,7 @@ fn void Parser.expectAndConsume(Parser* p, TokenKind k) { fn void Parser.expect(Parser* p, TokenKind k) { if (p.tok.isNot(k)) { - sprintf(p.errorMsg, "expected '%s' at %s", token2str(k), p.tok.loc.str()); + snprintf(p.errorMsg, p.errorMsgSize, "expected '%s' at %s", token2str(k), p.tok.loc.str()); longjmp(&p.jump_err, 1); } } @@ -408,9 +411,8 @@ public fn const char* Reader.getMsg(const Reader* r) { public fn bool Reader.parse(Reader* r, const char* filename) { file_utils.Reader file; if (!file.open(filename)) { - sprintf(r.message, "%s", strerror(file.errno)); + snprintf(r.message, elemsof(r.message), "%s", strerror(file.errno)); return false; - } if (file.isEmpty()) { @@ -419,7 +421,7 @@ public fn bool Reader.parse(Reader* r, const char* filename) { return false; } Parser parser; - bool status = parser.parse(file.data(), r.message, r.blocks); + bool status = parser.parse(file.data(), r.message, elemsof(r.message), r.blocks); file.close(); #if DEBUG_NODES diff --git a/examples/toml_parser/toml_tokenizer.c2 b/examples/toml_parser/toml_tokenizer.c2 index 06c1d1878..062f6c698 100644 --- a/examples/toml_parser/toml_tokenizer.c2 +++ b/examples/toml_parser/toml_tokenizer.c2 @@ -71,7 +71,7 @@ fn void Location.init(Location* l, u32 line, u32 col) { fn const char* Location.str(Location* l) { static char[32] msg; - sprintf(msg, "line %d:%d", l.line, l.column); + snprintf(msg, elemsof(msg), "line %d:%d", l.line, l.column); return msg; } @@ -144,7 +144,7 @@ fn void Tokenizer.lex(Tokenizer* t, Token* result) { return; case '#': if (t.loc.column != 1) { - sprintf(t.text, "unexpected '#' after line start at %s", t.loc.str()); + snprintf(t.text, elemsof(t.text), "unexpected '#' after line start at %s", t.loc.str()); result.kind = TokenKind.Error; result.text = t.text; return; @@ -222,7 +222,7 @@ fn void Tokenizer.lex(Tokenizer* t, Token* result) { t.parseKey(result); return; } - sprintf(t.text, "unexpected char '%c' at %s", t.current[0], t.loc.str()); + snprintf(t.text, elemsof(t.text), "unexpected char '%c' at %s", t.current[0], t.loc.str()); result.kind = TokenKind.Error; result.text = t.text; return; @@ -290,7 +290,7 @@ fn void Tokenizer.parseMultiText(Tokenizer* t, Token* result) { const char* start = t.current; while (1) { if (t.current[0] == 0) { - sprintf(t.text, "missing end \"\"\" %s", t.loc.str()); + snprintf(t.text, elemsof(t.text), "missing end \"\"\" %s", t.loc.str()); result.kind = TokenKind.Error; result.text = t.text; return; diff --git a/examples/unit_test/mod1_test.c2 b/examples/unit_test/mod1_test.c2 index 94f17cc12..a7e8bf27c 100644 --- a/examples/unit_test/mod1_test.c2 +++ b/examples/unit_test/mod1_test.c2 @@ -51,7 +51,7 @@ fn void Test.test6(Test* t) { fn void Test.test7(Test* t) { check_not_nil(t); - check_nil(t); + check_nil((void*)(usize)1); // make output reproducible } fn void Test.helper(Test* t) { diff --git a/examples/unit_test/test_main.c2 b/examples/unit_test/test_main.c2 index 5ce246a04..507dfabb4 100644 --- a/examples/unit_test/test_main.c2 +++ b/examples/unit_test/test_main.c2 @@ -23,5 +23,5 @@ public fn i32 main(i32 argc, char** argv) { } } - return 0; + return 0; } diff --git a/examples/xml_parser/main.c2 b/examples/xml_parser/main.c2 index 217b067fb..b038c09e0 100644 --- a/examples/xml_parser/main.c2 +++ b/examples/xml_parser/main.c2 @@ -16,20 +16,24 @@ module test; import stdio local; +import string local; import file_utils; import xml; public fn i32 main(i32 argc, char** argv) { if (argc != 2 && argc != 3) { - printf("Usage: parser [xml-file] \n"); - return 0; + fprintf(stderr, "usage: xml_parser [xml-file] \n"); + return 2; } const char* filename = argv[1]; bool verbose = (argc == 3); printf("parsing %s\n", filename); file_utils.Reader file; - file.open(filename); + if (!file.open(filename)) { + fprintf(stderr, "cannot read %s: %s\n", filename, strerror(file.errno)); + return 1; + } if (file.isEmpty()) { printf("file %s is empty\n", filename); diff --git a/examples/xml_parser/xml_parser.c2 b/examples/xml_parser/xml_parser.c2 index 8cec9fbf8..9b1552c14 100644 --- a/examples/xml_parser/xml_parser.c2 +++ b/examples/xml_parser/xml_parser.c2 @@ -19,7 +19,6 @@ import stdio local; import string local; import csetjmp local; import ctype local; -import color; const u32 MAX_NAME = 128; const u32 MAX_VALUE = 128; @@ -55,7 +54,7 @@ public fn bool Parser.parse(Parser* p, const char* text, bool verbose) { p.blocks.init(); XmlParser parser; - bool status = parser.parse(text, p.message, &p.blocks); + bool status = parser.parse(text, p.message, elemsof(p.message), &p.blocks); if (status && verbose) p.blocks.dump(true); return status; } @@ -82,14 +81,16 @@ type XmlParser struct { Location prev; JmpBufTag jmp_err; char* msg; + usize msg_size; Blocks* blocks; } -fn bool XmlParser.parse(XmlParser* p, const char* text, char* diagMsg, Blocks* blocks_) { +fn bool XmlParser.parse(XmlParser* p, const char* text, char* diagMsg, usize diagMsgSize, Blocks* blocks_) { p.tokenizer.init(text); p.token.init(); p.prev.set(0, 0); p.msg = diagMsg; + p.msg_size = diagMsgSize; p.blocks = blocks_; i32 res = setjmp(&p.jmp_err); @@ -109,7 +110,7 @@ fn bool XmlParser.parse(XmlParser* p, const char* text, char* diagMsg, Blocks* b } fn void XmlParser.fatal(XmlParser* p, const char* text) { - sprintf(p.msg, "error %s %s", text, p.diagLoc()); + snprintf(p.msg, p.msg_size, "error %s %s", text, p.diagLoc()); longjmp(&p.jmp_err, 1); } @@ -174,7 +175,7 @@ fn u32 XmlParser.parseNode(XmlParser* p) { } fn bool XmlParser.parseTag(XmlParser* p, Node* node) { - u32 last_attr_off = 0; + //u32 last_attr_off = 0; Attr* lastAttr = nil; // ' if (t.current[1] != '>') { - sprintf(t.msg, "expected '/>' %s", t.loc.str()); + snprintf(t.msg, elemsof(t.msg), "expected '/>' %s", t.loc.str()); result.setKind(TokenKind.Error); result.setData(t.msg); return; @@ -435,7 +436,7 @@ fn void Tokenizer.lex(Tokenizer* t, Token* result) { return; case '?': // expect '?>' if (t.current[1] != '>') { - sprintf(t.msg, "expected '?>' %s", t.loc.str()); + snprintf(t.msg, elemsof(t.msg), "expected '?>' %s", t.loc.str()); result.setKind(TokenKind.Error); result.setData(t.msg); return; @@ -456,7 +457,7 @@ fn void Tokenizer.lex(Tokenizer* t, Token* result) { return; case ']': // expect ']]>' if (t.current[1] != ']' || t.current[2] != '>') { - sprintf(t.msg, "expected ']]>' %s", t.loc.str()); + snprintf(t.msg, elemsof(t.msg), "expected ']]>' %s", t.loc.str()); result.setKind(TokenKind.Error); result.setData(t.msg); return; @@ -469,7 +470,7 @@ fn void Tokenizer.lex(Tokenizer* t, Token* result) { if (isNameStartChar(t.current[0])) { t.parseName(result); } else { - sprintf(t.msg, "unexpected char '%c' %s", t.current[0], t.loc.str()); + snprintf(t.msg, elemsof(t.msg), "unexpected char '%c' %s", t.current[0], t.loc.str()); result.setKind(TokenKind.Error); result.setData(t.msg); @@ -490,7 +491,7 @@ fn bool Tokenizer.readLtToken(Tokenizer* t, Token* result) { if (t.current[1] == '!') { // can be