Add docstrings, __doc__, and help()

This commit is contained in:
K. Lange 2020-12-30 16:59:21 +09:00
parent a5928b18c9
commit f7e1c0da0d
10 changed files with 111 additions and 42 deletions

View File

@ -1,4 +1,5 @@
class list():
"Resizable array with direct constant-time indexing."
def __init__():
self._list=__builtins__.list_new()
def __get__(i):
@ -6,10 +7,12 @@ class list():
def __set__(i,v):
return __builtins__.list_set(self._list,i,v)
def append(v):
"Add an entry to the end of the list."
return __builtins__.list_append(self._list,v)
def __len__():
return __builtins__.list_length(self._list)
def extend(i):
"Add all entries from an iterable to the end of this list."
for v in i:
self.append(v)
return self.__len__()
@ -37,6 +40,7 @@ class list():
return I(0)
class dict():
"Hashmap of arbitrary keys to arbitrary values."
def __init__(self):
self._map = __builtins__.hash_new()
def __get__(self, ind):
@ -58,6 +62,7 @@ class dict():
def capacity(self):
return __builtins__.hash_capacity(self._map)
def keys(self):
"Returns an iterable of the keys in this dictionary."
class KeyIterator():
def __init__(self, target):
self.target = target
@ -81,6 +86,7 @@ class dict():
return KeyIterator(self)
class range:
"Helpful iterable."
def __init__(self, min, max):
self.min = min
self.max = max
@ -104,6 +110,19 @@ def int(obj=None): return (obj and obj.__int__()) or 0
def float(obj=None): return (obj and obj.__float__()) or 0.0
def dir(obj): return obj.__dir__()
export list,dict,range,len,str,int,float,dir
def help(obj=None):
if not obj:
print "Kuroko - Interpreted bytecode VM."
print " usage:"
print " help() - Displays this message."
print " help(func) - Displays docstring for function, if present."
print " help(class) - Displays docstring for class, if present."
else:
try:
print obj.__doc__
except:
print "No docstring available for", obj
export list,dict,range,len,str,int,float,dir,help
return object()

View File

@ -51,6 +51,7 @@ typedef enum {
OP_GET_SUPER,
OP_PUSH_TRY,
OP_RAISE,
OP_DOCSTRING,
OP_CONSTANT_LONG = 128,
OP_PRINT_LONG,

View File

@ -137,6 +137,7 @@ static void classDeclaration();
static void declareVariable();
static void namedVariable(KrkToken name, int canAssign);
static void addLocal(KrkToken name);
static void string(int canAssign);
static void errorAt(KrkToken * token, const char * message) {
if (parser.panicMode) return;
@ -266,12 +267,6 @@ static void freeCompiler(Compiler * compiler) {
FREE_ARRAY(Upvalue,compiler->upvalues, compiler->upvaluesSpace);
}
static void endOfLine() {
if (!(match(TOKEN_EOL) || match(TOKEN_EOF))) {
errorAtCurrent("Expected end of line.");
}
}
static size_t emitConstant(KrkValue value) {
return krk_writeConstant(currentChunk(), value, parser.previous.line);
}
@ -466,25 +461,37 @@ static void endScope() {
static void block(size_t indentation, const char * blockName) {
if (match(TOKEN_EOL)) {
/* Begin actual blocks */
if (check(TOKEN_INDENTATION)) {
size_t currentIndentation = parser.current.length;
if (currentIndentation <= indentation) {
return;
if (currentIndentation <= indentation) return;
advance();
if (!strcmp(blockName,"def") && match(TOKEN_STRING)) {
size_t before = currentChunk()->count;
string(0);
/* That wrote to the chunk, rewind it; this should only ever go back two bytes
* because this should only happen as the first thing in a function definition,
* and thus this _should_ be the first constant and thus opcode + one-byte operand
* to OP_CONSTANT, but just to be safe we'll actually use the previous offset... */
currentChunk()->count = before;
/* Retreive the docstring from the constant table */
current->function->docstring = AS_STRING(currentChunk()->constants.values[currentChunk()->constants.count-1]);
consume(TOKEN_EOL,"Garbage after docstring defintion");
if (!check(TOKEN_INDENTATION) || parser.current.length != currentIndentation) {
error("Expected at least one statement in function with docstring.");
}
advance();
}
do {
declaration();
while (check(TOKEN_INDENTATION)) {
if (parser.current.length < currentIndentation) break;
advance(); /* Pass indentation */
advance();
declaration();
} while (check(TOKEN_INDENTATION));
};
#ifdef ENABLE_DEBUGGING
if (vm.flags & KRK_ENABLE_DEBUGGING) {
fprintf(stderr, "finished with block %s (ind=%d) on line %d, sitting on a %s (len=%d)\n",
blockName,
(int)indentation,
(int)parser.current.line,
getRule(parser.current.type)->name,
(int)parser.current.length);
blockName, (int)indentation, (int)parser.current.line,
getRule(parser.current.type)->name, (int)parser.current.length);
}
#endif
}
@ -609,18 +616,29 @@ static void classDeclaration() {
if (currentIndentation <= blockWidth) {
errorAtCurrent("Unexpected indentation level for class");
}
do {
advance();
if (match(TOKEN_STRING)) {
string(0);
emitByte(OP_DOCSTRING);
consume(TOKEN_EOL,"Garbage after docstring defintion");
if (!check(TOKEN_INDENTATION) || parser.current.length != currentIndentation) {
goto _pop_class;
}
advance();
}
method(currentIndentation);
while (check(TOKEN_INDENTATION)) {
if (parser.current.length < currentIndentation) break;
advance(); /* Pass the indentation */
method(currentIndentation);
if (check(TOKEN_EOL)) endOfLine();
} while (check(TOKEN_INDENTATION));
}
#ifdef ENABLE_SCAN_TRACING
if (vm.flags & KRK_ENABLE_SCAN_TRACING) fprintf(stderr, "Exiting from class definition on %s\n", getRule(parser.current.type)->name);
#endif
/* Exit from block */
}
} /* else empty class (and at end of file?) we'll allow it for now... */
_pop_class:
emitByte(OP_POP);
if (classCompiler.hasSuperClass) {
endScope();
@ -1247,7 +1265,10 @@ static void declareVariable() {
for (ssize_t i = current->localCount - 1; i >= 0; i--) {
Local * local = &current->locals[i];
if (local->depth != -1 && local->depth < (ssize_t)current->scopeDepth) break;
if (identifiersEqual(name, &local->name)) error("Duplicate definition");
if (identifiersEqual(name, &local->name)) {
error("Duplicate definition");
__asm__("int $3");
}
}
addLocal(*name);
}

View File

@ -74,6 +74,7 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) {
SIMPLE(OP_INHERIT)
SIMPLE(OP_RAISE)
SIMPLE(OP_CLOSE_UPVALUE)
SIMPLE(OP_DOCSTRING)
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
CONSTANT(OP_CONSTANT,(void)0)
CONSTANT(OP_GET_GLOBAL,(void)0)

View File

@ -12,23 +12,6 @@
#include "rline.h"
static KrkValue _krk_repl_help(int argc, KrkValue argv[]) {
fprintf(stderr, "Kuroko REPL\n");
fprintf(stderr, "Statements entered in the repl will be interpreted directly\n");
fprintf(stderr, "in a script context and results will be automatically printed.\n");
fprintf(stderr, "Classes, functions, and control flow statements may also be\n");
fprintf(stderr, "entered. When in an indented block context, entering a blank\n");
fprintf(stderr, "line will mark the end of the top-level statement.\n");
fprintf(stderr, "\n");
fprintf(stderr, "Some things to try:\n");
fprintf(stderr, " Basic mathematics: 1 + 2 + 3\n");
fprintf(stderr, " Import modules: import system\n");
fprintf(stderr, " Default a function: def method(foo):\n");
fprintf(stderr, " print foo\n");
fprintf(stderr, "\n");
return NONE_VAL();
}
int main(int argc, char * argv[]) {
int flags = 0;
int opt;
@ -57,9 +40,6 @@ int main(int argc, char * argv[]) {
/* Run the repl */
int exit = 0;
/* Bind help() command */
krk_defineNative(&vm.globals, "help", _krk_repl_help);
rline_exit_string="";
rline_exp_set_syntax("krk");
//rline_exp_set_shell_commands(shell_commands, shell_commands_len);

View File

@ -118,6 +118,7 @@ static void blackenObject(KrkObj * object) {
case OBJ_FUNCTION: {
KrkFunction * function = (KrkFunction *)object;
krk_markObject((KrkObj*)function->name);
krk_markObject((KrkObj*)function->docstring);
krk_markObject((KrkObj*)function->chunk.filename);
markArray(&function->chunk.constants);
break;
@ -129,6 +130,7 @@ static void blackenObject(KrkObj * object) {
KrkClass * _class = (KrkClass *)object;
krk_markObject((KrkObj*)_class->name);
krk_markObject((KrkObj*)_class->filename);
krk_markObject((KrkObj*)_class->docstring);
krk_markTable(&_class->methods);
break;
}

View File

@ -113,6 +113,7 @@ KrkFunction * krk_newFunction() {
function->defaultArgs = 0;
function->upvalueCount = 0;
function->name = NULL;
function->docstring = NULL;
krk_initChunk(&function->chunk);
return function;
}
@ -148,6 +149,7 @@ KrkClass * krk_newClass(KrkString * name) {
KrkClass * _class = ALLOCATE_OBJECT(KrkClass, OBJ_CLASS);
_class->name = name;
_class->filename = NULL;
_class->docstring = NULL;
krk_initTable(&_class->methods);
return _class;
}

View File

@ -62,6 +62,7 @@ typedef struct {
size_t upvalueCount;
KrkChunk chunk;
KrkString * name;
KrkString * docstring;
} KrkFunction;
typedef struct {
@ -75,6 +76,7 @@ typedef struct {
KrkObj obj;
KrkString * name;
KrkString * filename;
KrkString * docstring;
KrkTable methods;
} KrkClass;

40
vm.c
View File

@ -549,6 +549,7 @@ void krk_initVM(int flags) {
vm.specialMethodNames[METHOD_CHR] = OBJECT_VAL(S("__chr__"));
vm.specialMethodNames[METHOD_FLOAT]= OBJECT_VAL(S("__float__"));
vm.specialMethodNames[METHOD_LEN] = OBJECT_VAL(S("__len__"));
vm.specialMethodNames[METHOD_DOC] = OBJECT_VAL(S("__doc__"));
/* Create built-in class `object` */
vm.object_class = krk_newClass(S("object"));
@ -1148,6 +1149,10 @@ static KrkValue run() {
} else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_FILE])) {
krk_pop();
krk_push(OBJECT_VAL(_class->filename));
} else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_DOC])) {
KrkValue out = _class->docstring ? OBJECT_VAL(_class->docstring) : NONE_VAL();
krk_pop();
krk_push(out);
} else {
goto _undefined;
}
@ -1170,6 +1175,36 @@ static KrkValue run() {
}
break;
}
case OBJ_CLOSURE: {
if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_DOC])) {
KrkClosure * closure = AS_CLOSURE(krk_peek(0));
KrkValue out = closure->function->docstring ? OBJECT_VAL(closure->function->docstring) : NONE_VAL();
krk_pop();
krk_push(out);
} else {
goto _undefined;
}
break;
}
case OBJ_BOUND_METHOD: {
if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_DOC])) {
KrkBoundMethod * boundMethod = AS_BOUND_METHOD(krk_peek(0));
KrkObj * method = boundMethod->method;
KrkValue out = NONE_VAL();
switch (method->type) {
case OBJ_CLOSURE: out = ((KrkClosure*)method)->function->docstring ?
OBJECT_VAL(((KrkClosure*)method)->function->docstring) : NONE_VAL();
break;
default:
break;
}
krk_pop();
krk_push(out);
} else {
goto _undefined;
}
break;
}
default:
goto _undefined;
}
@ -1231,6 +1266,11 @@ _undefined:
krk_pop();
break;
}
case OP_DOCSTRING: {
KrkClass * me = AS_CLASS(krk_peek(1));
me->docstring = AS_STRING(krk_pop());
break;
}
case OP_GET_SUPER_LONG:
case OP_GET_SUPER: {
KrkString * name = READ_STRING(operandWidth);

1
vm.h
View File

@ -27,6 +27,7 @@ typedef enum {
METHOD_FLOAT,
METHOD_CHR,
METHOD_LEN,
METHOD_DOC,
METHOD__MAX,
} KrkSpecialMethods;