Add docstrings, __doc__, and help()
This commit is contained in:
parent
a5928b18c9
commit
f7e1c0da0d
21
builtins.krk
21
builtins.krk
@ -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()
|
||||
|
1
chunk.h
1
chunk.h
@ -51,6 +51,7 @@ typedef enum {
|
||||
OP_GET_SUPER,
|
||||
OP_PUSH_TRY,
|
||||
OP_RAISE,
|
||||
OP_DOCSTRING,
|
||||
|
||||
OP_CONSTANT_LONG = 128,
|
||||
OP_PRINT_LONG,
|
||||
|
63
compiler.c
63
compiler.c
@ -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 = ¤t->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);
|
||||
}
|
||||
|
1
debug.c
1
debug.c
@ -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)
|
||||
|
20
kuroko.c
20
kuroko.c
@ -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);
|
||||
|
2
memory.c
2
memory.c
@ -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;
|
||||
}
|
||||
|
2
object.c
2
object.c
@ -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;
|
||||
}
|
||||
|
2
object.h
2
object.h
@ -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
40
vm.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user