Script tests re-added, all tests can run under gdb

Also fixed a minor bug with printing, and removed the ability to
configure the parser.

Added and updated QUICKSTART.md as a quick way to get people started.

There's some broken scripts under 'scripts/' that require functions to
work properly.
This commit is contained in:
2026-04-10 15:28:56 +10:00
parent 211744535e
commit 547229e150
26 changed files with 916 additions and 48 deletions

146
QUICKSTART.md Normal file
View File

@@ -0,0 +1,146 @@
# Toy v2 Quick-Start Guide
To help you start using Toy as fast as possible, here are the most useful elements of the language. Not everything available is listed, but this should let you start coding right away.
## Keyword 'print'
The `print` keyword takes one value as a parameter, which is sent to stdout by default, or can be redirected elsewhere using C.
```
print "Hello World!";
```
## Keyword 'assert'
The `assert` keyword takes two values as parameters, separated by a comma. If the first value is falsy or `null`, the optional second parameter is sent to stderr by default, or can be redirected elsewhere using C. If no second parameter is provided, a generic error message is used instead.
```
//nothing happens
assert 1 < 2;
//this assert will fail, and output the second parameter
assert null, "Hello world!";
```
## Variables and Types
Variables can be declared with the `var` keyword, and can be given an optional type from the list below. If no type is specified, `any` is used by default.
```
var answer = 42;
var question: string = "How many roads must a man walk down?";
```
To make a variable immutable, use the `const` keyword after the type declaration. In this case, it must be assigend a value.
```
var quote: string const = "War. War never changes.";
```
The types available in Toy are:
| type | name | description |
| --- | --- | --- |
| `bool` | boolean | Either `true` or `false`. |
| `int` | integer | Any whole number (32-bits). |
| `float` | float | A decimal number (32-bits), using floating-point arithmetic. |
| `string` | string | A series of characters used for text processing. |
| `array` | array | A series of values stored sequentially in memory. |
| `table` | table | A series key-value pairs stored in such a way that allows for fast lookups. Booleans, functions, opaques and `null` can't be used as keys. |
| `function` | function | A chunk of reusable code that takes zero or more parameters, and returns zero or more results. Functions are declared with the `fn` keyword. |
| `opaque` | opaque | This value is unusable in Toy, but allows you to pass data between C functions. |
| `any` | any | The default type when nothing is specified. Theis can hold any value. |
## Control Flow
Choosing an option, or repeating a chunk of code multiple times, is essential for any general purpose language.
Choosing between two options can be done with the `if-then-else` else statement. If the condition is truthy, the 'then-branch' will be executed. Otherwise, the optional 'else-branch' is executed instead.
```
var answer = 42;
if (answer < 56) {
print "Cod dang it!";
}
else {
print "Something's fishy here...";
}
```
```
var challenge = "hard";
if (challenge == "hard") {
print "I choose to build a scripting language, not because it's easy, but because it's hard!";
}
//the else-branch is optional
```
To repeat a certain action, use the `while-then` loop, which repeats the body as long as the condition is true at the beginning of each loop.
```
var loops = 0;
while (loops++ < 8) {
print "These episodes are endless.";
}
```
To break out of a loop, you can use the `break` keyword. Alternatively, to restart the loop early, use the `continue` keyword.
```
var loops = 0;
while (true) {
if (++loops < 15532) {
continue;
}
break; //poor yuki ;_;
}
```
*Note: The `for` loop is coming, eventually, but isn't vital right now.*
## Arrays and Tables
Arrays are defined with a pair of brackets, and can contain a list of comma-separated values.
```
//'array' is a reserved keyword, so it can't be used as a name
var a = [1,2,3];
//instead, it's used as a type
var b: array = [4,5,6];
//define an empty array like this
var c: array = [];
//arrays are zero-indexed
print a[0]; //'1'
```
Tables are also defined with brackets, and contain a comma-separated list of key-value pairs defined by colons:
```
//most types can be used as keys
var t = ["alpha": 1, "beta": 2, "gamma": 3];
//the 'table' keyword can define the type, and an empty table still has a colon
var u: table = [:];
//printing a table does NOT guarantee internal order, but the elements can be accessed with the keys.
print t["beta"];
```
## Functions
Watch this space.
## External Libraries and Extending Toy
Watch this space.

View File

@@ -116,21 +116,21 @@ static void printCallback(const char* msg) {
}
static void errorAndExitCallback(const char* msg) {
fprintf(stderr, TOY_CC_ERROR "Error: %s\n" TOY_CC_RESET, msg);
fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
exit(-1);
}
static void errorAndContinueCallback(const char* msg) {
fprintf(stderr, TOY_CC_ERROR "Error: %s\n" TOY_CC_RESET, msg);
fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
}
static void assertFailureAndExitCallback(const char* msg) {
fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s\n" TOY_CC_RESET, msg);
fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
exit(-1);
}
static void assertFailureAndContinueCallback(const char* msg) {
fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s\n" TOY_CC_RESET, msg);
fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
}
static void noOpCallback(const char* msg) {
@@ -325,7 +325,6 @@ int repl(const char* filepath) {
Toy_bindLexer(&lexer, inputBuffer);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_configureParser(&parser, false);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser); //Ast is in the bucket, so it doesn't need to be freed
//parsing error, retry
@@ -478,8 +477,6 @@ int main(int argc, const char* argv[]) {
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_configureParser(&parser, cmd.removeAssert);
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
unsigned char* bytecode = Toy_compileToBytecode(ast);

17
scripts/benchpress.toy Normal file
View File

@@ -0,0 +1,17 @@
//calculate the nth fibonacci number, and print it
var counter: int = 0;
var first: int = 1;
var second: int = 0;
//BUG: This causes a stack overflow
while (counter < 100_000) {
var third: int = first + second;
first = second;
second = third;
print third;
++counter;
}

16
scripts/fib.toy Normal file
View File

@@ -0,0 +1,16 @@
//BUG: Not yet functional
//example of the fibonacci sequence
fn fib(n: int) {
if (n < 2) return n;
return fib(n-1) + fib(n-2);
}
//TODO: type coercion syntax hasn't been decided on yet, but it will be needed
for (var i = 1; i <= 10; i++) {
print i .. ":" .. fib(i);
}
//Note to my future self: yes, the base case in 'fib()' is 'n < 2', stop second guessing yourself!
//Note to my past self: don't tell me what to do!
//Note to both of you: keep it down you young whipper snappers!

24
scripts/fizzbuzz.toy Normal file
View File

@@ -0,0 +1,24 @@
//standard example, using 'while' instead of 'for', because it's not ready yet
var counter: int = 0;
while (++counter <= 100) {
var result: string = "";
if (counter % 3 == 0) {
result = result .. "fizz";
}
if (counter % 5 == 0) {
result = result .. "buzz";
}
//finally
if (result != "") {
print result;
}
else {
print counter;
}
}

20
scripts/funky.toy Normal file
View File

@@ -0,0 +1,20 @@
//BUG: functions aren't working yet
fn makeCounter() {
var counter: int = 0;
fn increment() {
return ++counter;
}
return increment;
}
var tally = makeCounter();
while (true) {
var result = tally();
if (result >= 10_000_000) {
break;
}
}

8
scripts/leapyear.toy Normal file
View File

@@ -0,0 +1,8 @@
//BUG: Not yet functional
//find the leap years
fn isLeapYear(n: int) {
if (n % 400 == 0) return true;
if (n % 100 == 0) return false;
return n % 4 == 0;
}

View File

@@ -43,7 +43,7 @@ reference: https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape
#define TOY_CC_WARN "\033[" TOY_CC_FONT_YELLOW ";" TOY_CC_BACK_DEFAULT "m"
#define TOY_CC_ERROR "\033[" TOY_CC_FONT_RED ";" TOY_CC_BACK_DEFAULT "m"
#define TOY_CC_ASSERT "\033[" TOY_CC_FONT_BLACK ";" TOY_CC_BACK_MAGENTA "m"
#define TOY_CC_RESET "\033[" "0" "m"
#define TOY_CC_RESET "\033[" TOY_CC_FONT_DEFAULT ";" TOY_CC_BACK_DEFAULT "m"
//for unsupported platforms, these become no-ops
#else

View File

@@ -818,18 +818,12 @@ static void makeAssertStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As
Toy_Ast* ast = NULL; //assert's emit function is a bit different
makeExpr(bucketHandle, parser, &ast);
//if assert is disabled, don't emit the assert
if (parser->removeAssert) {
Toy_private_emitAstPass(bucketHandle, rootHandle);
//NOTE: if it's an aggregate node, then it's got a second arg
if (ast->type == TOY_AST_AGGREGATE) {
Toy_private_emitAstAssert(bucketHandle, rootHandle, ast->aggregate.left, ast->aggregate.right);
}
else {
//NOTE: if it's an aggregate node, then it's got a second arg
if (ast->type == TOY_AST_AGGREGATE) {
Toy_private_emitAstAssert(bucketHandle, rootHandle, ast->aggregate.left, ast->aggregate.right);
}
else {
Toy_private_emitAstAssert(bucketHandle, rootHandle, ast, NULL);
}
Toy_private_emitAstAssert(bucketHandle, rootHandle, ast, NULL);
}
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of assert statement");
@@ -1159,10 +1153,4 @@ void Toy_resetParser(Toy_Parser* parser) {
parser->error = false;
parser->panic = false;
parser->removeAssert = false;
}
void Toy_configureParser(Toy_Parser* parser, bool removeAssert) {
parser->removeAssert = removeAssert;
}

View File

@@ -14,14 +14,8 @@ typedef struct Toy_Parser {
bool error;
bool panic; //currently processing an error
//configs
bool removeAssert;
} Toy_Parser;
TOY_API void Toy_bindParser(Toy_Parser* parser, Toy_Lexer* lexer);
TOY_API Toy_Ast* Toy_scanParser(Toy_Bucket** bucketHandle, Toy_Parser* parser);
TOY_API void Toy_resetParser(Toy_Parser* parser);
//configure certain options
TOY_API void Toy_configureParser(Toy_Parser* parser, bool removeAssert);

View File

@@ -838,11 +838,11 @@ static void processIndex(Toy_VM* vm) {
//extract cstring, based on type
if (str->info.type == TOY_STRING_LEAF) {
const char* cstr = str->leaf.data;
result = Toy_toStringLength(&vm->memoryBucket, cstr + i, l);
result = Toy_createStringLength(&vm->memoryBucket, cstr + i, l);
}
else if (str->info.type == TOY_STRING_NODE) {
char* cstr = Toy_getStringRaw(str);
result = Toy_toStringLength(&vm->memoryBucket, cstr + i, l);
result = Toy_createStringLength(&vm->memoryBucket, cstr + i, l);
free(cstr);
}
else {

View File

@@ -1,9 +1,11 @@
#bridge file
export CFLAGS+=-DTOY_CC_ENABLED
all:
$(MAKE) -C units -k
$(MAKE) -C scripts -k
gdb:
$(MAKE) -C units -k gdb
$(MAKE) -C scripts -k gdb
#TODO: valgrind

2
tests/scripts/gdb_init Normal file
View File

@@ -0,0 +1,2 @@
set breakpoint pending on

52
tests/scripts/makefile Normal file
View File

@@ -0,0 +1,52 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
ifeq ($(shell uname),Linux)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),NetBSD)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(OS),Windows_NT)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),Darwin)
LDFLAGS=-Wl,-dead_strip
else
@echo "LDFLAGS set failed - what platform is this?"
endif
#directories
TEST_ROOTDIR=../..
TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR)
TEST_REPLDIR=$(TEST_ROOTDIR)/$(TOY_REPLDIR)
TEST_SCRIPTDIR=.
#file names
TEST_SCRIPTFILES=$(wildcard $(TEST_SCRIPTDIR)/test_*.toy)
#build the source and repl, copy to the local dir, and run
all: source repl copy run
#compile the source and repl first
source:
$(MAKE) -C $(TEST_SOURCEDIR)
repl: source
$(MAKE) -C $(TEST_REPLDIR)
copy:
cp -r $(TEST_ROOTDIR)/$(TOY_OUTDIR) .
run: $(TEST_SCRIPTFILES:.toy=.toy-run)
%.toy-run: %.toy
find -name repl* -type f -exec {} -f ../$< --verbose \;
#using gdb
gdb: source repl copy run-gdb
run-gdb: $(TEST_SCRIPTFILES:.toy=.toy-run-gdb)
%.toy-run-gdb: %.toy
gdb $(TEST_OUTDIR)/$(TEST_REPLNAME) -ix gdb_init -ex=run --batch --return-child-result --args "out/repl.out" "-f" "../$<" "--verbose"

View File

@@ -0,0 +1,26 @@
//1-D array
var arr = [1, 2, 3];
arr[1] = 6;
assert arr == [1, 6, 3], "1-D array failed";
//we need to go deeper
var barr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
barr[1][1] = 99;
assert barr == [[1, 2, 3],[4,99,6],[7,8,9]], "2-D array failed";
//test trailing commas
var a = [1, 2, 3, ];
print a;
//test empty arrays
var b = [];
print b;

View File

@@ -0,0 +1,9 @@
//these are allowed
/* EMPTY */;
if (true) { }
if (true) pass;
//these are not allowed
// if (true) /* EMPTY */;

View File

@@ -0,0 +1,13 @@
//NOTE: these tests are all passing - failing tests can be found under the 'mustfails' directory
//basic assert statement
assert true;
//assert on a string (tests for it's truthiness)
assert "Hello world";
//assert on a condition
assert 1 < 2;
//assert with an optional message
assert true, "Assertion message";

View File

@@ -0,0 +1,65 @@
//literals
if (true) {
print "Success 1";
}
else {
print "Failure 1";
}
//false literals
if (false) {
print "Failure 2";
}
else {
print "Success 2";
}
//conditionals
if (1 < 2) {
print "Success 3";
}
if (1 > 2) {
print "Failure 3";
}
//variables
var a = 42;
if (a) {
print "Success 4";
}
else {
print "Failure 4";
}
if (a == 42) {
print "Success 5";
}
else {
print "Failure 5";
}
//concatenated strings
if ("foo" .. "bar" == "foobar") {
print "Success 6";
}
else {
print "Failure 6";
}
if ("foobar" == "foo" .. "bar") {
print "Success 7";
}
else {
print "Failure 7";
}
if ("fizz" .. "le" == "fi" .. "zzle") {
print "Success 8";
}
else {
print "Failure 8";
}

View File

@@ -0,0 +1,46 @@
//literals
if (true)
print "Success 1";
else
print "Failure 1";
//false literals
if (false)
print "Failure 2";
else
print "Success 2";
//conditionals
if (1 < 2)
print "Success 3";
if (1 > 2)
print "Failure 3";
//variables
var a = 42;
if (a)
print "Success 4";
else
print "Failure 4";
if (a == 42)
print "Success 5";
else
print "Failure 5";
//concatenated strings
if ("foo" .. "bar" == "foobar")
print "Success 6";
else
print "Failure 6";
if ("foobar" == "foo" .. "bar")
print "Success 7";
else
print "Failure 7";
if ("fizz" .. "le" == "fi" .. "zzle")
print "Success 8";
else
print "Failure 8";

View File

@@ -0,0 +1,21 @@
//basic print statement
print 42;
//print compount expressions
print 3 * 5;
//print a string
print "Hello world!";
//print a concatenated string
print "Hello" .. "world!";
//print with escaped characters
print "\tHello\nworld!";
//print from a substring string
print "Hello world"[0,5];
//print from a substring, after a concat
print ("hello" .. "world")[2,6];

View File

@@ -0,0 +1,132 @@
//make sure it works with multiple repititions
//-------------------------
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag1: bool = true;
while (flag1) {
flag1 = false;
continue;
assert false, "continue failed";
}
//-------------------------
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag2: bool = true;
while (flag2) {
flag2 = false;
continue;
assert false, "continue failed";
}
//-------------------------
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag3: bool = true;
while (flag3) {
flag3 = false;
continue;
assert false, "continue failed";
}
//-------------------------
{
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag4: bool = true;
while (flag4) {
flag4 = false;
continue;
assert false, "continue failed";
}
}
//-------------------------
{
//test break
while (true) {
{
break;
}
assert false, "break failed";
}
//test continue
var flag5: bool = true;
while (flag5) {
flag5 = false;
{
continue;
}
assert false, "continue failed";
}
}
//-------------------------
{
//iteration
var iteration = 0;
while(iteration < 10) {
print iteration;
iteration += 1;
}
}
{
//if and while work together
var count = 1;
while (count <= 10) {
if (count % 2 == 0) {
print "even";
}
else {
print "odd";
}
count += 1;
}
}
//-------------------------
{
//make sure break and continue point to the correct locations
var loops = 0;
while (true) {
if (++loops < 15532) {
continue;
}
break;
}
assert loops == 15532, "Yuki loop failed (break + continue)";
}

View File

@@ -0,0 +1,17 @@
//shadowing
var answer = 42;
print answer; //42
{
var answer = 7;
print answer; //7
}
print answer; //42
//rebinding
var question = 42;
print question; //42
{
var question = question;
print question; //42
}
print question; //42

View File

@@ -0,0 +1,33 @@
//1-D table
var a = ["alpha": 1, "beta": 2, "gamma": 3];
a["beta"] = 6;
print a;
assert a == ["alpha": 1, "beta": 6, "gamma": 3], "1-D tables failed";
//nested
var b = [
"outer": ["inner": true],
"alpha": 1,
"beta": 2,
"gamma": 3
];
print b;
assert b == ["alpha": 1, "beta": 2, "gamma": 3, "outer": ["inner": true]], "nested tables failed";
//test empty tables
var empty = [:];
print empty;
assert empty == [:], "empty tables failed";
//test trailing commas
var trailing = [
"alpha":1,
"beta":2,
"gamma":3,
];
print trailing;
assert trailing == ["alpha": 1, "beta": 2, "gamma": 3], "trailing tables failed";

View File

@@ -0,0 +1,80 @@
//booleans
{
var value: bool = true;
if (value) {
print "boolean";
}
else {
assert false, "boolean";
}
}
{
var value: bool = false;
if (value) {
assert false, "boolean";
}
else {
print "boolean";
}
}
//integers
{
var value: int = 42;
if (value) {
print "integer";
}
else {
assert false, "integer";
}
}
{
var value: int = 0;
if (value) {
assert false, "integer";
}
else {
print "integer";
}
}
//floats
{
var value: float = 42.8891;
if (value) {
print "float";
}
else {
assert false, "float";
}
}
{
var value: float = 0;
if (value) {
assert false, "float";
}
else {
print "float";
}
}
//everything else
{
var value: string = "foobar";
if (value) {
print "string";
}
else {
assert false, "string";
}
}

View File

@@ -0,0 +1,160 @@
//declare a variable with an initial value
var answer = 42;
//declare a variable without an initial value
var empty;
//assign a previously existing variable
answer = 6 * 9;
//access a variable
answer = answer + 1;
//compound assignments
answer += 5;
answer -= 5;
answer *= 9;
answer /= 2;
answer %= 10;
//equality checks
print 1 == 1; //true
print 1 != 1; //false
//comparison checks
print 1 < 2; //true
print "foo" > "bar"; //true
print 1 < 2; //true
print 1 > 2; //false
print 2 <= 2; //true
print 2 >= 2; //true
print 1 <= 2; //true
print 1 >= 2; //false
//logical checks
print true && true; //true
print true && false; //false
print false && true; //false
print false && false; //false
print true || true; //true
print true || false; //true
print false || true; //true
print false || false; //false
print !true; //false
print !false; //true
//logical AND short-circuits and chained assignments
{
var a = 1;
var b = 2;
var c = a + 1 && b + 2;
assert a == 1, "short circuit 1.1";
assert b == 2, "short circuit 1.2";
assert c == 4, "short circuit 1.3";
}
{
var a = 1;
var b = 2;
var c = a = (a + 1) && b + 2;
assert a == 4, "short circuit 2.1";
assert b == 2, "short circuit 2.2";
assert c == 4, "short circuit 2.3";
}
{
var a = 1;
var b = 2;
var c = a = a + 1 && b + 2;
assert a == 4, "short circuit 3.1";
assert b == 2, "short circuit 3.2";
assert c == 4, "short circuit 3.3";
}
//logical OR short-circuits and chained assignments
{
var a = 1;
var b = 2;
var c = a + 1 || b + 2;
assert a == 1, "short circuit 4.1";
assert b == 2, "short circuit 4.2";
assert c == 2, "short circuit 4.3";
}
{
var a = 1;
var b = 2;
var c = a = (a + 1) || b + 2;
assert a == 2, "short circuit 5.1";
assert b == 2, "short circuit 5.2";
assert c == 2, "short circuit 5.3";
}
{
var a = 1;
var b = 2;
var c = a = a + 1 || b + 2;
assert a == 2, "short circuit 6.1";
assert b == 2, "short circuit 6.2";
assert c == 2, "short circuit 6.3";
}
//types
{
var a: int;
var b: int = 42;
a = 69;
b = 8891;
print a;
print b;
}
//constants
{
var c: int const = 42;
print c;
}
//indexing
{
var s = "Hello" .. "world!";
print s[3, 3];
}
//increment & decrement (prefix)
{
var a = 42;
assert a == 42, "prefix increment & decrement 1.1";
assert ++a == 43, "prefix increment & decrement 1.2";
assert a == 43, "prefix increment & decrement 1.3";
assert --a == 42, "prefix increment & decrement 1.4";
assert a == 42, "prefix increment & decrement 1.5";
}
//increment & decrement (postfix)
{
var a = 42;
assert a == 42, "postfix increment & decrement 1.1";
assert a++ == 42, "postfix increment & decrement 1.2";
assert a == 43, "postfix increment & decrement 1.3";
assert a-- == 43, "postfix increment & decrement 1.4";
assert a == 42, "postfix increment & decrement 1.5";
print a;
}
//TODO: type casting

View File

@@ -19,30 +19,30 @@ endif
#directories
TEST_ROOTDIR=../..
TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR)
TEST_CASESDIR=.
TEST_UNITSDIR=.
TEST_OUTDIR=out
TEST_OBJDIR=obj
#file names
TEST_SOURCEFILES=$(wildcard $(TEST_SOURCEDIR)/*.c)
TEST_CASESFILES=$(wildcard $(TEST_CASESDIR)/test_*.c)
TEST_UNITSFILES=$(wildcard $(TEST_UNITSDIR)/test_*.c)
#build the object files, compile the test cases, and run
all: build-source build-cases build-link build-run
#build the object files, compile the tess, and run
all: build-source build-units build-link build-run
#targets for each step
.PHONY: build-source
build-source: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o)))
.PHONY: build-cases
build-cases: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_CASESFILES:.c=.o)))
.PHONY: build-units
build-units: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_UNITSFILES:.c=.o)))
.PHONY: build-link
build-link: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe)))
build-link: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_UNITSFILES:%.c=%.exe)))
.PHONY: build-run
build-run: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run)))
build-run: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_UNITSFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_UNITSFILES:%.c=%.run)))
#util targets
$(TEST_OUTDIR):
@@ -55,8 +55,8 @@ $(TEST_OBJDIR):
$(TEST_OBJDIR)/%.o: $(TEST_SOURCEDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OBJDIR)/%.o: $(TEST_CASESDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR) $(TEST_CASESDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OBJDIR)/%.o: $(TEST_UNITSDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR) $(TEST_UNITSDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OUTDIR)/%.exe: $(TEST_OBJDIR)/%.o
@$(CC) -o $@ $< $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) $(CFLAGS) $(LIBS) $(LDFLAGS)
@@ -66,20 +66,20 @@ $(TEST_OUTDIR)/%.run: $(TEST_OUTDIR)/%.exe
$<
#debugging targets
gdb: build-source build-cases build-link build-run-gdb
gdb: build-source build-units build-link build-run-gdb
.PHONY: build-run-gdb
build-run-gdb: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run-gdb)))
build-run-gdb: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_UNITSFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_UNITSFILES:%.c=%.run-gdb)))
.PRECIOUS: $(TEST_OUTDIR)/%.run-gdb
$(TEST_OUTDIR)/%.run-gdb: $(TEST_OUTDIR)/%.exe
gdb $< -ix gdb_init -ex=run --batch --return-child-result --args "$<"
#valgrind targets
valgrind: build-source build-cases build-link build-run-valgrind
valgrind: build-source build-units build-link build-run-valgrind
.PHONY: build-run-valgrind
build-run-valgrind: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run-valgrind)))
build-run-valgrind: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_UNITSFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_UNITSFILES:%.c=%.run-valgrind)))
.PRECIOUS: $(TEST_OUTDIR)/%.run-valgrind
$(TEST_OUTDIR)/%.run-valgrind: $(TEST_OUTDIR)/%.exe