kuroko: Update with upstream, changes to classes

This commit is contained in:
K. Lange 2021-03-18 09:57:22 +09:00
parent d54d93dd9d
commit 9ab445cb28
19 changed files with 3015 additions and 58 deletions

View File

@ -16,15 +16,10 @@
*/
#define ENABLE_THREADING
#include "bim.h"
#ifdef __toaru__
#include <kuroko.h>
#include "vm.h"
#include "debug.h"
#else
#include <kuroko/kuroko.h>
#include <kuroko/vm.h>
#include <kuroko/debug.h>
#endif
#include <kuroko/util.h>
global_config_t global_config = {
/* State */
@ -80,6 +75,7 @@ global_config_t global_config = {
.smart_complete = 0,
.has_terminal = 0,
.search_wraps = 1,
.had_error = 0,
/* Integer config values */
.cursor_padding = 4,
.split_percent = 50,
@ -3183,6 +3179,7 @@ void render_error(char * message, ...) {
/* Draw the message */
printf("%s", buf);
global_config.had_error = 1;
} else {
printf("bim: error during startup: %s\n", buf);
}
@ -9938,7 +9935,7 @@ struct CommandDef {
int process_krk_command(const char * cmd, KrkValue * outVal) {
place_cursor(global_config.term_width, global_config.term_height);
fprintf(stdout,"\n");
fprintf(stdout, "\n");
/* By resetting, we're at 0 frames. */
krk_resetStack();
/* Push something so we're not at the bottom of the stack when an
@ -9998,7 +9995,8 @@ int process_krk_command(const char * cmd, KrkValue * outVal) {
}
}
global_config.break_from_selection = 1;
redraw_all();
if (!global_config.had_error) redraw_all();
global_config.had_error = 0;
return retval;
}
@ -10184,11 +10182,11 @@ static KrkValue krk_bim_register_syntax(int argc, KrkValue argv[], int hasKw) {
if (argc < 1 || !IS_CLASS(argv[0]) || !checkClass(AS_CLASS(argv[0]), syntaxStateClass))
return krk_runtimeError(vm.exceptions->typeError, "Can not register '%s' as a syntax highlighter, expected subclass of SyntaxState.", krk_typeName(argv[0]));
KrkValue name = NONE_VAL(), extensions = NONE_VAL(), spaces = BOOLEAN_VAL(0), calculate = NONE_VAL();
krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("name")), &name);
krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("extensions")), &extensions);
krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("spaces")), &spaces);
krk_tableGet(&AS_CLASS(argv[0])->methods, OBJECT_VAL(S("calculate")), &calculate);
KrkValue name = krk_valueGetAttribute_default(argv[0], "name", NONE_VAL());
KrkValue extensions = krk_valueGetAttribute_default(argv[0], "extensions", NONE_VAL());
KrkValue spaces = krk_valueGetAttribute_default(argv[0], "spaces", BOOLEAN_VAL(0));
KrkValue calculate = krk_valueGetAttribute_default(argv[0], "calculate", NONE_VAL());
if (!IS_STRING(name))
return krk_runtimeError(vm.exceptions->typeError, "%s.name must be str", AS_CLASS(argv[0])->name->chars);
if (!IS_TUPLE(extensions))
@ -10527,7 +10525,7 @@ void import_directory(char * dirName) {
if (!dirp) {
/* Try one last fallback */
if (dirpath) free(dirpath);
dirpath = strdup("/usr/share/bim");
dirpath = strdup("/usr/local/share/bim");
sprintf(file, "%s/%s", dirpath, dirName);
extra = "/";
dirp = opendir(file);
@ -10620,6 +10618,37 @@ BIM_COMMAND(reload,"reload","Reloads all the Kuroko stuff.") {
return 0;
}
static KrkValue krk_bim_getDocumentText(int argc, KrkValue argv[], int hasKw) {
struct StringBuilder sb = {0};
int i, j;
for (i = 0; i < env->line_count; ++i) {
line_t * line = env->lines[i];
for (j = 0; j < line->actual; j++) {
char_t c = line->text[j];
if (c.codepoint == 0) {
pushStringBuilder(&sb, 0);
} else {
char tmp[8] = {0};
int len = to_eight(c.codepoint, tmp);
pushStringBuilderStr(&sb, tmp, len);
}
}
pushStringBuilder(&sb, '\n');
}
return finishStringBuilder(&sb);
}
static KrkValue krk_bim_renderError(int argc, KrkValue argv[], int hasKw) {
if (argc != 1 || !IS_STRING(argv[0])) return TYPE_ERROR(str,argv[0]);
if (AS_STRING(argv[0])->length == 0)
redraw_commandline();
else
render_error(AS_CSTRING(argv[0]));
return NONE_VAL();
}
/**
* Run global initialization tasks
*/
@ -10671,6 +10700,9 @@ void initialize(void) {
krk_bim_syntax_dict = krk_dict_of(0,NULL,0);
krk_attachNamedValue(&bimModule->fields, "highlighters", krk_bim_syntax_dict);
krk_defineNative(&bimModule->fields, "getDocumentText", krk_bim_getDocumentText);
krk_defineNative(&bimModule->fields, "renderError", krk_bim_renderError);
makeClass(bimModule, &ActionDef, "Action", vm.baseClasses->objectClass);
ActionDef->allocSize = sizeof(struct ActionDef);
krk_defineNative(&ActionDef->methods, ".__call__", bim_krk_action_call);
@ -10718,21 +10750,21 @@ void initialize(void) {
krk_defineNative(&syntaxStateClass->methods, ".matchAndPaint", bim_krk_state_matchAndPaint);
krk_defineNative(&syntaxStateClass->methods, ".commentBuzzwords", bim_krk_state_commentBuzzwords);
krk_defineNative(&syntaxStateClass->methods, ".rewind", bim_krk_state_rewind);
krk_defineNative(&syntaxStateClass->methods, ".__get__", bim_krk_state_get);
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_NONE", INTEGER_VAL(FLAG_NONE));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_KEYWORD", INTEGER_VAL(FLAG_KEYWORD));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_STRING", INTEGER_VAL(FLAG_STRING));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_COMMENT", INTEGER_VAL(FLAG_COMMENT));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_TYPE", INTEGER_VAL(FLAG_TYPE));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_PRAGMA", INTEGER_VAL(FLAG_PRAGMA));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_NUMERAL", INTEGER_VAL(FLAG_NUMERAL));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_ERROR", INTEGER_VAL(FLAG_ERROR));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_DIFFPLUS", INTEGER_VAL(FLAG_DIFFPLUS));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_DIFFMINUS", INTEGER_VAL(FLAG_DIFFMINUS));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_NOTICE", INTEGER_VAL(FLAG_NOTICE));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_BOLD", INTEGER_VAL(FLAG_BOLD));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_LINK", INTEGER_VAL(FLAG_LINK));
krk_attachNamedValue(&syntaxStateClass->fields, "FLAG_ESCAPE", INTEGER_VAL(FLAG_ESCAPE));
krk_defineNative(&syntaxStateClass->methods, ".__getitem__", bim_krk_state_get);
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_NONE", INTEGER_VAL(FLAG_NONE));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_KEYWORD", INTEGER_VAL(FLAG_KEYWORD));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_STRING", INTEGER_VAL(FLAG_STRING));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_COMMENT", INTEGER_VAL(FLAG_COMMENT));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_TYPE", INTEGER_VAL(FLAG_TYPE));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_PRAGMA", INTEGER_VAL(FLAG_PRAGMA));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_NUMERAL", INTEGER_VAL(FLAG_NUMERAL));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_ERROR", INTEGER_VAL(FLAG_ERROR));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_DIFFPLUS", INTEGER_VAL(FLAG_DIFFPLUS));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_DIFFMINUS", INTEGER_VAL(FLAG_DIFFMINUS));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_NOTICE", INTEGER_VAL(FLAG_NOTICE));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_BOLD", INTEGER_VAL(FLAG_BOLD));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_LINK", INTEGER_VAL(FLAG_LINK));
krk_attachNamedValue(&syntaxStateClass->methods, "FLAG_ESCAPE", INTEGER_VAL(FLAG_ESCAPE));
krk_finalizeClass(syntaxStateClass);

View File

@ -22,11 +22,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#ifdef __toaru__
#include "vm.h"
#else
#include <kuroko/vm.h>
#endif
#ifdef __DATE__
# define BIM_BUILD_DATE " built " __DATE__ " at " __TIME__
@ -213,6 +209,7 @@ typedef struct {
unsigned int smart_complete:1;
unsigned int has_terminal:1;
unsigned int search_wraps:1;
unsigned int had_error:1;
int cursor_padding;
int split_percent;

View File

@ -29,23 +29,120 @@
#include "memory.h"
#include "scanner.h"
#include "compiler.h"
#include "util.h"
#define PROMPT_MAIN ">>> "
#define PROMPT_BLOCK " > "
static int enableRline = 1;
static int exitRepl = 0;
static KrkValue exitFunc(int argc, KrkValue argv[], int hasKw) {
static int pasteEnabled = 0;
KRK_FUNC(exit,{
FUNCTION_TAKES_NONE();
exitRepl = 1;
})
KRK_FUNC(paste,{
FUNCTION_TAKES_AT_MOST(1);
if (argc) {
CHECK_ARG(0,bool,int,enabled);
pasteEnabled = enabled;
} else {
pasteEnabled = !pasteEnabled;
}
fprintf(stderr, "Pasting is %s.\n", pasteEnabled ? "enabled" : "disabled");
})
static int doRead(char * buf, size_t bufSize) {
#ifndef NO_RLINE
if (enableRline)
return rline(buf, bufSize);
else
#endif
return read(STDIN_FILENO, buf, bufSize);
}
static KrkValue readLine(char * prompt, int promptWidth, char * syntaxHighlighter) {
struct StringBuilder sb = {0};
#ifndef NO_RLINE
if (enableRline) {
rline_exit_string = "";
rline_exp_set_prompts(prompt, "", promptWidth, 0);
rline_exp_set_syntax(syntaxHighlighter);
rline_exp_set_tab_complete_func(NULL);
} else
#endif
{
fprintf(stdout, "%s", prompt);
fflush(stdout);
}
/* Read a line of input using a method that we can guarantee will be
* interrupted by signal delivery. */
while (1) {
char buf[4096];
ssize_t bytesRead = doRead(buf, 4096);
if (krk_currentThread.flags & KRK_THREAD_SIGNALLED) goto _exit;
if (bytesRead < 0) {
krk_runtimeError(vm.exceptions->ioError, "%s", strerror(errno));
goto _exit;
} else if (bytesRead == 0 && !sb.length) {
krk_runtimeError(vm.exceptions->baseException, "EOF");
goto _exit;
} else {
pushStringBuilderStr(&sb, buf, bytesRead);
}
/* Was there a linefeed? Then we can exit. */
if (sb.length && sb.bytes[sb.length-1] == '\n') {
sb.length--;
break;
}
}
return finishStringBuilder(&sb);
_exit:
discardStringBuilder(&sb);
return NONE_VAL();
}
static int pasteEnabled = 0;
static KrkValue paste(int argc, KrkValue argv[], int hasKw) {
pasteEnabled = !pasteEnabled;
fprintf(stderr, "Pasting is %s.\n", pasteEnabled ? "enabled" : "disabled");
return NONE_VAL();
}
/**
* @brief Read a line of input.
*
* In an interactive session, presents the configured prompt without
* a trailing linefeed.
*/
KRK_FUNC(input,{
FUNCTION_TAKES_AT_MOST(1);
char * prompt = "";
int promptLength = 0;
char * syntaxHighlighter = NULL;
if (argc) {
CHECK_ARG(0,str,KrkString*,_prompt);
prompt = _prompt->chars;
promptLength = _prompt->codesLength;
}
if (hasKw) {
KrkValue promptwidth;
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("promptwidth")), &promptwidth)) {
if (!IS_INTEGER(promptwidth)) return TYPE_ERROR(int,promptwidth);
promptLength = AS_INTEGER(promptwidth);
}
KrkValue syntax;
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("syntax")), &syntax)) {
if (!IS_STRING(syntax)) return TYPE_ERROR(str,syntax);
syntaxHighlighter = AS_CSTRING(syntax);
}
}
return readLine(prompt, promptLength, syntaxHighlighter);
})
#ifndef NO_RLINE
/**
@ -358,7 +455,7 @@ static int debuggerHook(KrkCallFrame * frame) {
} else {
size_t frameCount = krk_currentThread.frameCount;
/* Compile statement */
KrkFunction * expression = krk_compile(arg,"<debugger>");
KrkCodeObject * expression = krk_compile(arg,"<debugger>");
if (expression) {
/* Make sure stepping is disabled first. */
krk_debug_disableSingleStep();
@ -429,7 +526,7 @@ static int debuggerHook(KrkCallFrame * frame) {
}
if (!strcmp(arg,"breakpoints")) {
KrkFunction * codeObject = NULL;
KrkCodeObject * codeObject = NULL;
size_t offset = 0;
int flags = 0;
int enabled = 0;
@ -521,11 +618,13 @@ _dbgQuit:
}
static void handleSigint(int sigNum) {
if (krk_currentThread.frameCount) {
krk_currentThread.flags |= KRK_THREAD_SIGNALLED;
}
/* Don't set the signal flag if the VM is not running */
if (!krk_currentThread.frameCount) return;
krk_currentThread.flags |= KRK_THREAD_SIGNALLED;
}
signal(sigNum, handleSigint);
static void bindSignalHandlers(void) {
signal(SIGINT, handleSigint);
}
static void findInterpreter(char * argv[]) {
@ -566,6 +665,7 @@ static int runString(char * argv[], int flags, char * string) {
findInterpreter(argv);
krk_initVM(flags);
krk_startModule("__main__");
krk_attachNamedValue(&krk_currentThread.module->fields,"__doc__", NONE_VAL());
krk_interpret(string, "<stdin>");
krk_freeVM();
return 0;
@ -595,7 +695,7 @@ static int compileFile(char * argv[], int flags, char * fileName) {
krk_startModule("__main__");
/* Call the compiler directly. */
KrkFunction * func = krk_compile(buf, fileName);
KrkCodeObject * func = krk_compile(buf, fileName);
/* See if there was an exception. */
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
@ -715,7 +815,7 @@ _finishArgs:
for (int arg = optind; arg < argc + (optind == argc); ++arg) krk_pop();
/* Bind interrupt signal */
signal(SIGINT, handleSigint);
bindSignalHandlers();
#ifdef BUNDLE_LIBS
/* Add any other modules you want to include that are normally built as shared objects. */
@ -724,6 +824,24 @@ _finishArgs:
KrkValue result = INTEGER_VAL(0);
/**
* Add general builtins that aren't part of the core VM.
* This is where we provide @c input in particular.
*/
KRK_DOC(BIND_FUNC(vm.builtins,input), "@brief Read a line of input.\n"
"@arguments [prompt], promptwidth=None, syntax=None\n\n"
"Read a line of input from @c stdin. If the @c rline library is available, "
"it will be used to gather input. Input reading stops on end-of file or when "
"a read ends with a line feed, which will be removed from the returned string. "
"If a prompt is provided, it will be printed without a line feed before requesting "
"input. If @c rline is available, the prompt will be passed to the library as the "
"left-hand prompt string. If not provided, @p promptwidth will default to the width "
"of @p prompt in codepoints; if you are providing a prompt with escape characters or "
"characters with multi-column East-Asian Character Width be sure to pass a value "
"for @p promptwidth that reflects the display width of your prompt. "
"If provided, @p syntax specifies the name of an @c rline syntax module to "
"provide color highlighting of the input line.");
if (moduleAsMain) {
krk_push(OBJECT_VAL(krk_copyString("__main__",8)));
int out = !krk_importModule(
@ -736,8 +854,14 @@ _finishArgs:
return out;
} else if (optind == argc) {
/* Add builtins for the repl, but hide them from the globals() list. */
krk_defineNative(&vm.builtins->fields, "exit", exitFunc);
krk_defineNative(&vm.builtins->fields, "paste", paste);
KRK_DOC(BIND_FUNC(vm.builtins,exit), "@brief Exit the interactive repl.\n\n"
"Only available from the interactive interpreter; exits the repl.");
KRK_DOC(BIND_FUNC(vm.builtins,paste), "@brief Toggle paste mode.\n"
"@arguments enabled=None\n\n"
"Toggles paste-safe mode, disabling automatic indentation in the repl. "
"If @p enabled is specified, the mode can be directly specified, otherwise "
"it will be set to the opposite of the current mode. The new mode will be "
"printed to stderr.");
/* The repl runs in the context of a top-level module so each input
* line can share a globals state with the others. */

View File

@ -0,0 +1,219 @@
#pragma once
/**
* @file chunk.h
* @brief Structures and enums for bytecode chunks.
*/
#include "kuroko.h"
#include "value.h"
/**
* @brief Instruction opcode values
*
* The instruction opcode table is divided in four parts. The high two bits of each
* opcode encodes the number of operands to pull from the codeobject and thus the
* size (generally) of the instruction (note that OP_CLOSURE(_LONG) has additional
* arguments depending on the function it points to).
*
* 0-operand opcodes are "simple" instructions that generally only deal with stack
* values and require no additional arguments.
*
* 1- and 3- operand opcodes are paired as 'short' and 'long'. While the VM does not
* currently depend on these instructions having the same values in the lower 6 bits,
* it is recommended that this property remain true.
*
* 2-operand opcodes are generally jump instructions.
*/
typedef enum {
OP_ADD = 1,
OP_BITAND,
OP_BITNEGATE,
OP_BITOR,
OP_BITXOR,
OP_CLEANUP_WITH,
OP_CLOSE_UPVALUE,
OP_DIVIDE,
OP_DOCSTRING,
OP_EQUAL,
OP_FALSE,
OP_FINALIZE,
OP_GREATER,
OP_INHERIT,
OP_INVOKE_DELETE,
OP_INVOKE_DELSLICE,
OP_INVOKE_GETSLICE,
OP_INVOKE_GETTER,
OP_INVOKE_SETSLICE,
OP_INVOKE_SETTER,
OP_IS,
OP_LESS,
OP_MODULO,
OP_MULTIPLY,
OP_NEGATE,
OP_NONE,
OP_NOT,
OP_POP,
OP_POW,
OP_RAISE,
OP_RETURN,
OP_SHIFTLEFT,
OP_SHIFTRIGHT,
OP_SUBTRACT,
OP_SWAP,
OP_TRUE,
OP_FILTER_EXCEPT,
OP_INVOKE_ITER,
OP_INVOKE_CONTAINS,
OP_BREAKPOINT, /* NEVER output this instruction in the compiler or bad things can happen */
OP_YIELD,
OP_ANNOTATE,
/* current highest: 44 */
OP_CALL = 64,
OP_CLASS,
OP_CLOSURE,
OP_CONSTANT,
OP_DEFINE_GLOBAL,
OP_DEL_GLOBAL,
OP_DEL_PROPERTY,
OP_DUP,
OP_EXPAND_ARGS,
OP_GET_GLOBAL,
OP_GET_LOCAL,
OP_GET_PROPERTY,
OP_GET_SUPER,
OP_GET_UPVALUE,
OP_IMPORT,
OP_IMPORT_FROM,
OP_INC,
OP_KWARGS,
OP_CLASS_PROPERTY,
OP_SET_GLOBAL,
OP_SET_LOCAL,
OP_SET_PROPERTY,
OP_SET_UPVALUE,
OP_TUPLE,
OP_UNPACK,
OP_LIST_APPEND,
OP_DICT_SET,
OP_SET_ADD,
OP_MAKE_LIST,
OP_MAKE_DICT,
OP_MAKE_SET,
OP_REVERSE,
OP_JUMP_IF_FALSE = 128,
OP_JUMP_IF_TRUE,
OP_JUMP,
OP_LOOP,
OP_PUSH_TRY,
OP_PUSH_WITH,
OP_CALL_LONG = 192,
OP_CLASS_LONG,
OP_CLOSURE_LONG,
OP_CONSTANT_LONG,
OP_DEFINE_GLOBAL_LONG,
OP_DEL_GLOBAL_LONG,
OP_DEL_PROPERTY_LONG,
OP_DUP_LONG,
OP_EXPAND_ARGS_LONG,
OP_GET_GLOBAL_LONG,
OP_GET_LOCAL_LONG,
OP_GET_PROPERTY_LONG,
OP_GET_SUPER_LONG,
OP_GET_UPVALUE_LONG,
OP_IMPORT_LONG,
OP_IMPORT_FROM_LONG,
OP_INC_LONG,
OP_KWARGS_LONG,
OP_CLASS_PROPERTY_LONG,
OP_SET_GLOBAL_LONG,
OP_SET_LOCAL_LONG,
OP_SET_PROPERTY_LONG,
OP_SET_UPVALUE_LONG,
OP_TUPLE_LONG,
OP_UNPACK_LONG,
OP_LIST_APPEND_LONG,
OP_DICT_SET_LONG,
OP_SET_ADD_LONG,
OP_MAKE_LIST_LONG,
OP_MAKE_DICT_LONG,
OP_MAKE_SET_LONG,
OP_REVERSE_LONG,
} KrkOpCode;
/**
* @brief Map entry of instruction offsets to line numbers.
*
* Each code object contains an array of line mappings, indicating
* the start offset of each line. Since a line typically maps to
* multiple opcodes, and spans of many lines may map to no opcodes
* in the case of blank lines or docstrings, this array is stored
* as a sequence of <starOffset, line> pairs rather than a simple
* array of one or the other.
*/
typedef struct {
size_t startOffset;
size_t line;
} KrkLineMap;
/**
* @brief Opcode chunk of a code object.
*
* Opcode chunks are internal to code objects and I'm not really
* sure why we're still separating them from the KrkCodeObjects.
*
* Stores four flexible arrays using three different formats:
* - Code, representing opcodes and operands.
* - Lines, representing offset-to-line mappings.
* - Filename, the string name of the source file.
* - Constants, an array of values referenced by the code object.
*/
typedef struct {
size_t count;
size_t capacity;
uint8_t * code;
size_t linesCount;
size_t linesCapacity;
KrkLineMap * lines;
KrkString * filename;
KrkValueArray constants;
} KrkChunk;
/**
* @brief Initialize an opcode chunk.
* @memberof KrkChunk
*/
extern void krk_initChunk(KrkChunk * chunk);
/**
* @memberof KrkChunk
* @brief Append a byte to an opcode chunk.
*/
extern void krk_writeChunk(KrkChunk * chunk, uint8_t byte, size_t line);
/**
* @brief Release the resources allocated to an opcode chunk.
* @memberof KrkChunk
*/
extern void krk_freeChunk(KrkChunk * chunk);
/**
* @brief Add a new constant value to an opcode chunk.
* @memberof KrkChunk
*/
extern size_t krk_addConstant(KrkChunk * chunk, KrkValue value);
/**
* @brief Write an OP_CONSTANT(_LONG) instruction.
* @memberof KrkChunk
*/
extern void krk_emitConstant(KrkChunk * chunk, size_t ind, size_t line);
/**
* @brief Add a new constant and write an instruction for it.
* @memberof KrkChunk
*/
extern size_t krk_writeConstant(KrkChunk * chunk, KrkValue value, size_t line);

View File

@ -0,0 +1,22 @@
#pragma once
/**
* @file compiler.h
* @brief Exported methods for the source compiler.
*/
#include "object.h"
/**
* @brief Compile a string to a code object.
*
* Compiles the source string 'src' into a code object.
*
* @param src Source code string to compile.
* @param fileName Path name of the source file or a representative string like "<stdin>"
* @return The code object resulting from the compilation, or NULL if compilation failed.
*/
extern KrkCodeObject * krk_compile(const char * src, char * fileName);
/**
* @brief Mark objects owned by the compiler as in use.
*/
extern void krk_markCompilerRoots(void);

View File

@ -0,0 +1,248 @@
#pragma once
/**
* @file debug.h
* @brief Functions for debugging bytecode execution.
*
* This header provides functions for disassembly bytecode to
* readable instruction traces, mapping bytecode offsets to
* source code lines, and handling breakpoint instructions.
*
* Several of these functions are also exported to user code
* in the @ref mod_dis module.
*
* Note that these functions are not related to manage code
* exception handling, but instead inteaded to provide a low
* level interface to the VM's execution process and allow
* for the implementation of debuggers and debugger extensions.
*/
#include <stdio.h>
#include "vm.h"
#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_disassembleCodeObject(FILE * f, KrkCodeObject * 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, KrkCodeObject * 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);
/**
* @brief Called by the VM when a breakpoint is encountered.
*
* Internal method, should not generally be called.
*/
extern int krk_debugBreakpointHandler(void);
/**
* @brief Called by the VM on single step.
*
* Handles calling the registered debugger hook, if one has
* been set, and performing the requested response action.
* Also takes care of re-enabling REPEAT breakpoints.
*
* Internal method, should not generally be called.
*/
extern int krk_debuggerHook(KrkCallFrame * frame);
/**
* @brief Function pointer for a debugger hook.
* @ref krk_debug_registerCallback()
*/
typedef int (*KrkDebugCallback)(KrkCallFrame *frame);
/**
* @brief Register a debugger callback.
*
* The registered function @p hook will be called when an
* OP_BREAKPOINT instruction is encountered. The debugger
* is provided a pointer to the active frame and can use
* functions from the krk_debug_* suite to examine the
* thread state, execute more code, and resume execution.
*
* The debugger hook will be called from the thread that
* encountered the breakpoint, and should return one of
* the KRK_DEBUGGER_ status values to inform the VM of
* what action to take.
*
* @param hook The hook function to attach.
* @return 0 if the hook was registered; 1 if a hook was
* already registered, in which case the new hook
* has not been registered.
*/
extern int krk_debug_registerCallback(KrkDebugCallback hook);
/**
* @brief Add a breakpoint to the given line of a file.
*
* The interpreter will scan all code objects and attach
* a breakpoint instruction to the first such object that
* has a match filename and contains an instruction with
* a matching line mapping. Breakpoints can only be added
* to code which has already been compiled.
*
* @param filename KrkString * representation of a source
* code filename to look for.
* @param line The line to set the breakpoint at.
* @param flags Allows configuring the disposition of the breakpoint.
* @return A breakpoint identifier handle on success, or -1 on failure.
*/
extern int krk_debug_addBreakpointFileLine(KrkString * filename, size_t line, int flags);
/**
* @brief Add a breakpoint to the given code object.
*
* A new breakpoint is added to the breakpoint table and
* the code object's bytecode is overwritten to insert
* the OP_BREAKPOINT instruction.
*
* Callers must ensure that @p offset points to the opcode
* portion of an instruction, and not an operand, or the
* attempt to add a breakpoint can corrupt the bytecode.
*
* @param codeObject KrkCodeObject* for the code object to break on.
* @param offset Bytecode offset to insert the breakpoint at.
* @param flags Allows configuring the disposition of the breakpoint.
* @return A breakpoint identifier handle on success, or -1 on failure.
*/
extern int krk_debug_addBreakpointCodeOffset(KrkCodeObject * codeObject, size_t offset, int flags);
/**
* @brief Remove a breakpoint from the breakpoint table.
*
* Removes the breakpoint @p breakpointId from the breakpoint
* table, restoring the bytecode instruction for it if it
* was enabled.
*
* @param breakpointId The breakpoint to remove.
* @return 0 on success, 1 if the breakpoint identifier is invalid.
*/
extern int krk_debug_removeBreakpoint(int breakpointId);
/**
* @brief Enable a breakpoint.
*
* Writes the OP_BREAKPOINT instruction into the function
* bytecode chunk for the given breakpoint.
*
* @param breakpointId The breakpoint to enable.
* @return 0 on success, 1 if the breakpoint identifier is invalid.
*/
extern int krk_debug_enableBreakpoint(int breakpointId);
/**
* @brief Disable a breakpoint.
*
* Restores the bytecode instructions for the given breakpoint
* if it is currently enabled.
*
* No error is returned if the breakpoint was already disabled.
*
* @param breakpointId The breakpoint to disable.
* @return 0 on success, 1 if the breakpoint identifier is invalid.
*/
extern int krk_debug_disableBreakpoint(int breakpointId);
/**
* @brief Enable single stepping in the current thread.
*/
extern void krk_debug_enableSingleStep(void);
/**
* @brief Disable single stepping in the current thread.
*/
extern void krk_debug_disableSingleStep(void);
/**
* @brief Safely dump a traceback to stderr.
*
* Wraps @ref krk_dumpTraceback() so it can be safely
* called from a debugger.
*/
extern void krk_debug_dumpTraceback(void);
/**
* @brief Retreive information on a breakpoint.
*
* Can be called by debuggers to examine the details of a breakpoint by its handle.
* Information is returned through the pointers provided as parameters.
*
* @param breakIndex Breakpoint handle to examine.
* @param funcOut (Out) The code object this breakpoint is in.
* @param offsetOut (Out) The bytecode offset within the code object where the breakpoint is located.
* @param flagsOut (Out) The configuration flags for the breakpoint.
* @param enabledOut (Out) Whether the breakpoint is enabled or not.
* @return 0 on success, -1 on out of range, -2 if the selected slot was removed.
*/
extern int krk_debug_examineBreakpoint(int breakIndex, KrkCodeObject ** funcOut, size_t * offsetOut, int * flagsOut, int *enabledOut);
/**
* @brief Print the elements on the stack.
*
* Prints the elements on the stack for the current thread to @p file,
* highlighting @p frame as the activate call point and indicating
* where its arguments start.
*/
extern void krk_debug_dumpStack(FILE * f, KrkCallFrame * frame);
/**
* @def KRK_BREAKPOINT_NORMAL
*
* This breakpoint should fire once and then remain in the table
* to be re-enabled later.
*
* @def KRK_BREAKPOINT_ONCE
*
* This breakpoint should fire once and then be removed from the
* breakpoint table.
*
* @def KRK_BREAKPOINT_REPEAT
*
* After this breakpoint has fired and execution resumes, the
* interpreter should re-enable it to fire again until it is
* removed by a call to @ref krk_debug_removeBreakpoint()
*/
#define KRK_BREAKPOINT_NORMAL 0
#define KRK_BREAKPOINT_ONCE 1
#define KRK_BREAKPOINT_REPEAT 2
#define KRK_DEBUGGER_CONTINUE 0
#define KRK_DEBUGGER_ABORT 1
#define KRK_DEBUGGER_STEP 2
#define KRK_DEBUGGER_RAISE 3
#define KRK_DEBUGGER_QUIT 4

View File

@ -0,0 +1,37 @@
#pragma once
/**
* @file kuroko.h
* @brief Top-level header with configuration macros.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#if defined(__EMSCRIPTEN__)
typedef long long krk_integer_type;
# define PRIkrk_int "%lld"
# define PRIkrk_hex "%llx"
# define parseStrInt strtoll
#elif defined(_WIN32)
typedef long long krk_integer_type;
# define PRIkrk_int "%I64d"
# define PRIkrk_hex "%I64x"
# define parseStrInt strtoll
# define ENABLE_THREADING
# else
typedef long krk_integer_type;
# define PRIkrk_int "%ld"
# define PRIkrk_hex "%lx"
# define parseStrInt strtol
# define ENABLE_THREADING
#endif
#define ENABLE_THREADING
#ifdef DEBUG
#define ENABLE_DISASSEMBLY
#define ENABLE_TRACING
#define ENABLE_SCAN_TRACING
#define ENABLE_STRESS_GC
#endif

View File

@ -0,0 +1,80 @@
#pragma once
/**
* @file memory.h
* @brief Functions for dealing with garbage collection and memory allocation.
*/
#include "kuroko.h"
#include "object.h"
#include "table.h"
#define GROW_CAPACITY(c) ((c) < 8 ? 8 : (c) * 2)
#define GROW_ARRAY(t,p,o,n) (t*)krk_reallocate(p,sizeof(t)*o,sizeof(t)*n)
#define FREE_ARRAY(t,a,c) krk_reallocate(a,sizeof(t) * c, 0)
#define FREE(t,p) krk_reallocate(p,sizeof(t),0)
#define ALLOCATE(type, count) (type*)krk_reallocate(NULL,0,sizeof(type)*(count))
/**
* @brief Resize an allocated heap object.
*
* Allocates or reallocates the heap object 'ptr', tracking changes
* in sizes from 'old' to 'new'. If 'ptr' is NULL, 'old' should be 0,
* and a new pointer will be allocated of size 'new'.
*
* @param ptr Heap object to resize.
* @param old Current size of the object.
* @param new New size of the object.
* @return New pointer for heap object.
*/
extern void * krk_reallocate(void * ptr, size_t old, size_t new);
/**
* @brief Release all objects.
*
* Generally called automatically by krk_freeVM(); releases all of
* the GC-tracked heap objects.
*/
extern void krk_freeObjects(void);
/**
* @brief Run a cycle of the garbage collector.
*
* Runs one scan-sweep cycle of the garbage collector, potentially
* freeing unused resources and advancing potentially-unused
* resources to the next stage of removal.
*
* @return The number of bytes released by this collection cycle.
*/
extern size_t krk_collectGarbage(void);
/**
* @brief During a GC scan cycle, mark a value as used.
*
* When defining a new type in a C extension, this function should
* be used by the type's _ongcscan callback to mark any values not
* already tracked by the garbage collector.
*
* @param value The value to mark.
*/
extern void krk_markValue(KrkValue value);
/**
* @brief During a GC scan cycle, mark an object as used.
*
* Equivalent to krk_markValue but operates directly on an object.
*
* @param object The object to mark.
*/
extern void krk_markObject(KrkObj * object);
/**
* @brief During a GC scan cycle, mark the contents of a table as used.
*
* Marks all keys and values in a table as used. Generally applied
* to the internal storage of mapping types.
*
* @param table The table to mark.
*/
extern void krk_markTable(KrkTable * table);

View File

@ -0,0 +1,411 @@
#pragma once
/**
* @file object.h
* @brief Struct definitions for core object types.
*/
#include <stdio.h>
#include "kuroko.h"
#include "value.h"
#include "chunk.h"
#include "table.h"
#ifdef ENABLE_THREADING
#include <pthread.h>
#endif
typedef enum {
KRK_OBJ_CODEOBJECT,
KRK_OBJ_NATIVE,
KRK_OBJ_CLOSURE,
KRK_OBJ_STRING,
KRK_OBJ_UPVALUE,
KRK_OBJ_CLASS,
KRK_OBJ_INSTANCE,
KRK_OBJ_BOUND_METHOD,
KRK_OBJ_TUPLE,
KRK_OBJ_BYTES,
} KrkObjType;
#undef KrkObj
/**
* @brief The most basic object type.
*
* This is the base of all object types and contains
* the core structures for garbage collection.
*/
typedef struct KrkObj {
KrkObjType type;
unsigned char isMarked:1;
unsigned char inRepr:1;
unsigned char generation:2;
unsigned char isImmortal:1;
uint32_t hash;
struct KrkObj * next;
} KrkObj;
typedef enum {
KRK_STRING_ASCII = 0,
KRK_STRING_UCS1 = 1,
KRK_STRING_UCS2 = 2,
KRK_STRING_UCS4 = 4,
KRK_STRING_INVALID = 5,
} KrkStringType;
#undef KrkString
/**
* @brief Immutable sequence of Unicode codepoints.
* @extends KrkObj
*/
typedef struct KrkString {
KrkObj obj;
KrkStringType type;
size_t length;
size_t codesLength;
char * chars;
void * codes;
} KrkString;
/**
* @brief Immutable sequence of bytes.
* @extends KrkObj
*/
typedef struct {
KrkObj obj;
size_t length;
uint8_t * bytes;
} KrkBytes;
/**
* @brief Storage for values referenced from nested functions.
* @extends KrkObj
*/
typedef struct KrkUpvalue {
KrkObj obj;
int location;
KrkValue closed;
struct KrkUpvalue * next;
struct KrkThreadState * owner;
} KrkUpvalue;
/**
* @brief Metadata on a local variable name in a function.
*
* This is used by the disassembler to print the names of
* locals when they are referenced by instructions.
*/
typedef struct {
size_t id;
size_t birthday;
size_t deathday;
KrkString * name;
} KrkLocalEntry;
struct KrkInstance;
/**
* @brief Code object.
* @extends KrkObj
*
* Contains the static data associated with a chunk of bytecode.
*/
typedef struct {
KrkObj obj;
short requiredArgs;
short keywordArgs;
size_t upvalueCount;
KrkChunk chunk;
KrkString * name;
KrkString * docstring;
KrkValueArray requiredArgNames;
KrkValueArray keywordArgNames;
size_t localNameCapacity;
size_t localNameCount;
KrkLocalEntry * localNames;
unsigned char collectsArguments:1;
unsigned char collectsKeywords:1;
unsigned char isGenerator:1;
struct KrkInstance * globalsContext;
KrkString * qualname;
} KrkCodeObject;
/**
* @brief Function object.
* @extends KrkObj
*
* Not to be confused with code objects, a closure is a single instance of a function.
*/
typedef struct {
KrkObj obj;
KrkCodeObject * function;
KrkUpvalue ** upvalues;
size_t upvalueCount;
unsigned char isClassMethod:1;
unsigned char isStaticMethod:1;
KrkValue annotations;
KrkTable fields;
} KrkClosure;
typedef void (*KrkCleanupCallback)(struct KrkInstance *);
/**
* @brief Type object.
* @extends KrkObj
*
* Represents classes defined in user code as well as classes defined
* by C extensions to represent method tables for new types.
*/
typedef struct KrkClass {
KrkObj obj;
KrkString * name;
KrkString * filename;
KrkString * docstring;
struct KrkClass * base;
KrkTable methods;
size_t allocSize;
KrkCleanupCallback _ongcscan;
KrkCleanupCallback _ongcsweep;
/* Quick access for common stuff */
KrkObj * _getter;
KrkObj * _setter;
KrkObj * _getslice;
KrkObj * _reprer;
KrkObj * _tostr;
KrkObj * _call;
KrkObj * _init;
KrkObj * _eq;
KrkObj * _len;
KrkObj * _enter;
KrkObj * _exit;
KrkObj * _delitem;
KrkObj * _iter;
KrkObj * _getattr;
KrkObj * _dir;
KrkObj * _setslice;
KrkObj * _delslice;
KrkObj * _contains;
KrkObj * _descget;
KrkObj * _descset;
KrkObj * _classgetitem;
} KrkClass;
/**
* @brief An object of a class.
* @extends KrkObj
*
* Created by class initializers, instances are the standard type of objects
* built by managed code. Not all objects are instances, but all instances are
* objects, and all instances have well-defined class.
*/
typedef struct KrkInstance {
KrkObj obj;
KrkClass * _class;
KrkTable fields;
} KrkInstance;
/**
* @brief A function that has been attached to an object to serve as a method.
* @extends KrkObj
*
* When a bound method is called, its receiver is implicitly extracted as
* the first argument. Bound methods are created whenever a method is retreived
* from the class of a value.
*/
typedef struct {
KrkObj obj;
KrkValue receiver;
KrkObj * method;
} KrkBoundMethod;
typedef KrkValue (*NativeFn)(int argCount, KrkValue* args, int hasKwargs);
/**
* @brief Managed binding to a C function.
* @extends KrkObj
*
* Represents a C function that has been exposed to managed code.
*/
typedef struct {
KrkObj obj;
NativeFn function;
const char * name;
const char * doc;
int isMethod;
} KrkNative;
/**
* @brief Immutable sequence of arbitrary values.
* @extends KrkObj
*
* Tuples are fixed-length non-mutable collections of values intended
* for use in situations where the flexibility of a list is not needed.
*/
typedef struct {
KrkObj obj;
KrkValueArray values;
} KrkTuple;
/**
* @brief Mutable array of values.
* @extends KrkInstance
*
* A list is a flexible array of values that can be extended, cleared,
* sorted, rearranged, iterated over, etc.
*/
typedef struct {
KrkInstance inst;
KrkValueArray values;
#ifdef ENABLE_THREADING
pthread_rwlock_t rwlock;
#endif
} KrkList;
/**
* @brief Flexible mapping type.
* @extends KrkInstance
*
* Provides key-to-value mappings as a first-class object type.
*/
typedef struct {
KrkInstance inst;
KrkTable entries;
} KrkDict;
struct DictItems {
KrkInstance inst;
KrkValue dict;
size_t i;
};
struct DictKeys {
KrkInstance inst;
KrkValue dict;
size_t i;
};
/**
* @brief Yield ownership of a C string to the GC and obtain a string object.
* @memberof KrkString
*
* 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.
* @memberof KrkString
*
* 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.
* @memberof KrkString
*
* 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.
* @memberof KrkString
*
* 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.
* @memberof KrkString
*
* 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 NativeFn KrkGenericAlias;
extern KrkCodeObject * krk_newCodeObject(void);
extern KrkNative * krk_newNative(NativeFn function, const char * name, int type);
extern KrkClosure * krk_newClosure(KrkCodeObject * function);
extern KrkUpvalue * krk_newUpvalue(int slot);
extern KrkClass * krk_newClass(KrkString * name, KrkClass * base);
extern KrkInstance * krk_newInstance(KrkClass * _class);
extern KrkBoundMethod * krk_newBoundMethod(KrkValue receiver, KrkObj * method);
extern KrkTuple * krk_newTuple(size_t length);
extern KrkBytes * krk_newBytes(size_t length, uint8_t * source);
extern void krk_bytesUpdateHash(KrkBytes * bytes);
extern void krk_tupleUpdateHash(KrkTuple * self);
#define KRK_STRING_FAST(string,offset) (uint32_t)\
(string->type <= 1 ? ((uint8_t*)string->codes)[offset] : \
(string->type == 2 ? ((uint16_t*)string->codes)[offset] : \
((uint32_t*)string->codes)[offset]))
#define CODEPOINT_BYTES(cp) (cp < 0x80 ? 1 : (cp < 0x800 ? 2 : (cp < 0x10000 ? 3 : 4)))
#define krk_isObjType(v,t) (IS_OBJECT(v) && (AS_OBJECT(v)->type == (t)))
#define OBJECT_TYPE(value) (AS_OBJECT(value)->type)
#define IS_STRING(value) krk_isObjType(value, KRK_OBJ_STRING)
#define AS_STRING(value) ((KrkString *)AS_OBJECT(value))
#define AS_CSTRING(value) (((KrkString *)AS_OBJECT(value))->chars)
#define IS_BYTES(value) krk_isObjType(value, KRK_OBJ_BYTES)
#define AS_BYTES(value) ((KrkBytes*)AS_OBJECT(value))
#define IS_NATIVE(value) krk_isObjType(value, KRK_OBJ_NATIVE)
#define AS_NATIVE(value) ((KrkNative *)AS_OBJECT(value))
#define IS_CLOSURE(value) krk_isObjType(value, KRK_OBJ_CLOSURE)
#define AS_CLOSURE(value) ((KrkClosure *)AS_OBJECT(value))
#define IS_CLASS(value) krk_isObjType(value, KRK_OBJ_CLASS)
#define AS_CLASS(value) ((KrkClass *)AS_OBJECT(value))
#define IS_INSTANCE(value) krk_isObjType(value, KRK_OBJ_INSTANCE)
#define AS_INSTANCE(value) ((KrkInstance *)AS_OBJECT(value))
#define IS_BOUND_METHOD(value) krk_isObjType(value, KRK_OBJ_BOUND_METHOD)
#define AS_BOUND_METHOD(value) ((KrkBoundMethod*)AS_OBJECT(value))
#define IS_TUPLE(value) krk_isObjType(value, KRK_OBJ_TUPLE)
#define AS_TUPLE(value) ((KrkTuple *)AS_OBJECT(value))
#define AS_LIST(value) (&((KrkList *)AS_OBJECT(value))->values)
#define AS_DICT(value) (&((KrkDict *)AS_OBJECT(value))->entries)
#define IS_codeobject(value) krk_isObjType(value, KRK_OBJ_CODEOBJECT)
#define AS_codeobject(value) ((KrkCodeObject *)AS_OBJECT(value))

View File

@ -0,0 +1,80 @@
SIMPLE(OP_RETURN)
SIMPLE(OP_ADD)
SIMPLE(OP_SUBTRACT)
SIMPLE(OP_MULTIPLY)
SIMPLE(OP_DIVIDE)
SIMPLE(OP_NEGATE)
SIMPLE(OP_MODULO)
SIMPLE(OP_NONE)
SIMPLE(OP_TRUE)
SIMPLE(OP_FALSE)
SIMPLE(OP_NOT)
SIMPLE(OP_EQUAL)
SIMPLE(OP_GREATER)
SIMPLE(OP_LESS)
SIMPLE(OP_POP)
SIMPLE(OP_INHERIT)
SIMPLE(OP_RAISE)
SIMPLE(OP_CLOSE_UPVALUE)
SIMPLE(OP_DOCSTRING)
SIMPLE(OP_BITOR)
SIMPLE(OP_BITXOR)
SIMPLE(OP_BITAND)
SIMPLE(OP_SHIFTLEFT)
SIMPLE(OP_SHIFTRIGHT)
SIMPLE(OP_BITNEGATE)
SIMPLE(OP_INVOKE_GETTER)
SIMPLE(OP_INVOKE_SETTER)
SIMPLE(OP_INVOKE_DELETE)
SIMPLE(OP_INVOKE_GETSLICE)
SIMPLE(OP_INVOKE_SETSLICE)
SIMPLE(OP_INVOKE_DELSLICE)
SIMPLE(OP_INVOKE_ITER)
SIMPLE(OP_INVOKE_CONTAINS)
SIMPLE(OP_SWAP)
SIMPLE(OP_FINALIZE)
SIMPLE(OP_IS)
SIMPLE(OP_POW)
SIMPLE(OP_CLEANUP_WITH)
SIMPLE(OP_FILTER_EXCEPT)
SIMPLE(OP_BREAKPOINT)
SIMPLE(OP_YIELD)
SIMPLE(OP_ANNOTATE)
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
CONSTANT(OP_CONSTANT,(void)0)
CONSTANT(OP_GET_GLOBAL,(void)0)
CONSTANT(OP_SET_GLOBAL,(void)0)
CONSTANT(OP_DEL_GLOBAL,(void)0)
CONSTANT(OP_CLASS,(void)0)
CONSTANT(OP_GET_PROPERTY, (void)0)
CONSTANT(OP_SET_PROPERTY, (void)0)
CONSTANT(OP_DEL_PROPERTY,(void)0)
CONSTANT(OP_CLASS_PROPERTY, (void)0)
CONSTANT(OP_CLOSURE, CLOSURE_MORE)
CONSTANT(OP_IMPORT, (void)0)
CONSTANT(OP_IMPORT_FROM, (void)0)
CONSTANT(OP_GET_SUPER, (void)0)
OPERAND(OP_KWARGS, (void)0)
OPERAND(OP_SET_LOCAL, LOCAL_MORE)
OPERAND(OP_GET_LOCAL, LOCAL_MORE)
OPERAND(OP_SET_UPVALUE, (void)0)
OPERAND(OP_GET_UPVALUE, (void)0)
OPERAND(OP_CALL, (void)0)
OPERAND(OP_INC, (void)0)
OPERAND(OP_TUPLE, (void)0)
OPERAND(OP_UNPACK, (void)0)
OPERAND(OP_DUP,(void)0)
OPERAND(OP_EXPAND_ARGS,EXPAND_ARGS_MORE)
OPERAND(OP_LIST_APPEND, (void)0)
OPERAND(OP_DICT_SET, (void)0)
OPERAND(OP_SET_ADD, (void)0)
OPERAND(OP_MAKE_LIST, (void)0)
OPERAND(OP_MAKE_DICT, (void)0)
OPERAND(OP_MAKE_SET, (void)0)
OPERAND(OP_REVERSE, (void)0)
JUMP(OP_JUMP,+)
JUMP(OP_JUMP_IF_FALSE,+)
JUMP(OP_JUMP_IF_TRUE,+)
JUMP(OP_LOOP,-)
JUMP(OP_PUSH_TRY,+)
JUMP(OP_PUSH_WITH,+)

View File

@ -0,0 +1,186 @@
#pragma once
/**
* @file scanner.h
* @brief Definitions used by the token scanner.
*/
typedef enum {
TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN,
TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE,
TOKEN_LEFT_SQUARE, TOKEN_RIGHT_SQUARE,
TOKEN_COLON,
TOKEN_COMMA,
TOKEN_DOT,
TOKEN_MINUS,
TOKEN_PLUS,
TOKEN_SEMICOLON,
TOKEN_SOLIDUS,
TOKEN_ASTERISK,
TOKEN_POW,
TOKEN_MODULO,
TOKEN_AT,
TOKEN_CARET, /* ^ (xor) */
TOKEN_AMPERSAND, /* & (and) */
TOKEN_PIPE, /* | (or) */
TOKEN_TILDE, /* ~ (negate) */
TOKEN_LEFT_SHIFT, /* << */
TOKEN_RIGHT_SHIFT,/* >> */
TOKEN_BANG,
TOKEN_GREATER,
TOKEN_LESS,
TOKEN_ARROW, /* -> */
TOKEN_WALRUS, /* := */
/* Comparisons */
TOKEN_GREATER_EQUAL,
TOKEN_LESS_EQUAL,
TOKEN_BANG_EQUAL,
TOKEN_EQUAL_EQUAL,
/* Assignments */
TOKEN_EQUAL,
TOKEN_LSHIFT_EQUAL, /* <<= */
TOKEN_RSHIFT_EQUAL, /* >>= */
TOKEN_PLUS_EQUAL, /* += */
TOKEN_MINUS_EQUAL, /* -= */
TOKEN_PLUS_PLUS, /* ++ */
TOKEN_MINUS_MINUS, /* -- */
TOKEN_CARET_EQUAL,
TOKEN_PIPE_EQUAL,
TOKEN_AMP_EQUAL,
TOKEN_SOLIDUS_EQUAL,
TOKEN_ASTERISK_EQUAL,
TOKEN_POW_EQUAL,
TOKEN_MODULO_EQUAL,
TOKEN_STRING,
TOKEN_BIG_STRING,
TOKEN_NUMBER,
/*
* Everything after this, up to indentation,
* consists of alphanumerics.
*/
TOKEN_IDENTIFIER,
TOKEN_AND,
TOKEN_CLASS,
TOKEN_DEF,
TOKEN_DEL,
TOKEN_ELSE,
TOKEN_FALSE,
TOKEN_FOR,
TOKEN_IF,
TOKEN_IMPORT,
TOKEN_IN,
TOKEN_IS,
TOKEN_LET,
TOKEN_NONE,
TOKEN_NOT,
TOKEN_OR,
TOKEN_ELIF,
TOKEN_PASS,
TOKEN_RETURN,
TOKEN_SELF,
TOKEN_SUPER,
TOKEN_TRUE,
TOKEN_WHILE,
TOKEN_TRY,
TOKEN_EXCEPT,
TOKEN_RAISE,
TOKEN_BREAK,
TOKEN_CONTINUE,
TOKEN_AS,
TOKEN_FROM,
TOKEN_LAMBDA,
TOKEN_ASSERT,
TOKEN_YIELD,
TOKEN_WITH,
TOKEN_PREFIX_B,
TOKEN_PREFIX_F,
TOKEN_INDENTATION,
TOKEN_EOL,
TOKEN_RETRY,
TOKEN_ERROR,
TOKEN_EOF,
} KrkTokenType;
/**
* @brief A token from the scanner.
*
* Represents a single scanned item from the scanner, such as a keyword,
* string literal, numeric literal, identifier, etc.
*/
typedef struct {
KrkTokenType type;
const char * start;
size_t length;
size_t line;
const char * linePtr;
size_t col;
size_t literalWidth;
} KrkToken;
/**
* @brief Token scanner state.
*
* Stores the state of the compiler's scanner, reading from a source
* character string and tracking the current line.
*/
typedef struct {
const char * start;
const char * cur;
const char * linePtr;
size_t line;
int startOfLine;
int hasUnget;
KrkToken unget;
} KrkScanner;
/**
* @brief Initialize the compiler to scan tokens from 'src'.
*
* FIXME: There is currently only a single static scanner state;
* along with making the compiler re-entrant, the scanner
* needs to also be re-entrant; there's really no reason
* these can't all just take a KrkScanner* argument.
*/
extern void krk_initScanner(const char * src);
/**
* @brief Read the next token from the scanner.
*
* FIXME: Or, maybe the scanner shouldn't even be available outside
* of the compiler, that would make some sense as well, as it's
* a low-level detail, but we use it for tab completion in the
* main repl, so I'm not sure that's feasible right now.
*/
extern KrkToken krk_scanToken(void);
/**
* @brief Push a token back to the scanner to be reprocessed.
*
* Pushes a previously-scanned token back to the scanner.
* Used to implement small backtracking operations at the
* end of block constructs like 'if' and 'try'.
*/
extern void krk_ungetToken(KrkToken token);
/**
* @brief Rewind the scanner to a previous state.
*
* Resets the current scanner to the state in 'to'. Used by
* the compiler to implement comprehensions, which would otherwise
* not be possible in a single-pass compiler.
*/
extern void krk_rewindScanner(KrkScanner to);
/**
* @brief Retreive a copy of the current scanner state.
*
* Used with krk_rewindScanner() to implement rescanning
* for comprehensions.
*/
extern KrkScanner krk_tellScanner(void);

View File

@ -0,0 +1,153 @@
#pragma once
/**
* @file table.h
* @brief Implementation of a generic hash table.
*
* I was going to just use the ToaruOS hashmap library, but to make following
* the book easier, let's just start from their Table implementation; it has
* an advantage of using stored entries and fixed arrays, so it has some nice
* properties despite being chained internally...
*/
#include <stdlib.h>
#include "kuroko.h"
#include "value.h"
#include "threads.h"
/**
* @brief One (key,value) pair in a table.
*/
typedef struct {
KrkValue key;
KrkValue value;
} KrkTableEntry;
/**
* @brief Simple hash table of arbitrary keys to values.
*/
typedef struct {
size_t count;
size_t capacity;
KrkTableEntry * entries;
} KrkTable;
/**
* @brief Initialize a hash table.
* @memberof KrkTable
*
* 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.
* @memberof KrkTable
*
* 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'.
* @memberof KrkTable
*
* 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.
* @memberof KrkTable
*
* 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 table Should always be @c &vm.strings
* @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.
* @memberof KrkTable
*
* 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.
* @memberof KrkTable
*
* 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.
* @memberof KrkTable
*
* 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.
* @memberof KrkTable
*
* 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.
* @memberof KrkValue
*
* 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

@ -0,0 +1,41 @@
#pragma once
/**
* @file threads.h
* @brief Convience header for providing atomic operations to threads.
*/
#ifdef ENABLE_THREADING
#include <pthread.h>
#include <sched.h>
#ifdef _WIN32
#include <windows.h>
#include <winnt.h>
#define sched_yield() YieldProcessor()
#endif
static inline void _krk_internal_spin_lock(int volatile * lock) {
while(__sync_lock_test_and_set(lock, 0x01)) {
sched_yield();
}
}
static inline void _krk_internal_spin_unlock(int volatile * lock) {
__sync_lock_release(lock);
}
#define _obtain_lock(v) _krk_internal_spin_lock(&v);
#define _release_lock(v) _krk_internal_spin_unlock(&v);
#else
#define _obtain_lock(v)
#define _release_lock(v)
#define pthread_rwlock_init(a,b)
#define pthread_rwlock_wrlock(a)
#define pthread_rwlock_rdlock(a)
#define pthread_rwlock_unlock(a)
#endif

View File

@ -0,0 +1,296 @@
#pragma once
/**
* @file util.h
* @brief Utilities for creating native bindings.
*
* This is intended for use in C extensions to provide a uniform interface
* for defining extension methods and ensuring they have consistent argument
* and keyword argument usage.
*/
#include "object.h"
#include "vm.h"
#include "memory.h"
/* Quick macro for turning string constants into KrkString*s */
#define S(c) (krk_copyString(c,sizeof(c)-1))
#define likely(cond) __builtin_expect(!!(cond), 1)
#define unlikely(cond) __builtin_expect(!!(cond), 0)
#ifndef _WIN32
#define _noexport __attribute__((visibility("hidden")))
#else
#define _noexport
#endif
/**
* Binding macros.
*
* These macros are intended to be used together to define functions for a class.
*/
static inline const char * _method_name(const char * func) {
const char * out = func;
if (*out == '_') out++;
while (*out && *out != '_') out++;
if (*out == '_') out++;
return out;
}
#define ADD_BASE_CLASS(obj, name, baseClass) krk_makeClass(vm.builtins, &obj, name, baseClass)
/* _method_name works for this, but let's skip the inlined function call where possible */
#define _function_name(f) (f+5)
#define ATTRIBUTE_NOT_ASSIGNABLE() do { if (argc != 1) return krk_runtimeError(vm.exceptions->attributeError, "attribute '%s' is not assignable", \
_method_name(__func__)); } while (0)
#define METHOD_TAKES_NONE() do { if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes no arguments (%d given)", \
_method_name(__func__), (argc-1)); } while (0)
#define METHOD_TAKES_EXACTLY(n) do { if (argc != (n+1)) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)", \
_method_name(__func__), "exactly", n, (n != 1) ? "s" : "", (argc-1)); } while (0)
#define METHOD_TAKES_AT_LEAST(n) do { if (argc < (n+1)) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)", \
_method_name(__func__), "at least", n, (n != 1) ? "s" : "", (argc-1)); } while (0)
#define METHOD_TAKES_AT_MOST(n) do { if (argc > (n+1)) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)", \
_method_name(__func__), "at most", n, (n != 1) ? "s" : "", (argc-1)); } while (0)
#define FUNCTION_TAKES_NONE() do { if (argc != 0) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes no arguments (%d given)", \
_function_name(__func__), (argc)); } while (0)
#define FUNCTION_TAKES_EXACTLY(n) do { if (argc != n) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)", \
_function_name(__func__), "exactly", n, (n != 1) ? "s" : "", (argc)); } while (0)
#define FUNCTION_TAKES_AT_LEAST(n) do { if (argc < n) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)", \
_function_name(__func__), "at least", n, (n != 1) ? "s" : "", (argc)); } while (0)
#define FUNCTION_TAKES_AT_MOST(n) do { if (argc > n) return krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)", \
_function_name(__func__), "at most", n, (n != 1) ? "s" : "", (argc)); } while (0)
#define TYPE_ERROR(expected,value) krk_runtimeError(vm.exceptions->typeError, "%s() expects %s, not '%s'", \
/* Function name */ _method_name(__func__), /* expected type */ #expected, krk_typeName(value))
#define NOT_ENOUGH_ARGS() krk_runtimeError(vm.exceptions->argumentError, "%s() missing required positional argument", \
/* Function name */ _method_name(__func__))
#define CHECK_ARG(i, type, ctype, name) \
if (argc < (i+1)) return NOT_ENOUGH_ARGS(); \
if (!IS_ ## type (argv[i])) return TYPE_ERROR(type,argv[i]); \
ctype name __attribute__((unused)) = AS_ ## type (argv[i])
#define FUNC_NAME(klass, name) _ ## klass ## _ ## name
#define FUNC_SIG(klass, name) _noexport KrkValue FUNC_NAME(klass,name) (int argc, KrkValue argv[], int hasKw)
#define KRK_METHOD(klass, name, body) FUNC_SIG(klass, name) { \
CHECK_ARG(0,klass,CURRENT_CTYPE,CURRENT_NAME); \
body; return NONE_VAL(); }
#define KRK_FUNC(name,body) static KrkValue _krk_ ## name (int argc, KrkValue argv[], int hasKw) { \
body; return NONE_VAL(); }
/* This assumes you have a KrkInstance called `module` in the current scope. */
#define MAKE_CLASS(klass) do { krk_makeClass(module,&klass,#klass,vm.baseClasses->objectClass); klass ->allocSize = sizeof(struct klass); } while (0)
#define BIND_METHOD(klass,method) krk_defineNative(&klass->methods, "." #method, _ ## klass ## _ ## method)
#define BIND_FIELD(klass,method) krk_defineNative(&klass->methods, ":" #method, _ ## klass ## _ ## method)
#define BIND_PROP(klass,method) krk_defineNativeProperty(&klass->methods, #method, _ ## klass ## _ ## method)
#define BIND_FUNC(module,func) krk_defineNative(&module->fields, #func, _krk_ ## func)
/**
* @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;
sb->capacity = GROW_CAPACITY(old);
sb->bytes = GROW_ARRAY(char, sb->bytes, old, sb->capacity);
}
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) {
size_t old = sb->capacity;
sb->capacity = GROW_CAPACITY(old);
}
sb->bytes = realloc(sb->bytes, sb->capacity);
}
for (size_t i = 0; i < len; ++i) {
sb->bytes[sb->length++] = *(str++);
}
}
/**
* @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();
}
#define IS_int(o) (IS_INTEGER(o))
#define AS_int(o) (AS_INTEGER(o))
#define IS_bool(o) (IS_BOOLEAN(o))
#define AS_bool(o) (AS_BOOLEAN(o))
#define IS_float(o) (IS_FLOATING(o))
#define AS_float(o) (AS_FLOATING(o))
#define IS_list(o) krk_isInstanceOf(o,vm.baseClasses->listClass)
#define AS_list(o) (KrkList*)AS_OBJECT(o)
#define IS_listiterator(o) krk_isInstanceOf(o,vm.baseClasses->listiteratorClass)
#define AS_listiterator(o) AS_INSTANCE(o)
#define IS_str(o) (IS_STRING(o)||krk_isInstanceOf(o,vm.baseClasses->strClass))
#define AS_str(o) (KrkString*)AS_OBJECT(o)
#define IS_striterator(o) (krk_isInstanceOf(o,vm.baseClasses->striteratorClass))
#define AS_striterator(o) (AS_INSTANCE(o))
#define IS_dict(o) krk_isInstanceOf(o,vm.baseClasses->dictClass)
#define AS_dict(o) (KrkDict*)AS_OBJECT(o)
#define IS_dictitems(o) krk_isInstanceOf(o,vm.baseClasses->dictitemsClass)
#define AS_dictitems(o) ((struct DictItems*)AS_OBJECT(o))
#define IS_dictkeys(o) krk_isInstanceOf(o,vm.baseClasses->dictkeysClass)
#define AS_dictkeys(o) ((struct DictKeys*)AS_OBJECT(o))
#define IS_bytesiterator(o) (krk_isInstanceOf(o,vm.baseClasses->bytesiteratorClass))
#define AS_bytesiterator(o) (AS_INSTANCE(o))
#ifndef unpackError
#define unpackError(fromInput) return krk_runtimeError(vm.exceptions->typeError, "'%s' object is not iterable", krk_typeName(fromInput));
#endif
#define unpackIterable(fromInput) do { \
KrkClass * type = krk_getType(fromInput); \
if (type->_iter) { \
size_t stackOffset = krk_currentThread.stackTop - krk_currentThread.stack; \
krk_push(fromInput); \
krk_push(krk_callSimple(OBJECT_VAL(type->_iter), 1, 0)); \
do { \
krk_push(krk_currentThread.stack[stackOffset]); \
krk_push(krk_callSimple(krk_peek(0), 0, 1)); \
if (krk_valuesSame(krk_currentThread.stack[stackOffset], krk_peek(0))) { \
krk_pop(); \
krk_pop(); \
break; \
} \
unpackArray(1,krk_peek(0)); \
krk_pop(); \
} while (1); \
} else { \
unpackError(fromInput); \
} \
} while (0)
#define unpackIterableFast(fromInput) do { \
KrkValue iterableValue = (fromInput); \
if (IS_TUPLE(iterableValue)) { \
unpackArray(AS_TUPLE(iterableValue)->values.count, AS_TUPLE(iterableValue)->values.values[i]); \
} else if (IS_INSTANCE(iterableValue) && AS_INSTANCE(iterableValue)->_class == vm.baseClasses->listClass) { \
unpackArray(AS_LIST(iterableValue)->count, AS_LIST(iterableValue)->values[i]); \
} else if (IS_INSTANCE(iterableValue) && AS_INSTANCE(iterableValue)->_class == vm.baseClasses->dictClass) { \
unpackArray(AS_DICT(iterableValue)->count, krk_dict_nth_key_fast(AS_DICT(iterableValue)->capacity, AS_DICT(iterableValue)->entries, i)); \
} else if (IS_STRING(iterableValue)) { \
unpackArray(AS_STRING(iterableValue)->codesLength, krk_string_get(2,(KrkValue[]){iterableValue,INTEGER_VAL(i)},0)); \
} else { \
unpackIterable(iterableValue); \
} \
} while (0)
static inline void _setDoc_class(KrkClass * thing, const char * text, size_t size) {
thing->docstring = krk_copyString(text, size);
}
static inline void _setDoc_instance(KrkInstance * thing, const char * text, size_t size) {
krk_attachNamedObject(&thing->fields, "__doc__", (KrkObj*)krk_copyString(text, size));
}
static inline void _setDoc_native(KrkNative * thing, const char * text, size_t size) {
(void)size;
thing->doc = text;
}
/**
* @def KRK_DOC(thing,text)
* @brief Attach documentation to a thing of various types.
*
* Classes store their docstrings directly, rather than in their attribute tables.
* Instances use the attribute table and store strings with the name @c \__doc__.
* Native functions store direct C string pointers for documentation.
*
* This macro provides a generic interface for applying documentation strings to
* any of the above types, and handles not attaching documentation when built
* with KRK_NO_DOCUMENTATION.
*/
#ifdef KRK_NO_DOCUMENTATION
# define KRK_DOC(thing, text) (thing);
#else
# define KRK_DOC(thing, text) \
_Generic(&((thing)[0]), \
KrkClass*: _setDoc_class, \
KrkInstance*: _setDoc_instance, \
KrkNative*: _setDoc_native \
)(thing,text,sizeof(text)-1)
#endif
#define BUILTIN_FUNCTION(name, func, docStr) KRK_DOC(krk_defineNative(&vm.builtins->fields, name, func), docStr)

View File

@ -0,0 +1,210 @@
#pragma once
/**
* @file value.h
* @brief Definitions for primitive stack references.
*/
#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 KrkObj KrkObj;
typedef struct KrkString 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 {
KRK_VAL_NONE,
KRK_VAL_BOOLEAN,
KRK_VAL_INTEGER,
KRK_VAL_FLOATING,
KRK_VAL_HANDLER,
KRK_VAL_OBJECT,
KRK_VAL_KWARGS,
} 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 {
char boolean;
krk_integer_type integer;
double floating;
KrkJumpTarget handler;
KrkObj * object;
} 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.
* @memberof KrkValueArray
*
* 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.
* @memberof KrkValueArray
*
* 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.
* @memberof KrkValueArray
*
* 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.
* @memberof KrkValue
*
* 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.
* @memberof KrkValue
*
* 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.
* @memberof KrkValue
*
* 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.
* @memberof KrkValue
*
* 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){KRK_VAL_BOOLEAN, {.integer = value}})
#define NONE_VAL(value) ((KrkValue){KRK_VAL_NONE, {.integer = 0}})
#define INTEGER_VAL(value) ((KrkValue){KRK_VAL_INTEGER, {.integer = value}})
#define FLOATING_VAL(value) ((KrkValue){KRK_VAL_FLOATING,{.floating = value}})
#define HANDLER_VAL(ty,ta) ((KrkValue){KRK_VAL_HANDLER, {.handler = (KrkJumpTarget){.type = ty, .target = ta}}})
#define OBJECT_VAL(value) ((KrkValue){KRK_VAL_OBJECT, {.object = (KrkObj*)value}})
#define KWARGS_VAL(value) ((KrkValue){KRK_VAL_KWARGS, {.integer = value}})
#define AS_BOOLEAN(value) ((value).as.integer)
#define AS_INTEGER(value) ((value).as.integer)
#define AS_FLOATING(value) ((value).as.floating)
#define AS_HANDLER(value) ((value).as.handler)
#define AS_OBJECT(value) ((value).as.object)
#define IS_BOOLEAN(value) ((value).type == KRK_VAL_BOOLEAN)
#define IS_NONE(value) ((value).type == KRK_VAL_NONE)
#define IS_INTEGER(value) (((value).type == KRK_VAL_INTEGER) || ((value.type) == KRK_VAL_BOOLEAN))
#define IS_FLOATING(value) ((value).type == KRK_VAL_FLOATING)
#define IS_HANDLER(value) ((value).type == KRK_VAL_HANDLER)
#define IS_OBJECT(value) ((value).type == KRK_VAL_OBJECT)
#define IS_KWARGS(value) ((value).type == KRK_VAL_KWARGS)
#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)

View File

@ -0,0 +1,820 @@
#pragma once
/**
* @file vm.h
* @brief Core API for the bytecode virtual machine.
*
* Functions and structures declared here make up the bulk of the public C API
* for Kuroko, including initializing the VM and passing code to be interpreted.
*/
#include <stdarg.h>
#include <sys/types.h>
#include "kuroko.h"
#include "value.h"
#include "table.h"
#include "object.h"
/**
* @def KRK_CALL_FRAMES_MAX
* @brief Maximum depth of the call stack in managed-code function calls.
*/
#define KRK_CALL_FRAMES_MAX 64
/**
* @def KRK_THREAD_SCRATCH_SIZE
* @brief Extra space for each thread to store a set of working values safe from the GC.
*
* Various operations require threads to remove values from the stack but ensure
* they are not lost to garbage collection. This space allows each thread to keep
* a few things around during those operations.
*/
#define KRK_THREAD_SCRATCH_SIZE 3
/**
* @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; /**< 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 */
} KrkCallFrame;
/**
* @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 @c KrkString, we keep an array of the @c 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
* @c METHOD_INIT is the offset for the string value for @c "__init__".
*/
typedef enum {
METHOD_INIT,
METHOD_STR,
METHOD_REPR,
METHOD_GET,
METHOD_SET,
METHOD_CLASS,
METHOD_NAME,
METHOD_FILE,
METHOD_INT,
METHOD_FLOAT,
METHOD_CHR,
METHOD_LEN,
METHOD_DOC,
METHOD_BASE,
METHOD_GETSLICE,
METHOD_ORD,
METHOD_CALL,
METHOD_EQ,
METHOD_ENTER,
METHOD_EXIT,
METHOD_DELITEM,
METHOD_ITER,
METHOD_GETATTR,
METHOD_DIR,
METHOD_SETSLICE,
METHOD_DELSLICE,
METHOD_CONTAINS,
METHOD_DESCGET,
METHOD_DESCSET,
METHOD_CLASSGETITEM,
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; /**< @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. */
KrkClass * assertionError; /**< @exception AssertionError An @c assert statement failed. */
};
/**
* @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; /**< 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 function object (KrkClosure) or native bind (KrkNative) */
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. */
KrkClass * propertyClass; /**< Magic object that calls a function when accessed from an instance through the dot operator. */
KrkClass * codeobjectClass; /**< Static compiled bytecode container (KrkCodeObject) */
KrkClass * generatorClass; /**< Generator object. */
};
/**
* @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 KrkThreadState {
struct KrkThreadState * next; /**< Invasive list pointer to next thread. */
KrkCallFrame * 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; /**< 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. */
KrkValue scratchSpace[KRK_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 KrkVM {
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 */
KrkThreadState * threads; /**< Invasive linked list of all VM threads. */
} KrkVM;
/* Thread-specific flags */
#define KRK_THREAD_ENABLE_TRACING (1 << 0)
#define KRK_THREAD_ENABLE_DISASSEMBLY (1 << 1)
#define KRK_THREAD_ENABLE_SCAN_TRACING (1 << 2)
#define KRK_THREAD_HAS_EXCEPTION (1 << 3)
#define KRK_THREAD_SINGLE_STEP (1 << 4)
#define KRK_THREAD_SIGNALLED (1 << 5)
/* Global flags */
#define KRK_GLOBAL_ENABLE_STRESS_GC (1 << 8)
#define KRK_GLOBAL_GC_PAUSED (1 << 9)
#define KRK_GLOBAL_CLEAN_OUTPUT (1 << 10)
#ifdef ENABLE_THREADING
# define threadLocal __thread
#else
# define threadLocal
#endif
/**
* @brief Thread-local VM state.
*
* See @c KrkThreadState for more information.
*/
#if defined(_WIN32) && !defined(KRKINLIB)
#define krk_currentThread (*krk_getCurrentThread())
#else
extern threadLocal KrkThreadState krk_currentThread;
#endif
/**
* @brief Singleton instance of the shared VM state.
*/
extern KrkVM krk_vm;
/**
* @def vm
* @brief Convenience macro for namespacing.
*/
#define vm krk_vm
/**
* @brief Initialize the VM at program startup.
* @memberof KrkVM
*
* 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.
* @memberof KrkVM
*
* 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 should 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 @c 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 fromFile Path to the source file, or a representative string like "<stdin>".
* @return The value of the executed code, which is either the value of an explicit 'return'
* statement, or the last expression value from an executed statement. If an uncaught
* exception occurred, this will be @c None and @c krk_currentThread.flags should
* indicate @c KRK_THREAD_HAS_EXCEPTION and @c krk_currentThread.currentException
* should contain the raised exception value.
*/
extern KrkValue krk_interpret(const char * src, char * fromFile);
/**
* @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 fromFile Value to assign to @c \__file__
* @return As with @c 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, char * fromFile);
/**
* @brief Load and run a file as a module.
*
* Similar to @c krk_runfile but ensures that execution of the VM returns to the caller
* after the file is run. This should be run after calling @c 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 fromFile Value to assign to @c \__file__
* @return The object representing the module, or None if execution of the file failed.
*/
extern KrkValue krk_callfile(const char * fileName, 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 Swap the top of the stack of the value @p distance slots down.
*
* Exchanges the values at the top of the stack and @p distance slots from the top
* without removing or shuffling anything in between.
*
* @param distance How from down from the top of the stack to swap (0 = the top)
*/
extern void krk_swap(int distance);
/**
* @brief Get the name of the type of a value.
* @memberof KrkValue
*
* 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 @p value
*/
extern const char * krk_typeName(KrkValue value);
/**
* @brief Attach a native C function to an attribute table.
* @memberof KrkTable
*
* 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 @p name begins with a '.', the native function
* is marked as a method. If @p name begins with a ':', the native function
* is marked as a dynamic property.
*
* @param table Attribute table to attach to, such as @c &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.
* @memberof KrkTable
*
* Mostly the same as @c krk_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 @c KrkProperty* objects.
*
* @param table Attribute table to attach to, such as @c &someInstance->fields
* @param name Nil-terminated C string with the name to assign
* @param func Native function pointer to attach
* @return A pointer to the property object created.
*/
extern KrkNative * krk_defineNativeProperty(KrkTable * table, const char * name, NativeFn func);
/**
* @brief Attach a value to an attribute table.
* @memberof KrkTable
*
* Manages the stack shuffling and boxing of the name string when attaching
* a value to an attribute table. Rather than using @c krk_tableSet, this is
* the preferred method of supplying fields to objects from C code.
*
* @param table Attribute table to attach to, such as @c &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.
* @memberof KrkTable
*
* Manages the stack shuffling and boxing of the name string when attaching
* an object to an attribute table. Rather than using @c krk_tableSet, this is
* the preferred method of supplying fields to objects from C code.
*
* This is a convenience wrapper around @c krk_attachNamedValue.
*
* @param table Attribute table to attach to, such as @c &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 @c KRK_THREAD_HAS_EXCEPTION flag is set.
*
* @param type Class pointer for the exception type, eg. @c vm.exceptions->valueError
* @param fmt Format string.
* @return As a convenience to C extension authors, returns @c None
*/
extern KrkValue krk_runtimeError(KrkClass * type, const char * fmt, ...);
/**
* @brief Get a pointer to the current thread state.
*
* Generally equivalent to @c &krk_currentThread, though @c 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);
/**
* @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
* @c krk_runNext. Alternatively, see @c 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);
/**
* @brief Get the class representing a value.
* @memberof KrkValue
*
* 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.
* @memberof KrkValue
*
* Searches the class hierarchy of the given value to determine if it is
* a subtype of @p type. As this chains through the inheritence tree, the
* more deeply subclassed @p obj is, the longer it may take to determine
* if it is a subtype of @p type. All types should eventually be subtypes
* of the @ref object type, so this condition should not be checked. For
* some types, convenience macros are available. If you need to check if
* @p obj is a specific type, exclusive of subtypes, look at
* @c krk_getType() instead of using this function.
*
* @param obj Value to examine.
* @param type Class object to test for membership of.
* @return 1 if @p obj is an instance of @p type or of a subclass of @p type
*/
extern int krk_isInstanceOf(KrkValue obj, KrkClass * type);
/**
* @brief Perform method binding on the stack.
* @memberof KrkClass
*
* Performs attribute lookup from the class @p _class for @p name.
* If @p name is not a valid method, the binding fails.
* If @p 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 @ref 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.
* @memberof KrkValue
*
* Executes the given callable object (function, bound method, object
* with a @c \__call__() method, etc.) using @p 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.
* @memberof KrkList
*
* This is the native function bound to @c listOf
*/
extern KrkValue krk_list_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Create a dict object.
* @memberof KrkDict
*
* This is the native function bound to @c dictOf
*/
extern KrkValue krk_dict_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Create a tuple object.
* @memberof KrkTuple
*
* This is the native function bound to @c tupleOf
*/
extern KrkValue krk_tuple_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Create a set object.
* @memberof Set
*
* This is the native function bound to @c setOf
*/
extern KrkValue krk_set_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Call a callable and manage VM state to obtain the return value.
* @memberof KrkValue
*
* 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
* @c krk_callValue 's @c extra paramater for details on how @p 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.
* @memberof KrkClass
*
* Creates a class object, output to @p _class, setting its name to @p name, inheriting
* from @p base, and attaching it with its name to the fields table of the given @p module.
*
* @param module Pointer to an instance for a module to attach to, or @c 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 @p _class.
*/
extern KrkClass * krk_makeClass(KrkInstance * module, KrkClass ** _class, const char * name, KrkClass * base);
/**
* @brief Finalize a class by collecting pointers to core methods.
* @memberof KrkClass
*
* 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 @c stderr
*
* This function is exposed as a convenience for repl developers. Normally,
* the VM will call @c krk_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 @c \__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 @c \__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 @c \__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 @c object.__dir__
*/
extern KrkValue krk_dirObject(int argc, KrkValue argv[], int hasKw);
/**
* @brief Load a module from a file with a specified name.
*
* This is generally called by the import mechanisms to load a single module and
* will establish a module context internally to load the new module into, return
* a KrkValue representing that module context through the @p moduleOut parameter.
*
* @param path Dotted path of the module, used for file lookup.
* @param moduleOut Receives a value with the module object.
* @param runAs Name to attach to @c \__name__ for this module, different from @p path
* @return 1 if the module was loaded, 0 if an @ref ImportError occurred.
*/
extern int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs);
/**
* @brief Load a module by a dotted name.
*
* Given a package identifier, attempt to the load module into the module table.
* This is a thin wrapper around `krk_importModule()`.
*
* @param name String object of the dot-separated package path to import.
* @return 1 if the module was loaded, 0 if an @ref ImportError occurred.
*/
extern int krk_doRecursiveModuleLoad(KrkString * name);
/**
* @brief Load the dotted name @p name with the final element as @p runAs
*
* If @p name was imported previously with a name different from @p runAs,
* it will be imported again with the new name; this may result in
* unexpected behaviour. Generally, @p runAs is used to specify that the
* module should be run as `__main__`.
*
* @param name Dotted path name of a module.
* @param runAs Alternative name to attach to @c \__name__ for the module.
* @return 1 on success, 0 on failure.
*/
extern int krk_importModule(KrkString * name, KrkString * runAs);
/**
* @brief Determine the truth of a value.
* @memberof KrkValue
*
* 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 Obtain a property of an object by name.
* @memberof KrkValue
*
* This is a convenience function that works in essentially the
* same way as the OP_GET_PROPERTY instruction.
*
* @param value Value to examine.
* @param name C-string of the property name to query.
* @return The requested property, or None with an @ref AttributeError
* exception set in the current thread if the attribute was
* not found.
*/
extern KrkValue krk_valueGetAttribute(KrkValue value, char * name);
/**
* @brief See @ref krk_valueGetAttribute
*/
extern KrkValue krk_valueGetAttribute_default(KrkValue value, char * name, KrkValue defaultVal);
/**
* @brief Set a property of an object by name.
* @memberof KrkValue
*
* This is a convenience function that works in essentially the
* same way as the OP_SET_PROPERTY instruction.
*
* @param owner The owner of the property to modify.
* @param name C-string of the property name to modify.
* @param to The value to assign.
* @return The set value, or None with an @ref AttributeError
* exception set in the current thread if the object can
* not have a property set.
*/
extern KrkValue krk_valueSetAttribute(KrkValue owner, char * name, KrkValue to);
/**
* @brief Concatenate two strings.
*
* This is a convenience function which calls @c str.__add__ on the top stack
* values. Generally, this should be avoided - use @c 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___getitem__(int argc, KrkValue argv[], int hasKw);
#define krk_string_get _str___getitem__
extern KrkValue _str___int__(int argc, KrkValue argv[], int hasKw);
#define krk_string_int _str___int__
extern KrkValue _str___float__(int argc, KrkValue argv[], int hasKw);
#define krk_string_float _str___float__
extern KrkValue _str_split(int argc, KrkValue argv[], int hasKw);
#define krk_string_split _str_split
extern KrkValue _str_format(int argc, KrkValue argv[], int hasKw);
#define krk_string_format _str_format
extern KrkValue krk_dict_nth_key_fast(size_t capacity, KrkTableEntry * entries, size_t index);
extern void _createAndBind_numericClasses(void);
extern void _createAndBind_strClass(void);
extern void _createAndBind_listClass(void);
extern void _createAndBind_tupleClass(void);
extern void _createAndBind_bytesClass(void);
extern void _createAndBind_dictClass(void);
extern void _createAndBind_functionClass(void);
extern void _createAndBind_rangeClass(void);
extern void _createAndBind_setClass(void);
extern void _createAndBind_builtins(void);
extern void _createAndBind_type(void);
extern void _createAndBind_exceptions(void);
extern void _createAndBind_gcMod(void);
extern void _createAndBind_timeMod(void);
extern void _createAndBind_osMod(void);
extern void _createAndBind_fileioMod(void);
#ifdef ENABLE_THREADING
extern void _createAndBind_threadsMod(void);
#endif
extern KrkValue krk_operator_lt(KrkValue,KrkValue);
extern KrkValue krk_operator_gt(KrkValue,KrkValue);
extern void _createAndBind_generatorClass(void);
extern KrkInstance * krk_buildGenerator(KrkClosure *, KrkValue *, size_t);

2
kuroko

@ -1 +1 @@
Subproject commit 34c8d68720ea5ee736b49094b8b1836a267604a9
Subproject commit ca7d0dae2ec78a7746b49ecaae1c8836488a811b

View File

@ -1186,7 +1186,7 @@ KrkValue krk_module_onload__yutani(void) {
Message->allocSize = sizeof(struct MessageClass);
Message->_ongcsweep = _message_sweep;
/* All the MSG_ constants */
#define TYPE(type) krk_attachNamedValue(&Message->fields, "MSG_" #type, INTEGER_VAL(YUTANI_MSG_ ## type))
#define TYPE(type) krk_attachNamedValue(&Message->methods, "MSG_" #type, INTEGER_VAL(YUTANI_MSG_ ## type))
TYPE(HELLO); TYPE(WINDOW_NEW); TYPE(FLIP); TYPE(KEY_EVENT); TYPE(MOUSE_EVENT);
TYPE(WINDOW_MOVE); TYPE(WINDOW_CLOSE); TYPE(WINDOW_SHOW); TYPE(WINDOW_HIDE);
TYPE(WINDOW_STACK); TYPE(WINDOW_FOCUS_CHANGE); TYPE(WINDOW_MOUSE_EVENT);
@ -1347,7 +1347,7 @@ KrkValue krk_module_onload__yutani(void) {
" Calculate the rendered width of the given string when drawn with this font.";
krk_defineNative(&YutaniFont->methods, ":size", _font_size);
/* Some static values */
#define ATTACH_FONT(name) krk_attachNamedValue(&YutaniFont->fields, #name, INTEGER_VAL(SDF_ ## name))
#define ATTACH_FONT(name) krk_attachNamedValue(&YutaniFont->methods, #name, INTEGER_VAL(SDF_ ## name))
ATTACH_FONT(FONT_THIN);
ATTACH_FONT(FONT_BOLD);
ATTACH_FONT(FONT_MONO);
@ -1387,11 +1387,11 @@ KrkValue krk_module_onload__yutani(void) {
krk_finalizeClass(MenuEntrySeparatorClass);
Decorator = krk_createClass(module, "Decorator", NULL);
krk_defineNative(&Decorator->fields, "get_bounds", _decor_get_bounds);
krk_defineNative(&Decorator->fields, "render", _decor_render);
krk_defineNative(&Decorator->fields, "handle_event", _decor_handle_event);
krk_defineNative(&Decorator->fields, "show_default_menu", _decor_show_default_menu);
#define ATTACH_CONSTANT(name) krk_attachNamedValue(&Decorator->fields, #name, INTEGER_VAL(name))
krk_defineNative(&Decorator->methods, "get_bounds", _decor_get_bounds);
krk_defineNative(&Decorator->methods, "render", _decor_render);
krk_defineNative(&Decorator->methods, "handle_event", _decor_handle_event);
krk_defineNative(&Decorator->methods, "show_default_menu", _decor_show_default_menu);
#define ATTACH_CONSTANT(name) krk_attachNamedValue(&Decorator->methods, #name, INTEGER_VAL(name))
ATTACH_CONSTANT(DECOR_OTHER);
ATTACH_CONSTANT(DECOR_CLOSE);
ATTACH_CONSTANT(DECOR_RESIZE);

View File

@ -37,6 +37,7 @@ class Classifier(object):
'<toaru/button.h>': (None, '-ltoaru_button', ['<toaru/graphics.h>','<toaru/sdf.h>', '<toaru/icon_cache.h>']),
# Kuroko
'<kuroko.h>': ('../../../kuroko/src', '-lkuroko', []),
'<kuroko/kuroko.h>': (None, '-lkuroko', []),
# OPTIONAL third-party libraries, for extensions / ports
'<ft2build.h>': ('freetype2', '-lfreetype', []),
'<pixman.h>': ('pixman-1', '-lpixman-1', []),