mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
381 lines
13 KiB
C
381 lines
13 KiB
C
#pragma once
|
|
|
|
/*!
|
|
# toy_literal.h
|
|
|
|
This header defines the literal structure, which is used extensively throughout Toy to represent values of some kind.
|
|
|
|
The main way of interacting with literals is to use a macro of some kind, as the exact implementation of `Toy_Literal` has and will change based on the needs of Toy.
|
|
|
|
User data can be passed around within Toy as an opaque type - use the tag value for determining what kind of opaque it is, or leave it as 0.
|
|
!*/
|
|
|
|
#include "toy_common.h"
|
|
|
|
#include "toy_refstring.h"
|
|
#include "toy_reffunction.h"
|
|
|
|
//forward delcare stuff
|
|
struct Toy_Literal;
|
|
struct Toy_Interpreter;
|
|
struct Toy_LiteralArray;
|
|
struct Toy_LiteralDictionary;
|
|
struct Toy_Scope;
|
|
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);
|
|
typedef void (*Toy_PrintFn)(const char*);
|
|
|
|
/*!
|
|
## Defined Enums
|
|
|
|
### Toy_LiteralType
|
|
|
|
* `TOY_LITERAL_NULL`
|
|
* `TOY_LITERAL_BOOLEAN`
|
|
* `TOY_LITERAL_INTEGER`
|
|
* `TOY_LITERAL_FLOAT`
|
|
* `TOY_LITERAL_STRING`
|
|
* `TOY_LITERAL_ARRAY`
|
|
* `TOY_LITERAL_DICTIONARY`
|
|
* `TOY_LITERAL_FUNCTION`
|
|
* `TOY_LITERAL_FUNCTION_NATIVE`
|
|
* `TOY_LITERAL_FUNCTION_HOOK`
|
|
* `TOY_LITERAL_IDENTIFIER`
|
|
* `TOY_LITERAL_TYPE`
|
|
* `TOY_LITERAL_OPAQUE`
|
|
* `TOY_LITERAL_ANY`
|
|
|
|
These are the main values of `Toy_LiteralType`, each of which represents a potential state of the `Toy_Literal` structure. Do not interact with a literal without determining its type with the `IS_*` macros first.
|
|
|
|
Other type values are possible, but are only used internally.
|
|
!*/
|
|
|
|
typedef enum {
|
|
TOY_LITERAL_NULL,
|
|
TOY_LITERAL_BOOLEAN,
|
|
TOY_LITERAL_INTEGER,
|
|
TOY_LITERAL_FLOAT,
|
|
TOY_LITERAL_STRING,
|
|
TOY_LITERAL_ARRAY,
|
|
TOY_LITERAL_DICTIONARY,
|
|
TOY_LITERAL_FUNCTION,
|
|
TOY_LITERAL_IDENTIFIER,
|
|
TOY_LITERAL_TYPE,
|
|
TOY_LITERAL_OPAQUE,
|
|
TOY_LITERAL_ANY,
|
|
|
|
//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 Toy_Literal {
|
|
union {
|
|
bool boolean; //1
|
|
int integer; //4
|
|
float number;//4
|
|
|
|
struct {
|
|
Toy_RefString* ptr; //8
|
|
//string hash?
|
|
} string; //8
|
|
|
|
struct Toy_LiteralArray* array; //8
|
|
struct Toy_LiteralDictionary* dictionary; //8
|
|
|
|
struct {
|
|
union {
|
|
Toy_RefFunction* ptr; //8
|
|
Toy_NativeFn native; //8
|
|
Toy_HookFn hook; //8
|
|
} inner; //8
|
|
struct Toy_Scope* scope; //8
|
|
} function; //16
|
|
|
|
struct { //for variable names
|
|
Toy_RefString* ptr; //8
|
|
int hash; //4
|
|
} identifier; //16
|
|
|
|
struct {
|
|
struct Toy_Literal* subtypes; //8
|
|
Toy_LiteralType typeOf; //4
|
|
unsigned char capacity; //1
|
|
unsigned char count; //1
|
|
bool constant; //1
|
|
} type; //16
|
|
|
|
struct {
|
|
void* ptr; //8
|
|
int tag; //4
|
|
} opaque; //16
|
|
|
|
void* generic; //8
|
|
} as; //16
|
|
|
|
Toy_LiteralType type; //4
|
|
//4 - unused
|
|
//shenanigans with byte alignment reduces the size of Toy_Literal
|
|
} Toy_Literal;
|
|
|
|
/*!
|
|
## Defined Macros
|
|
!*/
|
|
|
|
/*!
|
|
The following macros are used to determine if a given literal, passed in as `value`, is of a specific type. It should be noted that `TOY_IS_FUNCTION` will return false for native and hook functions.
|
|
|
|
* `TOY_IS_NULL(value)`
|
|
* `TOY_IS_BOOLEAN(value)`
|
|
* `TOY_IS_INTEGER(value)`
|
|
* `TOY_IS_FLOAT(value)`
|
|
* `TOY_IS_STRING(value)`
|
|
* `TOY_IS_ARRAY(value)`
|
|
* `TOY_IS_DICTIONARY(value)`
|
|
* `TOY_IS_FUNCTION(value)`
|
|
* `TOY_IS_FUNCTION_NATIVE(value)`
|
|
* `TOY_IS_FUNCTION_HOOK(value)`
|
|
* `TOY_IS_IDENTIFIER(value)`
|
|
* `TOY_IS_TYPE(value)`
|
|
* `TOY_IS_OPAQUE(value)`
|
|
!*/
|
|
|
|
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
|
|
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
|
|
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
|
|
#define TOY_IS_FLOAT(value) ((value).type == TOY_LITERAL_FLOAT)
|
|
#define TOY_IS_STRING(value) ((value).type == TOY_LITERAL_STRING)
|
|
#define TOY_IS_ARRAY(value) ((value).type == TOY_LITERAL_ARRAY)
|
|
#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)
|
|
|
|
/*!
|
|
The following macros are used to cast a literal to a specific C type to be used.
|
|
|
|
* `TOY_AS_BOOLEAN(value)`
|
|
* `TOY_AS_INTEGER(value)`
|
|
* `TOY_AS_FLOAT(value)`
|
|
* `TOY_AS_STRING(value)`
|
|
* `TOY_AS_ARRAY(value)`
|
|
* `TOY_AS_DICTIONARY(value)`
|
|
* `TOY_AS_FUNCTION(value)`
|
|
* `TOY_AS_FUNCTION_NATIVE(value)`
|
|
* `TOY_AS_FUNCTION_HOOK(value)`
|
|
* `TOY_AS_IDENTIFIER(value)`
|
|
* `TOY_AS_TYPE(value)`
|
|
* `TOY_AS_OPAQUE(value)`
|
|
!*/
|
|
|
|
#define TOY_AS_BOOLEAN(value) ((value).as.boolean)
|
|
#define TOY_AS_INTEGER(value) ((value).as.integer)
|
|
#define TOY_AS_FLOAT(value) ((value).as.number)
|
|
#define TOY_AS_STRING(value) ((value).as.string.ptr)
|
|
#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.inner.native)
|
|
#define TOY_AS_FUNCTION_HOOK(value) ((value).as.function.inner.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)
|
|
|
|
/*!
|
|
The following macros are used to create a new literal, with the given `value` as it's internal value.
|
|
|
|
* `TOY_TO_NULL_LITERAL` - does not need parantheses
|
|
* `TOY_TO_BOOLEAN_LITERAL(value)`
|
|
* `TOY_TO_INTEGER_LITERAL(value)`
|
|
* `TOY_TO_FLOAT_LITERAL(value)`
|
|
* `TOY_TO_STRING_LITERAL(value)`
|
|
* `TOY_TO_ARRAY_LITERAL(value)`
|
|
* `TOY_TO_DICTIONARY_LITERAL(value)`
|
|
* `TOY_TO_FUNCTION_LITERAL(value, l)` - `l` represents the length of the bytecode passed as `value`
|
|
* `TOY_TO_FUNCTION_NATIVE_LITERAL(value)`
|
|
* `TOY_TO_FUNCTION_HOOK_LITERAL(value)`
|
|
* `TOY_TO_IDENTIFIER_LITERAL(value)`
|
|
* `TOY_TO_TYPE_LITERAL(value, c)` - `c` is the true of the type should be const
|
|
* `TOY_TO_OPAQUE_LITERAL(value, t)` - `t` is the integer tag
|
|
!*/
|
|
|
|
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL})
|
|
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN})
|
|
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER})
|
|
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT})
|
|
#define TOY_TO_STRING_LITERAL(value) ((Toy_Literal){{ .string = { .ptr = value }},TOY_LITERAL_STRING})
|
|
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY})
|
|
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY})
|
|
#define TOY_TO_FUNCTION_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .ptr = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION})
|
|
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .native = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_NATIVE})
|
|
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .hook = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_HOOK})
|
|
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
|
|
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type = { .typeOf = value, .constant = c, .subtypes = NULL, .capacity = 0, .count = 0 }}, TOY_LITERAL_TYPE})
|
|
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque = { .ptr = value, .tag = t }}, TOY_LITERAL_OPAQUE})
|
|
|
|
//BUGFIX: For blank indexing - not for general use
|
|
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
|
|
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
|
|
|
|
/*!
|
|
## More Defined Macros
|
|
|
|
The following macros are utilities used throughout Toy's internals, and are available for the user as well.
|
|
!*/
|
|
|
|
/*!
|
|
### TOY_IS_TRUTHY(x)
|
|
|
|
Returns true of the literal `x` is truthy, otherwise it returns false.
|
|
|
|
Currently, every value is considered truthy except `false`, which is falsy and `null`, which is neither true or false.
|
|
!*/
|
|
|
|
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
|
|
|
|
/*!
|
|
### TOY_AS_FUNCTION_BYTECODE_LENGTH(lit)
|
|
|
|
Returns the length of a Toy function's bytecode.
|
|
|
|
This macro is only valid on `TOY_LITERAL_FUNCTION`.
|
|
!*/
|
|
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) (Toy_lengthRefFunction((lit).inner.ptr))
|
|
|
|
/*!
|
|
### TOY_MAX_STRING_LENGTH
|
|
|
|
The maximum length of a string in Toy, which is 4096 bytes by default. This can be changed at compile time, but the results of doing so are not officially supported.
|
|
!*/
|
|
#define TOY_MAX_STRING_LENGTH 4096
|
|
|
|
/*!
|
|
### TOY_HASH_I(lit)
|
|
|
|
Identifiers are the names of values within Toy; to speed up execution, their "hash value" is computed at compile time and stored within them. Use this to access it, if needed.
|
|
|
|
This macro is only valid on `TOY_LITERAL_IDENTIFIER`.
|
|
!*/
|
|
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
|
|
|
|
/*!
|
|
### TOY_TYPE_PUSH_SUBTYPE(lit, subtype)
|
|
|
|
When building a complex type, such as the type of an array or dictionary, you may need to specify inner types. Use this to push a `subtype`. calling `Toy_freeLiteral()` on the outermost type should clean up all inner types, as expected.
|
|
|
|
This macro returns the index of the newly pushed value within it's parent.
|
|
|
|
This macro is only valid on `TOY_LITERAL_TYPE`, for both `type` and `subtype`.
|
|
!*/
|
|
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
|
|
|
|
/*!
|
|
### TOY_GET_OPAQUE_TAG(o)
|
|
|
|
Returns the value of the opaque `o`'s tag.
|
|
|
|
This macro is only valid on `TOY_LITERAL_OPAQUE`.
|
|
!*/
|
|
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
|
|
|
/*!
|
|
## Defined Functions
|
|
!*/
|
|
|
|
/*!
|
|
### void Toy_freeLiteral(Toy_Literal literal)
|
|
|
|
This function frees the given literal's memory. Any internal pointers are now invalid.
|
|
|
|
This function should be called on EVERY literal when it is no longer needed, regardless of type.
|
|
!*/
|
|
TOY_API void Toy_freeLiteral(Toy_Literal literal);
|
|
|
|
/*!
|
|
### Toy_Literal Toy_copyLiteral(Toy_Literal original)
|
|
|
|
This function returns a copy of the given literal. Literals should never be copied without this function, as it handles a lot of internal memory allocations.
|
|
!*/
|
|
TOY_API Toy_Literal Toy_copyLiteral(Toy_Literal original);
|
|
|
|
/*!
|
|
### bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs)
|
|
|
|
This checks to see if two given literals are equal.
|
|
|
|
When an integer and a float are compared, the integer is cooerced into a float for the duration of the call.
|
|
|
|
Arrays or dictionaries are equal only if their keys and values all equal. Likewise, types only equal if all subtypes are equal, in order.
|
|
|
|
Functions and opaques are never equal to anything, while values with the type `TOY_LITERAL_ANY` are always equal.
|
|
!*/
|
|
TOY_API bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs);
|
|
|
|
/*!
|
|
### int Toy_hashLiteral(Toy_Literal lit)
|
|
|
|
This finds the hash of a literal, for various purposes. Different hashing algorithms are used for different types, and some types can't be hashed at all.
|
|
|
|
types that can't be hashed are
|
|
|
|
* all kinds of functions
|
|
* type
|
|
* opaque
|
|
* any
|
|
|
|
In the case of identifiers, their hashes are precomputed on creation and are stored within the literal.
|
|
!*/
|
|
TOY_API int Toy_hashLiteral(Toy_Literal lit);
|
|
|
|
/*!
|
|
### void Toy_printLiteral(Toy_Literal literal)
|
|
|
|
This wraps a call to `Toy_printLiteralCustom`, with a printf-stdout wrapper as `printFn`.
|
|
!*/
|
|
TOY_API void Toy_printLiteral(Toy_Literal literal);
|
|
|
|
/*!
|
|
### void Toy_printLiteralCustom(Toy_Literal literal, PrintFn printFn)
|
|
|
|
This function passes the string representation of `literal` to `printFn`.
|
|
|
|
This function is not thread safe - due to the loopy and recursive nature of printing compound values, this function uses some globally persistent variables.
|
|
!*/
|
|
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn);
|
|
|
|
/*!
|
|
### bool Toy_private_isTruthy(Toy_Literal x)
|
|
|
|
Utilized by the `TOY_IS_TRUTHY` macro.
|
|
|
|
Private functions are not intended for general use.
|
|
!*/
|
|
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
|
|
|
|
/*!
|
|
### bool Toy_private_toIdentifierLiteral(Toy_RefString* ptr)
|
|
|
|
Utilized by the `TOY_TO_IDENTIFIER_LITERAL` macro.
|
|
|
|
Private functions are not intended for general use.
|
|
!*/
|
|
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
|
|
|
|
/*!
|
|
### bool Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype)
|
|
|
|
Utilized by the `TOY_TYPE_PUSH_SUBTYPE` macro.
|
|
|
|
Private functions are not intended for general use.
|
|
!*/
|
|
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
|