Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aeb008c684 | |||
| 53012dbce1 | |||
| 4fe57f9562 | |||
| 3ba2e420ea | |||
| c81a139c97 | |||
| 66ea684a90 | |||
| a26a6a56d0 | |||
| ee226ea426 | |||
| 76a0290290 | |||
| 85dc24c4a6 | |||
| 1804e380a0 | |||
| 7567f4f3b2 | |||
| e8160eb9df | |||
| 276648630e | |||
| e946a5f071 | |||
| 932401c26d | |||
| fc67d6a18b | |||
| 60908c8bf3 | |||
| b8e3324acb | |||
| 0fc8183799 | |||
| 9a6aa8d15e | |||
| 0b9051cab2 | |||
| 0d7e4db661 | |||
| d5c833b344 | |||
| c875ae7a0e | |||
| c0ec5ef28e | |||
| be91de9535 | |||
| 3088c4fe6d | |||
| 9bd0cd23e7 | |||
| 29f8a698b4 | |||
| 41d274177a | |||
| 7ea249f723 | |||
| 3949be3706 | |||
| 0e932f24cc | |||
| 60225b733b | |||
| 15f99495a1 | |||
| 7b26527e95 | |||
| 386201b6e9 | |||
| 8d278077b1 | |||
| 57c16d2ede | |||
| 2f1613e306 | |||
| 2776c87026 | |||
| 8cc33f5fbb | |||
| 90f91a234a | |||
| fcd1cdf70b | |||
| e265038547 | |||
| a357e0b0d4 | |||
| 2c5e3c6aa5 | |||
| aeda0a0d94 | |||
| ebbcba1b16 | |||
| 4bce10803e | |||
| daee91904d | |||
| c397384766 | |||
| 667efc2feb | |||
| f4469fc53d | |||
| c86c5800a7 | |||
| e4d843ad3a | |||
| 6667bce3d7 | |||
| 50b07da188 | |||
| 708db75760 | |||
| b632ce77ba | |||
| b2c3dd894c | |||
| 9b21bfb53b | |||
| d3adac8a59 | |||
| 5183037a99 |
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
labels: bug
|
||||
---
|
||||
|
||||
## Describe the bug
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
## To Reproduce
|
||||
|
||||
Steps to reproduce the behaviour:
|
||||
|
||||
1. run `git pull` on the repository
|
||||
2. run `make rebuild` on the code
|
||||
3. ...
|
||||
|
||||
You can include some screenshots here if you'd like!
|
||||
|
||||
## Versioning
|
||||
|
||||
- OS: [for example MacOS, Windows, iOS, Android]
|
||||
- Version: [What version of Toy was this running?]
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
### Describe the feature you’d like
|
||||
|
||||
A clear and concise description of what you’d like to be able to do with Toy.
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
A clear and concise description of any alternative solutions or workarounds you've considered.
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the feature request here.
|
||||
@@ -8,7 +8,6 @@ on:
|
||||
|
||||
jobs:
|
||||
test-valgrind:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -19,10 +18,17 @@ jobs:
|
||||
run: make test
|
||||
|
||||
test-sanitized:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: make test (sanitized)
|
||||
run: make test-sanitized
|
||||
|
||||
test-mingw32:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: make test (mingw32)
|
||||
run: make test
|
||||
|
||||
@@ -21,9 +21,7 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
|
||||
|
||||
## Building
|
||||
|
||||
For Windows, Linux and MacOS, simply run `make` in the root directory.
|
||||
|
||||
Note: For Linux, you may need to `cd` into the `out` directory before running.
|
||||
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
|
||||
|
||||
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
|
||||
|
||||
@@ -38,7 +36,6 @@ Run `make install-tools` to install a number of tools, including:
|
||||
```
|
||||
import standard; //for a bunch of utility functions
|
||||
|
||||
|
||||
print "Hello world"; //"print" is a keyword
|
||||
|
||||
var msg = "foobar"; //declare a variable like this
|
||||
@@ -62,8 +59,6 @@ var tally = makeCounter();
|
||||
print tally(); //1
|
||||
print tally(); //2
|
||||
print tally(); //3
|
||||
|
||||
export tally; //export this variable to the host program
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# export CFLAGS+=-O2 -mtune=native -march=native
|
||||
# export CFLAGS+=-fsanitize=address,undefined
|
||||
|
||||
export CFLAGS+=-std=c18 -pedantic -Werror
|
||||
|
||||
export TOY_OUTDIR = out
|
||||
|
||||
all: $(TOY_OUTDIR) repl
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "lib_about.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//the about keys
|
||||
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
|
||||
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
|
||||
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
|
||||
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
|
||||
Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author"));
|
||||
|
||||
//the about identifiers
|
||||
Toy_Literal majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major"));
|
||||
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
|
||||
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
|
||||
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
|
||||
Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author"));
|
||||
|
||||
//the about values
|
||||
Toy_Literal majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR);
|
||||
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
|
||||
Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH);
|
||||
Toy_Literal buildLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(TOY_VERSION_BUILD));
|
||||
Toy_Literal authorLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("Kayne Ruse, KR Game Studios"));
|
||||
|
||||
//store as an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with values
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//set each key/value pair
|
||||
Toy_setLiteralDictionary(dictionary, majorKeyLiteral, majorLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, minorKeyLiteral, minorLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, patchKeyLiteral, patchLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, buildKeyLiteral, buildLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, authorKeyLiteral, authorLiteral);
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal anyType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, anyType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
}
|
||||
|
||||
//store globally
|
||||
else {
|
||||
//make sure the names aren't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal intType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_INTEGER, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
|
||||
//major
|
||||
Toy_declareScopeVariable(interpreter->scope, majorIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, majorIdentifierLiteral, majorLiteral, false);
|
||||
|
||||
//minor
|
||||
Toy_declareScopeVariable(interpreter->scope, minorIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, minorIdentifierLiteral, minorLiteral, false);
|
||||
|
||||
//patch
|
||||
Toy_declareScopeVariable(interpreter->scope, patchIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, patchIdentifierLiteral, patchLiteral, false);
|
||||
|
||||
//build
|
||||
Toy_declareScopeVariable(interpreter->scope, buildIdentifierLiteral, strType);
|
||||
Toy_setScopeVariable(interpreter->scope, buildIdentifierLiteral, buildLiteral, false);
|
||||
|
||||
//author
|
||||
Toy_declareScopeVariable(interpreter->scope, authorIdentifierLiteral, strType);
|
||||
Toy_setScopeVariable(interpreter->scope, authorIdentifierLiteral, authorLiteral, false);
|
||||
|
||||
Toy_freeLiteral(intType);
|
||||
Toy_freeLiteral(strType);
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
+1468
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
+97
-64
@@ -10,7 +10,7 @@
|
||||
|
||||
typedef struct Toy_Runner {
|
||||
Toy_Interpreter interpreter;
|
||||
unsigned char* bytecode;
|
||||
const unsigned char* bytecode;
|
||||
size_t size;
|
||||
|
||||
bool dirty;
|
||||
@@ -24,87 +24,44 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the argument
|
||||
//get the file path literal with a handle
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
|
||||
|
||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||
int driveLength = 0;
|
||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||
interpreter->errorOutput("Incorrect drive path format given to loadScript\n");
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
driveLength++;
|
||||
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteralIdn);
|
||||
}
|
||||
|
||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||
Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral);
|
||||
|
||||
//get the real drive file path
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
|
||||
|
||||
if (!TOY_IS_STRING(realDriveLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\n");
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
if (TOY_IS_NULL(filePathLiteral)) {
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
|
||||
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
|
||||
|
||||
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
|
||||
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
|
||||
|
||||
//clean up the drivepath stuff
|
||||
Toy_deleteRefString(realDrive);
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
|
||||
//check for file extensions
|
||||
if (!(filePath[realLength - 5] == '.' && filePath[realLength - 4] == 't' && filePath[realLength - 3] == 'o' && filePath[realLength - 2] == 'y')) {
|
||||
interpreter->errorOutput("Bad script file extension (expected .toy)\n");
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check for break-out attempts
|
||||
for (int i = 0; i < realLength - 1; i++) {
|
||||
if (filePath[i] == '.' && filePath[i + 1] == '.') {
|
||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
//use raw types - easier
|
||||
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
|
||||
int filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
|
||||
|
||||
//load and compile the bytecode
|
||||
size_t fileSize = 0;
|
||||
char* source = Toy_readFile(filePath, &fileSize);
|
||||
const char* source = Toy_readFile(filePath, &fileSize);
|
||||
|
||||
if (!source) {
|
||||
interpreter->errorOutput("Failed to load source file\n");
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* bytecode = Toy_compileString(source, &fileSize);
|
||||
const unsigned char* bytecode = Toy_compileString(source, &fileSize);
|
||||
free((void*)source);
|
||||
|
||||
if (!bytecode) {
|
||||
interpreter->errorOutput("Failed to compile source file\n");
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -124,7 +81,8 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
|
||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
//free the drive path
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -138,10 +96,16 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
|
||||
|
||||
//get the argument
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteralIdn);
|
||||
}
|
||||
|
||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
|
||||
|
||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||
int driveLength = 0;
|
||||
size_t driveLength = 0;
|
||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||
interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n");
|
||||
@@ -528,7 +492,7 @@ static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
char* name;
|
||||
const char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
@@ -552,7 +516,7 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
@@ -562,8 +526,7 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = TOY_LITERAL_FUNCTION_NATIVE;
|
||||
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
@@ -610,4 +573,74 @@ void Toy_freeDriveDictionary() {
|
||||
|
||||
Toy_LiteralDictionary* Toy_getDriveDictionary() {
|
||||
return &Toy_driveDictionary;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
|
||||
//check argument types
|
||||
if (!TOY_IS_STRING(*drivePathLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to Toy_getFilePathLiteral\n");
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
|
||||
|
||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||
size_t driveLength = 0;
|
||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||
interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n");
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
driveLength++;
|
||||
}
|
||||
|
||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||
|
||||
//get the real drive file path
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
|
||||
|
||||
if (!TOY_IS_STRING(realDriveLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\n");
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
|
||||
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
|
||||
|
||||
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
|
||||
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
|
||||
|
||||
//clean up the drivepath stuff
|
||||
Toy_deleteRefString(realDrive);
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
|
||||
//check for break-out attempts
|
||||
for (int i = 0; i < realLength - 1; i++) {
|
||||
if (filePath[i] == '.' && filePath[i + 1] == '.') {
|
||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||
TOY_FREE_ARRAY(char, filePath, realLength + 1);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(filePath, realLength));
|
||||
|
||||
TOY_FREE_ARRAY(char, filePath, realLength + 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -10,3 +10,6 @@ void Toy_freeDriveDictionary();
|
||||
Toy_LiteralDictionary* Toy_getDriveDictionary();
|
||||
|
||||
#define TOY_OPAQUE_TAG_RUNNER 100
|
||||
|
||||
//file system API - for use with other libs
|
||||
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
|
||||
|
||||
+2
-3
@@ -49,7 +49,7 @@ int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
@@ -59,8 +59,7 @@ int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = TOY_LITERAL_FUNCTION_NATIVE;
|
||||
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
|
||||
+2
-3
@@ -366,7 +366,7 @@ int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
@@ -376,8 +376,7 @@ int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = TOY_LITERAL_FUNCTION_NATIVE;
|
||||
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
|
||||
+31
-21
@@ -1,4 +1,6 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_about.h"
|
||||
#include "lib_compound.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_timer.h"
|
||||
#include "lib_runner.h"
|
||||
@@ -26,6 +28,8 @@ void repl() {
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
||||
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
@@ -49,6 +53,7 @@ void repl() {
|
||||
Toy_Compiler compiler;
|
||||
|
||||
Toy_initLexer(&lexer, input);
|
||||
Toy_private_setComments(&lexer, false); //BUGFIX: disable comments here
|
||||
Toy_initParser(&parser, &lexer);
|
||||
Toy_initCompiler(&compiler);
|
||||
|
||||
@@ -57,7 +62,9 @@ void repl() {
|
||||
while(node != NULL) {
|
||||
//pack up and restart
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
printf(TOY_CC_ERROR "error node detected\n" TOY_CC_RESET);
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf(TOY_CC_ERROR "Error node detected\n" TOY_CC_RESET);
|
||||
}
|
||||
error = true;
|
||||
Toy_freeASTNode(node);
|
||||
break;
|
||||
@@ -88,7 +95,7 @@ void repl() {
|
||||
|
||||
//entry point
|
||||
int main(int argc, const char* argv[]) {
|
||||
Toy_initCommand(argc, argv);
|
||||
Toy_initCommandLine(argc, argv);
|
||||
|
||||
//lib setup (hacky - only really for this program)
|
||||
Toy_initDriveDictionary();
|
||||
@@ -101,30 +108,30 @@ int main(int argc, const char* argv[]) {
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
|
||||
//command specific actions
|
||||
if (command.error) {
|
||||
Toy_usageCommand(argc, argv);
|
||||
//command line specific actions
|
||||
if (Toy_commandLine.error) {
|
||||
Toy_usageCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.help) {
|
||||
Toy_helpCommand(argc, argv);
|
||||
if (Toy_commandLine.help) {
|
||||
Toy_helpCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.version) {
|
||||
Toy_copyrightCommand(argc, argv);
|
||||
if (Toy_commandLine.version) {
|
||||
Toy_copyrightCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//version
|
||||
if (command.verbose) {
|
||||
printf(TOY_CC_NOTICE "Toy Programming Language Version %d.%d.%d\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf(TOY_CC_NOTICE "Toy Programming Language Version %d.%d.%d, built '%s'\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||
}
|
||||
|
||||
//run source file
|
||||
if (command.sourcefile) {
|
||||
Toy_runSourceFile(command.sourcefile);
|
||||
if (Toy_commandLine.sourcefile) {
|
||||
Toy_runSourceFile(Toy_commandLine.sourcefile);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
@@ -133,8 +140,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
|
||||
//run from stdin
|
||||
if (command.source) {
|
||||
Toy_runSource(command.source);
|
||||
if (Toy_commandLine.source) {
|
||||
Toy_runSource(Toy_commandLine.source);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
@@ -143,20 +150,23 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
|
||||
//compile source file
|
||||
if (command.compilefile && command.outfile) {
|
||||
if (Toy_commandLine.compilefile && Toy_commandLine.outfile) {
|
||||
size_t size = 0;
|
||||
char* source = Toy_readFile(command.compilefile, &size);
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
const char* source = Toy_readFile(Toy_commandLine.compilefile, &size);
|
||||
if (!source) {
|
||||
return 1;
|
||||
}
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return 1;
|
||||
}
|
||||
Toy_writeFile(command.outfile, tb, size);
|
||||
Toy_writeFile(Toy_commandLine.outfile, tb, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//run binary
|
||||
if (command.binaryfile) {
|
||||
Toy_runBinaryFile(command.binaryfile);
|
||||
if (Toy_commandLine.binaryfile) {
|
||||
Toy_runBinaryFile(Toy_commandLine.binaryfile);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
|
||||
+18
-11
@@ -1,4 +1,6 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_about.h"
|
||||
#include "lib_compound.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_timer.h"
|
||||
#include "lib_runner.h"
|
||||
@@ -14,7 +16,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
//IO functions
|
||||
char* Toy_readFile(char* path, size_t* fileSize) {
|
||||
const char* Toy_readFile(const char* path, size_t* fileSize) {
|
||||
FILE* file = fopen(path, "rb");
|
||||
|
||||
if (file == NULL) {
|
||||
@@ -47,7 +49,7 @@ char* Toy_readFile(char* path, size_t* fileSize) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int Toy_writeFile(char* path, unsigned char* bytes, size_t size) {
|
||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
|
||||
FILE* file = fopen(path, "wb");
|
||||
|
||||
if (file == NULL) {
|
||||
@@ -68,7 +70,7 @@ int Toy_writeFile(char* path, unsigned char* bytes, size_t size) {
|
||||
}
|
||||
|
||||
//repl functions
|
||||
unsigned char* Toy_compileString(char* source, size_t* size) {
|
||||
const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler compiler;
|
||||
@@ -94,7 +96,7 @@ unsigned char* Toy_compileString(char* source, size_t* size) {
|
||||
}
|
||||
|
||||
//get the bytecode dump
|
||||
unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
|
||||
const unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
|
||||
|
||||
//cleanup
|
||||
Toy_freeCompiler(&compiler);
|
||||
@@ -105,11 +107,13 @@ unsigned char* Toy_compileString(char* source, size_t* size) {
|
||||
return tb;
|
||||
}
|
||||
|
||||
void Toy_runBinary(unsigned char* tb, size_t size) {
|
||||
void Toy_runBinary(const unsigned char* tb, size_t size) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
||||
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
@@ -118,9 +122,9 @@ void Toy_runBinary(unsigned char* tb, size_t size) {
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
void Toy_runBinaryFile(char* fname) {
|
||||
void Toy_runBinaryFile(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
unsigned char* tb = (unsigned char*)Toy_readFile(fname, &size);
|
||||
const unsigned char* tb = (const unsigned char*)Toy_readFile(fname, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
@@ -128,9 +132,9 @@ void Toy_runBinaryFile(char* fname) {
|
||||
//interpreter takes ownership of the binary data
|
||||
}
|
||||
|
||||
void Toy_runSource(char* source) {
|
||||
void Toy_runSource(const char* source) {
|
||||
size_t size = 0;
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
@@ -138,9 +142,12 @@ void Toy_runSource(char* source) {
|
||||
Toy_runBinary(tb, size);
|
||||
}
|
||||
|
||||
void Toy_runSourceFile(char* fname) {
|
||||
void Toy_runSourceFile(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
char* source = Toy_readFile(fname, &size);
|
||||
const char* source = Toy_readFile(fname, &size);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
Toy_runSource(source);
|
||||
free((void*)source);
|
||||
}
|
||||
|
||||
+7
-7
@@ -2,13 +2,13 @@
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
char* Toy_readFile(char* path, size_t* fileSize);
|
||||
int Toy_writeFile(char* path, unsigned char* bytes, size_t size);
|
||||
const char* Toy_readFile(const char* path, size_t* fileSize);
|
||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
|
||||
|
||||
unsigned char* Toy_compileString(char* source, size_t* size);
|
||||
const unsigned char* Toy_compileString(const char* source, size_t* size);
|
||||
|
||||
void Toy_runBinary(unsigned char* tb, size_t size);
|
||||
void Toy_runBinaryFile(char* fname);
|
||||
void Toy_runSource(char* source);
|
||||
void Toy_runSourceFile(char* fname);
|
||||
void Toy_runBinary(const unsigned char* tb, size_t size);
|
||||
void Toy_runBinaryFile(const char* fname);
|
||||
void Toy_runSource(const char* source);
|
||||
void Toy_runSourceFile(const char* fname);
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import node;
|
||||
|
||||
//constants
|
||||
var SPEED: int const = 10;
|
||||
|
||||
//variables
|
||||
var parent: opaque = null;
|
||||
var posX: int = 50;
|
||||
var posY: int = 50;
|
||||
var WIDTH: int const = 100;
|
||||
var HEIGHT: int const = 100;
|
||||
|
||||
var xspeed: int = 0;
|
||||
var yspeed: int = 0;
|
||||
|
||||
//accessors - variables are private, functions are public
|
||||
fn getX(node: opaque) {
|
||||
return posX;
|
||||
}
|
||||
|
||||
fn getY(node: opaque) {
|
||||
return posY;
|
||||
}
|
||||
|
||||
//lifecycle functions
|
||||
fn onInit(node: opaque) {
|
||||
print "render.toy:onInit() called\n";
|
||||
|
||||
node.loadTexture("sprites:/character.png");
|
||||
parent = node.getNodeParent();
|
||||
}
|
||||
|
||||
fn onStep(node: opaque) {
|
||||
posX += xspeed;
|
||||
posY += yspeed;
|
||||
}
|
||||
|
||||
fn onFree(node: opaque) {
|
||||
print "render.toy:onFree() called\n";
|
||||
|
||||
node.freeTexture();
|
||||
}
|
||||
|
||||
fn onDraw(node: opaque) {
|
||||
// print "render.toy:onDraw() called\n";
|
||||
|
||||
var px = parent.callNode("getX");
|
||||
var py = parent.callNode("getY");
|
||||
|
||||
if (px == null) {
|
||||
px = 0;
|
||||
}
|
||||
|
||||
if (py == null) {
|
||||
py = 0;
|
||||
}
|
||||
|
||||
node.drawNode(posX + px, posY + py, WIDTH, HEIGHT);
|
||||
}
|
||||
|
||||
//event functions
|
||||
fn onKeyDown(node: opaque, event: string) {
|
||||
if (event == "character_up") {
|
||||
yspeed -= SPEED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == "character_down") {
|
||||
yspeed += SPEED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == "character_left") {
|
||||
xspeed -= SPEED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == "character_right") {
|
||||
xspeed += SPEED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn onKeyUp(node: opaque, event: string) {
|
||||
if (event == "character_up" && yspeed < 0) {
|
||||
yspeed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == "character_down" && yspeed > 0) {
|
||||
yspeed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == "character_left" && xspeed < 0) {
|
||||
xspeed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == "character_right" && xspeed > 0) {
|
||||
xspeed = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn onMouseMotion(node: opaque, x: int, y: int, xrel: int, yrel: int) {
|
||||
// print "entity.toy:onMouseMotion(" + string x + ", " + string y + ", " + string xrel + ", " + string yrel + ")\n";
|
||||
}
|
||||
|
||||
fn onMouseButtonDown(node: opaque, x: int, y: int, button: string) {
|
||||
// print "entity.toy:onMouseButtonDown(" + string x + ", " + string y + ", " + button + ")\n";
|
||||
|
||||
//jump to pos
|
||||
posX = x - WIDTH / 2;
|
||||
posY = y - HEIGHT / 2;
|
||||
}
|
||||
|
||||
fn onMouseButtonUp(node: opaque, x: int, y: int, button: string) {
|
||||
// print "entity.toy:onMouseButtonUp(" + string x + ", " + string y + ", " + button + ")\n";
|
||||
}
|
||||
|
||||
fn onMouseWheel(node: opaque, xrel: int, yrel: int) {
|
||||
// print "entity.toy:onMouseWheel(" + string xrel + ", " + string yrel + ")\n";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//memoize the fib function
|
||||
var memo: [int : int] = [:];
|
||||
|
||||
fn fib(n : int) {
|
||||
if (n < 2) {
|
||||
return n;
|
||||
}
|
||||
|
||||
var result = memo[n];
|
||||
if (result == null) {
|
||||
result = fib(n-1) + fib(n-2);
|
||||
memo[n] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 40; i++) {
|
||||
var res = fib(i);
|
||||
print string i + ": " + string res + "\n";
|
||||
}
|
||||
+3
-6
@@ -1,12 +1,9 @@
|
||||
fn fib(n : int) {
|
||||
if (n < 2) {
|
||||
return n;
|
||||
}
|
||||
|
||||
return fib(n-1) + fib(n-2);
|
||||
if (n < 2) return n;
|
||||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 20; i++) {
|
||||
var res = fib(i);
|
||||
print string i + ": " + string res;
|
||||
print string i + ": " + string res + "\n";
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//constants
|
||||
var WIDTH: int const = 10;
|
||||
var HEIGHT: int const = 10;
|
||||
|
||||
//WIDTH * HEIGHT in size
|
||||
var tiles: [[int]] const = [
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
];
|
||||
|
||||
var tileset: [int: string] const = [
|
||||
0: " ",
|
||||
1: " X "
|
||||
];
|
||||
|
||||
//variables
|
||||
var posX: int = 5;
|
||||
var posY: int = 5;
|
||||
|
||||
//functions
|
||||
fn draw() {
|
||||
for (var j: int = 0; j < HEIGHT; j++) {
|
||||
for (var i: int = 0; i < WIDTH; i++) {
|
||||
//draw the player pos
|
||||
if (i == posX && j == posY) {
|
||||
print " O ";
|
||||
continue;
|
||||
}
|
||||
|
||||
print tileset[ tiles[i][j] ];
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
fn move(xrel: int, yrel: int) {
|
||||
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1 || (xrel != 0 && yrel != 0)) {
|
||||
print "too fast!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (tiles[posX + xrel][posY + yrel] > 0) {
|
||||
print "Can't move that way\n";
|
||||
return;
|
||||
}
|
||||
|
||||
posX += xrel;
|
||||
posY += yrel;
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
//number of iterations
|
||||
var SIZE: int const = 100;
|
||||
var SIZE: int const = 260;
|
||||
|
||||
//lookup table
|
||||
var lookup = [
|
||||
@@ -29,10 +29,10 @@ for (var i = 0; i < SIZE -1; i++) {
|
||||
prev += " ";
|
||||
}
|
||||
prev += "*"; //initial
|
||||
print prev;
|
||||
print prev + "\n";
|
||||
|
||||
//run
|
||||
for (var iteration = 0; iteration < 100; iteration++) {
|
||||
for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
||||
//left
|
||||
var output = (lookup[" "][prev[0]][prev[1]]);
|
||||
|
||||
@@ -44,6 +44,6 @@ for (var iteration = 0; iteration < 100; iteration++) {
|
||||
//right
|
||||
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
||||
|
||||
print output;
|
||||
print output + "\n";
|
||||
prev = output;
|
||||
}
|
||||
|
||||
+9
-20
@@ -1,21 +1,10 @@
|
||||
//test basic truth ternaries
|
||||
{
|
||||
assert true ? true : false, "Basic true ternary failed";
|
||||
assert false ? false : true, "Basic false ternary failed";
|
||||
|
||||
var xrel: int = 0;
|
||||
var yrel: int = 0;
|
||||
|
||||
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1) {
|
||||
print "outside";
|
||||
}
|
||||
|
||||
|
||||
//test nesting
|
||||
{
|
||||
fn least(a, b, c) {
|
||||
return a < b ? a : b < c ? b : c;
|
||||
}
|
||||
|
||||
assert least(1, 2, 3) == 1, "Least 1, 2, 3 failed";
|
||||
assert least(10, 5, 7) == 5, "Least 10, 5, 7 failed";
|
||||
assert least(9, 7, 5) == 5, "Least 9, 7, 5 failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
else {
|
||||
print "inside";
|
||||
}
|
||||
+279
-153
@@ -249,7 +249,7 @@ static Toy_Literal modulo(Toy_Interpreter* interpreter, Toy_Literal lhs, Toy_Lit
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//_index(compound, first, second, third, assignValue, op)
|
||||
Toy_Literal op = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal assign = Toy_popLiteralArray(arguments);
|
||||
@@ -258,8 +258,6 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_Literal first = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal compound = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal value = TOY_TO_NULL_LITERAL;
|
||||
|
||||
//dictionary - no slicing
|
||||
if (TOY_IS_DICTIONARY(compound)) {
|
||||
if (TOY_IS_IDENTIFIER(first)) {
|
||||
@@ -280,7 +278,22 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(idn);
|
||||
}
|
||||
|
||||
value = Toy_getLiteralDictionary(TOY_AS_DICTIONARY(compound), first);
|
||||
//second and third are bad args to dictionaries
|
||||
if (!TOY_IS_NULL(second) || !TOY_IS_NULL(third)) {
|
||||
interpreter->errorOutput("Index slicing not allowed for dictionaries\n");
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the value
|
||||
Toy_Literal value = Toy_getLiteralDictionary(TOY_AS_DICTIONARY(compound), first);
|
||||
|
||||
//dictionary
|
||||
if (TOY_IS_NULL(op)) {
|
||||
@@ -330,6 +343,19 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_setLiteralDictionary(TOY_AS_DICTIONARY(compound), first, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
//leave the dictionary on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//array - slicing
|
||||
@@ -374,8 +400,10 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(idn);
|
||||
}
|
||||
|
||||
//handle each null case
|
||||
if (TOY_IS_NULL(first) || !TOY_IS_INTEGER(first)) {
|
||||
//handle each error case
|
||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
|
||||
interpreter->errorOutput("Bad first indexing\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -383,17 +411,42 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_IS_NULL(second)) { //assign only a single character
|
||||
//get the "first" within the array, then skip out
|
||||
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= TOY_AS_ARRAY(compound)->count) {
|
||||
interpreter->errorOutput("Bad second indexing\n");
|
||||
|
||||
Toy_freeLiteral(value);
|
||||
value = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
|
||||
Toy_pushLiteralArray(&interpreter->stack, value);
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(third) == 0) {
|
||||
interpreter->errorOutput("Bad third indexing\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//simple indexing if second is null
|
||||
if (TOY_IS_NULL(second)) {
|
||||
Toy_Literal result = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -401,43 +454,52 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
Toy_freeLiteral(result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!TOY_IS_INTEGER(second) || (!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) > TOY_AS_ARRAY(compound)->count || TOY_AS_INTEGER(third) == 0) {
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//start building a new array from the old one
|
||||
Toy_LiteralArray* result = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(result);
|
||||
|
||||
int min = TOY_AS_INTEGER(third) > 0 ? TOY_AS_INTEGER(first) : TOY_AS_INTEGER(second);
|
||||
|
||||
//copy compound into result
|
||||
for (int i = min; i >= 0 && i <= TOY_AS_ARRAY(compound)->count && i >= TOY_AS_INTEGER(first) && i <= TOY_AS_INTEGER(second); i += TOY_AS_INTEGER(third)) {
|
||||
Toy_Literal idx = TOY_TO_INTEGER_LITERAL(i);
|
||||
Toy_Literal tmp = Toy_getLiteralArray(TOY_AS_ARRAY(compound), idx);
|
||||
Toy_pushLiteralArray(result, tmp);
|
||||
if (TOY_AS_INTEGER(third) > 0) {
|
||||
for (int i = TOY_AS_INTEGER(first); i <= TOY_AS_INTEGER(second); i += TOY_AS_INTEGER(third)) {
|
||||
Toy_Literal idx = TOY_TO_INTEGER_LITERAL(i);
|
||||
Toy_Literal tmp = Toy_getLiteralArray(TOY_AS_ARRAY(compound), idx);
|
||||
Toy_pushLiteralArray(result, tmp);
|
||||
|
||||
Toy_freeLiteral(idx);
|
||||
Toy_freeLiteral(tmp);
|
||||
Toy_freeLiteral(idx);
|
||||
Toy_freeLiteral(tmp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = TOY_AS_INTEGER(second); i >= TOY_AS_INTEGER(first); i += TOY_AS_INTEGER(third)) {
|
||||
Toy_Literal idx = TOY_TO_INTEGER_LITERAL(i);
|
||||
Toy_Literal tmp = Toy_getLiteralArray(TOY_AS_ARRAY(compound), idx);
|
||||
Toy_pushLiteralArray(result, tmp);
|
||||
|
||||
Toy_freeLiteral(idx);
|
||||
Toy_freeLiteral(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//finally, swap out the compound for the result
|
||||
Toy_freeLiteral(compound);
|
||||
compound = TOY_TO_ARRAY_LITERAL(result);
|
||||
|
||||
//leave the array on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//array slice assignment
|
||||
@@ -480,8 +542,10 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(idn);
|
||||
}
|
||||
|
||||
//handle each null case
|
||||
if (TOY_IS_NULL(first) || !TOY_IS_INTEGER(first)) {
|
||||
//handle each error case
|
||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
|
||||
interpreter->errorOutput("Bad first indexing assignment\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -489,28 +553,50 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= TOY_AS_ARRAY(compound)->count) {
|
||||
interpreter->errorOutput("Bad second indexing assignment\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(third) == 0) {
|
||||
interpreter->errorOutput("Bad third indexing assignment\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//simple indexing assignment if second is null
|
||||
if (TOY_IS_NULL(second)) {
|
||||
//set the "first" within the array, then skip out
|
||||
bool ret = -1;
|
||||
|
||||
if (!Toy_setLiteralArray(TOY_AS_ARRAY(compound), first, assign)) {
|
||||
interpreter->errorOutput("Index assignment out of bounds\n");
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
interpreter->errorOutput("Array index out of bounds in assignment");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
else {
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound); //leave the array on the stack
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -518,22 +604,8 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!TOY_IS_INTEGER(second) || (!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) > TOY_AS_ARRAY(compound)->count || TOY_AS_INTEGER(third) == 0) {
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//start building a new array from the old one
|
||||
@@ -611,15 +683,28 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//finally, swap out the compound for the result
|
||||
Toy_freeLiteral(compound);
|
||||
compound = TOY_TO_ARRAY_LITERAL(result);
|
||||
|
||||
//leave the array on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//assignment and other operations
|
||||
if (TOY_IS_IDENTIFIER(first)) {
|
||||
Toy_Literal idn = first;
|
||||
Toy_parseIdentifierToValue(interpreter, &first);
|
||||
Toy_freeLiteral(idn);
|
||||
}
|
||||
|
||||
value = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
|
||||
Toy_Literal value = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
|
||||
|
||||
if (TOY_IS_STRING(op) && Toy_equalsRefStringCString(TOY_AS_STRING(op), "+=")) {
|
||||
Toy_Literal lit = addition(interpreter, value, assign);
|
||||
@@ -650,6 +735,18 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_setLiteralArray(TOY_AS_ARRAY(compound), first, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
//leave the array on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//string - slicing
|
||||
@@ -674,7 +771,7 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
if (!TOY_IS_NULL(second)) {
|
||||
if (TOY_IS_INDEX_BLANK(second)) {
|
||||
Toy_freeLiteral(second);
|
||||
second = TOY_TO_INTEGER_LITERAL(compoundLength);
|
||||
second = TOY_TO_INTEGER_LITERAL(compoundLength - 1);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(second)) {
|
||||
@@ -695,8 +792,10 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(idn);
|
||||
}
|
||||
|
||||
//handle each null case
|
||||
if (TOY_IS_NULL(first) || !TOY_IS_INTEGER(first)) {
|
||||
//handle each error case
|
||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
||||
interpreter->errorOutput("Bad first indexing in string\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -704,22 +803,48 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_IS_NULL(second)) { //assign only a single character
|
||||
char c = Toy_toCString(TOY_AS_STRING(compound))[TOY_AS_INTEGER(first)];
|
||||
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
||||
interpreter->errorOutput("Bad second indexing in string\n");
|
||||
|
||||
char buffer[16];
|
||||
snprintf(buffer, 16, "%c", c);
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
Toy_freeLiteral(value);
|
||||
int totalLength = strlen(buffer);
|
||||
value = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, totalLength));
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, value);
|
||||
if ((!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(third) == 0) {
|
||||
interpreter->errorOutput("Bad third indexing in string\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//simple indexing if second is null
|
||||
if (TOY_IS_NULL(second)) {
|
||||
|
||||
const char* cstr = Toy_toCString(TOY_AS_STRING(compound));
|
||||
char buf[16];
|
||||
|
||||
snprintf(buf, 16, "%s", &(cstr[ TOY_AS_INTEGER(first) ]) );
|
||||
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buf, 1));
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -727,35 +852,26 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
Toy_freeLiteral(result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!TOY_IS_INTEGER(second) || (!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) > compoundLength || TOY_AS_INTEGER(third) == 0) {
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//start building a new string from the old one
|
||||
char* result = TOY_ALLOCATE(char, TOY_MAX_STRING_LENGTH);
|
||||
|
||||
int lower = TOY_AS_INTEGER(third) > 0 ? TOY_AS_INTEGER(first) : TOY_AS_INTEGER(first) -1;
|
||||
int min = TOY_AS_INTEGER(third) > 0 ? TOY_AS_INTEGER(first) : TOY_AS_INTEGER(second) -1;
|
||||
int max = TOY_AS_INTEGER(third) > 0 ? TOY_AS_INTEGER(second) + (TOY_AS_INTEGER(second) == compoundLength ? -1 : 0) : TOY_AS_INTEGER(second);
|
||||
|
||||
//copy compound into result
|
||||
int resultIndex = 0;
|
||||
for (int i = min; i >= 0 && i >= lower && i <= max; i += TOY_AS_INTEGER(third)) {
|
||||
result[ resultIndex++ ] = Toy_toCString(TOY_AS_STRING(compound))[ i ];
|
||||
|
||||
if (TOY_AS_INTEGER(third) > 0) {
|
||||
for (int i = TOY_AS_INTEGER(first); i <= TOY_AS_INTEGER(second); i += TOY_AS_INTEGER(third)) {
|
||||
result[ resultIndex++ ] = Toy_toCString(TOY_AS_STRING(compound))[ i ];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = TOY_AS_INTEGER(second); i >= TOY_AS_INTEGER(first); i += TOY_AS_INTEGER(third)) {
|
||||
result[ resultIndex++ ] = Toy_toCString(TOY_AS_STRING(compound))[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
result[ resultIndex ] = '\0';
|
||||
@@ -765,6 +881,18 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
compound = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(result, resultIndex));
|
||||
|
||||
TOY_FREE_ARRAY(char, result, TOY_MAX_STRING_LENGTH);
|
||||
|
||||
//leave the string on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//string slice assignment
|
||||
@@ -787,7 +915,7 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
if (!TOY_IS_NULL(second)) {
|
||||
if (TOY_IS_INDEX_BLANK(second)) {
|
||||
Toy_freeLiteral(second);
|
||||
second = TOY_TO_INTEGER_LITERAL(compoundLength);
|
||||
second = TOY_TO_INTEGER_LITERAL(compoundLength - 1);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(second)) {
|
||||
@@ -808,8 +936,10 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(idn);
|
||||
}
|
||||
|
||||
//handle each null case
|
||||
if (TOY_IS_NULL(first) || !TOY_IS_INTEGER(first)) {
|
||||
//handle each error case
|
||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
||||
interpreter->errorOutput("Bad first indexing in string assignment\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -817,44 +947,27 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_IS_NULL(second)) { //assign only a single character
|
||||
//set the "first" within the array, then skip out
|
||||
if (TOY_AS_STRING(assign)->length != 1) {
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
||||
interpreter->errorOutput("Bad second indexing in string assignment\n");
|
||||
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(third) == 0) {
|
||||
interpreter->errorOutput("Bad third indexing in string assignment\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal copiedCompound = TOY_TO_STRING_LITERAL(Toy_deepCopyRefString(TOY_AS_STRING(compound)));
|
||||
|
||||
TOY_AS_STRING(copiedCompound)->data[TOY_AS_INTEGER(first)] = Toy_toCString(TOY_AS_STRING(assign))[0];
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, copiedCompound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!TOY_IS_INTEGER(second) || (!TOY_IS_NULL(third) && !TOY_IS_INTEGER(third)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) > compoundLength || TOY_AS_INTEGER(third) == 0) {
|
||||
//something is weird - skip out
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
@@ -862,7 +975,6 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -911,6 +1023,19 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
compound = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(result, resultIndex));
|
||||
|
||||
TOY_FREE_ARRAY(char, result, TOY_MAX_STRING_LENGTH);
|
||||
|
||||
//leave the string on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
else if (TOY_IS_STRING(op) && Toy_equalsRefStringCString(TOY_AS_STRING(op), "+=")) {
|
||||
@@ -918,23 +1043,24 @@ int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
Toy_freeLiteral(compound);
|
||||
compound = tmp; //don't clear tmp
|
||||
}
|
||||
|
||||
//leave the string on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//leave the compound on the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, compound);
|
||||
|
||||
Toy_freeLiteral(op);
|
||||
Toy_freeLiteral(assign);
|
||||
Toy_freeLiteral(third);
|
||||
Toy_freeLiteral(second);
|
||||
Toy_freeLiteral(first);
|
||||
Toy_freeLiteral(compound);
|
||||
Toy_freeLiteral(value);
|
||||
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 3) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _set\n");
|
||||
@@ -1052,7 +1178,7 @@ int _set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _get");
|
||||
@@ -1123,7 +1249,7 @@ int _get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
}
|
||||
}
|
||||
|
||||
int _push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _push\n");
|
||||
@@ -1186,7 +1312,7 @@ int _push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
}
|
||||
}
|
||||
|
||||
int _pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _pop\n");
|
||||
@@ -1229,7 +1355,7 @@ int _pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
}
|
||||
}
|
||||
|
||||
int _length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _length\n");
|
||||
@@ -1280,7 +1406,7 @@ int _length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _clear\n");
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
//the _index function is a historical oddity - it's used whenever a compound is indexed
|
||||
int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
|
||||
//globally available native functions
|
||||
int _set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
|
||||
+33
-33
@@ -18,74 +18,74 @@ STATIC_ASSERT(sizeof(unsigned int) == 4);
|
||||
#ifndef TOY_EXPORT
|
||||
|
||||
//declare the singleton
|
||||
Command command;
|
||||
Toy_CommandLine Toy_commandLine;
|
||||
|
||||
void Toy_initCommand(int argc, const char* argv[]) {
|
||||
void Toy_initCommandLine(int argc, const char* argv[]) {
|
||||
//default values
|
||||
command.error = false;
|
||||
command.help = false;
|
||||
command.version = false;
|
||||
command.binaryfile = NULL;
|
||||
command.sourcefile = NULL;
|
||||
command.compilefile = NULL;
|
||||
command.outfile = "out.tb";
|
||||
command.source = NULL;
|
||||
command.verbose = false;
|
||||
Toy_commandLine.error = false;
|
||||
Toy_commandLine.help = false;
|
||||
Toy_commandLine.version = false;
|
||||
Toy_commandLine.binaryfile = NULL;
|
||||
Toy_commandLine.sourcefile = NULL;
|
||||
Toy_commandLine.compilefile = NULL;
|
||||
Toy_commandLine.outfile = "out.tb";
|
||||
Toy_commandLine.source = NULL;
|
||||
Toy_commandLine.verbose = false;
|
||||
|
||||
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
||||
command.error = true; //error state by default, set to false by successful flags
|
||||
Toy_commandLine.error = true; //error state by default, set to false by successful flags
|
||||
|
||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||
command.help = true;
|
||||
command.error = false;
|
||||
Toy_commandLine.help = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
||||
command.version = true;
|
||||
command.error = false;
|
||||
Toy_commandLine.version = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
||||
command.verbose = true;
|
||||
command.error = false;
|
||||
Toy_commandLine.verbose = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
|
||||
command.sourcefile = (char*)argv[i + 1];
|
||||
Toy_commandLine.sourcefile = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
||||
command.source = (char*)argv[i + 1];
|
||||
Toy_commandLine.source = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
|
||||
command.compilefile = (char*)argv[i + 1];
|
||||
Toy_commandLine.compilefile = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
|
||||
command.outfile = (char*)argv[i + 1];
|
||||
Toy_commandLine.outfile = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
//option without a flag + ending in .tb = binary input
|
||||
if (i < argc) {
|
||||
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
|
||||
command.binaryfile = (char*)argv[i];
|
||||
command.error = false;
|
||||
Toy_commandLine.binaryfile = (char*)argv[i];
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -95,12 +95,12 @@ void Toy_initCommand(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_usageCommand(int argc, const char* argv[]) {
|
||||
void Toy_usageCommandLine(int argc, const char* argv[]) {
|
||||
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
|
||||
}
|
||||
|
||||
void Toy_helpCommand(int argc, const char* argv[]) {
|
||||
Toy_usageCommand(argc, argv);
|
||||
void Toy_helpCommandLine(int argc, const char* argv[]) {
|
||||
Toy_usageCommandLine(argc, argv);
|
||||
|
||||
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||
printf("-h\t| --help\t\tShow this help then exit.\n\n");
|
||||
@@ -112,9 +112,9 @@ void Toy_helpCommand(int argc, const char* argv[]) {
|
||||
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
|
||||
}
|
||||
|
||||
void Toy_copyrightCommand(int argc, const char* argv[]) {
|
||||
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
|
||||
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
|
||||
printf("Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios\n\n");
|
||||
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
|
||||
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
|
||||
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
|
||||
|
||||
+7
-7
@@ -6,7 +6,7 @@
|
||||
|
||||
#define TOY_VERSION_MAJOR 0
|
||||
#define TOY_VERSION_MINOR 8
|
||||
#define TOY_VERSION_PATCH 0
|
||||
#define TOY_VERSION_PATCH 2
|
||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||
|
||||
//platform-specific specifications
|
||||
@@ -33,13 +33,13 @@ typedef struct {
|
||||
char* outfile; //defaults to out.tb
|
||||
char* source;
|
||||
bool verbose;
|
||||
} Command;
|
||||
} Toy_CommandLine;
|
||||
|
||||
extern Command command;
|
||||
extern Toy_CommandLine Toy_commandLine;
|
||||
|
||||
void Toy_initCommand(int argc, const char* argv[]);
|
||||
void Toy_initCommandLine(int argc, const char* argv[]);
|
||||
|
||||
void Toy_usageCommand(int argc, const char* argv[]);
|
||||
void Toy_helpCommand(int argc, const char* argv[]);
|
||||
void Toy_copyrightCommand(int argc, const char* argv[]);
|
||||
void Toy_usageCommandLine(int argc, const char* argv[]);
|
||||
void Toy_helpCommandLine(int argc, const char* argv[]);
|
||||
void Toy_copyrightCommandLine(int argc, const char* argv[]);
|
||||
#endif
|
||||
|
||||
+66
-11
@@ -15,6 +15,7 @@ void Toy_initCompiler(Toy_Compiler* compiler) {
|
||||
compiler->bytecode = NULL;
|
||||
compiler->capacity = 0;
|
||||
compiler->count = 0;
|
||||
compiler->panic = false;
|
||||
}
|
||||
|
||||
//separated out, so it can be recursive
|
||||
@@ -129,10 +130,11 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
|
||||
|
||||
//push the store to the cache, with instructions about how pack it
|
||||
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL(store);
|
||||
literal.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE; //god damn it
|
||||
literal.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE; //god damn it - nested in a dictionary
|
||||
index = Toy_pushLiteralArray(&compiler->literalCache, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
else if (node->compound.literalType == TOY_LITERAL_ARRAY) {
|
||||
//ensure each literal value is in the cache, individually
|
||||
for (int i = 0; i < node->compound.count; i++) {
|
||||
@@ -160,18 +162,20 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized node type in writeNodeCompoundToCache()" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized node type in writeNodeCompoundToCache()\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//push the store to the cache, with instructions about how pack it
|
||||
Toy_Literal literal = TOY_TO_ARRAY_LITERAL(store);
|
||||
literal.type = TOY_LITERAL_ARRAY_INTERMEDIATE; //god damn it - nested in an array
|
||||
index = Toy_pushLiteralArray(&compiler->literalCache, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized compound type in writeNodeCompoundToCache()" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized compound type in writeNodeCompoundToCache()\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return index;
|
||||
@@ -311,7 +315,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
//return this if...
|
||||
Toy_Opcode ret = Toy_writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
||||
|
||||
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && rootNode->binary.opcode == TOY_OP_VAR_ASSIGN) { //why var assign?
|
||||
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && (rootNode->binary.opcode >= TOY_OP_VAR_ASSIGN && rootNode->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN)) { //range-based check for assignment type
|
||||
return TOY_OP_INDEX_ASSIGN_INTERMEDIATE;
|
||||
}
|
||||
|
||||
@@ -332,7 +336,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_TERNARY: {
|
||||
// TODO
|
||||
// TODO: a ?: b;
|
||||
|
||||
//process the condition
|
||||
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, node->ternary.condition, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
||||
@@ -365,7 +369,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
//write the else path
|
||||
Toy_Opcode override2 = Toy_writeCompilerWithJumps(compiler, node->pathIf.elsePath, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
||||
if (override2 != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)override2; //1 byte
|
||||
}
|
||||
|
||||
//update the jumpToEnd to point here
|
||||
@@ -401,6 +405,11 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
case TOY_AST_NODE_COMPOUND: {
|
||||
int index = writeNodeCompoundToCache(compiler, node);
|
||||
|
||||
if (index < 0) {
|
||||
compiler->panic = true;
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
//push the node opcode to the bytecode
|
||||
if (index >= 256) {
|
||||
//push a "long" index
|
||||
@@ -468,6 +477,11 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||
}
|
||||
|
||||
//adopt the panic state if anything happened
|
||||
if (fnCompiler->panic) {
|
||||
compiler->panic = true;
|
||||
}
|
||||
|
||||
//create the function in the literal cache (by storing the compiler object)
|
||||
Toy_Literal fnLiteral = TOY_TO_FUNCTION_LITERAL(fnCompiler, 0);
|
||||
fnLiteral.type = TOY_LITERAL_FUNCTION_INTERMEDIATE; //NOTE: changing type
|
||||
@@ -504,6 +518,11 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
case TOY_AST_NODE_FN_COLLECTION: {
|
||||
//embed these in the bytecode...
|
||||
unsigned short index = (unsigned short)writeNodeCollectionToCache(compiler, node);
|
||||
|
||||
if (index == (unsigned short)-1) {
|
||||
compiler->panic = true;
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
memcpy(compiler->bytecode + compiler->count, &index, sizeof(index));
|
||||
compiler->count += sizeof(unsigned short);
|
||||
@@ -754,7 +773,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
|
||||
case TOY_AST_NODE_BREAK: {
|
||||
if (!breakAddressesPtr) {
|
||||
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Can't place a break statement here\n" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Can't place a break statement here\n" TOY_CC_RESET);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -772,7 +791,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
||||
|
||||
case TOY_AST_NODE_CONTINUE: {
|
||||
if (!continueAddressesPtr) {
|
||||
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Can't place a continue statement here\n" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Can't place a continue statement here\n" TOY_CC_RESET);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1007,6 +1026,11 @@ static void emitFloat(unsigned char** collationPtr, int* capacityPtr, int* count
|
||||
|
||||
//return the result
|
||||
static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size, bool embedHeader) {
|
||||
if (compiler->panic) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Can't collate a panicked compiler\n" TOY_CC_RESET);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int capacity = TOY_GROW_CAPACITY(0);
|
||||
int count = 0;
|
||||
unsigned char* collation = TOY_ALLOCATE(unsigned char, capacity);
|
||||
@@ -1072,7 +1096,7 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
|
||||
|
||||
Toy_Literal str = compiler->literalCache.literals[i];
|
||||
|
||||
for (int c = 0; c < TOY_AS_STRING(str)->length; c++) {
|
||||
for (int c = 0; c < (int)Toy_lengthRefString(TOY_AS_STRING(str)); c++) {
|
||||
emitByte(&collation, &capacity, &count, Toy_toCString(TOY_AS_STRING(str))[c]);
|
||||
}
|
||||
|
||||
@@ -1095,7 +1119,22 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
|
||||
case TOY_LITERAL_ARRAY_INTERMEDIATE: {
|
||||
emitByte(&collation, &capacity, &count, TOY_LITERAL_ARRAY_INTERMEDIATE);
|
||||
|
||||
Toy_LiteralArray* ptr = TOY_AS_ARRAY(compiler->literalCache.literals[i]);
|
||||
|
||||
//length of the array, as a short
|
||||
Toy_emitShort(&collation, &capacity, &count, ptr->count);
|
||||
|
||||
//each element of the array
|
||||
for (int i = 0; i < ptr->count; i++) {
|
||||
Toy_emitShort(&collation, &capacity, &count, (unsigned short)TOY_AS_INTEGER(ptr->literals[i])); //shorts representing the indexes of the values
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
emitByte(&collation, &capacity, &count, TOY_LITERAL_DICTIONARY);
|
||||
|
||||
Toy_LiteralArray* ptr = TOY_AS_ARRAY(compiler->literalCache.literals[i]); //used an array for storage above
|
||||
@@ -1110,6 +1149,21 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
|
||||
emitByte(&collation, &capacity, &count, TOY_LITERAL_DICTIONARY_INTERMEDIATE);
|
||||
|
||||
Toy_LiteralArray* ptr = TOY_AS_ARRAY(compiler->literalCache.literals[i]); //used an array for storage above
|
||||
|
||||
//length of the array, as a short
|
||||
Toy_emitShort(&collation, &capacity, &count, ptr->count); //count is the array size, NOT the dictionary size
|
||||
|
||||
//each element of the array
|
||||
for (int i = 0; i < ptr->count; i++) {
|
||||
Toy_emitShort(&collation, &capacity, &count, (unsigned short)TOY_AS_INTEGER(ptr->literals[i])); //shorts representing the indexes of the values
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE: {
|
||||
//extract the compiler
|
||||
Toy_Literal fn = compiler->literalCache.literals[i];
|
||||
@@ -1144,7 +1198,7 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
|
||||
|
||||
Toy_Literal identifier = compiler->literalCache.literals[i];
|
||||
|
||||
for (int c = 0; c < TOY_AS_IDENTIFIER(identifier)->length; c++) {
|
||||
for (int c = 0; c < (int)Toy_lengthRefString(TOY_AS_IDENTIFIER(identifier)); c++) {
|
||||
emitByte(&collation, &capacity, &count, Toy_toCString(TOY_AS_IDENTIFIER(identifier))[c]);
|
||||
}
|
||||
|
||||
@@ -1230,6 +1284,7 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
|
||||
return collation;
|
||||
}
|
||||
|
||||
//the whole point of the compiler is to alter bytecode, so leave it as non-const
|
||||
unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, int* size) {
|
||||
return collateCompilerHeaderOpt(compiler, size, true);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ typedef struct Toy_Compiler {
|
||||
unsigned char* bytecode;
|
||||
int capacity;
|
||||
int count;
|
||||
bool panic;
|
||||
} Toy_Compiler;
|
||||
|
||||
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
|
||||
|
||||
+234
-223
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ typedef void (*Toy_PrintFn)(const char*);
|
||||
//the interpreter acts depending on the bytecode instructions
|
||||
typedef struct Toy_Interpreter {
|
||||
//input
|
||||
unsigned char* bytecode;
|
||||
const unsigned char* bytecode;
|
||||
int length;
|
||||
int count;
|
||||
int codeStart; //BUGFIX: for jumps, must be initialized to -1
|
||||
@@ -34,14 +34,11 @@ typedef struct Toy_Interpreter {
|
||||
} Toy_Interpreter;
|
||||
|
||||
//native API
|
||||
typedef int (*Toy_NativeFn)(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, char* name, Toy_NativeFn func);
|
||||
|
||||
typedef int (*Toy_HookFn)(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, char* name, Toy_HookFn hook);
|
||||
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func);
|
||||
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook);
|
||||
|
||||
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||
|
||||
//utilities for the host program
|
||||
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
|
||||
@@ -51,6 +48,6 @@ TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn e
|
||||
|
||||
//main access
|
||||
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
|
||||
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
|
||||
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, int length); //run the code
|
||||
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
||||
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
|
||||
|
||||
+49
-15
@@ -12,6 +12,7 @@ static void cleanLexer(Toy_Lexer* lexer) {
|
||||
lexer->start = 0;
|
||||
lexer->current = 0;
|
||||
lexer->line = 1;
|
||||
lexer->commentsEnabled = true;
|
||||
}
|
||||
|
||||
static bool isAtEnd(Toy_Lexer* lexer) {
|
||||
@@ -54,9 +55,13 @@ static void eatWhitespace(Toy_Lexer* lexer) {
|
||||
|
||||
//comments
|
||||
case '/':
|
||||
if (!lexer->commentsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
//eat the line
|
||||
if (peekNext(lexer) == '/') {
|
||||
while (advance(lexer) != '\n' && !isAtEnd(lexer));
|
||||
while (!isAtEnd(lexer) && advance(lexer) != '\n');
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -64,7 +69,7 @@ static void eatWhitespace(Toy_Lexer* lexer) {
|
||||
if (peekNext(lexer) == '*') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
||||
while(!isAtEnd(lexer) && !(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
break;
|
||||
@@ -110,7 +115,7 @@ static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("err:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
@@ -129,7 +134,7 @@ static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
||||
if (command.verbose) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("tok:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
@@ -141,12 +146,12 @@ static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
|
||||
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
||||
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //what am I making?
|
||||
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||
|
||||
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
||||
type = TOY_TOKEN_LITERAL_FLOAT;
|
||||
advance(lexer);
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||
}
|
||||
|
||||
Toy_Token token;
|
||||
@@ -157,7 +162,7 @@ static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
if (type == TOY_TOKEN_LITERAL_INTEGER) {
|
||||
printf("int:");
|
||||
} else {
|
||||
@@ -170,13 +175,38 @@ static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
||||
return token;
|
||||
}
|
||||
|
||||
static bool isEscapableCharacter(char c) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
case 't':
|
||||
case '\\':
|
||||
case '"':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
||||
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
|
||||
while (!isAtEnd(lexer)) {
|
||||
//stop if you've hit the terminator
|
||||
if (peek(lexer) == terminator) {
|
||||
advance(lexer); //eat terminator
|
||||
break;
|
||||
}
|
||||
|
||||
//skip escaped control characters
|
||||
if (peek(lexer) == '\\' && isEscapableCharacter(peekNext(lexer))) {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
continue;
|
||||
}
|
||||
|
||||
//otherwise
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
advance(lexer); //eat terminator
|
||||
|
||||
if (isAtEnd(lexer)) {
|
||||
return makeErrorToken(lexer, "Unterminated string");
|
||||
}
|
||||
@@ -189,7 +219,7 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("str:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
@@ -216,7 +246,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("kwd:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
@@ -235,7 +265,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("idf:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
@@ -245,7 +275,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLexer(Toy_Lexer* lexer, char* source) {
|
||||
void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
|
||||
cleanLexer(lexer);
|
||||
|
||||
lexer->source = source;
|
||||
@@ -338,7 +368,7 @@ void Toy_printToken(Toy_Token* token) {
|
||||
if (keyword != NULL) {
|
||||
printf("%s", keyword);
|
||||
} else {
|
||||
char* str = token->lexeme;
|
||||
char* str = (char*)token->lexeme; //strip const-ness for trimming
|
||||
int length = token->length;
|
||||
trim(&str, &length);
|
||||
printf("%.*s", length, str);
|
||||
@@ -347,3 +377,7 @@ void Toy_printToken(Toy_Token* token) {
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled) {
|
||||
lexer->commentsEnabled = enabled;
|
||||
}
|
||||
+6
-3
@@ -5,22 +5,25 @@
|
||||
|
||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||
typedef struct {
|
||||
char* source;
|
||||
const char* source;
|
||||
int start; //start of the token
|
||||
int current; //current position of the lexer
|
||||
int line; //track this for error handling
|
||||
bool commentsEnabled; //BUGFIX: enable comments (disabled in repl)
|
||||
} Toy_Lexer;
|
||||
|
||||
//tokens are intermediaries between lexers and parsers
|
||||
typedef struct {
|
||||
Toy_TokenType type;
|
||||
char* lexeme;
|
||||
const char* lexeme;
|
||||
int length;
|
||||
int line;
|
||||
} Toy_Token;
|
||||
|
||||
TOY_API void Toy_initLexer(Toy_Lexer* lexer, char* source);
|
||||
TOY_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
|
||||
Toy_Token Toy_scanLexer(Toy_Lexer* lexer);
|
||||
|
||||
//for debugging
|
||||
void Toy_printToken(Toy_Token* token);
|
||||
|
||||
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
|
||||
|
||||
+27
-6
@@ -42,7 +42,7 @@ void Toy_freeLiteral(Toy_Literal literal) {
|
||||
}
|
||||
|
||||
//compounds
|
||||
if (TOY_IS_ARRAY(literal) || literal.type == TOY_LITERAL_DICTIONARY_INTERMEDIATE || literal.type == TOY_LITERAL_TYPE_INTERMEDIATE) {
|
||||
if (TOY_IS_ARRAY(literal) || literal.type == TOY_LITERAL_ARRAY_INTERMEDIATE || literal.type == TOY_LITERAL_DICTIONARY_INTERMEDIATE || literal.type == TOY_LITERAL_TYPE_INTERMEDIATE) {
|
||||
Toy_freeLiteralArray(TOY_AS_ARRAY(literal));
|
||||
TOY_FREE(Toy_LiteralArray, TOY_AS_ARRAY(literal));
|
||||
return;
|
||||
@@ -72,7 +72,7 @@ void Toy_freeLiteral(Toy_Literal literal) {
|
||||
|
||||
bool Toy_private_isTruthy(Toy_Literal x) {
|
||||
if (TOY_IS_NULL(x)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Null is neither true nor false\n" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "Null is neither true nor false\n" TOY_CC_RESET);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -172,6 +172,22 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
||||
return original; //literally a shallow copy
|
||||
}
|
||||
|
||||
case TOY_LITERAL_ARRAY_INTERMEDIATE: {
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_ARRAY_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
@@ -206,12 +222,13 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
case TOY_LITERAL_INDEX_BLANK:
|
||||
//no copying possible
|
||||
return original;
|
||||
|
||||
default:
|
||||
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
|
||||
fprintf(stderr, TOY_CC_ERROR "Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
@@ -249,6 +266,7 @@ bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
|
||||
return Toy_equalsRefString(TOY_AS_STRING(lhs), TOY_AS_STRING(rhs));
|
||||
|
||||
case TOY_LITERAL_ARRAY:
|
||||
case TOY_LITERAL_ARRAY_INTERMEDIATE:
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
|
||||
//mismatched sizes
|
||||
@@ -287,6 +305,7 @@ bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
return false; //functions are never equal
|
||||
break;
|
||||
|
||||
@@ -384,6 +403,7 @@ int Toy_hashLiteral(Toy_Literal lit) {
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
return 0; //can't hash these
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
@@ -467,10 +487,10 @@ void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
|
||||
case TOY_LITERAL_STRING: {
|
||||
char buffer[TOY_MAX_STRING_LENGTH];
|
||||
if (!quotes) {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%.*s", Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)));
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%.*s", (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%c%.*s%c", quotes, Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)), quotes);
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)), quotes);
|
||||
}
|
||||
printFn(buffer);
|
||||
}
|
||||
@@ -570,12 +590,13 @@ void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
printFn("(function)");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%.*s", Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
|
||||
snprintf(buffer, 256, "%.*s", (int)Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
+29
-13
@@ -4,6 +4,13 @@
|
||||
|
||||
#include "toy_refstring.h"
|
||||
|
||||
//forward delcare stuff
|
||||
struct Toy_Literal;
|
||||
struct Toy_Interpreter;
|
||||
struct Toy_LiteralArray;
|
||||
typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments);
|
||||
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef enum {
|
||||
@@ -22,14 +29,16 @@ typedef enum {
|
||||
|
||||
//these are meta-level types - not for general use
|
||||
TOY_LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
||||
TOY_LITERAL_ARRAY_INTERMEDIATE, //used to process arrays in the compiler only
|
||||
TOY_LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
|
||||
TOY_LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
||||
TOY_LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
|
||||
TOY_LITERAL_FUNCTION_NATIVE, //for handling native functions only
|
||||
TOY_LITERAL_FUNCTION_HOOK, //for handling hook functions within literals only
|
||||
TOY_LITERAL_INDEX_BLANK, //for blank indexing i.e. arr[:]
|
||||
} Toy_LiteralType;
|
||||
|
||||
typedef struct {
|
||||
typedef struct Toy_Literal {
|
||||
Toy_LiteralType type;
|
||||
union {
|
||||
bool boolean;
|
||||
@@ -45,6 +54,8 @@ typedef struct {
|
||||
|
||||
struct {
|
||||
void* bytecode;
|
||||
Toy_NativeFn native; //already a pointer
|
||||
Toy_HookFn hook; //already a pointer
|
||||
void* scope;
|
||||
int length;
|
||||
} function;
|
||||
@@ -55,7 +66,7 @@ typedef struct {
|
||||
} identifier;
|
||||
|
||||
struct {
|
||||
Toy_LiteralType typeOf; //no longer a mask
|
||||
Toy_LiteralType typeOf;
|
||||
bool constant;
|
||||
void* subtypes; //for nested types caused by compounds
|
||||
int capacity;
|
||||
@@ -64,7 +75,7 @@ typedef struct {
|
||||
|
||||
struct {
|
||||
void* ptr;
|
||||
int tag; //TODO: remove tags?
|
||||
int tag;
|
||||
} opaque;
|
||||
} as;
|
||||
} Toy_Literal;
|
||||
@@ -78,6 +89,7 @@ typedef struct {
|
||||
#define TOY_IS_DICTIONARY(value) ((value).type == TOY_LITERAL_DICTIONARY)
|
||||
#define TOY_IS_FUNCTION(value) ((value).type == TOY_LITERAL_FUNCTION)
|
||||
#define TOY_IS_FUNCTION_NATIVE(value) ((value).type == TOY_LITERAL_FUNCTION_NATIVE)
|
||||
#define TOY_IS_FUNCTION_HOOK(value) ((value).type == TOY_LITERAL_FUNCTION_HOOK)
|
||||
#define TOY_IS_IDENTIFIER(value) ((value).type == TOY_LITERAL_IDENTIFIER)
|
||||
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
|
||||
#define TOY_IS_OPAQUE(value) ((value).type == TOY_LITERAL_OPAQUE)
|
||||
@@ -89,21 +101,25 @@ typedef struct {
|
||||
#define TOY_AS_ARRAY(value) ((Toy_LiteralArray*)((value).as.array))
|
||||
#define TOY_AS_DICTIONARY(value) ((Toy_LiteralDictionary*)((value).as.dictionary))
|
||||
#define TOY_AS_FUNCTION(value) ((value).as.function)
|
||||
#define TOY_AS_FUNCTION_NATIVE(value) ((value).as.function.native)
|
||||
#define TOY_AS_FUNCTION_HOOK(value) ((value).as.function.hook)
|
||||
#define TOY_AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
||||
#define TOY_AS_TYPE(value) ((value).as.type)
|
||||
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
|
||||
|
||||
#define TOY_TO_NULL_LITERAL ((Toy_Literal){TOY_LITERAL_NULL, { .integer = 0 }})
|
||||
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){TOY_LITERAL_BOOLEAN, { .boolean = value }})
|
||||
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){TOY_LITERAL_INTEGER, { .integer = value }})
|
||||
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FLOAT, { .number = value }})
|
||||
#define TOY_TO_NULL_LITERAL ((Toy_Literal){TOY_LITERAL_NULL, { .integer = 0 }})
|
||||
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){TOY_LITERAL_BOOLEAN, { .boolean = value }})
|
||||
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){TOY_LITERAL_INTEGER, { .integer = value }})
|
||||
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FLOAT, { .number = value }})
|
||||
#define TOY_TO_STRING_LITERAL(value) Toy_private_toStringLiteral(value)
|
||||
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_ARRAY, { .array = value }})
|
||||
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_DICTIONARY, { .dictionary = value }})
|
||||
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){TOY_LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
||||
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_ARRAY, { .array = value }})
|
||||
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_DICTIONARY, { .dictionary = value }})
|
||||
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){TOY_LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
||||
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FUNCTION_NATIVE, { .function.native = value, .function.scope = NULL, .function.length = 0 }})
|
||||
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FUNCTION_HOOK, { .function.hook = value, .function.scope = NULL, .function.length = 0 }})
|
||||
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
|
||||
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){ TOY_LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
||||
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){ TOY_LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
|
||||
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){ TOY_LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
||||
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){ TOY_LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
|
||||
|
||||
//BUGFIX: For blank indexing
|
||||
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
|
||||
@@ -116,7 +132,7 @@ TOY_API void Toy_freeLiteral(Toy_Literal literal);
|
||||
#define TOY_MAX_STRING_LENGTH 4096
|
||||
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
|
||||
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
|
||||
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
||||
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
||||
|
||||
//BUGFIX: macros are not functions
|
||||
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
|
||||
|
||||
+3
-8
@@ -7,7 +7,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
//default allocator
|
||||
static void* defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||
if (newSize == 0 && oldSize == 0) {
|
||||
//causes issues, so just skip out with a NO-OP
|
||||
return NULL;
|
||||
@@ -22,7 +22,7 @@ static void* defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSiz
|
||||
void* mem = realloc(pointer, newSize);
|
||||
|
||||
if (mem == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %d for %ld, replacing %d)\n" TOY_CC_RESET, (int)newSize, (long int)pointer, (int)oldSize);
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)oldSize);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -30,12 +30,7 @@ static void* defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSiz
|
||||
}
|
||||
|
||||
//static variables
|
||||
static Toy_MemoryAllocatorFn allocator;
|
||||
|
||||
//preload
|
||||
static void __attribute__((constructor)) preloadMemoryAllocator() {
|
||||
Toy_setMemoryAllocator(defaultMemoryAllocator);
|
||||
}
|
||||
static Toy_MemoryAllocatorFn allocator = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
//exposed API
|
||||
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
||||
|
||||
+100
-17
@@ -58,8 +58,8 @@ static void consume(Toy_Parser* parser, Toy_TokenType tokenType, const char* msg
|
||||
|
||||
static void synchronize(Toy_Parser* parser) {
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
fprintf(stderr, TOY_CC_ERROR "synchronizing\n" TOY_CC_RESET);
|
||||
if (Toy_commandLine.verbose) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Synchronizing input\n" TOY_CC_RESET);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -112,6 +112,7 @@ typedef struct {
|
||||
PrecedenceRule precedence;
|
||||
} ParseRule;
|
||||
|
||||
//no static!
|
||||
ParseRule parseRules[];
|
||||
|
||||
//forward declarations
|
||||
@@ -260,17 +261,48 @@ static Toy_Opcode string(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
//handle strings
|
||||
switch(parser->previous.type) {
|
||||
case TOY_TOKEN_LITERAL_STRING: {
|
||||
int length = parser->previous.length;
|
||||
//unescape valid escaped characters
|
||||
int strLength = 0;
|
||||
char* buffer = TOY_ALLOCATE(char, parser->previous.length);
|
||||
|
||||
//for safety
|
||||
if (length > TOY_MAX_STRING_LENGTH) {
|
||||
length = TOY_MAX_STRING_LENGTH;
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, TOY_CC_ERROR "Strings can only be a maximum of %d characters long" TOY_CC_RESET, TOY_MAX_STRING_LENGTH);
|
||||
error(parser, parser->previous, buffer);
|
||||
for (int i = 0; i < parser->previous.length; i++) {
|
||||
if (parser->previous.lexeme[i] != '\\') { //copy normally
|
||||
buffer[strLength++] = parser->previous.lexeme[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
//unescape based on the character
|
||||
switch(parser->previous.lexeme[++i]) {
|
||||
case 'n':
|
||||
buffer[strLength++] = '\n';
|
||||
break;
|
||||
case 't':
|
||||
buffer[strLength++] = '\t';
|
||||
break;
|
||||
case '\\':
|
||||
buffer[strLength++] = '\\';
|
||||
break;
|
||||
case '"':
|
||||
buffer[strLength++] = '"';
|
||||
break;
|
||||
default: {
|
||||
char msg[256];
|
||||
snprintf(msg, 256, TOY_CC_ERROR "Unrecognized escape character %c in string" TOY_CC_RESET, parser->previous.lexeme[++i]);
|
||||
error(parser, parser->previous, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Literal literal = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(parser->previous.lexeme, length));
|
||||
//for length safety
|
||||
if (strLength > TOY_MAX_STRING_LENGTH) {
|
||||
strLength = TOY_MAX_STRING_LENGTH;
|
||||
char msg[256];
|
||||
snprintf(msg, 256, TOY_CC_ERROR "Strings can only be a maximum of %d characters long" TOY_CC_RESET, TOY_MAX_STRING_LENGTH);
|
||||
error(parser, parser->previous, msg);
|
||||
}
|
||||
|
||||
Toy_Literal literal = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strLength));
|
||||
TOY_FREE_ARRAY(char, buffer, parser->previous.length);
|
||||
Toy_emitASTNodeLiteral(nodeHandle, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
return TOY_OP_EOF;
|
||||
@@ -396,12 +428,12 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
}
|
||||
|
||||
case TOY_TOKEN_AND: {
|
||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
||||
parsePrecedence(parser, nodeHandle, PREC_AND);
|
||||
return TOY_OP_AND;
|
||||
}
|
||||
|
||||
case TOY_TOKEN_OR: {
|
||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
||||
parsePrecedence(parser, nodeHandle, PREC_OR);
|
||||
return TOY_OP_OR;
|
||||
}
|
||||
|
||||
@@ -416,7 +448,7 @@ static Toy_Opcode unary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
|
||||
if (parser->previous.type == TOY_TOKEN_MINUS) {
|
||||
//temp handle to potentially negate values
|
||||
parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal
|
||||
parsePrecedence(parser, &tmpNode, PREC_TERM); //can be a literal
|
||||
|
||||
//optimisation: check for negative literals
|
||||
if (tmpNode != NULL && tmpNode->type == TOY_AST_NODE_LITERAL && (TOY_IS_INTEGER(tmpNode->atomic.literal) || TOY_IS_FLOAT(tmpNode->atomic.literal))) {
|
||||
@@ -476,6 +508,22 @@ static Toy_Opcode unary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
static char* removeChar(const char* lexeme, int length, char c) {
|
||||
int resPos = 0;
|
||||
char* result = TOY_ALLOCATE(char, length + 1);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (lexeme[i] == c) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result[resPos++] = lexeme[i];
|
||||
}
|
||||
|
||||
result[resPos] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
static Toy_Opcode atomic(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
switch(parser->previous.type) {
|
||||
case TOY_TOKEN_NULL:
|
||||
@@ -492,14 +540,18 @@ static Toy_Opcode atomic(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
|
||||
case TOY_TOKEN_LITERAL_INTEGER: {
|
||||
int value = 0;
|
||||
sscanf(parser->previous.lexeme, "%d", &value);
|
||||
const char* lexeme = removeChar(parser->previous.lexeme, parser->previous.length, '_');
|
||||
sscanf(lexeme, "%d", &value);
|
||||
TOY_FREE_ARRAY(char, lexeme, parser->previous.length + 1);
|
||||
Toy_emitASTNodeLiteral(nodeHandle, TOY_TO_INTEGER_LITERAL(value));
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
case TOY_TOKEN_LITERAL_FLOAT: {
|
||||
float value = 0;
|
||||
sscanf(parser->previous.lexeme, "%f", &value);
|
||||
const char* lexeme = removeChar(parser->previous.lexeme, parser->previous.length, '_');
|
||||
sscanf(lexeme, "%f", &value);
|
||||
TOY_FREE_ARRAY(char, lexeme, parser->previous.length + 1);
|
||||
Toy_emitASTNodeLiteral(nodeHandle, TOY_TO_FLOAT_LITERAL(value));
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
@@ -624,6 +676,10 @@ static Toy_Opcode incrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle)
|
||||
Toy_ASTNode* tmpNode = NULL;
|
||||
identifier(parser, &tmpNode);
|
||||
|
||||
if (!tmpNode) {
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
Toy_emitASTNodePrefixIncrement(nodeHandle, tmpNode->atomic.literal);
|
||||
|
||||
Toy_freeASTNode(tmpNode);
|
||||
@@ -637,6 +693,10 @@ static Toy_Opcode incrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
|
||||
advance(parser);
|
||||
|
||||
if (!tmpNode) {
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
Toy_emitASTNodePostfixIncrement(nodeHandle, tmpNode->atomic.literal);
|
||||
|
||||
Toy_freeASTNode(tmpNode);
|
||||
@@ -648,7 +708,11 @@ static Toy_Opcode decrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle)
|
||||
advance(parser);
|
||||
|
||||
Toy_ASTNode* tmpNode = NULL;
|
||||
identifier(parser, &tmpNode); //weird
|
||||
identifier(parser, &tmpNode);
|
||||
|
||||
if (!tmpNode) {
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
Toy_emitASTNodePrefixDecrement(nodeHandle, tmpNode->atomic.literal);
|
||||
|
||||
@@ -663,6 +727,10 @@ static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||
|
||||
advance(parser);
|
||||
|
||||
if (!tmpNode) {
|
||||
return TOY_OP_EOF;
|
||||
}
|
||||
|
||||
Toy_emitASTNodePostfixDecrement(nodeHandle, tmpNode->atomic.literal);
|
||||
|
||||
Toy_freeASTNode(tmpNode);
|
||||
@@ -950,6 +1018,21 @@ static bool calcStaticBinaryArithmetic(Toy_Parser* parser, Toy_ASTNode** nodeHan
|
||||
Toy_Literal rhs = (*nodeHandle)->binary.right->atomic.literal;
|
||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||
|
||||
//special case for string concatenation ONLY
|
||||
if (TOY_IS_STRING(lhs) && TOY_IS_STRING(rhs) && (*nodeHandle)->binary.opcode == TOY_OP_ADDITION) {
|
||||
//check for overflow
|
||||
int totalLength = TOY_AS_STRING(lhs)->length + TOY_AS_STRING(rhs)->length;
|
||||
if (totalLength > TOY_MAX_STRING_LENGTH) {
|
||||
error(parser, parser->previous, "Can't concatenate these strings, result is too long (error found in constant folding)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//concat the strings
|
||||
char buffer[TOY_MAX_STRING_LENGTH];
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%s%s", Toy_toCString(TOY_AS_STRING(lhs)), Toy_toCString(TOY_AS_STRING(rhs)));
|
||||
result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, totalLength));
|
||||
}
|
||||
|
||||
//type coersion
|
||||
if (TOY_IS_FLOAT(lhs) && TOY_IS_INTEGER(rhs)) {
|
||||
rhs = TOY_TO_FLOAT_LITERAL(TOY_AS_INTEGER(rhs));
|
||||
@@ -1153,7 +1236,7 @@ static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, Preced
|
||||
Toy_emitASTNodeBinary(nodeHandle, rhsNode, opcode);
|
||||
|
||||
//optimise away the constants
|
||||
if (!calcStaticBinaryArithmetic(parser, nodeHandle)) {
|
||||
if (!parser->panic && !calcStaticBinaryArithmetic(parser, nodeHandle)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+21
-24
@@ -1,35 +1,32 @@
|
||||
#include "toy_refstring.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//test variable sizes based on platform (safety)
|
||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||
|
||||
STATIC_ASSERT(sizeof(Toy_RefString) == 12);
|
||||
STATIC_ASSERT(sizeof(int) == 4);
|
||||
STATIC_ASSERT(sizeof(char) == 1);
|
||||
|
||||
//memory allocation
|
||||
static Toy_RefStringAllocatorFn allocate;
|
||||
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||
static Toy_RefStringAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn allocator) {
|
||||
allocate = allocator;
|
||||
}
|
||||
|
||||
//API
|
||||
Toy_RefString* Toy_createRefString(char* cstring) {
|
||||
int length = strnlen(cstring, 4096);
|
||||
Toy_RefString* Toy_createRefString(const char* cstring) {
|
||||
size_t length = strlen(cstring);
|
||||
|
||||
return Toy_createRefStringLength(cstring, length);
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_createRefStringLength(char* cstring, int length) {
|
||||
Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length) {
|
||||
//allocate the memory area (including metadata space)
|
||||
Toy_RefString* refString = (Toy_RefString*)allocate(NULL, 0, sizeof(int) * 2 + sizeof(char) * length + 1);
|
||||
Toy_RefString* refString = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * (length + 1));
|
||||
|
||||
if (refString == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//set the data
|
||||
refString->refcount = 1;
|
||||
refString->refCount = 1;
|
||||
refString->length = length;
|
||||
strncpy(refString->data, cstring, refString->length);
|
||||
|
||||
@@ -40,32 +37,32 @@ Toy_RefString* Toy_createRefStringLength(char* cstring, int length) {
|
||||
|
||||
void Toy_deleteRefString(Toy_RefString* refString) {
|
||||
//decrement, then check
|
||||
refString->refcount--;
|
||||
if (refString->refcount <= 0) {
|
||||
allocate(refString, sizeof(int) * 2 + sizeof(char) * refString->length + 1, 0);
|
||||
refString->refCount--;
|
||||
if (refString->refCount <= 0) {
|
||||
allocate(refString, sizeof(size_t) + sizeof(int) + sizeof(char) * (refString->length + 1), 0);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_countRefString(Toy_RefString* refString) {
|
||||
return refString->refcount;
|
||||
return refString->refCount;
|
||||
}
|
||||
|
||||
int Toy_lengthRefString(Toy_RefString* refString) {
|
||||
size_t Toy_lengthRefString(Toy_RefString* refString) {
|
||||
return refString->length;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_copyRefString(Toy_RefString* refString) {
|
||||
//Cheaty McCheater Face
|
||||
refString->refcount++;
|
||||
refString->refCount++;
|
||||
return refString;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString) {
|
||||
//create a new string, with a new refcount
|
||||
//create a new string, with a new refCount
|
||||
return Toy_createRefStringLength(refString->data, refString->length);
|
||||
}
|
||||
|
||||
char* Toy_toCString(Toy_RefString* refString) {
|
||||
const char* Toy_toCString(Toy_RefString* refString) {
|
||||
return refString->data;
|
||||
}
|
||||
|
||||
@@ -86,7 +83,7 @@ bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs) {
|
||||
|
||||
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring) {
|
||||
//get the rhs length
|
||||
int length = strnlen(cstring, 4096);
|
||||
size_t length = strlen(cstring);
|
||||
|
||||
//different length
|
||||
if (lhs->length != length) {
|
||||
@@ -95,4 +92,4 @@ bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring) {
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, cstring, lhs->length) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,19 +9,19 @@ void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
|
||||
|
||||
//the RefString structure
|
||||
typedef struct Toy_RefString {
|
||||
int refcount;
|
||||
int length;
|
||||
char data[1];
|
||||
size_t length;
|
||||
int refCount;
|
||||
char data[];
|
||||
} Toy_RefString;
|
||||
|
||||
//API
|
||||
Toy_RefString* Toy_createRefString(char* cstring);
|
||||
Toy_RefString* Toy_createRefStringLength(char* cstring, int length);
|
||||
Toy_RefString* Toy_createRefString(const char* cstring);
|
||||
Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length);
|
||||
void Toy_deleteRefString(Toy_RefString* refString);
|
||||
int Toy_countRefString(Toy_RefString* refString);
|
||||
int Toy_lengthRefString(Toy_RefString* refString);
|
||||
size_t Toy_lengthRefString(Toy_RefString* refString);
|
||||
Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
|
||||
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
|
||||
char* Toy_toCString(Toy_RefString* refString);
|
||||
const char* Toy_toCString(Toy_RefString* refString);
|
||||
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
|
||||
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
|
||||
|
||||
@@ -28,4 +28,15 @@ a %= 8;
|
||||
|
||||
assert a == 4, "%= failed";
|
||||
|
||||
//strings as special cases
|
||||
var s = "foo";
|
||||
|
||||
assert s + "bar" == "foobar", "string addition failed";
|
||||
assert s == "foo", "string addition failed (was too sticky)";
|
||||
|
||||
s += "bar";
|
||||
|
||||
assert s == "foobar", "string addition failed (wasn't sticky enough)";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -72,6 +72,16 @@
|
||||
}
|
||||
|
||||
|
||||
//test nested indexing multipliciation assignment
|
||||
{
|
||||
var a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
|
||||
|
||||
a[1][1] *= 10;
|
||||
|
||||
assert a == [[1, 2, 3], [4, 50, 6], [7, 8, 9]], "nested indexing multipliciation assignment failed";
|
||||
}
|
||||
|
||||
|
||||
//test combine example
|
||||
{
|
||||
fn combine(a, b, c) {
|
||||
|
||||
@@ -30,13 +30,11 @@
|
||||
|
||||
//test nested indexing
|
||||
{
|
||||
var d = ["foo": ["bar": 0]];
|
||||
var d = ["foo" : ["bar" : ["bazz" : ["fizz" : 5]]]];
|
||||
|
||||
d["foo"]["bar"] = 42;
|
||||
d["foo"]["bar"]["bazz"]["fizz"] = 66;
|
||||
|
||||
print d;
|
||||
|
||||
assert d == ["foo": ["bar": 42]], "nested indexing failed";
|
||||
assert d == ["foo": ["bar": ["bazz": ["fizz": 66]]]], "nested indexing failed";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ assert greeting == "goodnight world", "basic replacement failed";
|
||||
|
||||
//test backwards string
|
||||
assert greeting[::-1] == "dlrow thgindoog", "backwards string failed";
|
||||
assert greeting[11:15:-1] == "dlrow", "backwards indexed string failed";
|
||||
assert greeting[10:14:-1] == "dlrow", "backwards indexed string failed";
|
||||
|
||||
|
||||
//test string weird manipulation
|
||||
@@ -26,12 +26,12 @@ var numbers = "0123456789";
|
||||
|
||||
numbers[::-2] = "abc";
|
||||
|
||||
assert numbers == "01234c6b8a", "string weird manipulation failed";
|
||||
assert numbers == "0123c5b7a9", "string weird manipulation failed";
|
||||
|
||||
|
||||
//test indexing with variables
|
||||
var first = 11;
|
||||
var second = 15;
|
||||
var first = 10;
|
||||
var second = 14;
|
||||
var third = -1;
|
||||
|
||||
assert greeting[first:second:third] == "dlrow", "indexing with variables failed";
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import about as about;
|
||||
import about;
|
||||
|
||||
assert author == "Kayne Ruse, KR Game Studios", "Author failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,280 @@
|
||||
import compound;
|
||||
|
||||
//test concat
|
||||
{
|
||||
//test array concat
|
||||
{
|
||||
var a = [1, 2, 3];
|
||||
var b = [4, 5, 6];
|
||||
|
||||
var c = a.concat(b).concat(b);
|
||||
|
||||
assert c == [1, 2, 3, 4, 5, 6, 4, 5, 6], "array.concat() failed";
|
||||
}
|
||||
|
||||
//test dictionary concat
|
||||
{
|
||||
var a = ["one" : 1, "two": 2, "three": 3];
|
||||
var b = ["four" : 4, "five": 5, "six": 6];
|
||||
|
||||
var c = a.concat(b);
|
||||
|
||||
assert c.length() == 6, "dictionary.concat().length() failed";
|
||||
|
||||
assert c == ["one" : 1, "two": 2, "three": 3, "four" : 4, "five": 5, "six": 6], "dictionary.concat() comparison failed";
|
||||
}
|
||||
|
||||
//test dictionary concat with clashing keys
|
||||
{
|
||||
var a = ["one" : 1, "two": 2, "three": 3, "random": 1];
|
||||
var b = ["four" : 4, "five": 5, "six": 6, "random": 2];
|
||||
|
||||
var c = a.concat(b);
|
||||
|
||||
assert c["random"] == 1, "dictionary.concat() clashing keys failed";
|
||||
}
|
||||
|
||||
//test string concat
|
||||
{
|
||||
var a = "foo";
|
||||
var b = "bar";
|
||||
|
||||
var c = a.concat(b);
|
||||
|
||||
assert c == "foobar", "string.concat() failed";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//test containsKey
|
||||
{
|
||||
var d = ["one": 1, "two": 2];
|
||||
|
||||
assert d.containsKey("one") == true, "dictionary.containsKey() == true failed";
|
||||
assert d.containsKey("three") == false, "dictionary.containsKey() == false failed";
|
||||
}
|
||||
|
||||
|
||||
//test containsValue
|
||||
{
|
||||
var a = [1, 2, 3];
|
||||
var d = ["one": 1, "two": 2];
|
||||
|
||||
assert a.containsValue(1) == true, "array.containsValue() == true failed";
|
||||
assert a.containsValue(5) == false, "array.containsValue() == false failed";
|
||||
assert d.containsValue(1) == true, "dictionary.containsValue() == true failed";
|
||||
assert d.containsValue(3) == false, "dictionary.containsValue() == false failed";
|
||||
}
|
||||
|
||||
|
||||
//test every
|
||||
{
|
||||
var a = [1, 2, 3];
|
||||
var d = ["one": 1, "two": 2];
|
||||
|
||||
var counter = 0;
|
||||
fn f(k, v) {
|
||||
counter++;
|
||||
return v;
|
||||
}
|
||||
|
||||
assert a.every(f) == true, "array.every() == true failed";
|
||||
assert d.every(f) == true, "dictionary.every() == true failed";
|
||||
|
||||
assert counter == 5, "Unexpected number of calls for _every() == true";
|
||||
|
||||
counter = 0;
|
||||
a[1] = false;
|
||||
d["two"] = false;
|
||||
|
||||
assert a.every(f) == false, "array.every() == false failed";
|
||||
assert d.every(f) == false, "dictionary.every() == false failed";
|
||||
|
||||
assert counter == 4, "Unexpected number of calls for _every() == false";
|
||||
}
|
||||
|
||||
|
||||
//test filter
|
||||
{
|
||||
var a = [1, 2, 3, 4];
|
||||
var d = ["one": 1, "two": 2, "three": 3, "four": 4];
|
||||
|
||||
fn f(k, v) {
|
||||
return v % 2 == 0;
|
||||
}
|
||||
|
||||
assert a.filter(f) == [2, 4], "array.filter() failed";
|
||||
assert d.filter(f) == ["two": 2, "four": 4], "dictionary.filter() failed";
|
||||
}
|
||||
|
||||
|
||||
//test forEach
|
||||
{
|
||||
var counter = 0;
|
||||
|
||||
fn p(k, v) {
|
||||
counter++;
|
||||
print string k + ": " + string v;
|
||||
}
|
||||
|
||||
var a = ["a", "b"];
|
||||
var d = ["foo": 1, "bar": 2, "bazz": 3, "fizz": 4];
|
||||
|
||||
a.forEach(p);
|
||||
assert counter == 2, "forEach ran an unusual number of times";
|
||||
|
||||
counter = 0;
|
||||
d.forEach(p);
|
||||
assert counter == 4, "forEach ran an unusual number of times";
|
||||
}
|
||||
|
||||
|
||||
//test getKeys
|
||||
{
|
||||
var d = ["foo": 1, "bar": 2];
|
||||
|
||||
var a = d.getKeys();
|
||||
|
||||
assert a.length() == 2, "_getKeys() length failed";
|
||||
|
||||
//NOTE: dependant on hash algorithm
|
||||
assert a == ["bar", "foo"], "_getKeys() result failed";
|
||||
}
|
||||
|
||||
|
||||
//test getValues
|
||||
{
|
||||
var d = ["foo": 1, "bar": 2];
|
||||
|
||||
var a = d.getValues();
|
||||
|
||||
assert a.length() == 2, "_getValues() length failed";
|
||||
|
||||
//NOTE: dependant on hash algorithm
|
||||
assert a == [2, 1], "_getValues() result failed";
|
||||
}
|
||||
|
||||
|
||||
//test map
|
||||
{
|
||||
//test map with toy functions
|
||||
{
|
||||
fn increment(k, v) {
|
||||
return v + 1;
|
||||
}
|
||||
|
||||
var a = [1, 2, 3];
|
||||
var d = ["four": 4, "five": 5, "six": 6];
|
||||
|
||||
assert a.map(increment).map(increment).map(increment) == [4,5,6], "array.map() failed";
|
||||
assert d.map(increment).map(increment).map(increment) == [8,9,7], "dictionary.map() failed";
|
||||
}
|
||||
|
||||
//test map with native functions
|
||||
{
|
||||
//TODO: write some native functions for use with map
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//test reduce
|
||||
{
|
||||
var a = [1, 2, 3, 4];
|
||||
var d = ["one": 1, "two": 2, "three": 3, "four": 4];
|
||||
|
||||
fn f(acc, k, v) {
|
||||
return acc + v;
|
||||
}
|
||||
|
||||
assert a.reduce(0, f) == 10, "array.reduce() failed";
|
||||
assert d.reduce(0, f) == 10, "dictionary.reduce() failed";
|
||||
}
|
||||
|
||||
|
||||
//test some
|
||||
{
|
||||
var a = [false, false, false];
|
||||
var d = ["one": false, "two": false];
|
||||
|
||||
var counter = 0;
|
||||
fn f(k, v) {
|
||||
counter++;
|
||||
return v;
|
||||
}
|
||||
|
||||
assert a.some(f) == false, "array.some() == false failed";
|
||||
assert d.some(f) == false, "dictionary.some() == false failed";
|
||||
|
||||
assert counter == 5, "Unexpected number of calls for _some() == false";
|
||||
|
||||
counter = 0;
|
||||
a[1] = true;
|
||||
d["two"] = true;
|
||||
|
||||
assert a.some(f) == true, "array.some() == true failed";
|
||||
assert d.some(f) == true, "dictionary.some() == true failed";
|
||||
|
||||
assert counter == 4, "Unexpected number of calls for _some() == true";
|
||||
}
|
||||
|
||||
|
||||
//test toLower
|
||||
{
|
||||
assert "Hello World".toLower() == "hello world", "_toLower() failed";
|
||||
}
|
||||
|
||||
|
||||
//test toString
|
||||
{
|
||||
var a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
|
||||
|
||||
var s = a.toString();
|
||||
|
||||
assert s == "[[1,2,3],[4,5,6],[7,8,9]]", "array._toString() failed";
|
||||
}
|
||||
|
||||
|
||||
//test toUpper
|
||||
{
|
||||
assert "Hello World".toUpper() == "HELLO WORLD", "_toUpper() failed";
|
||||
}
|
||||
|
||||
|
||||
//test trim defaults
|
||||
{
|
||||
{
|
||||
//test a bunch
|
||||
fn test(s, pass) {
|
||||
var result = s.trim();
|
||||
assert result == pass, "_trim(" + result + ") failed";
|
||||
}
|
||||
|
||||
test("hello world", "hello world");
|
||||
test(" hello world", "hello world");
|
||||
test("hello world ", "hello world");
|
||||
test(" hello world ", "hello world");
|
||||
test(" hello world", "hello world");
|
||||
test("hello world ", "hello world");
|
||||
test(" hello world ", "hello world");
|
||||
test(" hello world", "hello world");
|
||||
test("hello world ", "hello world");
|
||||
test(" hello world ", "hello world");
|
||||
|
||||
//one for goot luck
|
||||
assert " hello world ".trim() == "hello world", "hello world.trim() failed";
|
||||
}
|
||||
|
||||
//test trim custom values
|
||||
{
|
||||
var chars = "heliod";
|
||||
|
||||
assert "hello world".trim(chars) == " wor", "custom _trim() failed";
|
||||
}
|
||||
|
||||
//test trimBegin() & trimEnd()
|
||||
assert " foo ".trimBegin() == "foo ", "string.trimBegin() failed";
|
||||
assert " foo ".trimEnd() == " foo", "string.trimBegin() failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,40 @@
|
||||
//test this logic for memory leaks
|
||||
{
|
||||
import compound;
|
||||
import timer;
|
||||
|
||||
fn start(k, v) {
|
||||
return startTimer();
|
||||
}
|
||||
|
||||
fn check(k, v) {
|
||||
var l = v.stopTimer();
|
||||
print l.timerToString();
|
||||
l.destroyTimer();
|
||||
return v;
|
||||
}
|
||||
|
||||
fn destroy(k, v) {
|
||||
v.destroyTimer();
|
||||
}
|
||||
|
||||
var arr = [1];
|
||||
|
||||
arr
|
||||
.map(start)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(check)
|
||||
.map(destroy)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1 @@
|
||||
print "foo" + + "bar";
|
||||
@@ -0,0 +1,2 @@
|
||||
1.type();
|
||||
|
||||
@@ -2,6 +2,3 @@
|
||||
var t = astype [int];
|
||||
var arr: t = [1, 2, 3.14];
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -2,6 +2,3 @@
|
||||
var t = astype [string:int];
|
||||
var dict: t = ["one": 1, "two": 2, 3:4];
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -2,6 +2,3 @@
|
||||
var t = astype [string:int];
|
||||
var dict: t = ["one": 1, "two": 2, "pi": 3.14];
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -2,6 +2,3 @@
|
||||
var a: [int] = [1, 2, 3];
|
||||
print a[a];
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
var s = "foo" - "bar";
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//This is just a check to ensure that unary minus doesn't screw up the AST
|
||||
|
||||
var xrel: int = 0;
|
||||
var yrel: int = 0;
|
||||
|
||||
assert (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1) == false, "or-chaining bugfix failed";
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
assert false ? false : true, "Basic false ternary failed";
|
||||
}
|
||||
|
||||
|
||||
//test nesting
|
||||
{
|
||||
fn least(a, b, c) {
|
||||
@@ -16,6 +15,23 @@
|
||||
assert least(9, 7, 5) == 5, "Least 9, 7, 5 failed";
|
||||
}
|
||||
|
||||
//test division prevention
|
||||
{
|
||||
var x = 0;
|
||||
assert (x ? 0 : 1 / x) == 0, "Division by zero prevention failed";
|
||||
}
|
||||
|
||||
//test ambiguous syntax
|
||||
{
|
||||
var aa = 1;
|
||||
var bbb = 2;
|
||||
var cccc = 3;
|
||||
var ddddd = 4;
|
||||
var eeeeee = 5;
|
||||
|
||||
assert (aa ? bbb ? cccc : ddddd : eeeeee) == 3, "Ambiguous syntax failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ void error(char* msg) {
|
||||
int main() {
|
||||
{
|
||||
size_t size = 0;
|
||||
char* source = Toy_readFile("scripts/call-from-host.toy", &size);
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
const char* source = Toy_readFile("scripts/call-from-host.toy", &size);
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
free((void*)source);
|
||||
|
||||
if (!tb) {
|
||||
@@ -41,7 +41,7 @@ int main() {
|
||||
|
||||
//test answer
|
||||
{
|
||||
interpreter.printOutput("Testing answer");
|
||||
interpreter.printOutput("Testing answer\n");
|
||||
|
||||
Toy_LiteralArray arguments;
|
||||
Toy_initLiteralArray(&arguments);
|
||||
@@ -69,7 +69,7 @@ int main() {
|
||||
|
||||
//test identity
|
||||
{
|
||||
interpreter.printOutput("Testing identity");
|
||||
interpreter.printOutput("Testing identity\n");
|
||||
|
||||
Toy_LiteralArray arguments;
|
||||
Toy_initLiteralArray(&arguments);
|
||||
@@ -104,7 +104,7 @@ int main() {
|
||||
|
||||
//test makeCounter (closures)
|
||||
{
|
||||
interpreter.printOutput("Testing makeCounter (closures)");
|
||||
interpreter.printOutput("Testing makeCounter (closures)\n");
|
||||
|
||||
Toy_LiteralArray arguments;
|
||||
Toy_initLiteralArray(&arguments);
|
||||
@@ -209,7 +209,7 @@ int main() {
|
||||
|
||||
//test assertion failure
|
||||
{
|
||||
interpreter.printOutput("Testing assertion failure");
|
||||
interpreter.printOutput("Testing assertion failure\n");
|
||||
|
||||
Toy_setInterpreterAssert(&interpreter, noPrintFn);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ int main() {
|
||||
{
|
||||
//source
|
||||
size_t sourceLength = 0;
|
||||
char* source = Toy_readFile("scripts/compiler_sample_code.toy", &sourceLength);
|
||||
const char* source = Toy_readFile("scripts/compiler_sample_code.toy", &sourceLength);
|
||||
|
||||
//test basic compilation & collation
|
||||
Toy_Lexer lexer;
|
||||
|
||||
@@ -30,7 +30,7 @@ static void noAssertFn(const char* output) {
|
||||
}
|
||||
}
|
||||
|
||||
void runBinaryCustom(unsigned char* tb, size_t size) {
|
||||
void runBinaryCustom(const unsigned char* tb, size_t size) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
@@ -42,18 +42,18 @@ void runBinaryCustom(unsigned char* tb, size_t size) {
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
void runSourceCustom(char* source) {
|
||||
void runSourceCustom(const char* source) {
|
||||
size_t size = 0;
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
runBinaryCustom(tb, size);
|
||||
}
|
||||
|
||||
void runSourceFileCustom(char* fname) {
|
||||
void runSourceFileCustom(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
char* source = Toy_readFile(fname, &size);
|
||||
const char* source = Toy_readFile(fname, &size);
|
||||
runSourceCustom(source);
|
||||
free((void*)source);
|
||||
}
|
||||
@@ -68,7 +68,7 @@ int main() {
|
||||
|
||||
{
|
||||
//source
|
||||
char* source = "print null;";
|
||||
const char* source = "print null;";
|
||||
|
||||
//test basic compilation & collation
|
||||
Toy_Lexer lexer;
|
||||
@@ -88,7 +88,7 @@ int main() {
|
||||
|
||||
//collate
|
||||
int size = 0;
|
||||
unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
||||
const unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
||||
|
||||
//NOTE: suppress print output for testing
|
||||
Toy_setInterpreterPrint(&interpreter, noPrintFn);
|
||||
@@ -106,7 +106,7 @@ int main() {
|
||||
|
||||
{
|
||||
//run each file in tests/scripts/
|
||||
char* filenames[] = {
|
||||
const char* filenames[] = {
|
||||
"arithmetic.toy",
|
||||
"casting.toy",
|
||||
"coercions.toy",
|
||||
@@ -126,6 +126,7 @@ int main() {
|
||||
"long-dictionary.toy",
|
||||
"long-literals.toy",
|
||||
"native-functions.toy",
|
||||
"or-chaining-bugfix.toy",
|
||||
"panic-within-functions.toy",
|
||||
"ternary-expressions.toy",
|
||||
"types.toy",
|
||||
|
||||
+64
-5
@@ -13,9 +13,11 @@
|
||||
|
||||
#include "../repl/repl_tools.h"
|
||||
|
||||
#include "../repl/lib_about.h"
|
||||
#include "../repl/lib_compound.h"
|
||||
#include "../repl/lib_runner.h"
|
||||
#include "../repl/lib_standard.h"
|
||||
#include "../repl/lib_timer.h"
|
||||
#include "../repl/lib_runner.h"
|
||||
|
||||
//supress the print output
|
||||
static void noPrintFn(const char* output) {
|
||||
@@ -35,7 +37,7 @@ static void errorWrapper(const char* output) {
|
||||
fprintf(stderr, TOY_CC_ERROR "%s" TOY_CC_RESET, output);
|
||||
}
|
||||
|
||||
void runBinaryWithLibrary(unsigned char* tb, size_t size, char* library, Toy_HookFn hook) {
|
||||
void runBinaryWithLibrary(const unsigned char* tb, size_t size, const char* library, Toy_HookFn hook) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
@@ -51,6 +53,26 @@ void runBinaryWithLibrary(unsigned char* tb, size_t size, char* library, Toy_Hoo
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
void runBinaryQuietly(const unsigned char* tb, size_t size) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//NOTE: supress print output for testing
|
||||
Toy_setInterpreterPrint(&interpreter, noPrintFn);
|
||||
Toy_setInterpreterAssert(&interpreter, assertWrapper);
|
||||
Toy_setInterpreterError(&interpreter, errorWrapper);
|
||||
|
||||
//inject the libs
|
||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
||||
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
|
||||
Toy_runInterpreter(&interpreter, tb, size);
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
typedef struct Payload {
|
||||
char* fname;
|
||||
char* libname;
|
||||
@@ -73,9 +95,11 @@ int main() {
|
||||
//run each file in test/scripts
|
||||
Payload payloads[] = {
|
||||
{"interactions.toy", "standard", Toy_hookStandard}, //interactions needs standard
|
||||
{"about.toy", "about", Toy_hookAbout},
|
||||
{"compound.toy", "compound", Toy_hookCompound},
|
||||
{"runner.toy", "runner", Toy_hookRunner},
|
||||
{"standard.toy", "standard", Toy_hookStandard},
|
||||
{"timer.toy", "timer", Toy_hookTimer},
|
||||
{"runner.toy", "runner", Toy_hookRunner},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -87,14 +111,14 @@ int main() {
|
||||
|
||||
//compile the source
|
||||
size_t size = 0;
|
||||
char* source = Toy_readFile(fname, &size);
|
||||
const char* source = Toy_readFile(fname, &size);
|
||||
if (!source) {
|
||||
printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname);
|
||||
failedAsserts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
free((void*)source);
|
||||
|
||||
if (!tb) {
|
||||
@@ -107,6 +131,41 @@ int main() {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
//run whatever, testing stuff together to check for memory leaks
|
||||
char* whatever[] = {
|
||||
"random-stuff.toy",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int i = 0; whatever[i]; i++) {
|
||||
printf("Running %s\n", whatever[i]);
|
||||
|
||||
char fname[128];
|
||||
snprintf(fname, 128, "scripts/lib/%s", whatever[i]);
|
||||
|
||||
//compile the source
|
||||
size_t size = 0;
|
||||
const char* source = Toy_readFile(fname, &size);
|
||||
if (!source) {
|
||||
printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname);
|
||||
failedAsserts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
free((void*)source);
|
||||
|
||||
if (!tb) {
|
||||
printf(TOY_CC_ERROR "Failed to compile file: %s\n" TOY_CC_RESET, fname);
|
||||
failedAsserts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
runBinaryQuietly(tb, size);
|
||||
}
|
||||
}
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
|
||||
|
||||
+10
-7
@@ -23,7 +23,7 @@ static void noErrorFn(const char* output) {
|
||||
errorsTriggered++;
|
||||
}
|
||||
|
||||
unsigned char* compileStringCustom(char* source, size_t* size) {
|
||||
const unsigned char* compileStringCustom(const char* source, size_t* size) {
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler compiler;
|
||||
@@ -50,7 +50,7 @@ unsigned char* compileStringCustom(char* source, size_t* size) {
|
||||
}
|
||||
|
||||
//get the bytecode dump
|
||||
unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
|
||||
const unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
|
||||
|
||||
//cleanup
|
||||
Toy_freeCompiler(&compiler);
|
||||
@@ -61,7 +61,7 @@ unsigned char* compileStringCustom(char* source, size_t* size) {
|
||||
return tb;
|
||||
}
|
||||
|
||||
void runBinaryCustom(unsigned char* tb, size_t size) {
|
||||
void runBinaryCustom(const unsigned char* tb, size_t size) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
@@ -73,18 +73,18 @@ void runBinaryCustom(unsigned char* tb, size_t size) {
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
void runSourceCustom(char* source) {
|
||||
void runSourceCustom(const char* source) {
|
||||
size_t size = 0;
|
||||
unsigned char* tb = compileStringCustom(source, &size);
|
||||
const unsigned char* tb = compileStringCustom(source, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
runBinaryCustom(tb, size);
|
||||
}
|
||||
|
||||
void runSourceFileCustom(char* fname) {
|
||||
void runSourceFileCustom(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
char* source = Toy_readFile(fname, &size);
|
||||
const char* source = Toy_readFile(fname, &size);
|
||||
runSourceCustom(source);
|
||||
free((void*)source);
|
||||
}
|
||||
@@ -96,10 +96,13 @@ int main() {
|
||||
//run each file in tests/scripts/
|
||||
char* filenames[] = {
|
||||
"access-parent-directory.toy",
|
||||
"arithmetic-without-operand.toy",
|
||||
"bad-function-identifier.toy",
|
||||
"declare-types-array.toy",
|
||||
"declare-types-dictionary-key.toy",
|
||||
"declare-types-dictionary-value.toy",
|
||||
"index-arrays-non-integer.toy",
|
||||
"string-concat.toy",
|
||||
"unary-inverted-nothing.toy",
|
||||
"unary-negative-nothing.toy",
|
||||
NULL
|
||||
|
||||
@@ -68,8 +68,8 @@ static int consume(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
int main() {
|
||||
{
|
||||
size_t size = 0;
|
||||
char* source = Toy_readFile("scripts/opaque-data-type.toy", &size);
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
const char* source = Toy_readFile("scripts/opaque-data-type.toy", &size);
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
free((void*)source);
|
||||
|
||||
if (!tb) {
|
||||
|
||||
+83
-7
@@ -11,7 +11,7 @@
|
||||
int main() {
|
||||
{
|
||||
//source
|
||||
char* source = "print null;";
|
||||
const char* source = "print null;";
|
||||
|
||||
//test init & quit
|
||||
Toy_Lexer lexer;
|
||||
@@ -24,7 +24,7 @@ int main() {
|
||||
|
||||
{
|
||||
//source
|
||||
char* source = "print null;";
|
||||
const char* source = "print null;";
|
||||
|
||||
//test parsing
|
||||
Toy_Lexer lexer;
|
||||
@@ -36,17 +36,17 @@ int main() {
|
||||
|
||||
//inspect the node
|
||||
if (node == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is null" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is null\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->type != TOY_AST_NODE_UNARY || node->unary.opcode != TOY_OP_PRINT) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is not a unary print instruction" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is not a unary print instruction\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->unary.child->type != TOY_AST_NODE_LITERAL || !TOY_IS_NULL(node->unary.child->atomic.literal)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not a null literal" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not a null literal\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ int main() {
|
||||
{
|
||||
//get the source file
|
||||
size_t size = 0;
|
||||
char* source = Toy_readFile("scripts/parser_sample_code.toy", &size);
|
||||
const char* source = Toy_readFile("scripts/parser_sample_code.toy", &size);
|
||||
|
||||
//test parsing a chunk of junk (valgrind will find leaks)
|
||||
Toy_Lexer lexer;
|
||||
@@ -70,7 +70,7 @@ int main() {
|
||||
|
||||
while (node != NULL) {
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Error node detected" TOY_CC_RESET);
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Error node detected\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,82 @@ int main() {
|
||||
free((void*)source);
|
||||
}
|
||||
|
||||
{
|
||||
//test parsing of escaped characters
|
||||
const char* source = "print \"\\\"\";"; //NOTE: this string goes through two layers of escaping
|
||||
|
||||
//test parsing
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_initLexer(&lexer, source);
|
||||
Toy_initParser(&parser, &lexer);
|
||||
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
|
||||
//inspect the node
|
||||
if (node == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is null\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->type != TOY_AST_NODE_UNARY || node->unary.opcode != TOY_OP_PRINT) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is not a unary print instruction\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->unary.child->type != TOY_AST_NODE_LITERAL || !TOY_IS_STRING(node->unary.child->atomic.literal)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not a string literal\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!Toy_equalsRefStringCString(TOY_AS_STRING(node->unary.child->atomic.literal), "\"")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not an escaped character, found: %s\n" TOY_CC_RESET, Toy_toCString(TOY_AS_STRING(node->unary.child->atomic.literal)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeASTNode(node);
|
||||
Toy_freeParser(&parser);
|
||||
}
|
||||
|
||||
{
|
||||
//test parsing of underscored numbers
|
||||
const char* source = "print 1_000_000;";
|
||||
|
||||
//test parsing
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_initLexer(&lexer, source);
|
||||
Toy_initParser(&parser, &lexer);
|
||||
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
|
||||
//inspect the node
|
||||
if (node == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is null\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->type != TOY_AST_NODE_UNARY || node->unary.opcode != TOY_OP_PRINT) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is not a unary print instruction\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->unary.child->type != TOY_AST_NODE_LITERAL || !TOY_IS_INTEGER(node->unary.child->atomic.literal)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not a string literal\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_AS_INTEGER(node->unary.child->atomic.literal) != 1000000) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not the correct value, found: %d\n" TOY_CC_RESET, TOY_AS_INTEGER(node->unary.child->atomic.literal));
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeASTNode(node);
|
||||
Toy_freeParser(&parser);
|
||||
}
|
||||
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "repl_tools.h"
|
||||
#include "toy_memory.h"
|
||||
#include "toy_console_colors.h"
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//tracker allocator
|
||||
int currentMemoryUsed = 0;
|
||||
int maxMemoryUsed = 0;
|
||||
int memoryAllocCalls = 0;
|
||||
|
||||
static void* trackerAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||
if (newSize == 0 && oldSize == 0) {
|
||||
//causes issues, so just skip out with a NO-OP
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memoryAllocCalls++;
|
||||
|
||||
//track the changes
|
||||
currentMemoryUsed = currentMemoryUsed - oldSize + newSize;
|
||||
maxMemoryUsed = currentMemoryUsed > maxMemoryUsed ? currentMemoryUsed : maxMemoryUsed;
|
||||
|
||||
if (newSize == 0) {
|
||||
free(pointer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* mem = realloc(pointer, newSize);
|
||||
|
||||
if (mem == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)oldSize);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc <= 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//setup for runner
|
||||
Toy_initDriveDictionary();
|
||||
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
||||
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
||||
|
||||
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
|
||||
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
|
||||
Toy_setMemoryAllocator(trackerAllocator);
|
||||
|
||||
//run memory tests
|
||||
for (int fileCounter = 1; fileCounter < argc; fileCounter++) {
|
||||
Toy_runSourceFile(argv[fileCounter]);
|
||||
}
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
|
||||
//report output
|
||||
printf("Memory report: %d max bytes, %d calls\n", maxMemoryUsed, memoryAllocCalls);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
CC=gcc
|
||||
|
||||
TOY_OUTDIR=out
|
||||
|
||||
IDIR+=. ../../source
|
||||
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||
LIBS+=-ltoy
|
||||
|
||||
ODIR = obj
|
||||
SRC = $(wildcard *.c)
|
||||
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
||||
OUTNAME=toy
|
||||
OUT=../../$(TOY_OUTDIR)/memusage
|
||||
|
||||
all:
|
||||
cp $(shell find ../../repl/repl_tools*) .
|
||||
cp $(shell find ../../repl/lib*) .
|
||||
$(MAKE) build
|
||||
|
||||
build: $(OBJ)
|
||||
echo $(OBJ)
|
||||
ifeq ($(shell uname),Darwin)
|
||||
cp $(PWD)/$(TOY_OUTDIR)/lib$(OUTNAME).dylib /usr/local/lib/
|
||||
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) $(LIBS)
|
||||
else
|
||||
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) -Wl,-rpath,. -L$(realpath $(shell pwd)/../../$(TOY_OUTDIR)) $(LIBS)
|
||||
endif
|
||||
|
||||
$(OBJ): | $(ODIR)
|
||||
|
||||
$(ODIR):
|
||||
mkdir $(ODIR)
|
||||
|
||||
$(ODIR)/%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(RM) -r $(ODIR)
|
||||
$(RM) $(shell find ./repl_tools*)
|
||||
$(RM) $(shell find ./lib*)
|
||||
Reference in New Issue
Block a user