Start writing doxygen/javadoc-style doc comments for C API

This commit is contained in:
K. Lange 2021-02-18 19:18:35 +09:00
parent d112107edc
commit 076b702afa
8 changed files with 936 additions and 107 deletions

View File

@ -4,7 +4,44 @@
#include "chunk.h"
#include "object.h"
/**
* @brief Print a disassembly of 'func' to the stream 'f'.
*
* Generates and prints a bytecode disassembly of the code object 'func',
* writing it to the requested stream.
*
* @param f Stream to write to.
* @param func Code object to disassemble.
* @param name Function name to display in disassembly output.
*/
extern void krk_disassembleChunk(FILE * f, KrkFunction * func, const char * name);
/**
* @brief Print a disassembly of a single opcode instruction.
*
* Generates and prints a bytecode disassembly for one instruction from
* the code object 'func' at byte offset 'offset', printing the result to
* the requested stream and returning the size of the instruction.
*
* @param f Stream to write to.
* @param func Code object to disassemble.
* @param offset Byte offset of the instruction to disassemble.
* @return The size of the instruction in bytes.
*/
extern size_t krk_disassembleInstruction(FILE * f, KrkFunction * func, size_t offset);
/**
* @brief Obtain the line number for a byte offset into a bytecode chunk.
*
* Scans the line mapping table for the given chunk to find the
* correct line number from the original source file for the instruction
* at byte index 'offset'.
*
* @param chunk Bytecode chunk containing the instruction.
* @param offset Byte offset of the instruction to locate.
* @return Line number, 1-indexed.
*/
extern size_t krk_lineNumber(KrkChunk * chunk, size_t offset);
/* Internal stuff */
extern void _createAndBind_disMod(void);

View File

@ -221,8 +221,83 @@ static inline int isObjType(KrkValue value, ObjType type) {
return IS_OBJECT(value) && AS_OBJECT(value)->type == type;
}
/**
* @brief Yield ownership of a C string to the GC and obtain a string object.
*
* Creates a string object represented by the characters in 'chars' and of
* length 'length'. The source string must be nil-terminated and must
* remain valid for the lifetime of the object, as its ownership is yielded
* to the GC. Useful for strings which were allocated on the heap by
* other mechanisms.
*
* 'chars' must be a nil-terminated C string representing a UTF-8
* character sequence.
*
* @param chars C string to take ownership of.
* @param length Length of the C string.
* @return A string object.
*/
extern KrkString * krk_takeString(char * chars, size_t length);
/**
* @brief Obtain a string object representation of the given C string.
*
* Converts the C string 'chars' into a string object by checking the
* string table for it. If the string table does not have an equivalent
* string, a new one will be created by copying 'chars'.
*
* 'chars' must be a nil-terminated C string representing a UTF-8
* character sequence.
*
* @param chars C string to convert to a string object.
* @param length Length of the C string.
* @return A string object.
*/
extern KrkString * krk_copyString(const char * chars, size_t length);
/**
* @brief Ensure that a codepoint representation of a string is available.
*
* Obtain an untyped pointer to the codepoint representation of a string.
* If the string does not have a codepoint representation allocated, it will
* be generated by this function and remain with the string for the duration
* of its lifetime.
*
* @param string String to obtain the codepoint representation of.
* @return A pointer to the bytes of the codepoint representation.
*/
extern void * krk_unicodeString(KrkString * string);
/**
* @brief Obtain the codepoint at a given index in a string.
*
* This is a convenience function which ensures that a Unicode codepoint
* representation has been generated and returns the codepoint value at
* the requested index. If you need to find multiple codepoints, it
* is recommended that you use the KRK_STRING_FAST macro after calling
* krk_unicodeString instead.
*
* @note This function does not perform any bounds checking.
*
* @param string String to index into.
* @param index Offset of the codepoint to obtain.
* @return Integer representation of the codepoint at the requested index.
*/
extern uint32_t krk_unicodeCodepoint(KrkString * string, size_t index);
/**
* @brief Convert an integer codepoint to a UTF-8 byte representation.
*
* Converts a single codepoint to a sequence of bytes containing the
* UTF-8 representation. 'out' must be allocated by the caller.
*
* @param value Codepoint to encode.
* @param out Array to write UTF-8 sequence into.
* @return The length of the UTF-8 sequence, in bytes.
*/
extern size_t krk_codepointToBytes(krk_integer_type value, unsigned char * out);
/* Internal stuff. */
extern KrkFunction * krk_newFunction(void);
extern KrkNative * krk_newNative(NativeFn function, const char * name, int type);
extern KrkClosure * krk_newClosure(KrkFunction * function);
@ -233,9 +308,6 @@ extern KrkBoundMethod * krk_newBoundMethod(KrkValue receiver, KrkObj * method);
extern KrkTuple * krk_newTuple(size_t length);
extern KrkProperty * krk_newProperty(KrkValue method);
extern void * krk_unicodeString(KrkString * string);
extern uint32_t krk_unicodeCodepoint(KrkString * string, size_t index);
#define KRK_STRING_FAST(string,offset) (uint32_t)\
(string->type <= 1 ? ((uint8_t*)string->codes)[offset] : \
(string->type == 2 ? ((uint16_t*)string->codes)[offset] : \
@ -245,6 +317,5 @@ extern uint32_t krk_unicodeCodepoint(KrkString * string, size_t index);
extern KrkBytes * krk_newBytes(size_t length, uint8_t * source);
extern void krk_bytesUpdateHash(KrkBytes * bytes);
extern size_t krk_codepointToBytes(krk_integer_type value, unsigned char * out);
extern void krk_tupleUpdateHash(KrkTuple * self);

View File

@ -23,12 +23,113 @@ typedef struct {
KrkTableEntry * entries;
} KrkTable;
/**
* @brief Initialize a hash table.
*
* This should be called for any new hash table, especially ones
* initialized in heap or stack space, to set up the capacity, count
* and initial entries pointer.
*
* @param table Hash table to initialize.
*/
extern void krk_initTable(KrkTable * table);
/**
* @brief Release resources associated with a hash table.
*
* Frees the entries array for the table and resets count and capacity.
*
* @param table Hash table to release.
*/
extern void krk_freeTable(KrkTable * table);
/**
* @brief Add all key-value pairs from 'from' into 'to'.
*
* Copies each key-value pair from one hash table to another. If a key
* from 'from' already exists in 'to', the existing value in 'to' will be
* overwritten with the value from 'from'.
*
* @param from Source table.
* @param to Destination table.
*/
extern void krk_tableAddAll(KrkTable * from, KrkTable * to);
/**
* @brief Find a character sequence in the string interning table.
*
* Scans through the entries in a given table - usually vm.strings - to find
* an entry equivalent to the string specified by the 'chars' and 'length'
* parameters, using the 'hash' parameter to speed up lookup.
*
* @param chars C array of chars representing the string.
* @param length Length of the string.
* @param hash Precalculated hash value for the string.
* @return If the string was found, the string object representation, else NULL.
*/
extern KrkString * krk_tableFindString(KrkTable * table, const char * chars, size_t length, uint32_t hash);
/**
* @brief Assign a value to a key in a table.
*
* Inserts the key-value pair specified by 'key' and 'value' into the hash
* table 'table', replacing any value that was already preseng with the
* same key.
*
* @param table Table to assign to.
* @param key Key to assign.
* @param value Value to assign to the key.
* @return 0 if the key was already present and has been overwritten, 1 if the key is new to the table.
*/
extern int krk_tableSet(KrkTable * table, KrkValue key, KrkValue value);
/**
* @brief Obtain the value associated with a key in a table.
*
* Scans the table 'table' for the key 'key' and, if found, outputs
* the associated value to *value. If the key is not found, then
* *value will not be changed.
*
* @param table Table to look up.
* @param key Key to look for.
* @param value Output pointer to place resulting value in.
* @return 0 if the key was not found, 1 if it was.
*/
extern int krk_tableGet(KrkTable * table, KrkValue key, KrkValue * value);
/**
* @brief Remove a key from a hash table.
*
* Scans the table 'table' for the key 'key' and, if found, removes
* the entry, replacing it with a tombstone value.
*
* @param table Table to delete from.
* @param key Key to delete.
* @return 1 if the value was found and deleted, 0 if it was not present.
*/
extern int krk_tableDelete(KrkTable * table, KrkValue key);
/**
* @brief Internal table scan function.
*
* Scans through the the entry array 'entries' to find the appropriate entry
* for 'key', return a pointer to the entry, which may be or may not have
* an associated pair.
*
* @param entries Table entry array to scan.
* @param capacity Size of the table entry array, in entries.
* @param key Key to locate.
* @return A pointer to the entry for 'key'.
*/
extern KrkTableEntry * krk_findEntry(KrkTableEntry * entries, size_t capacity, KrkValue key);
/**
* @brief Calculate the hash for a value.
*
* Retreives or calculates the hash value for 'value'.
*
* @param value Value to hash.
* @return An unsigned 32-bit hash value.
*/
extern uint32_t krk_hashValue(KrkValue value);

View File

@ -47,7 +47,7 @@ KRK_FUNC(current_thread,{
static volatile int _threadLock = 0;
static void * _startthread(void * _threadObj) {
memset(&krk_currentThread, 0, sizeof(KrkThreadState));
krk_currentThread.frames = calloc(FRAMES_MAX,sizeof(CallFrame));
krk_currentThread.frames = calloc(KRK_CALL_FRAMES_MAX,sizeof(CallFrame));
_obtain_lock(_threadLock);
if (vm.threads->next) {

View File

@ -95,12 +95,21 @@ static inline const char * _method_name(const char * func) {
#define BIND_PROP(klass,method) do { krk_defineNativeProperty(&klass->fields, #method, _ ## klass ## _ ## method); } while (0)
#define BIND_FUNC(module,func) do { krk_defineNative(&module->fields, #func, _krk_ ## func); } while (0)
/**
* @brief Inline flexible string array.
*/
struct StringBuilder {
size_t capacity;
size_t length;
char * bytes;
};
/**
* @brief Add a character to the end of a string builder.
*
* @param sb String builder to append to.
* @param c Character to append.
*/
static inline void pushStringBuilder(struct StringBuilder * sb, char c) {
if (sb->capacity < sb->length + 1) {
size_t old = sb->capacity;
@ -110,6 +119,13 @@ static inline void pushStringBuilder(struct StringBuilder * sb, char c) {
sb->bytes[sb->length++] = c;
}
/**
* @brief Append a string to the end of a string builder.
*
* @param sb String builder to append to.
* @param str C string to add.
* @param len Length of the C string.
*/
static inline void pushStringBuilderStr(struct StringBuilder * sb, char *str, size_t len) {
if (sb->capacity < sb->length + len) {
while (sb->capacity < sb->length + len) {
@ -123,18 +139,47 @@ static inline void pushStringBuilderStr(struct StringBuilder * sb, char *str, si
}
}
/**
* @brief Finalize a string builder into a string object.
*
* Creates a string object from the contents of the string builder and
* frees the space allocated for the builder, returning a value representing
* the newly created string object.
*
* @param sb String builder to finalize.
* @return A value representing a string object.
*/
static inline KrkValue finishStringBuilder(struct StringBuilder * sb) {
KrkValue out = OBJECT_VAL(krk_copyString(sb->bytes, sb->length));
FREE_ARRAY(char,sb->bytes, sb->capacity);
return out;
}
/**
* @brief Finalize a string builder in a bytes object.
*
* Converts the contents of a string builder into a bytes object and
* frees the space allocated for the builder.
*
* @param sb String builder to finalize.
* @return A value representing a bytes object.
*/
static inline KrkValue finishStringBuilderBytes(struct StringBuilder * sb) {
KrkValue out = OBJECT_VAL(krk_newBytes(sb->length, (uint8_t*)sb->bytes));
FREE_ARRAY(char,sb->bytes, sb->capacity);
return out;
}
/**
* @brief Discard the contents of a string builder.
*
* Frees the resources allocated for the string builder without converting
* it to a string or bytes object. Call this when an error has been encountered
* and the contents of a string builder are no longer needed.
*
* @param sb String builder to discard.
* @return None, as a convenience.
*/
static inline KrkValue discardStringBuilder(struct StringBuilder * sb) {
FREE_ARRAY(char,sb->bytes, sb->capacity);
return NONE_VAL();

View File

@ -3,9 +3,24 @@
#include <stdio.h>
#include "kuroko.h"
/**
* @brief Base structure of all heap objects.
*
* KrkObj is the base type of all objects stored on the heap and
* managed by the garbage collector.
*/
typedef struct Obj KrkObj;
typedef struct ObjString KrkString;
/**
* @brief Tag enum for basic value types.
*
* Value types are tagged unions of a handful of small
* types represented directly on the stack: Integers,
* double-precision floating point values, booleans,
* exception handler references, complex function argument
* processing sentinels, object reference pointers, and None.
*/
typedef enum {
VAL_NONE,
VAL_BOOLEAN,
@ -14,14 +29,36 @@ typedef enum {
VAL_HANDLER,
VAL_OBJECT,
VAL_KWARGS,
/* More here later */
} KrkValueType;
/**
* @brief Stack value representation of a 'with' or 'try' block.
*
* When a 'with' or 'try' block is entered, a handler value is
* created on the stack representing the type (with, try) and the
* jump target to leave the block (entering the 'except' block of
* a 'try', if present, or calling the __exit__ method of an object
* __enter__'d by a 'with' block). When the relevant conditions are
* triggered in the VM, the stack will be scanned from top to bottom
* to look for these values.
*/
typedef struct {
unsigned short type;
unsigned short target;
} KrkJumpTarget;
/**
* @brief Stack reference or primative value.
*
* This type stores a stack reference to an object, or the contents of
* a primitive type. Each VM thread's stack consists of an array of
* these values, and they are generally passed around in the VM through
* direct copying rather than as pointers, avoiding the need to track
* memory used by them.
*
* Each value is a tagged union with a type (see the enum KrkValueType)
* and its contents.
*/
typedef struct {
KrkValueType type;
union {
@ -33,6 +70,109 @@ typedef struct {
} as;
} KrkValue;
/**
* @brief Flexible vector of stack references.
*
* Value Arrays provide a resizable collection of values and are the
* backbone of lists and tuples.
*/
typedef struct {
size_t capacity; /**< Available allocated space. */
size_t count; /**< Current number of used slots. */
KrkValue * values; /**< Pointer to heap-allocated storage. */
} KrkValueArray;
/**
* @brief Initialize a value array.
*
* This should be called for any new value array, especially ones
* initialized in heap or stack space, to set up the capacity, count
* and initial value pointer.
*
* @param array Value array to initialize.
*/
extern void krk_initValueArray(KrkValueArray * array);
/**
* @brief Add a value to a value array.
*
* Appends 'value' to the end of the given array, adjusting count values
* and resizing as necessary.
*
* @param array Array to append to.
* @param value Value to append to array.
*/
extern void krk_writeValueArray(KrkValueArray * array, KrkValue value);
/**
* @brief Release relesources used by a value array.
*
* Frees the storage associated with a given value array and resets
* its capacity and count. Does not directly free resources associated
* with heap objects referenced by the values in this array: The GC
* is responsible for taking care of that.
*
* @param array Array to release.
*/
extern void krk_freeValueArray(KrkValueArray * array);
/**
* @brief Print a string representation of a value.
*
* Print a string representation of 'value' to the stream 'f'.
* For primitives, performs appropriate formatting. For objects,
* this will call __str__ on the object's representative type.
* If the type does not have a __str__ method, __repr__ will be
* tried before falling back to krk_typeName to directly print
* the name of the class with no information on the value.
*
* This function provides the backend for the print() built-in.
*
* @param f Stream to write to.
* @param value Value to display.
*/
extern void krk_printValue(FILE * f, KrkValue value);
/**
* @brief Print a value without calling the VM.
*
* Print a string representation of 'value' to the stream 'f',
* avoiding calls to managed code by using simplified representations
* where necessary. This is intended for use in debugging code, such
* as during disassembly, or when printing values in an untrusted context.
*
* @note This function will truncate long strings and print them in a form
* closer to the 'repr()' representation, with escaped bytes, rather
* than directly printing them to the stream.
*
* @param f Stream to write to.
* @param value Value to display.
*/
extern void krk_printValueSafe(FILE * f, KrkValue value);
/**
* @brief Compare two values for equality.
*
* Performs a relaxed equality comparison between two values,
* check for equivalence by contents. This may call managed
* code to run __eq__ methods.
*
* @return 1 if values are equivalent, 0 otherwise.
*/
extern int krk_valuesEqual(KrkValue a, KrkValue b);
/**
* @brief Compare two values by identity.
*
* Performs a strict comparison between two values, comparing
* their identities. For primitive values, this is generally
* the same as comparing by equality. For objects, this compares
* pointer values directly.
*
* @return 1 if values represent the same object or value, 0 otherwise.
*/
extern int krk_valuesSame(KrkValue a, KrkValue b);
#define BOOLEAN_VAL(value) ((KrkValue){VAL_BOOLEAN, {.boolean = value}})
#define NONE_VAL(value) ((KrkValue){VAL_NONE, {.integer = 0}})
#define INTEGER_VAL(value) ((KrkValue){VAL_INTEGER, {.integer = value}})
@ -58,17 +198,3 @@ typedef struct {
#define IS_TRY_HANDLER(value) (IS_HANDLER(value) && AS_HANDLER(value).type == OP_PUSH_TRY)
#define IS_WITH_HANDLER(value) (IS_HANDLER(value) && AS_HANDLER(value).type == OP_PUSH_WITH)
typedef struct {
size_t capacity;
size_t count;
KrkValue * values;
} KrkValueArray;
extern void krk_initValueArray(KrkValueArray * array);
extern void krk_writeValueArray(KrkValueArray * array, KrkValue value);
extern void krk_freeValueArray(KrkValueArray * array);
extern void krk_printValue(FILE * f, KrkValue value);
extern void krk_printValueSafe(FILE * f, KrkValue value);
extern int krk_valuesEqual(KrkValue a, KrkValue b);
extern int krk_valuesSame(KrkValue a, KrkValue b);

View File

@ -790,7 +790,7 @@ _finishKwarg:
krk_push(KWARGS_VAL(0));
argCount++;
}
if (krk_currentThread.frameCount == FRAMES_MAX) {
if (krk_currentThread.frameCount == KRK_CALL_FRAMES_MAX) {
krk_runtimeError(vm.exceptions->baseException, "Too many call frames.");
return 0;
}
@ -1118,7 +1118,7 @@ void krk_initVM(int flags) {
/* Reset current thread */
krk_resetStack();
krk_currentThread.frames = calloc(FRAMES_MAX,sizeof(CallFrame));
krk_currentThread.frames = calloc(KRK_CALL_FRAMES_MAX,sizeof(CallFrame));
krk_currentThread.flags = flags & 0x00FF;
krk_currentThread.module = NULL;
krk_currentThread.watchdog = 0;

619
src/vm.h
View File

@ -7,16 +7,39 @@
#include "table.h"
#include "object.h"
#define FRAMES_MAX 64
#define KRK_CALL_FRAMES_MAX 64
/**
* @brief Represents a managed call state in a VM thread.
*
* For every managed function call, including the top-level module,
* a call frame is added to the stack to track the running function,
* the current opcode instruction, the offset into the stack, and
* the valid globals table.
*
* Call frames are used directly by the VM as the source of
* opcodes and operands during execution, and are used by the exception
* handler to roll back execution to the appropriate environment.
*/
typedef struct {
KrkClosure * closure;
uint8_t * ip;
size_t slots;
size_t outSlots;
KrkTable * globals;
KrkClosure * closure; /**< Pointer to the function object containing the code object for this frame */
uint8_t * ip; /**< Instruction pointer within the code object's bytecode data */
size_t slots; /**< Offset into the stack at which this function call's arguments begin */
size_t outSlots; /**< Offset into the stack at which stackTop will be reset upon return */
KrkTable * globals; /**< Pointer to the attribute table containing valud global vairables for this call */
} CallFrame;
/**
* @brief Index numbers for always-available interned strings representing important method and member names.
*
* The VM must look up many methods and members by fixed names. To avoid
* continuously having to box and unbox these from C strings to the appropriate
* interned KrkString, we keep an array of the KrkString pointers in the global VM state.
*
* These values are the offsets into that index for each of the relevant
* function names (generally with extra underscores removed). For example
* METHOD_INIT is the offset for the string value for "__init__".
*/
typedef enum {
METHOD_INIT,
METHOD_STR,
@ -51,103 +74,131 @@ typedef enum {
METHOD__MAX,
} KrkSpecialMethods;
/**
* @brief Table of basic exception types.
*
* These are the core exception types, available in managed code
* from the builtin namespace. A single instance of this struct
* is attached to the global VM state so that C code can quickly
* access these exception types for use with krk_runtimeException.
*
* @see krk_runtimeException
*/
struct Exceptions {
KrkClass * baseException;
KrkClass * typeError;
KrkClass * argumentError;
KrkClass * indexError;
KrkClass * keyError;
KrkClass * attributeError;
KrkClass * nameError;
KrkClass * importError;
KrkClass * ioError;
KrkClass * valueError;
KrkClass * keyboardInterrupt;
KrkClass * zeroDivisionError;
KrkClass * notImplementedError;
KrkClass * syntaxError;
KrkClass * baseException; /**< @exception Exception The base exception type. */
KrkClass * typeError; /**< @exception TypeError An argument or value was not of the expected type. */
KrkClass * argumentError; /**< @exception ArgumentException The number of arguments passed to a function was not as expected. */
KrkClass * indexError; /**< @exception IndexError An attempt was made to reference an invalid array index. */
KrkClass * keyError; /**< @exception KeyError An attempt was made to reference an invalid mapping key. */
KrkClass * attributeError; /**< @exception AttributeError An attempt was made to reference an invalid object property. */
KrkClass * nameError; /**< @exception NameError An attempt was made to reference an undeclared global variable. */
KrkClass * importError; /**< @exception ImportError An error was encountered when attempting to import a module. */
KrkClass * ioError; /**< @exception IOError An error was encountered in operating system's IO library. */
KrkClass * valueError; /**< @exception ValueError The value of a parameter or variable is not valid. */
KrkClass * keyboardInterrupt; /**< @exception KeyboardInterrupt An interrupt signal was received. */
KrkClass * zeroDivisionError; /**< @exception ZeroDivisionError A mathematical function attempted to divide by zero. */
KrkClass * notImplementedError; /**< @exception NotImplementedError The method is not implemented, either for the given arguments or in general. */
KrkClass * syntaxError; /**< @exception SyntaxError The compiler encountered an unrecognized or invalid source code input. */
};
/**
* Be sure not to reorder this, even if it looks better, to maintain
* ABI compatibility with existing binaries.
* @brief Table of classes for built-in object types.
*
* For use by C modules and within the VM, an instance of this struct
* is attached to the global VM state. At VM initialization, each
* built-in class is attached to this table, and the class values
* stored here are used for integrated type checking with krk_isInstanceOf.
*
* @note As this and other tables are used directly by embedders, do not
* reorder the layout of the individual class pointers, even if
* it looks nicer. The ordering here is part of our library ABI.
*/
struct BaseClasses {
KrkClass * objectClass; /* Root of everything */
KrkClass * moduleClass; /* Simple class mostly to provide __repr__ for modules */
KrkClass * typeClass; /* Class */
KrkClass * intClass; /* Integer */
KrkClass * floatClass; /* Floating */
KrkClass * boolClass; /* Boolean */
KrkClass * noneTypeClass; /* None */
KrkClass * strClass; /* String */
KrkClass * functionClass; /* Functions, Closures */
KrkClass * methodClass; /* BoundMethod */
KrkClass * tupleClass; /* Tuple */
KrkClass * bytesClass; /* Bytes */
KrkClass * listiteratorClass;
KrkClass * rangeClass;
KrkClass * rangeiteratorClass;
KrkClass * striteratorClass;
KrkClass * tupleiteratorClass;
KrkClass * listClass;
KrkClass * dictClass;
KrkClass * dictitemsClass;
KrkClass * dictkeysClass;
KrkClass * bytesiteratorClass;
KrkClass * objectClass; /**< The base of all classes within the type tree. */
KrkClass * moduleClass; /**< A class for representing imported modules, both managed and C. */
KrkClass * typeClass; /**< Classes themselves are of this class. */
KrkClass * intClass; /**< Primitive integer type. */
KrkClass * floatClass; /**< Primitive double-precision floating-point type. */
KrkClass * boolClass; /**< Primitive boolean type. */
KrkClass * noneTypeClass; /**< The class of the None value. */
KrkClass * strClass; /**< Built-in Unicode string type. */
KrkClass * functionClass; /**< Represents a code object (KrkFunction) or function object (KrkClosure) */
KrkClass * methodClass; /**< Represents a bound method (KrkBoundMethod) */
KrkClass * tupleClass; /**< An immutable collection of arbitrary values. */
KrkClass * bytesClass; /**< An immutable sequence of bytes. */
KrkClass * listiteratorClass; /**< Iterator over lists */
KrkClass * rangeClass; /**< An object representing a start and end point for a sequence of integers. */
KrkClass * rangeiteratorClass; /**< Iterator over a range of values */
KrkClass * striteratorClass; /**< Iterator over characters (by codepoint) in a string */
KrkClass * tupleiteratorClass; /**< Iterator over values in a tuple */
KrkClass * listClass; /**< Mutable collection of arbitrary values. */
KrkClass * dictClass; /**< Mutable mapping of hashable keys to arbitrary values. */
KrkClass * dictitemsClass; /**< Iterator over the (key,value) pairs of a dict */
KrkClass * dictkeysClass; /**< Iterator over the keys of a dict */
KrkClass * bytesiteratorClass; /**< Iterator over the integer byte values of a bytes object. */
};
/**
* Thread state represents everything that changes during execution
* and isn't a global property of the shared garbage collector.
* @brief Execution state of a VM thread.
*
* Each thread in the VM has its own local thread state, which contains
* the thread's stack, stack pointer, call frame stack, a thread-specific
* VM flags bitarray, and an exception state.
*
* @see krk_currentThread
*/
typedef struct ThreadState {
struct ThreadState * next;
struct ThreadState * next; /**< Invasive list pointer to next thread. */
CallFrame * frames;
size_t frameCount;
size_t stackSize;
KrkValue * stack;
KrkValue * stackTop;
KrkUpvalue * openUpvalues;
ssize_t exitOnFrame;
CallFrame * frames; /**< Call frame stack for this thread, max KRK_CALL_FRAMES_MAX */
size_t frameCount; /**< Number of active call frames. */
size_t stackSize; /**< Size of the allocated stack space for this thread. */
KrkValue * stack; /**< Pointer to the bottom of the stack for this thread. */
KrkValue * stackTop; /**< Pointer to the top of the stack. */
KrkUpvalue * openUpvalues; /**< Flexible array of unclosed upvalues. */
ssize_t exitOnFrame; /**< When called in a nested context, the frame offset to exit the VM dispatch loop on. */
KrkInstance * module;
KrkValue currentException;
int flags;
long watchdog;
KrkInstance * module; /**< The current module execution context. */
KrkValue currentException; /**< When an exception is thrown, it is stored here. */
int flags; /**< Thread-local VM flags; each thread inherits the low byte of the global VM flags. */
long watchdog; /**< Decrementing watchdog timer for embedding. */
#define THREAD_SCRATCH_SIZE 3
KrkValue scratchSpace[THREAD_SCRATCH_SIZE];
KrkValue scratchSpace[THREAD_SCRATCH_SIZE]; /**< A place to store a few values to keep them from being prematurely GC'd. */
} KrkThreadState;
/**
* @brief Global VM state.
*
* This state is shared by all VM threads and stores the
* path to the VM binary, global execution flags, the
* string and module tables, tables of builtin types,
* and the state of the (shared) garbage collector.
*/
typedef struct {
int globalFlags; /* Global VM state flags */
char * binpath; /* A string representing the name of the interpreter binary. */
KrkTable strings; /* Strings table */
KrkTable modules; /* Module cache */
KrkInstance * builtins; /* '__builtins__' module */
KrkInstance * system; /* 'kuroko' module */
KrkValue * specialMethodNames; /* Cached strings of important method and function names */
struct BaseClasses * baseClasses; /* Pointer to a (static) namespacing struct for the KrkClass*'s of built-in object types */
struct Exceptions * exceptions; /* Pointer to a (static) namespacing struct for the KrkClass*'s of basic exception types */
int globalFlags; /**< Global VM state flags */
char * binpath; /**< A string representing the name of the interpreter binary. */
KrkTable strings; /**< Strings table */
KrkTable modules; /**< Module cache */
KrkInstance * builtins; /**< '__builtins__' module */
KrkInstance * system; /**< 'kuroko' module */
KrkValue * specialMethodNames; /**< Cached strings of important method and function names */
struct BaseClasses * baseClasses; /**< Pointer to a (static) namespacing struct for the KrkClass*'s of built-in object types */
struct Exceptions * exceptions; /**< Pointer to a (static) namespacing struct for the KrkClass*'s of basic exception types */
/* Garbage collector state */
KrkObj * objects; /* Linked list of all objects in the GC */
size_t bytesAllocated; /* Running total of bytes allocated */
size_t nextGC; /* Point at which we should sweep again */
size_t grayCount; /* Count of objects marked by scan. */
size_t grayCapacity; /* How many objects we can fit in the scan list. */
KrkObj** grayStack; /* Scan list */
KrkObj * objects; /**< Linked list of all objects in the GC */
size_t bytesAllocated; /**< Running total of bytes allocated */
size_t nextGC; /**< Point at which we should sweep again */
size_t grayCount; /**< Count of objects marked by scan. */
size_t grayCapacity; /**< How many objects we can fit in the scan list. */
KrkObj** grayStack; /**< Scan list */
KrkThreadState * threads; /* All the threads. */
KrkThreadState * threads; /**< Invasive linked list of all VM threads. */
} KrkVM;
/* Thread-specific flags */
/* TODO Should these be in an enum instead? */
#define KRK_ENABLE_TRACING (1 << 0)
#define KRK_ENABLE_DISASSEMBLY (1 << 1)
#define KRK_ENABLE_SCAN_TRACING (1 << 2)
@ -165,47 +216,447 @@ extern __thread KrkThreadState krk_currentThread;
extern KrkThreadState krk_currentThread;
#endif
/**
* Singleton instance of the shared VM state.
*/
extern KrkVM krk_vm;
#define vm krk_vm
/**
* @brief Initialize the VM at program startup.
*
* All library users must call this exactly once on startup to create
* the built-in types, modules, and functions for the VM and prepare
* the string and module tables. Optionally, callers may set `vm.binpath`
* before calling krk_initVM to allow the VM to locate the interpreter
* binary and establish the default module paths.
*
* @param flags Combination of global VM flags and initial thread flags.
*/
extern void krk_initVM(int flags);
/**
* @brief Release resources from the VM.
*
* Generally, it is desirable to call this once before the hosting program exits.
* If a fresh VM state is needed, krk_freeVM should be called before a further
* call to krk_initVM is made. The resources released here can include allocated
* heap memory, FILE pointers or descriptors, or various other things which were
* initialized by C extension modules.
*/
extern void krk_freeVM(void);
/**
* @brief Reset the current thread's stack state to the top level.
*
* In a repl, this can be called before or after each iteration to clean up any
* remnant stack entries from an uncaught exception. It should not be called
* during normal execution by C extensions. Values on the stack may be lost
* to garbage collection after a call to krk_resetStack .
*/
extern void krk_resetStack(void);
/**
* @brief Compile and execute a source code input.
*
* This is the lowest level call for most usecases, including execution
* of commands from a REPL or when executing a file.
*
* @param src Source code to compile and run.
* @param newScope Whether this code should be interpreted in the context of a new module.
* @param fromName Name of the function or module being interpreted.
* @param fromFile Path to the source file, or a representative string like "<stdin>".
* @return When newScope is non-zero, an object representing the globals of the new scope;
* otherwise, the returned result of the execution of this code. If an uncaught
* exception occurred, this will be None, krk_currentThread.flags should indicate
* KRK_HAS_EXCEPTION, and krk_currentThread.currentException should contain the raised
* exception value.
*/
extern KrkValue krk_interpret(const char * src, int newScope, char *, char *);
/**
* @brief Load and run a source file and return when execution completes.
*
* Loads and runs a source file. Can be used by interpreters to run scripts,
* either in the context of a new a module or as if they were continuations
* of the current module state (eg. as if they were lines entered on a repl)
*
* @param fileName Path to the source file to read and execute.
* @param newScope Whether this file should be run in the context of a new module.
* @param fromName Value to assign to __name__
* @param fromFile Value to assign to __file__
* @return As with krk_interpret, an object representing the newly created module,
* or the final return value of the VM execution.
*/
extern KrkValue krk_runfile(const char * fileName, int newScope, char *, char *);
/**
* @brief Load and run a file as a module.
*
* Similar to krk_runfile, but ensures that execution of the VM returns to the caller
* after the file is run. This should be run after calling krk_startModule to initialize
* a new module context and is used internally by the import mechanism.
*
* @param fileName Path to the source file to read and execute.
* @param fromName Value to assign to __name__
* @param fromFile Value to assign to __file__
* @return The object representing the module, or None if execution of the file failed.
*/
extern KrkValue krk_callfile(const char * fileName, char * fromName, char * fromFile);
/**
* @brief Push a stack value.
*
* Pushes a value onto the current thread's stack, triggering a
* stack resize if there is not enough space to hold the new value.
*
* @param value Value to push.
*/
extern void krk_push(KrkValue value);
/**
* @brief Pop the top of the stack.
*
* Removes and returns the value at the top of current thread's stack.
* Generally, it is preferably to leave values on the stack and use
* krk_peek if the value is desired, as removing a value from the stack
* may result in it being garbage collected.
*
* @return The value previously at the top of the stack.
*/
extern KrkValue krk_pop(void);
/**
* @brief Peek down from the top of the stack.
*
* Obtains a value from the current thread's stack without modifying the stack.
*
* @param distance How far down from the top of the stack to peek (0 = the top)
* @return The value from the stack.
*/
extern KrkValue krk_peek(int distance);
/**
* @brief Get the name of the type of a value.
*
* Obtains the C string representing the name of the class
* associated with the given value. Useful for crafting
* exception messages, such as those describing TypeErrors.
*
* @param value Value to examine
* @return Nul-terminated C string of the type of 'value'.
*/
extern const char * krk_typeName(KrkValue value);
/**
* @brief Attach a native C function to an attribute table.
*
* Attaches the given native function pointer to an attribute table
* while managing the stack shuffling and boxing of both the name and
* the function object. If 'name' begins with a '.', the native function
* is marked as a method. If 'name' begins with a ':', the native function
* is marked as a dynamic property.
*
* @param table Attribute table to attach to, such as &someInstance->fields
* @param name Nil-terminated C string with the name to assign
* @param function Native function pointer to attach
* @return A pointer to the object representing the attached function.
*/
extern KrkNative * krk_defineNative(KrkTable * table, const char * name, NativeFn function);
/**
* @brief Attach a native dynamic property to an attribute table.
*
* Mostly the same as defineNative, but ensures the creation of a dynamic property.
* The intention of this function is to replace uses of defineNative with ":" names,
* and replace specialized methods with KrkProperty* objects.
*
* @param table Attribute table to attach to, such as &someInstance->fields
* @param name Nil-terminated C string with the name to assign
* @param function Native function pointer to attach
* @return A pointer to the property object created.
*/
extern KrkProperty * krk_defineNativeProperty(KrkTable * table, const char * name, NativeFn func);
extern void krk_attachNamedObject(KrkTable * table, const char name[], KrkObj * obj);
/**
* @brief Attach a value to an attribute table.
*
* Manages the stack shuffling and boxing of the name string when attaching
* a value to an attribute table. Rather than using krk_tableSet, this is
* the preferred method of supplying fields to objects from C code.
*
* @param table Attribute table to attach to, such as &someInstance->fields
* @param name Nil-terminated C string with the name to assign
* @param obj Value to attach.
*/
extern void krk_attachNamedValue(KrkTable * table, const char name[], KrkValue obj);
/**
* @brief Attach an object to an attribute table.
*
* Manages the stack shuffling and boxing of the name string when attaching
* an object to an attribute table. Rather than using krk_tableSet, this is
* the preferred method of supplying fields to objects from C code.
*
* This is a convenience wrapper around krk_attachNamedValue.
*
* @param table Attribute table to attach to, such as &someInstance->fields
* @param name Nil-terminated C string with the name to assign
* @param obj Object to attach.
*/
extern void krk_attachNamedObject(KrkTable * table, const char name[], KrkObj * obj);
/**
* @brief Raise an exception.
*
* Creates an instance of the given exception type, passing a formatted
* string to the initializer. All of the core exception types take an option
* string value to attach to the exception, but third-party exception types
* may have different initializer signatures and need separate initialization.
*
* The created exception object is attached to the current thread state and
* the HAS_EXCEPTION flag is set.
*
* @param type Class pointer for the exception type, eg. vm.exceptions->valueError
* @param fmt Format string.
* @return As a convenience to C extension authors, returns None.
*/
extern KrkValue krk_runtimeError(KrkClass * type, const char * fmt, ...);
/**
* @brief Get a pointer to the current thread state.
*
* Generally equivalent to &krk_currentThread, though krk_currentThread
* itself may be implemented as a macro that calls this function depending
* on the platform's thread support.
*
* @return Pointer to current thread's thread state.
*/
extern KrkThreadState * krk_getCurrentThread(void);
extern KrkInstance * krk_dictCreate(void);
/**
* @brief Continue VM execution until the next exit trigger.
*
* Resumes the VM dispatch loop, returning to the caller when
* the next exit trigger event happens. Generally, callers will
* want to set the current thread's exitOnFrame before calling
* krk_runNext. Alternatively, see krk_callValue which manages
* exit triggers automatically when calling function objects.
*
* @return Value returned by the exit trigger, generally the value
* returned by the inner function before the VM returned
* to the exit frame.
*/
extern KrkValue krk_runNext(void);
extern KrkClass * krk_getType(KrkValue);
/**
* @brief Get the class representing a value.
*
* Returns the class object representing the type of a value.
* This may be the direct class of an instance, or a pseudoclass
* for other value types.
*
* @param value Reference value to examine.
* @return A pointer to the value's type's class object.
*/
extern KrkClass * krk_getType(KrkValue value);
/**
* @brief Determine if a class is an instance or subclass of a given type.
*
* Searches the class hierarchy of the given value to determine if it is
* a subtype of 'type'. As this chains through the inheritence tree, the
* more deeply subclassed 'obj' is, the longer it may take to determine
* if it is a subtype of 'type'. All types should eventually be subtypes
* of the 'object' type, so this condition should not be checked. For
* some types, convenience macros are available. If you need to check if
* 'obj' is a specific type, exclusive of subtypes, compare krk_getType
* instead of using this function.
*
* @param obj Value to examine.
* @param type Class object to test for membership of.
* @return 1 if obj is an instance of type or of a subclass of type
*/
extern int krk_isInstanceOf(KrkValue obj, KrkClass * type);
/**
* @brief Perform method binding on the stack.
*
* Performs attribute lookup from the class '_class' for 'name'.
* If 'name' is not a valid method, the binding fails.
* If 'name' is a valid method, the method will be retrieved and
* bound to the instance on the top of the stack, replacing it
* with a BoundMethod object.
*
* @param _class Class object to resolve methods from.
* @param name String object with the name of the method to resolve.
* @return 1 if the method has been bound, 0 if binding failed.
*/
extern int krk_bindMethod(KrkClass * _class, KrkString * name);
/**
* @brief Call a callable value in the current stack context.
*
* Executes the given callable object (function, bound method, object
* with a __call__() method, etc.) using 'argCount' arguments from the stack.
*
* @param callee Value referencing a callable object.
* @param argCount Arguments to retreive from stack.
* @param extra Whether extra arguments below argCount should be
* considered as part of this call frame. Generally,
* when this is 1, the value below the arguments is
* the callable object. Most library users will want
* to leave this as 0 when calling normal functions,
* bound method objects, or ubound methods when the
* instance is included in the arguments already.
* @return An indicator of how the result should be obtained:
* 1: The VM must be resumed to run managed code.
* 2: The callable was a native function and result should be popped now.
* Else: The call failed. An exception may have already been set.
*/
extern int krk_callValue(KrkValue callee, int argCount, int extra);
/**
* @brief Create a list object.
*
* This is the native function bound to 'listOf'.
*/
extern KrkValue krk_list_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Create a dict object.
*
* This is the native function bound to 'dictOf'
*/
extern KrkValue krk_dict_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Create a tuple object.
*
* This is the native function bound to 'tupleOf'.
*/
extern KrkValue krk_tuple_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Call a callable and manage VM state to obtain the return value.
*
* This is a wrapper around various mechanisms including krk_callValue
* intended for use by C extensions to call arbitrary functions without
* knowledge of their implementation details. See the notes for
* krk_callValue's 'extra' paramater for details on how 'isMethod' is used.
*
* @param value Callable object reference.
* @param argCount Arguments to collect from the stack.
* @param isMethod This should almost always be 0.
* @return The return value of the function.
*/
extern KrkValue krk_callSimple(KrkValue value, int argCount, int isMethod);
/**
* @brief Convenience function for creating new types.
*
* Creates a class object, output to *_class, setting its name to 'name', inheriting
* from 'base', and attaching it with its name to the fields table of the given 'module'.
*
* @param module Pointer to an instance for a module to attach to, or NULL to skip attaching.
* @param _class Output pointer to assign the new class object to.
* @param name Name of the new class.
* @param base Pointer to class object to inherit from.
* @return A pointer to the class object, equivalent to the value assigned to *_class.
*/
extern KrkClass * krk_makeClass(KrkInstance * module, KrkClass ** _class, const char * name, KrkClass * base);
/**
* @brief Finalize a class by collecting pointers to core methods.
*
* Scans through the methods table of a class object to find special
* methods and assign them to the class object's pointer table so they
* can be referenced directly without performing hash lookups.
*
* @param _class Class object to finalize.
*/
extern void krk_finalizeClass(KrkClass * _class);
/**
* @brief If there is an active exception, print a traceback to stderr.
*
* This function is exposed as a convenience for repl developers. Normally,
* the VM will call dumpTraceback itself if an exception is unhandled and no
* exit trigger is current set. The traceback is obtained from the exception
* object. If the exception object does not have a traceback, only the
* exception itself will be printed. The traceback printer will attempt to
* open source files to print faulting lines and may call into the VM if the
* exception object has a managed implementation of __str__.
*/
extern void krk_dumpTraceback();
/**
* @brief Set up a new module object in the current thread.
*
* Creates a new instance of the module type and attaches a __builtins__
* reference to its fields. The module becomes the current thread's
* main module, but is not directly attached to the module table.
*
* @param name Name of the module, which is assigned to __name__
* @return The instance object representing the module.
*/
extern KrkInstance * krk_startModule(const char * name);
/**
* @brief Obtain a list of properties for an object.
*
* This is the native function bound to 'object.__dir__'
*/
extern KrkValue krk_dirObject(int argc, KrkValue argv[], int hasKw);
/**
* @brief Load a module by name.
*
* This is generally called by the import mechanisms to load a single module.
*
* @param name Name of the module, used for file lookup.
* @param moduleOut Receives a value with the module object.
* @param runAs Name to attach to __name__ for this module, different from 'name'.
* @return 1 if the module was loaded, 0 if an ImportError occurred.
*/
extern int krk_loadModule(KrkString * name, KrkValue * moduleOut, KrkString * runAs);
/* obj_str.h */
/**
* @brief Load a module from a package.
*
* Given a package identifier, attempt to the load module
* into the module table.
*
* @param name String object of the dot-separated package path to import.
* @return 1 if the module was loaded, 0 if an ImportError occurred.
*/
extern int krk_doRecursiveModuleLoad(KrkString * name);
/**
* @brief Determine the truth of a value.
*
* Determines if a value represents a "falsey" value.
* Empty collections, 0-length strings, False, numeric 0,
* None, etc. are "falsey". Other values are generally "truthy".
*
* @param value Value to examine.
* @return 1 if falsey, 0 if truthy
*/
extern int krk_isFalsey(KrkValue value);
/**
* @brief Concatenate two strings.
*
* This is a convenience function which calls 'str.__add__' on the top stack
* values. Generally, this should be avoided - use StringBuilder instead.
*/
extern void krk_addObjects(void);
/**
* All of the remaining stuff is internal and shouldn't be used by library users or embedders.
* FIXME This stuff needs to be moved to another header! FIXME
*/
extern KrkValue _str___get__(int argc, KrkValue argv[], int hasKw);
#define krk_string_get _str___get__
extern KrkValue _str___int__(int argc, KrkValue argv[], int hasKw);
@ -220,7 +671,6 @@ extern KrkValue _str_format(int argc, KrkValue argv[], int hasKw);
/* obj_dict.h */
extern KrkValue krk_dict_nth_key_fast(size_t capacity, KrkTableEntry * entries, size_t index);
extern int krk_isFalsey(KrkValue value);
extern void _createAndBind_numericClasses(void);
extern void _createAndBind_strClass(void);
@ -239,7 +689,6 @@ extern void _createAndBind_timeMod(void);
extern void _createAndBind_osMod(void);
extern void _createAndBind_fileioMod(void);
extern int krk_doRecursiveModuleLoad(KrkString * name);
extern KrkValue krk_operator_lt(KrkValue,KrkValue);
extern KrkValue krk_operator_gt(KrkValue,KrkValue);