Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0e3a007452 |
14
Makefile
14
Makefile
@ -21,7 +21,7 @@ KRKMODS = $(wildcard modules/*.krk modules/*/*.krk modules/*/*/*.krk)
|
|||||||
|
|
||||||
all: ${TARGET} ${MODULES} ${TOOLS} ${GENMODS}
|
all: ${TARGET} ${MODULES} ${TOOLS} ${GENMODS}
|
||||||
|
|
||||||
ifneq ($(shell tools/can-floor-without-libm.sh "$(CC) $(filter-out -g,$(CFLAGS))"),yes)
|
ifneq ($(shell tools/can-floor-without-libm.sh $(CC)),yes)
|
||||||
LDLIBS += -lm
|
LDLIBS += -lm
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -84,17 +84,6 @@ ifdef KRK_HEAP_TAG_BYTE
|
|||||||
CFLAGS += -DKRK_HEAP_TAG_BYTE=${KRK_HEAP_TAG_BYTE}
|
CFLAGS += -DKRK_HEAP_TAG_BYTE=${KRK_HEAP_TAG_BYTE}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef KRK_NO_NAN_BOXING
|
|
||||||
CFLAGS += -DKRK_NO_NAN_BOXING=1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef KRK_BUNDLE_LIBS
|
|
||||||
KRK_BUNDLE_LIBS_CFLAG=$(patsubst %,BUNDLED(%);,${KRK_BUNDLE_LIBS})
|
|
||||||
KRK_BUNDLE_LIBS_BOBJS=$(patsubst %,src/modules/module_%.o,${KRK_BUNDLE_LIBS})
|
|
||||||
BIN_OBJS += ${KRK_BUNDLE_LIBS_BOBJS}
|
|
||||||
CFLAGS += -DKRK_BUNDLE_LIBS="${KRK_BUNDLE_LIBS_CFLAG}"
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@ -133,7 +122,6 @@ src/debug.o: src/opcodes.h
|
|||||||
src/value.o: src/opcodes.h
|
src/value.o: src/opcodes.h
|
||||||
src/vm.o: src/opcodes.h
|
src/vm.o: src/opcodes.h
|
||||||
src/exceptions.o: src/opcodes.h
|
src/exceptions.o: src/opcodes.h
|
||||||
modules/dis.so: src/opcodes.h
|
|
||||||
|
|
||||||
|
|
||||||
%.o: %.c ${HEADERS}
|
%.o: %.c ${HEADERS}
|
||||||
|
@ -45,6 +45,8 @@ Halts execution and calls the debugger before every instruction.
|
|||||||
Prints a message each time the garbage collector is run.
|
Prints a message each time the garbage collector is run.
|
||||||
- `KRK_GLOBAL_ENABLE_STRESS_GC`
|
- `KRK_GLOBAL_ENABLE_STRESS_GC`
|
||||||
Causes the garbage collector to be called on every allocation (from the main thread).
|
Causes the garbage collector to be called on every allocation (from the main thread).
|
||||||
|
- `KRK_GLOBAL_CALLGRIND`
|
||||||
|
Generate tracing data. `vm.callgrindFile` must be set to a writable stream to store the intermediate data collected by the VM.
|
||||||
- `KRK_GLOBAL_CLEAN_OUTPUT`
|
- `KRK_GLOBAL_CLEAN_OUTPUT`
|
||||||
Disables automatic printing of uncaught exception tracebacks. Use `krk_dumpTraceback()` to print a traceback from the exception in the current thread to `stderr`.
|
Disables automatic printing of uncaught exception tracebacks. Use `krk_dumpTraceback()` to print a traceback from the exception in the current thread to `stderr`.
|
||||||
|
|
||||||
|
65
modules/callgrind.krk
Normal file
65
modules/callgrind.krk
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
'''
|
||||||
|
@brief Tool to process VM trace reports and generate callgrind-formatted output.
|
||||||
|
'''
|
||||||
|
import kuroko
|
||||||
|
import fileio
|
||||||
|
|
||||||
|
def processFile(sourcePath: str, pid: int , cmd: str):
|
||||||
|
'''@brief Process @p sourcePath from process @p pid with command @p cmd as a VM trace file.'''
|
||||||
|
let funcs = {}
|
||||||
|
|
||||||
|
with fileio.open(sourcePath,'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
let callerFile, callerFunc, callerLine, calleeFile, calleeFunc, calleeLine, nsecs = line.split(' ')
|
||||||
|
nsecs = float(nsecs)
|
||||||
|
let data = funcs.get((callerFile,callerFunc),{})
|
||||||
|
let call = data.get((callerLine,calleeFile,calleeFunc,calleeLine),(0,0))
|
||||||
|
let out = (call[0] + 1, call[1] + nsecs)
|
||||||
|
data[callerLine,calleeFile,calleeFunc,calleeLine] = out
|
||||||
|
funcs[callerFile,callerFunc] = data
|
||||||
|
let called = funcs.get((calleeFile,calleeFunc),{})
|
||||||
|
let times = called.get(None,(1,0))
|
||||||
|
called[None] = (calleeLine, times[1] + nsecs)
|
||||||
|
funcs[calleeFile,calleeFunc] = called
|
||||||
|
|
||||||
|
with fileio.open(f'callgrind.out.{pid}','w') as out:
|
||||||
|
out.write('# callgrind format\n')
|
||||||
|
out.write('creator: Kuroko\n')
|
||||||
|
out.write('positions: line\n')
|
||||||
|
out.write('events: nanoseconds\n')
|
||||||
|
out.write(f'cmd: {cmd}\n\n')
|
||||||
|
|
||||||
|
def defloat(f):
|
||||||
|
let s = int(f)
|
||||||
|
let n = str(int(f * 1000000000))
|
||||||
|
if len(n) > 9: n = n[-9:]
|
||||||
|
let o = (str(s) + '0' * (9 - len(n)) + n).lstrip('0')
|
||||||
|
return o if o else '0'
|
||||||
|
|
||||||
|
def cleanfunc(func):
|
||||||
|
for s in ['<module>','<listcomp>','<setcomp>','<dictcomp>','<lambda>']:
|
||||||
|
func = func.replace(s,'['+s[1:-1]+']')
|
||||||
|
return func.replace('@','<') + '>'
|
||||||
|
|
||||||
|
for key, value in funcs.items():
|
||||||
|
let sFile, sFunc = key
|
||||||
|
out.write(f'fl={sFile}\n')
|
||||||
|
out.write(f'fn={cleanfunc(sFunc)}\n')
|
||||||
|
if not sFunc.startswith('(root)'):
|
||||||
|
let startLine, totalNsecs = value[None]
|
||||||
|
for k, v in value.items():
|
||||||
|
if not k: continue
|
||||||
|
let sourceLine, file, func, destLine = k
|
||||||
|
let count, nsecs = v
|
||||||
|
totalNsecs -= nsecs
|
||||||
|
|
||||||
|
out.write(f'{startLine} {defloat(totalNsecs)}\n')
|
||||||
|
for k, v in value.items():
|
||||||
|
if not k: continue
|
||||||
|
let sourceLine, file, func, destLine = k
|
||||||
|
let count, totalNsecs = v
|
||||||
|
if file != sFile: out.write(f'cfi={file}\n')
|
||||||
|
out.write(f'cfn={cleanfunc(func)}\n')
|
||||||
|
out.write(f'calls={count} {destLine}\n')
|
||||||
|
out.write(f'{sourceLine} {defloat(totalNsecs)}\n')
|
||||||
|
out.write('\n')
|
28
modules/dis.krk
Normal file
28
modules/dis.krk
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
'''
|
||||||
|
@brief Called when 'dis' is run as the main module (-m dis)
|
||||||
|
'''
|
||||||
|
# Get the real dis
|
||||||
|
import dis
|
||||||
|
|
||||||
|
def disrec(code, seen):
|
||||||
|
let next = [code]
|
||||||
|
while next:
|
||||||
|
let co = next[0]
|
||||||
|
next = next[1:]
|
||||||
|
dis.dis(co)
|
||||||
|
for inst,size,operand in dis.examine(co):
|
||||||
|
if isinstance(operand,codeobject) and operand not in seen and operand not in next:
|
||||||
|
next.append(operand)
|
||||||
|
if next:
|
||||||
|
print()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import kuroko
|
||||||
|
if (len(kuroko.argv) < 2):
|
||||||
|
print("Usage: kuroko -m dis FILE")
|
||||||
|
return 1
|
||||||
|
import fileio
|
||||||
|
for file in kuroko.argv[1:]:
|
||||||
|
with fileio.open(file,'r') as f:
|
||||||
|
let result = dis.build(f.read(), file)
|
||||||
|
disrec(result,set())
|
@ -1,104 +0,0 @@
|
|||||||
'''
|
|
||||||
@brief Pairing heap.
|
|
||||||
@author K. Lange <klange@toaruos.org>
|
|
||||||
|
|
||||||
Provides a simple min-heap with insert, pop, peek, and visit.
|
|
||||||
|
|
||||||
A Kuroko implementation is provided as a backup alongside a
|
|
||||||
faster C implementation in a shared object module.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __make_pheap():
|
|
||||||
def pheap_meld(left, right, comp):
|
|
||||||
if left is None:
|
|
||||||
return right
|
|
||||||
if right is None:
|
|
||||||
return left
|
|
||||||
if comp(left[0],right[0]):
|
|
||||||
if left[1]:
|
|
||||||
right[2] = left[1]
|
|
||||||
left[1] = right
|
|
||||||
return left
|
|
||||||
else:
|
|
||||||
if right[1]:
|
|
||||||
left[2] = right[1]
|
|
||||||
right[1] = left
|
|
||||||
return right
|
|
||||||
|
|
||||||
def pheap_merge_pairs(lst, comp):
|
|
||||||
if lst is None:
|
|
||||||
return None
|
|
||||||
else if lst[2] is None:
|
|
||||||
return lst
|
|
||||||
else:
|
|
||||||
let next = lst[2]
|
|
||||||
lst[2] = None
|
|
||||||
let rest = next[2]
|
|
||||||
next[2] = None
|
|
||||||
return pheap_meld(pheap_meld(lst,next,comp), pheap_merge_pairs(rest, comp), comp)
|
|
||||||
|
|
||||||
def pheap_delete_min(heap, comp):
|
|
||||||
let subs = heap[1]
|
|
||||||
return pheap_merge_pairs(subs, comp)
|
|
||||||
|
|
||||||
def pheap_visit_heap(heap, func):
|
|
||||||
if not heap: return
|
|
||||||
func(heap)
|
|
||||||
pheap_visit_heap(heap[1], func)
|
|
||||||
pheap_visit_heap(heap[2], func)
|
|
||||||
|
|
||||||
def pheap_visit_heap_after(heap, func):
|
|
||||||
if not heap: return
|
|
||||||
pheap_visit_heap(heap[1], func)
|
|
||||||
pheap_visit_heap(heap[2], func)
|
|
||||||
func(heap)
|
|
||||||
|
|
||||||
class PHeap:
|
|
||||||
def __init__(self, comp):
|
|
||||||
'''Create a new pairing heap governed by the given comparator function.'''
|
|
||||||
self.heap = None
|
|
||||||
self.comp = comp
|
|
||||||
self.count = 0
|
|
||||||
|
|
||||||
def insert(self, value):
|
|
||||||
'''Insert a new element into the heap.'''
|
|
||||||
self.heap = pheap_meld(self.heap, [value, None, None], self.comp)
|
|
||||||
self.count += 1
|
|
||||||
|
|
||||||
def peek(self):
|
|
||||||
'''Retrieve the root (smallest) element of the heap, or None if it is empty.'''
|
|
||||||
return self.heap[0] if self.heap else None
|
|
||||||
|
|
||||||
def pop(self):
|
|
||||||
'''Remove and return the root (smallest) element of the heap. If the heap is empty, IndexError is raised.'''
|
|
||||||
let out = self.heap
|
|
||||||
if not out:
|
|
||||||
raise IndexError('pop from empty heap')
|
|
||||||
self.heap = pheap_delete_min(self.heap, self.comp)
|
|
||||||
self.count -= 1
|
|
||||||
return out[0] if out else None
|
|
||||||
|
|
||||||
def __bool__(self):
|
|
||||||
return self.heap is not None
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return self.count
|
|
||||||
|
|
||||||
def visit(self, func, after=False):
|
|
||||||
'''Call a function for each element of the heap.'''
|
|
||||||
(pheap_visit_heap_after if after else pheap_visit_heap)(self.heap, func)
|
|
||||||
|
|
||||||
# Clean up qualified name.
|
|
||||||
PHeap.__qualname__ = 'PHeap'
|
|
||||||
|
|
||||||
return PHeap
|
|
||||||
|
|
||||||
# Keep the Kuroko version available for testing.
|
|
||||||
let PHeap_krk = __make_pheap()
|
|
||||||
let PHeap = PHeap_krk
|
|
||||||
|
|
||||||
# Try to load the C implementation.
|
|
||||||
try:
|
|
||||||
import _pheap
|
|
||||||
PHeap = _pheap.PHeap
|
|
||||||
|
|
149
src/builtins.c
149
src/builtins.c
@ -107,7 +107,7 @@ KRK_Method(object,__hash__) {
|
|||||||
}
|
}
|
||||||
KrkObj * obj = AS_OBJECT(self);
|
KrkObj * obj = AS_OBJECT(self);
|
||||||
if (!(obj->flags & KRK_OBJ_FLAGS_VALID_HASH)) {
|
if (!(obj->flags & KRK_OBJ_FLAGS_VALID_HASH)) {
|
||||||
obj->hash = (uint32_t)((intptr_t)(obj) >> 3);
|
obj->hash = INTEGER_VAL((int)((intptr_t)self >> 3));
|
||||||
obj->flags |= KRK_OBJ_FLAGS_VALID_HASH;
|
obj->flags |= KRK_OBJ_FLAGS_VALID_HASH;
|
||||||
}
|
}
|
||||||
return INTEGER_VAL(obj->hash);
|
return INTEGER_VAL(obj->hash);
|
||||||
@ -115,7 +115,7 @@ KRK_Method(object,__hash__) {
|
|||||||
|
|
||||||
KRK_Method(object,__eq__) {
|
KRK_Method(object,__eq__) {
|
||||||
METHOD_TAKES_EXACTLY(1);
|
METHOD_TAKES_EXACTLY(1);
|
||||||
if (krk_valuesSame(argv[0],argv[1])) return BOOLEAN_VAL(1);
|
if (argv[0] == argv[1]) return BOOLEAN_VAL(1);
|
||||||
return NOTIMPL_VAL();
|
return NOTIMPL_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ KRK_StaticMethod(object,__init_subclass__) {
|
|||||||
* all types should have a string representation available through
|
* all types should have a string representation available through
|
||||||
* those methods.
|
* those methods.
|
||||||
*/
|
*/
|
||||||
KRK_Method(object,__repr__) {
|
KRK_Method(object,__str__) {
|
||||||
KrkClass * type = krk_getType(self);
|
KrkClass * type = krk_getType(self);
|
||||||
|
|
||||||
KrkValue module = NONE_VAL();
|
KrkValue module = NONE_VAL();
|
||||||
@ -262,13 +262,6 @@ _error:
|
|||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(object,__str__) {
|
|
||||||
KrkClass * type = krk_getType(self);
|
|
||||||
if (unlikely(!type->_reprer)) return krk_runtimeError(vm.exceptions->typeError, "object is not representable");
|
|
||||||
krk_push(self);
|
|
||||||
return krk_callDirect(type->_reprer, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(object,__format__) {
|
KRK_Method(object,__format__) {
|
||||||
METHOD_TAKES_EXACTLY(1);
|
METHOD_TAKES_EXACTLY(1);
|
||||||
if (!IS_STRING(argv[1])) return TYPE_ERROR(str,argv[1]);
|
if (!IS_STRING(argv[1])) return TYPE_ERROR(str,argv[1]);
|
||||||
@ -316,7 +309,7 @@ KRK_Function(dir) {
|
|||||||
|
|
||||||
/* Put all the keys from the globals table in it */
|
/* Put all the keys from the globals table in it */
|
||||||
KrkTable * globals = krk_currentThread.frames[krk_currentThread.frameCount-1].globals;
|
KrkTable * globals = krk_currentThread.frames[krk_currentThread.frameCount-1].globals;
|
||||||
for (size_t i = 0; i < globals->used; ++i) {
|
for (size_t i = 0; i < globals->capacity; ++i) {
|
||||||
KrkTableEntry * entry = &globals->entries[i];
|
KrkTableEntry * entry = &globals->entries[i];
|
||||||
if (IS_KWARGS(entry->key)) continue;
|
if (IS_KWARGS(entry->key)) continue;
|
||||||
krk_writeValueArray(AS_LIST(myList), entry->key);
|
krk_writeValueArray(AS_LIST(myList), entry->key);
|
||||||
@ -363,19 +356,19 @@ KRK_Function(chr) {
|
|||||||
|
|
||||||
KRK_Function(hex) {
|
KRK_Function(hex) {
|
||||||
FUNCTION_TAKES_EXACTLY(1);
|
FUNCTION_TAKES_EXACTLY(1);
|
||||||
trySlowMethod(vm.specialMethodNames[METHOD_HEX]);
|
trySlowMethod(OBJECT_VAL(S("__hex__")));
|
||||||
return TYPE_ERROR(int,argv[0]);
|
return TYPE_ERROR(int,argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(oct) {
|
KRK_Function(oct) {
|
||||||
FUNCTION_TAKES_EXACTLY(1);
|
FUNCTION_TAKES_EXACTLY(1);
|
||||||
trySlowMethod(vm.specialMethodNames[METHOD_OCT]);
|
trySlowMethod(OBJECT_VAL(S("__oct__")));
|
||||||
return TYPE_ERROR(int,argv[0]);
|
return TYPE_ERROR(int,argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(bin) {
|
KRK_Function(bin) {
|
||||||
FUNCTION_TAKES_EXACTLY(1);
|
FUNCTION_TAKES_EXACTLY(1);
|
||||||
trySlowMethod(vm.specialMethodNames[METHOD_BIN]);
|
trySlowMethod(OBJECT_VAL(S("__bin__")));
|
||||||
return TYPE_ERROR(int,argv[0]);
|
return TYPE_ERROR(int,argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +383,7 @@ int krk_unpackIterable(KrkValue iterable, void * context, int callback(void *, c
|
|||||||
} else if (IS_list(iterable)) {
|
} else if (IS_list(iterable)) {
|
||||||
if (callback(context, AS_LIST(iterable)->values, AS_LIST(iterable)->count)) return 1;
|
if (callback(context, AS_LIST(iterable)->values, AS_LIST(iterable)->count)) return 1;
|
||||||
} else if (IS_dict(iterable)) {
|
} else if (IS_dict(iterable)) {
|
||||||
for (size_t i = 0; i < AS_DICT(iterable)->used; ++i) {
|
for (size_t i = 0; i < AS_DICT(iterable)->capacity; ++i) {
|
||||||
if (!IS_KWARGS(AS_DICT(iterable)->entries[i].key)) {
|
if (!IS_KWARGS(AS_DICT(iterable)->entries[i].key)) {
|
||||||
if (callback(context, &AS_DICT(iterable)->entries[i].key, 1)) return 1;
|
if (callback(context, &AS_DICT(iterable)->entries[i].key, 1)) return 1;
|
||||||
}
|
}
|
||||||
@ -684,18 +677,18 @@ KRK_Method(filter,__call__) {
|
|||||||
#define IS_enumerate(o) (krk_isInstanceOf(o,KRK_BASE_CLASS(enumerate)))
|
#define IS_enumerate(o) (krk_isInstanceOf(o,KRK_BASE_CLASS(enumerate)))
|
||||||
#define AS_enumerate(o) (AS_INSTANCE(o))
|
#define AS_enumerate(o) (AS_INSTANCE(o))
|
||||||
KRK_Method(enumerate,__init__) {
|
KRK_Method(enumerate,__init__) {
|
||||||
KrkValue iterator;
|
METHOD_TAKES_EXACTLY(1);
|
||||||
KrkValue start = INTEGER_VAL(0);
|
KrkValue start = INTEGER_VAL(0);
|
||||||
if (!krk_parseArgs(".V|V", (const char*[]){"iterable","start"}, &iterator, &start)) return NONE_VAL();
|
if (hasKw) krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("start")), &start);
|
||||||
|
|
||||||
krk_attachNamedValue(&self->fields, "_counter", start);
|
krk_attachNamedValue(&self->fields, "_counter", start);
|
||||||
|
|
||||||
/* Attach iterator */
|
/* Attach iterator */
|
||||||
KrkClass * type = krk_getType(iterator);
|
KrkClass * type = krk_getType(argv[1]);
|
||||||
if (!type->_iter) {
|
if (!type->_iter) {
|
||||||
return krk_runtimeError(vm.exceptions->typeError, "'%T' object is not iterable", iterator);
|
return krk_runtimeError(vm.exceptions->typeError, "'%T' object is not iterable", argv[1]);
|
||||||
}
|
}
|
||||||
krk_push(iterator);
|
krk_push(argv[1]);
|
||||||
KrkValue asIter = krk_callDirect(type->_iter, 1);
|
KrkValue asIter = krk_callDirect(type->_iter, 1);
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return NONE_VAL();
|
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return NONE_VAL();
|
||||||
krk_attachNamedValue(&self->fields, "_iterator", asIter);
|
krk_attachNamedValue(&self->fields, "_iterator", asIter);
|
||||||
@ -757,11 +750,13 @@ static int _sum_callback(void * context, const KrkValue * values, size_t count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(sum) {
|
KRK_Function(sum) {
|
||||||
KrkValue iterable;
|
FUNCTION_TAKES_AT_LEAST(1);
|
||||||
KrkValue base = INTEGER_VAL(0);
|
KrkValue base = INTEGER_VAL(0);
|
||||||
if (!krk_parseArgs("V|$V", (const char*[]){"iterable","start"}, &iterable, &base)) return NONE_VAL();
|
if (hasKw) {
|
||||||
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("start")), &base);
|
||||||
|
}
|
||||||
struct SimpleContext context = { base };
|
struct SimpleContext context = { base };
|
||||||
if (krk_unpackIterable(iterable, &context, _sum_callback)) return NONE_VAL();
|
if (krk_unpackIterable(argv[0], &context, _sum_callback)) return NONE_VAL();
|
||||||
return context.base;
|
return context.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,81 +811,42 @@ KRK_Function(max) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(print) {
|
KRK_Function(print) {
|
||||||
char * sep = NULL;
|
KrkValue sepVal;
|
||||||
char * end = NULL;
|
KrkValue endVal;
|
||||||
size_t sepLen = 0;
|
char * sep = " "; size_t sepLen = 1;
|
||||||
size_t endLen = 0;
|
char * end = "\n"; size_t endLen = 1;
|
||||||
int remArgc;
|
if (hasKw) {
|
||||||
const KrkValue * remArgv;
|
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("sep")), &sepVal)) {
|
||||||
KrkValue file = NONE_VAL();
|
if (!IS_STRING(sepVal)) return krk_runtimeError(vm.exceptions->typeError, "'%s' should be a string, not '%T'", "sep", sepVal);
|
||||||
int flush = 0;
|
sep = AS_CSTRING(sepVal);
|
||||||
|
sepLen = AS_STRING(sepVal)->length;
|
||||||
if (!krk_parseArgs("*z#z#Vp", (const char*[]){"sep","end","file","flush"},
|
}
|
||||||
&remArgc, &remArgv,
|
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("end")), &endVal)) {
|
||||||
&sep, &sepLen, &end, &endLen,
|
if (!IS_STRING(endVal)) return krk_runtimeError(vm.exceptions->typeError, "'%s' should be a string, not '%T'", "end", endVal);
|
||||||
&file, &flush)) {
|
end = AS_CSTRING(endVal);
|
||||||
return NONE_VAL();
|
endLen = AS_STRING(endVal)->length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!argc) {
|
||||||
/* Set default separator and end if not provided or set to None. */
|
for (size_t j = 0; j < endLen; ++j) {
|
||||||
if (!sep) { sep = " "; sepLen = 1; }
|
fputc(end[j], stdout);
|
||||||
if (!end) { end = "\n"; endLen = 1; }
|
|
||||||
|
|
||||||
for (int i = 0; i < remArgc; ++i) {
|
|
||||||
|
|
||||||
/* If printing through a file object, get its @c write method */
|
|
||||||
if (!IS_NONE(file)) {
|
|
||||||
krk_push(krk_valueGetAttribute(file, "write"));
|
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* Convert the argument to a printable form, first by trying __str__, then __repr__ */
|
for (int i = 0; i < argc; ++i) {
|
||||||
KrkValue printable = remArgv[i];
|
KrkValue printable = argv[i];
|
||||||
krk_push(printable);
|
if (IS_STRING(printable)) { /* krk_printValue runs repr */
|
||||||
if (!IS_STRING(printable)) {
|
/* Make sure we handle nil bits correctly. */
|
||||||
KrkClass * type = krk_getType(printable);
|
for (size_t j = 0; j < AS_STRING(printable)->length; ++j) {
|
||||||
if (type->_tostr) {
|
fputc(AS_CSTRING(printable)[j], stdout);
|
||||||
krk_push((printable = krk_callDirect(type->_tostr, 1)));
|
|
||||||
} else if (type->_reprer) {
|
|
||||||
krk_push((printable = krk_callDirect(type->_reprer, 1)));
|
|
||||||
}
|
|
||||||
if (!IS_STRING(printable)) return krk_runtimeError(vm.exceptions->typeError, "__str__ returned non-string (type %T)", printable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_NONE(file)) {
|
|
||||||
/* Call @c write */
|
|
||||||
krk_callStack(1);
|
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return NONE_VAL();
|
|
||||||
|
|
||||||
/* Print separator */
|
|
||||||
if (i + 1 < remArgc) {
|
|
||||||
krk_push(krk_valueGetAttribute(file, "write"));
|
|
||||||
krk_push(OBJECT_VAL(krk_copyString(sep,sepLen)));
|
|
||||||
krk_callStack(1);
|
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fwrite(AS_CSTRING(printable), AS_STRING(printable)->length, 1, stdout);
|
krk_printValue(stdout, printable);
|
||||||
krk_pop();
|
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return NONE_VAL();
|
||||||
if (i + 1 < remArgc) fwrite(sep, sepLen, 1, stdout);
|
|
||||||
}
|
}
|
||||||
}
|
char * thingToPrint = (i == argc - 1) ? end : sep;
|
||||||
|
for (size_t j = 0; j < ((i == argc - 1) ? endLen : sepLen); ++j) {
|
||||||
if (!IS_NONE(file)) {
|
fputc(thingToPrint[j], stdout);
|
||||||
/* Print end */
|
|
||||||
krk_push(krk_valueGetAttribute(file, "write"));
|
|
||||||
krk_push(OBJECT_VAL(krk_copyString(end,endLen)));
|
|
||||||
krk_callStack(1);
|
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return NONE_VAL();
|
|
||||||
|
|
||||||
/* Maybe flush */
|
|
||||||
if (flush) {
|
|
||||||
krk_push(krk_valueGetAttribute(file, "flush"));
|
|
||||||
krk_callStack(0);
|
|
||||||
}
|
}
|
||||||
}else {
|
|
||||||
fwrite(end, endLen, 1, stdout);
|
|
||||||
if (flush) fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
@ -1267,7 +1223,7 @@ KRK_Function(abs) {
|
|||||||
return FLOATING_VAL(i >= 0 ? i : -i);
|
return FLOATING_VAL(i >= 0 ? i : -i);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
trySlowMethod(vm.specialMethodNames[METHOD_ABS]);
|
trySlowMethod(OBJECT_VAL(S("__abs__")));
|
||||||
return krk_runtimeError(vm.exceptions->typeError, "bad operand type for 'abs()': '%T'", argv[0]);
|
return krk_runtimeError(vm.exceptions->typeError, "bad operand type for 'abs()': '%T'", argv[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1297,7 +1253,7 @@ static void module_sweep(KrkInstance * inst) {
|
|||||||
#ifndef KRK_STATIC_ONLY
|
#ifndef KRK_STATIC_ONLY
|
||||||
struct KrkModule * module = (struct KrkModule*)inst;
|
struct KrkModule * module = (struct KrkModule*)inst;
|
||||||
if (module->libHandle) {
|
if (module->libHandle) {
|
||||||
krk_dlClose(module->libHandle);
|
dlClose(module->libHandle);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -1444,7 +1400,6 @@ void _createAndBind_builtins(void) {
|
|||||||
KrkClass * object = vm.baseClasses->objectClass;
|
KrkClass * object = vm.baseClasses->objectClass;
|
||||||
BIND_METHOD(object,__dir__);
|
BIND_METHOD(object,__dir__);
|
||||||
BIND_METHOD(object,__str__);
|
BIND_METHOD(object,__str__);
|
||||||
BIND_METHOD(object,__repr__);
|
|
||||||
BIND_METHOD(object,__hash__);
|
BIND_METHOD(object,__hash__);
|
||||||
BIND_METHOD(object,__eq__);
|
BIND_METHOD(object,__eq__);
|
||||||
BIND_METHOD(object,__format__);
|
BIND_METHOD(object,__format__);
|
||||||
@ -1452,6 +1407,7 @@ void _createAndBind_builtins(void) {
|
|||||||
BIND_STATICMETHOD(object,__new__);
|
BIND_STATICMETHOD(object,__new__);
|
||||||
BIND_METHOD(object,__init__);
|
BIND_METHOD(object,__init__);
|
||||||
BIND_CLASSMETHOD(object,__init_subclass__);
|
BIND_CLASSMETHOD(object,__init_subclass__);
|
||||||
|
krk_defineNative(&object->methods, "__repr__", FUNC_NAME(object,__str__));
|
||||||
krk_finalizeClass(object);
|
krk_finalizeClass(object);
|
||||||
KRK_DOC(object,
|
KRK_DOC(object,
|
||||||
"@brief Base class for all types.\n\n"
|
"@brief Base class for all types.\n\n"
|
||||||
@ -1468,6 +1424,7 @@ void _createAndBind_builtins(void) {
|
|||||||
krk_push(OBJECT_VAL(module));
|
krk_push(OBJECT_VAL(module));
|
||||||
|
|
||||||
BIND_METHOD(module,__repr__);
|
BIND_METHOD(module,__repr__);
|
||||||
|
krk_defineNative(&module->methods, "__str__", FUNC_NAME(module,__repr__));
|
||||||
krk_finalizeClass(module);
|
krk_finalizeClass(module);
|
||||||
KRK_DOC(module, "Type of imported modules and packages.");
|
KRK_DOC(module, "Type of imported modules and packages.");
|
||||||
|
|
||||||
@ -1627,7 +1584,7 @@ void _createAndBind_builtins(void) {
|
|||||||
"@c repr strings should convey all information needed to recreate the object, if this is possible.");
|
"@c repr strings should convey all information needed to recreate the object, if this is possible.");
|
||||||
BUILTIN_FUNCTION("print", FUNC_NAME(krk,print),
|
BUILTIN_FUNCTION("print", FUNC_NAME(krk,print),
|
||||||
"@brief Print text to the standard output.\n"
|
"@brief Print text to the standard output.\n"
|
||||||
"@arguments *args,sep=' ',end='\\n',file=None,flush=False\n\n"
|
"@arguments *args,sep=' ',end='\\n'\n\n"
|
||||||
"Prints the string representation of each argument to the standard output. "
|
"Prints the string representation of each argument to the standard output. "
|
||||||
"The keyword argument @p sep specifies the string to print between values. "
|
"The keyword argument @p sep specifies the string to print between values. "
|
||||||
"The keyword argument @p end specifies the string to print after all of the values have been printed.");
|
"The keyword argument @p end specifies the string to print after all of the values have been printed.");
|
||||||
|
41
src/chunk.c
41
src/chunk.c
@ -13,7 +13,6 @@ void krk_initChunk(KrkChunk * chunk) {
|
|||||||
chunk->linesCapacity = 0;
|
chunk->linesCapacity = 0;
|
||||||
chunk->lines = NULL;
|
chunk->lines = NULL;
|
||||||
chunk->filename = NULL;
|
chunk->filename = NULL;
|
||||||
|
|
||||||
krk_initValueArray(&chunk->constants);
|
krk_initValueArray(&chunk->constants);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,8 +20,8 @@ static void addLine(KrkChunk * chunk, size_t line) {
|
|||||||
if (chunk->linesCount && chunk->lines[chunk->linesCount-1].line == line) return;
|
if (chunk->linesCount && chunk->lines[chunk->linesCount-1].line == line) return;
|
||||||
if (chunk->linesCapacity < chunk->linesCount + 1) {
|
if (chunk->linesCapacity < chunk->linesCount + 1) {
|
||||||
int old = chunk->linesCapacity;
|
int old = chunk->linesCapacity;
|
||||||
chunk->linesCapacity = KRK_GROW_CAPACITY(old);
|
chunk->linesCapacity = GROW_CAPACITY(old);
|
||||||
chunk->lines = KRK_GROW_ARRAY(KrkLineMap, chunk->lines, old, chunk->linesCapacity);
|
chunk->lines = GROW_ARRAY(KrkLineMap, chunk->lines, old, chunk->linesCapacity);
|
||||||
}
|
}
|
||||||
chunk->lines[chunk->linesCount] = (KrkLineMap){chunk->count, line};
|
chunk->lines[chunk->linesCount] = (KrkLineMap){chunk->count, line};
|
||||||
chunk->linesCount++;
|
chunk->linesCount++;
|
||||||
@ -31,8 +30,8 @@ static void addLine(KrkChunk * chunk, size_t line) {
|
|||||||
void krk_writeChunk(KrkChunk * chunk, uint8_t byte, size_t line) {
|
void krk_writeChunk(KrkChunk * chunk, uint8_t byte, size_t line) {
|
||||||
if (chunk->capacity < chunk->count + 1) {
|
if (chunk->capacity < chunk->count + 1) {
|
||||||
int old = chunk->capacity;
|
int old = chunk->capacity;
|
||||||
chunk->capacity = KRK_GROW_CAPACITY(old);
|
chunk->capacity = GROW_CAPACITY(old);
|
||||||
chunk->code = KRK_GROW_ARRAY(uint8_t, chunk->code, old, chunk->capacity);
|
chunk->code = GROW_ARRAY(uint8_t, chunk->code, old, chunk->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk->code[chunk->count] = byte;
|
chunk->code[chunk->count] = byte;
|
||||||
@ -41,8 +40,8 @@ void krk_writeChunk(KrkChunk * chunk, uint8_t byte, size_t line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void krk_freeChunk(KrkChunk * chunk) {
|
void krk_freeChunk(KrkChunk * chunk) {
|
||||||
KRK_FREE_ARRAY(uint8_t, chunk->code, chunk->capacity);
|
FREE_ARRAY(uint8_t, chunk->code, chunk->capacity);
|
||||||
KRK_FREE_ARRAY(KrkLineMap, chunk->lines, chunk->linesCapacity);
|
FREE_ARRAY(KrkLineMap, chunk->lines, chunk->linesCapacity);
|
||||||
krk_freeValueArray(&chunk->constants);
|
krk_freeValueArray(&chunk->constants);
|
||||||
krk_initChunk(chunk);
|
krk_initChunk(chunk);
|
||||||
}
|
}
|
||||||
@ -73,29 +72,11 @@ size_t krk_writeConstant(KrkChunk * chunk, KrkValue value, size_t line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t krk_lineNumber(KrkChunk * chunk, size_t offset) {
|
size_t krk_lineNumber(KrkChunk * chunk, size_t offset) {
|
||||||
|
size_t line = 0;
|
||||||
size_t lo = 0;
|
for (size_t i = 0; i < chunk->linesCount; ++i) {
|
||||||
size_t hi = chunk->linesCount;
|
if (chunk->lines[i].startOffset > offset) break;
|
||||||
|
line = chunk->lines[i].line;
|
||||||
while (lo != hi) {
|
|
||||||
if (hi - lo < 10) {
|
|
||||||
size_t line = 0;
|
|
||||||
for (size_t i = lo; i < hi; ++i) {
|
|
||||||
if (chunk->lines[i].startOffset > offset) break;
|
|
||||||
line = chunk->lines[i].line;
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t mp = lo + (hi - lo) / 2;
|
|
||||||
|
|
||||||
if (chunk->lines[mp].startOffset > offset) {
|
|
||||||
hi = mp;
|
|
||||||
} else {
|
|
||||||
lo = mp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return line;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
296
src/compiler.c
296
src/compiler.c
@ -259,7 +259,6 @@ typedef struct ChunkRecorder {
|
|||||||
size_t count; /**< @brief Offset into the bytecode */
|
size_t count; /**< @brief Offset into the bytecode */
|
||||||
size_t lines; /**< @brief Offset into the line map */
|
size_t lines; /**< @brief Offset into the line map */
|
||||||
size_t constants; /**< @brief Number of constants in the constants table */
|
size_t constants; /**< @brief Number of constants in the constants table */
|
||||||
size_t expressions;/**< @brief Number of expression mapping entries */
|
|
||||||
} ChunkRecorder;
|
} ChunkRecorder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -337,18 +336,15 @@ _exit:
|
|||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define codeobject_from_chunk_pointer(ptr) ((KrkCodeObject*)((char *)(ptr) - offsetof(KrkCodeObject, chunk)))
|
|
||||||
static ChunkRecorder recordChunk(KrkChunk * in) {
|
static ChunkRecorder recordChunk(KrkChunk * in) {
|
||||||
return (ChunkRecorder){in->count, in->linesCount, in->constants.count, codeobject_from_chunk_pointer(in)->expressionsCount};
|
return (ChunkRecorder){in->count, in->linesCount, in->constants.count};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rewindChunk(KrkChunk * out, ChunkRecorder from) {
|
static void rewindChunk(KrkChunk * out, ChunkRecorder from) {
|
||||||
out->count = from.count;
|
out->count = from.count;
|
||||||
out->linesCount = from.lines;
|
out->linesCount = from.lines;
|
||||||
out->constants.count = from.constants;
|
out->constants.count = from.constants;
|
||||||
codeobject_from_chunk_pointer(out)->expressionsCount = from.expressions;
|
|
||||||
}
|
}
|
||||||
#undef codeobject_from_chunk_pointer
|
|
||||||
|
|
||||||
static size_t renameLocal(struct GlobalState * state, size_t ind, KrkToken name);
|
static size_t renameLocal(struct GlobalState * state, size_t ind, KrkToken name);
|
||||||
|
|
||||||
@ -362,7 +358,7 @@ static void initCompiler(struct GlobalState * state, Compiler * compiler, Functi
|
|||||||
compiler->codeobject = krk_newCodeObject();
|
compiler->codeobject = krk_newCodeObject();
|
||||||
compiler->localCount = 0;
|
compiler->localCount = 0;
|
||||||
compiler->localsSpace = 8;
|
compiler->localsSpace = 8;
|
||||||
compiler->locals = KRK_GROW_ARRAY(Local,NULL,0,8);
|
compiler->locals = GROW_ARRAY(Local,NULL,0,8);
|
||||||
compiler->upvaluesSpace = 0;
|
compiler->upvaluesSpace = 0;
|
||||||
compiler->upvalues = NULL;
|
compiler->upvalues = NULL;
|
||||||
compiler->breakCount = 0;
|
compiler->breakCount = 0;
|
||||||
@ -576,25 +572,13 @@ static KrkCodeObject * endCompiler(struct GlobalState * state) {
|
|||||||
function->localNames[i].deathday = currentChunk()->count;
|
function->localNames[i].deathday = currentChunk()->count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function->localNames = KRK_GROW_ARRAY(KrkLocalEntry, function->localNames,
|
function->localNames = GROW_ARRAY(KrkLocalEntry, function->localNames, \
|
||||||
state->current->localNameCapacity, function->localNameCount); /* Shorten this down for runtime */
|
state->current->localNameCapacity, function->localNameCount); /* Shorten this down for runtime */
|
||||||
|
|
||||||
if (state->current->continueCount) { state->parser.previous = state->current->continues[0].token; error("continue without loop"); }
|
if (state->current->continueCount) { state->parser.previous = state->current->continues[0].token; error("continue without loop"); }
|
||||||
if (state->current->breakCount) { state->parser.previous = state->current->breaks[0].token; error("break without loop"); }
|
if (state->current->breakCount) { state->parser.previous = state->current->breaks[0].token; error("break without loop"); }
|
||||||
emitReturn(state);
|
emitReturn(state);
|
||||||
|
|
||||||
/* Reduce the size of dynamic arrays to their fixed sizes. */
|
|
||||||
function->chunk.lines = KRK_GROW_ARRAY(KrkLineMap, function->chunk.lines,
|
|
||||||
function->chunk.linesCapacity, function->chunk.linesCount);
|
|
||||||
function->chunk.linesCapacity = function->chunk.linesCount;
|
|
||||||
function->chunk.code = KRK_GROW_ARRAY(uint8_t, function->chunk.code,
|
|
||||||
function->chunk.capacity, function->chunk.count);
|
|
||||||
function->chunk.capacity = function->chunk.count;
|
|
||||||
|
|
||||||
function->expressions = KRK_GROW_ARRAY(KrkExpressionsMap, function->expressions,
|
|
||||||
function->expressionsCapacity, function->expressionsCount);
|
|
||||||
function->expressionsCapacity = function->expressionsCount;
|
|
||||||
|
|
||||||
/* Attach contants for arguments */
|
/* Attach contants for arguments */
|
||||||
for (int i = 0; i < function->potentialPositionals; ++i) {
|
for (int i = 0; i < function->potentialPositionals; ++i) {
|
||||||
if (i < state->current->unnamedArgs) {
|
if (i < state->current->unnamedArgs) {
|
||||||
@ -637,7 +621,7 @@ static KrkCodeObject * endCompiler(struct GlobalState * state) {
|
|||||||
|
|
||||||
function->totalArguments = function->potentialPositionals + function->keywordArgs + !!(function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS);
|
function->totalArguments = function->potentialPositionals + function->keywordArgs + !!(function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS);
|
||||||
|
|
||||||
#if !defined(KRK_NO_DISASSEMBLY) && !defined(KRK_DISABLE_DEBUG)
|
#ifndef KRK_NO_DISASSEMBLY
|
||||||
if ((krk_currentThread.flags & KRK_THREAD_ENABLE_DISASSEMBLY) && !state->parser.hadError) {
|
if ((krk_currentThread.flags & KRK_THREAD_ENABLE_DISASSEMBLY) && !state->parser.hadError) {
|
||||||
krk_disassembleCodeObject(stderr, function, function->name ? function->name->chars : "(module)");
|
krk_disassembleCodeObject(stderr, function, function->name ? function->name->chars : "(module)");
|
||||||
}
|
}
|
||||||
@ -648,10 +632,10 @@ static KrkCodeObject * endCompiler(struct GlobalState * state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void freeCompiler(Compiler * compiler) {
|
static void freeCompiler(Compiler * compiler) {
|
||||||
KRK_FREE_ARRAY(Local,compiler->locals, compiler->localsSpace);
|
FREE_ARRAY(Local,compiler->locals, compiler->localsSpace);
|
||||||
KRK_FREE_ARRAY(Upvalue,compiler->upvalues, compiler->upvaluesSpace);
|
FREE_ARRAY(Upvalue,compiler->upvalues, compiler->upvaluesSpace);
|
||||||
KRK_FREE_ARRAY(struct LoopExit,compiler->breaks, compiler->breakSpace);
|
FREE_ARRAY(struct LoopExit,compiler->breaks, compiler->breakSpace);
|
||||||
KRK_FREE_ARRAY(struct LoopExit,compiler->continues, compiler->continueSpace);
|
FREE_ARRAY(struct LoopExit,compiler->continues, compiler->continueSpace);
|
||||||
|
|
||||||
while (compiler->properties) {
|
while (compiler->properties) {
|
||||||
void * tmp = compiler->properties;
|
void * tmp = compiler->properties;
|
||||||
@ -714,8 +698,8 @@ static ssize_t resolveLocal(struct GlobalState * state, Compiler * compiler, Krk
|
|||||||
static size_t renameLocal(struct GlobalState * state, size_t ind, KrkToken name) {
|
static size_t renameLocal(struct GlobalState * state, size_t ind, KrkToken name) {
|
||||||
if (state->current->codeobject->localNameCount + 1 > state->current->localNameCapacity) {
|
if (state->current->codeobject->localNameCount + 1 > state->current->localNameCapacity) {
|
||||||
size_t old = state->current->localNameCapacity;
|
size_t old = state->current->localNameCapacity;
|
||||||
state->current->localNameCapacity = KRK_GROW_CAPACITY(old);
|
state->current->localNameCapacity = GROW_CAPACITY(old);
|
||||||
state->current->codeobject->localNames = KRK_GROW_ARRAY(KrkLocalEntry, state->current->codeobject->localNames, old, state->current->localNameCapacity);
|
state->current->codeobject->localNames = GROW_ARRAY(KrkLocalEntry, state->current->codeobject->localNames, old, state->current->localNameCapacity);
|
||||||
}
|
}
|
||||||
state->current->codeobject->localNames[state->current->codeobject->localNameCount].id = ind;
|
state->current->codeobject->localNames[state->current->codeobject->localNameCount].id = ind;
|
||||||
state->current->codeobject->localNames[state->current->codeobject->localNameCount].birthday = currentChunk()->count;
|
state->current->codeobject->localNames[state->current->codeobject->localNameCount].birthday = currentChunk()->count;
|
||||||
@ -727,8 +711,8 @@ static size_t renameLocal(struct GlobalState * state, size_t ind, KrkToken name)
|
|||||||
static size_t addLocal(struct GlobalState * state, KrkToken name) {
|
static size_t addLocal(struct GlobalState * state, KrkToken name) {
|
||||||
if (state->current->localCount + 1 > state->current->localsSpace) {
|
if (state->current->localCount + 1 > state->current->localsSpace) {
|
||||||
size_t old = state->current->localsSpace;
|
size_t old = state->current->localsSpace;
|
||||||
state->current->localsSpace = KRK_GROW_CAPACITY(old);
|
state->current->localsSpace = GROW_CAPACITY(old);
|
||||||
state->current->locals = KRK_GROW_ARRAY(Local,state->current->locals,old,state->current->localsSpace);
|
state->current->locals = GROW_ARRAY(Local,state->current->locals,old,state->current->localsSpace);
|
||||||
}
|
}
|
||||||
size_t out = state->current->localCount;
|
size_t out = state->current->localCount;
|
||||||
Local * local = &state->current->locals[state->current->localCount++];
|
Local * local = &state->current->locals[state->current->localCount++];
|
||||||
@ -800,10 +784,10 @@ static void number(struct GlobalState * state, int exprType, RewindState *rewind
|
|||||||
invalidTarget(state, exprType, "literal");
|
invalidTarget(state, exprType, "literal");
|
||||||
|
|
||||||
for (size_t j = 0; j < state->parser.previous.length; ++j) {
|
for (size_t j = 0; j < state->parser.previous.length; ++j) {
|
||||||
if (start[j] == 'x' || start[j] == 'X') break;
|
if (state->parser.previous.start[j] == '.') {
|
||||||
if (start[j] == '.' || start[j] == 'e' || start[j] == 'E') {
|
|
||||||
#ifndef KRK_NO_FLOAT
|
#ifndef KRK_NO_FLOAT
|
||||||
emitConstant(krk_parse_float(start, state->parser.previous.length));
|
double value = strtod(start, NULL);
|
||||||
|
emitConstant(FLOATING_VAL(value));
|
||||||
#else
|
#else
|
||||||
error("no float support");
|
error("no float support");
|
||||||
#endif
|
#endif
|
||||||
@ -827,43 +811,9 @@ static int _emitJump(struct GlobalState * state, uint8_t opcode) {
|
|||||||
}
|
}
|
||||||
#define emitJump(o) _emitJump(state,o)
|
#define emitJump(o) _emitJump(state,o)
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Emit over-long jump target.
|
|
||||||
*
|
|
||||||
* Our jump instructions take only two bytes as operands, as that typically suffices
|
|
||||||
* for storing the appropriate forward or backwards offset, without needing to waste
|
|
||||||
* lots of bytes for small jumps, or recalculate everything to expand a jump to
|
|
||||||
* fit a larger offset. If we *do* get a jump offset that is too big to fit in our
|
|
||||||
* available operand space, we replace the whole instruction with one that fetches
|
|
||||||
* the desired target, slowly, from a table attached to the codeobject, alongside
|
|
||||||
* the original instruction opcode.
|
|
||||||
*/
|
|
||||||
static void _emitOverlongJump(struct GlobalState * state, int offset, int jump) {
|
|
||||||
KrkCodeObject * co = state->current->codeobject;
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < co->overlongJumpsCount && co->overlongJumps[i].instructionOffset != (uint32_t)offset) i++;
|
|
||||||
if (i == co->overlongJumpsCount) {
|
|
||||||
/* Not an existing overlong jump, need to make a new one. */
|
|
||||||
if (co->overlongJumpsCount + 1 > co->overlongJumpsCapacity) {
|
|
||||||
size_t old = co->overlongJumpsCapacity;
|
|
||||||
co->overlongJumpsCapacity = KRK_GROW_CAPACITY(old);
|
|
||||||
co->overlongJumps = KRK_GROW_ARRAY(KrkOverlongJump,co->overlongJumps,old,co->overlongJumpsCapacity);
|
|
||||||
}
|
|
||||||
co->overlongJumps[i].instructionOffset = offset;
|
|
||||||
co->overlongJumps[i].originalOpcode = currentChunk()->code[offset-1];
|
|
||||||
co->overlongJumpsCount++;
|
|
||||||
currentChunk()->code[offset-1] = OP_OVERLONG_JUMP;
|
|
||||||
}
|
|
||||||
/* Update jump target */
|
|
||||||
co->overlongJumps[i].intendedTarget = jump >> 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _patchJump(struct GlobalState * state, int offset) {
|
static void _patchJump(struct GlobalState * state, int offset) {
|
||||||
int jump = currentChunk()->count - offset - 2;
|
int jump = currentChunk()->count - offset - 2;
|
||||||
|
if (jump > 0xFFFF) error("Jump offset is too large for opcode.");
|
||||||
if (jump > 0xFFFF) {
|
|
||||||
_emitOverlongJump(state, offset, jump);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentChunk()->code[offset] = (jump >> 8) & 0xFF;
|
currentChunk()->code[offset] = (jump >> 8) & 0xFF;
|
||||||
currentChunk()->code[offset + 1] = (jump) & 0xFF;
|
currentChunk()->code[offset + 1] = (jump) & 0xFF;
|
||||||
@ -871,49 +821,10 @@ static void _patchJump(struct GlobalState * state, int offset) {
|
|||||||
|
|
||||||
#define patchJump(o) _patchJump(state,o)
|
#define patchJump(o) _patchJump(state,o)
|
||||||
|
|
||||||
/**
|
static void compareChained(struct GlobalState * state, int inner) {
|
||||||
* @brief Add expression debug information.
|
|
||||||
*
|
|
||||||
* This allows us to print tracebacks with underlined expressions,
|
|
||||||
* implementing Python's PEP 657. We store information on the
|
|
||||||
* left and right sides of binary expressions and the "main" token
|
|
||||||
* of the expression (the binary operator, usually).
|
|
||||||
*
|
|
||||||
* This should be called immediately after the relevant opcode is
|
|
||||||
* emitted. The mapping applies to only a single opcode, so it should
|
|
||||||
* be the one that is going to cause an exception to be raised - be
|
|
||||||
* particularly careful not emit this after an @c OP_NOT if it comes
|
|
||||||
* after a more relevant opcode.
|
|
||||||
*
|
|
||||||
* If the token mechanism here doesn't provide sufficient flexibility,
|
|
||||||
* artificial tokens can be created to set up the spans.
|
|
||||||
*/
|
|
||||||
static void writeExpressionLocation(KrkToken * before, KrkToken * after, KrkToken * current, struct GlobalState * state) {
|
|
||||||
#ifndef KRK_DISABLE_DEBUG
|
|
||||||
/* We can only support the display of underlines when the whole expression is on one line. */
|
|
||||||
if (after->linePtr != before->linePtr) return;
|
|
||||||
if (after->line != state->parser.previous.line) return;
|
|
||||||
|
|
||||||
/* For the sake of runtime storage, we only store this debug information
|
|
||||||
* when it fits within the first 256 characters of a line, so we can
|
|
||||||
* use uint8_ts to store these. */
|
|
||||||
if (before->col > 255 || current->col > 255 ||
|
|
||||||
(current->col + current->literalWidth) > 255 ||
|
|
||||||
(after->col + after->literalWidth) > 255) return;
|
|
||||||
|
|
||||||
krk_debug_addExpression(state->current->codeobject,
|
|
||||||
before->col, current->col,
|
|
||||||
current->col + current->literalWidth,
|
|
||||||
after->col + after->literalWidth);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void compareChained(struct GlobalState * state, int inner, KrkToken * preceding) {
|
|
||||||
KrkTokenType operatorType = state->parser.previous.type;
|
KrkTokenType operatorType = state->parser.previous.type;
|
||||||
if (operatorType == TOKEN_NOT) consume(TOKEN_IN, "'in' must follow infix 'not'");
|
if (operatorType == TOKEN_NOT) consume(TOKEN_IN, "'in' must follow infix 'not'");
|
||||||
int invert = (operatorType == TOKEN_IS && match(TOKEN_NOT));
|
int invert = (operatorType == TOKEN_IS && match(TOKEN_NOT));
|
||||||
KrkToken this = state->parser.previous;
|
|
||||||
KrkToken next = state->parser.current;
|
|
||||||
|
|
||||||
ParseRule * rule = getRule(operatorType);
|
ParseRule * rule = getRule(operatorType);
|
||||||
parsePrecedence(state, (Precedence)(rule->precedence + 1));
|
parsePrecedence(state, (Precedence)(rule->precedence + 1));
|
||||||
@ -924,28 +835,25 @@ static void compareChained(struct GlobalState * state, int inner, KrkToken * pre
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (operatorType) {
|
switch (operatorType) {
|
||||||
case TOKEN_BANG_EQUAL: emitByte(OP_EQUAL); invert = 1; break;
|
case TOKEN_BANG_EQUAL: emitBytes(OP_EQUAL, OP_NOT); break;
|
||||||
case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); break;
|
case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); break;
|
||||||
case TOKEN_GREATER: emitByte(OP_GREATER); break;
|
case TOKEN_GREATER: emitByte(OP_GREATER); break;
|
||||||
case TOKEN_GREATER_EQUAL: emitByte(OP_GREATER_EQUAL); break;
|
case TOKEN_GREATER_EQUAL: emitByte(OP_GREATER_EQUAL); break;
|
||||||
case TOKEN_LESS: emitByte(OP_LESS); break;
|
case TOKEN_LESS: emitByte(OP_LESS); break;
|
||||||
case TOKEN_LESS_EQUAL: emitByte(OP_LESS_EQUAL); break;
|
case TOKEN_LESS_EQUAL: emitByte(OP_LESS_EQUAL); break;
|
||||||
|
|
||||||
case TOKEN_IS: emitByte(OP_IS); break;
|
case TOKEN_IS: emitByte(OP_IS); if (invert) emitByte(OP_NOT); break;
|
||||||
|
|
||||||
case TOKEN_IN: emitByte(OP_INVOKE_CONTAINS); break;
|
case TOKEN_IN: emitByte(OP_INVOKE_CONTAINS); break;
|
||||||
case TOKEN_NOT: emitByte(OP_INVOKE_CONTAINS); invert = 1; break;
|
case TOKEN_NOT: emitBytes(OP_INVOKE_CONTAINS, OP_NOT); break;
|
||||||
|
|
||||||
default: error("Invalid binary comparison operator?"); break;
|
default: error("Invalid binary comparison operator?"); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeExpressionLocation(preceding, &state->parser.previous, &this, state);
|
|
||||||
if (invert) emitByte(OP_NOT);
|
|
||||||
|
|
||||||
if (getRule(state->parser.current.type)->precedence == PREC_COMPARISON) {
|
if (getRule(state->parser.current.type)->precedence == PREC_COMPARISON) {
|
||||||
size_t exitJump = emitJump(OP_JUMP_IF_FALSE_OR_POP);
|
size_t exitJump = emitJump(OP_JUMP_IF_FALSE_OR_POP);
|
||||||
advance();
|
advance();
|
||||||
compareChained(state, 1, &next);
|
compareChained(state, 1);
|
||||||
patchJump(exitJump);
|
patchJump(exitJump);
|
||||||
if (getRule(state->parser.current.type)->precedence != PREC_COMPARISON) {
|
if (getRule(state->parser.current.type)->precedence != PREC_COMPARISON) {
|
||||||
if (!inner) {
|
if (!inner) {
|
||||||
@ -959,14 +867,13 @@ static void compareChained(struct GlobalState * state, int inner, KrkToken * pre
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void compare(struct GlobalState * state, int exprType, RewindState *rewind) {
|
static void compare(struct GlobalState * state, int exprType, RewindState *rewind) {
|
||||||
compareChained(state, 0, &rewind->oldParser.current);
|
compareChained(state, 0);
|
||||||
invalidTarget(state, exprType, "operator");
|
invalidTarget(state, exprType, "operator");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void binary(struct GlobalState * state, int exprType, RewindState *rewind) {
|
static void binary(struct GlobalState * state, int exprType, RewindState *rewind) {
|
||||||
KrkTokenType operatorType = state->parser.previous.type;
|
KrkTokenType operatorType = state->parser.previous.type;
|
||||||
ParseRule * rule = getRule(operatorType);
|
ParseRule * rule = getRule(operatorType);
|
||||||
KrkToken this = state->parser.previous;
|
|
||||||
parsePrecedence(state, (Precedence)(rule->precedence + (rule->precedence != PREC_EXPONENT)));
|
parsePrecedence(state, (Precedence)(rule->precedence + (rule->precedence != PREC_EXPONENT)));
|
||||||
invalidTarget(state, exprType, "operator");
|
invalidTarget(state, exprType, "operator");
|
||||||
|
|
||||||
@ -988,7 +895,6 @@ static void binary(struct GlobalState * state, int exprType, RewindState *rewind
|
|||||||
case TOKEN_AT: emitByte(OP_MATMUL); break;
|
case TOKEN_AT: emitByte(OP_MATMUL); break;
|
||||||
default: return;
|
default: return;
|
||||||
}
|
}
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &state->parser.previous, &this, state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int matchAssignment(struct GlobalState * state) {
|
static int matchAssignment(struct GlobalState * state) {
|
||||||
@ -1025,7 +931,6 @@ static int invalidTarget(struct GlobalState * state, int exprType, const char *
|
|||||||
|
|
||||||
static void assignmentValue(struct GlobalState * state) {
|
static void assignmentValue(struct GlobalState * state) {
|
||||||
KrkTokenType type = state->parser.previous.type;
|
KrkTokenType type = state->parser.previous.type;
|
||||||
KrkToken left = state->parser.previous;
|
|
||||||
if (type == TOKEN_PLUS_PLUS || type == TOKEN_MINUS_MINUS) {
|
if (type == TOKEN_PLUS_PLUS || type == TOKEN_MINUS_MINUS) {
|
||||||
emitConstant(INTEGER_VAL(1));
|
emitConstant(INTEGER_VAL(1));
|
||||||
} else {
|
} else {
|
||||||
@ -1054,7 +959,6 @@ static void assignmentValue(struct GlobalState * state) {
|
|||||||
error("Unexpected operand in assignment");
|
error("Unexpected operand in assignment");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
writeExpressionLocation(&left,&state->parser.previous,&left,state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expression(struct GlobalState * state) {
|
static void expression(struct GlobalState * state) {
|
||||||
@ -1097,9 +1001,6 @@ static void sliceExpression(struct GlobalState * state) {
|
|||||||
|
|
||||||
static void getitem(struct GlobalState * state, int exprType, RewindState *rewind) {
|
static void getitem(struct GlobalState * state, int exprType, RewindState *rewind) {
|
||||||
|
|
||||||
KrkToken *left = &rewind->oldParser.current;
|
|
||||||
KrkToken this = state->parser.previous;
|
|
||||||
|
|
||||||
sliceExpression(state);
|
sliceExpression(state);
|
||||||
|
|
||||||
if (match(TOKEN_COMMA)) {
|
if (match(TOKEN_COMMA)) {
|
||||||
@ -1118,7 +1019,6 @@ static void getitem(struct GlobalState * state, int exprType, RewindState *rewin
|
|||||||
if (matchComplexEnd(state)) {
|
if (matchComplexEnd(state)) {
|
||||||
EMIT_OPERAND_OP(OP_DUP, 2);
|
EMIT_OPERAND_OP(OP_DUP, 2);
|
||||||
emitByte(OP_INVOKE_SETTER);
|
emitByte(OP_INVOKE_SETTER);
|
||||||
writeExpressionLocation(left,&state->parser.current,&this,state);
|
|
||||||
emitByte(OP_POP);
|
emitByte(OP_POP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1127,21 +1027,16 @@ static void getitem(struct GlobalState * state, int exprType, RewindState *rewin
|
|||||||
if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) {
|
if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) {
|
||||||
parsePrecedence(state, PREC_ASSIGNMENT);
|
parsePrecedence(state, PREC_ASSIGNMENT);
|
||||||
emitByte(OP_INVOKE_SETTER);
|
emitByte(OP_INVOKE_SETTER);
|
||||||
writeExpressionLocation(left,&state->parser.previous,&this,state);
|
|
||||||
} else if (exprType == EXPR_CAN_ASSIGN && matchAssignment(state)) {
|
} else if (exprType == EXPR_CAN_ASSIGN && matchAssignment(state)) {
|
||||||
emitBytes(OP_DUP, 1); /* o e o */
|
emitBytes(OP_DUP, 1); /* o e o */
|
||||||
emitBytes(OP_DUP, 1); /* o e o e */
|
emitBytes(OP_DUP, 1); /* o e o e */
|
||||||
emitByte(OP_INVOKE_GETTER); /* o e v */
|
emitByte(OP_INVOKE_GETTER); /* o e v */
|
||||||
writeExpressionLocation(left,&state->parser.previous,&this,state);
|
|
||||||
assignmentValue(state); /* o e v a */
|
assignmentValue(state); /* o e v a */
|
||||||
emitByte(OP_INVOKE_SETTER); /* r */
|
emitByte(OP_INVOKE_SETTER); /* r */
|
||||||
writeExpressionLocation(left,&state->parser.previous,&this,state);
|
|
||||||
} else if (exprType == EXPR_DEL_TARGET && checkEndOfDel(state)) {
|
} else if (exprType == EXPR_DEL_TARGET && checkEndOfDel(state)) {
|
||||||
emitByte(OP_INVOKE_DELETE);
|
emitByte(OP_INVOKE_DELETE);
|
||||||
writeExpressionLocation(left,&state->parser.previous,&this,state);
|
|
||||||
} else {
|
} else {
|
||||||
emitByte(OP_INVOKE_GETTER);
|
emitByte(OP_INVOKE_GETTER);
|
||||||
writeExpressionLocation(left,&state->parser.previous,&this,state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1149,13 +1044,13 @@ static void attributeUnpack(struct GlobalState * state, int exprType) {
|
|||||||
startEatingWhitespace();
|
startEatingWhitespace();
|
||||||
size_t argCount = 0;
|
size_t argCount = 0;
|
||||||
size_t argSpace = 1;
|
size_t argSpace = 1;
|
||||||
ssize_t * args = KRK_GROW_ARRAY(ssize_t,NULL,0,1);
|
ssize_t * args = GROW_ARRAY(ssize_t,NULL,0,1);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (argSpace < argCount + 1) {
|
if (argSpace < argCount + 1) {
|
||||||
size_t old = argSpace;
|
size_t old = argSpace;
|
||||||
argSpace = KRK_GROW_CAPACITY(old);
|
argSpace = GROW_CAPACITY(old);
|
||||||
args = KRK_GROW_ARRAY(ssize_t,args,old,argSpace);
|
args = GROW_ARRAY(ssize_t,args,old,argSpace);
|
||||||
}
|
}
|
||||||
consume(TOKEN_IDENTIFIER, "Expected attribute name");
|
consume(TOKEN_IDENTIFIER, "Expected attribute name");
|
||||||
size_t ind = identifierConstant(state, &state->parser.previous);
|
size_t ind = identifierConstant(state, &state->parser.previous);
|
||||||
@ -1207,7 +1102,7 @@ static void attributeUnpack(struct GlobalState * state, int exprType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_dotDone:
|
_dotDone:
|
||||||
KRK_FREE_ARRAY(ssize_t,args,argSpace);
|
FREE_ARRAY(ssize_t,args,argSpace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1216,15 +1111,12 @@ static void dot(struct GlobalState * state, int exprType, RewindState *rewind) {
|
|||||||
attributeUnpack(state, exprType);
|
attributeUnpack(state, exprType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
KrkToken name = state->parser.current;
|
|
||||||
KrkToken this = state->parser.previous;
|
|
||||||
consume(TOKEN_IDENTIFIER, "Expected property name");
|
consume(TOKEN_IDENTIFIER, "Expected property name");
|
||||||
size_t ind = identifierConstant(state, &state->parser.previous);
|
size_t ind = identifierConstant(state, &state->parser.previous);
|
||||||
if (exprType == EXPR_ASSIGN_TARGET) {
|
if (exprType == EXPR_ASSIGN_TARGET) {
|
||||||
if (matchComplexEnd(state)) {
|
if (matchComplexEnd(state)) {
|
||||||
EMIT_OPERAND_OP(OP_DUP, 1);
|
EMIT_OPERAND_OP(OP_DUP, 1);
|
||||||
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &state->parser.previous, &name, state);
|
|
||||||
emitByte(OP_POP);
|
emitByte(OP_POP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1233,24 +1125,18 @@ static void dot(struct GlobalState * state, int exprType, RewindState *rewind) {
|
|||||||
if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) {
|
if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) {
|
||||||
parsePrecedence(state, PREC_ASSIGNMENT);
|
parsePrecedence(state, PREC_ASSIGNMENT);
|
||||||
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &state->parser.previous, &name, state);
|
|
||||||
} else if (exprType == EXPR_CAN_ASSIGN && matchAssignment(state)) {
|
} else if (exprType == EXPR_CAN_ASSIGN && matchAssignment(state)) {
|
||||||
emitBytes(OP_DUP, 0); /* Duplicate the object */
|
emitBytes(OP_DUP, 0); /* Duplicate the object */
|
||||||
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &name, &this, state);
|
|
||||||
assignmentValue(state);
|
assignmentValue(state);
|
||||||
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &state->parser.previous, &name, state);
|
|
||||||
} else if (exprType == EXPR_DEL_TARGET && checkEndOfDel(state)) {
|
} else if (exprType == EXPR_DEL_TARGET && checkEndOfDel(state)) {
|
||||||
EMIT_OPERAND_OP(OP_DEL_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_DEL_PROPERTY, ind);
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &name, &this, state);
|
|
||||||
} else if (match(TOKEN_LEFT_PAREN)) {
|
} else if (match(TOKEN_LEFT_PAREN)) {
|
||||||
EMIT_OPERAND_OP(OP_GET_METHOD, ind);
|
EMIT_OPERAND_OP(OP_GET_METHOD, ind);
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &name, &this, state);
|
call(state, EXPR_METHOD_CALL,NULL);
|
||||||
call(state, EXPR_METHOD_CALL, rewind);
|
|
||||||
} else {
|
} else {
|
||||||
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
||||||
writeExpressionLocation(&rewind->oldParser.current, &name, &this, state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1264,16 +1150,6 @@ static void literal(struct GlobalState * state, int exprType, RewindState *rewin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ellipsis(struct GlobalState * state, int exprType, RewindState *rewind) {
|
|
||||||
invalidTarget(state, exprType, "literal");
|
|
||||||
KrkValue value;
|
|
||||||
if (!krk_tableGet_fast(&vm.builtins->fields, S("Ellipsis"), &value)) {
|
|
||||||
error("internal compiler error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emitConstant(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void typeHintLocal(struct GlobalState * state) {
|
static void typeHintLocal(struct GlobalState * state) {
|
||||||
state->current->enclosing->enclosed = state->current;
|
state->current->enclosing->enclosed = state->current;
|
||||||
state->current = state->current->enclosing;
|
state->current = state->current->enclosing;
|
||||||
@ -1287,13 +1163,13 @@ static void typeHintLocal(struct GlobalState * state) {
|
|||||||
static void letDeclaration(struct GlobalState * state) {
|
static void letDeclaration(struct GlobalState * state) {
|
||||||
size_t argCount = 0;
|
size_t argCount = 0;
|
||||||
size_t argSpace = 1;
|
size_t argSpace = 1;
|
||||||
ssize_t * args = KRK_GROW_ARRAY(ssize_t,NULL,0,1);
|
ssize_t * args = GROW_ARRAY(ssize_t,NULL,0,1);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (argSpace < argCount + 1) {
|
if (argSpace < argCount + 1) {
|
||||||
size_t old = argSpace;
|
size_t old = argSpace;
|
||||||
argSpace = KRK_GROW_CAPACITY(old);
|
argSpace = GROW_CAPACITY(old);
|
||||||
args = KRK_GROW_ARRAY(ssize_t,args,old,argSpace);
|
args = GROW_ARRAY(ssize_t,args,old,argSpace);
|
||||||
}
|
}
|
||||||
ssize_t ind = parseVariable(state, "Expected variable name.");
|
ssize_t ind = parseVariable(state, "Expected variable name.");
|
||||||
if (state->parser.hadError) goto _letDone;
|
if (state->parser.hadError) goto _letDone;
|
||||||
@ -1353,7 +1229,7 @@ static void letDeclaration(struct GlobalState * state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_letDone:
|
_letDone:
|
||||||
KRK_FREE_ARRAY(ssize_t,args,argSpace);
|
FREE_ARRAY(ssize_t,args,argSpace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1786,7 +1662,9 @@ static KrkToken classDeclaration(struct GlobalState * state) {
|
|||||||
|
|
||||||
consume(TOKEN_IDENTIFIER, "Expected class name after 'class'.");
|
consume(TOKEN_IDENTIFIER, "Expected class name after 'class'.");
|
||||||
|
|
||||||
emitByte(OP_PUSH_BUILD_CLASS);
|
KrkToken buildClass = syntheticToken("__build_class__");
|
||||||
|
size_t ind = identifierConstant(state, &buildClass);
|
||||||
|
EMIT_OPERAND_OP(OP_GET_GLOBAL, ind);
|
||||||
|
|
||||||
Compiler subcompiler;
|
Compiler subcompiler;
|
||||||
initCompiler(state, &subcompiler, TYPE_CLASS);
|
initCompiler(state, &subcompiler, TYPE_CLASS);
|
||||||
@ -1863,19 +1741,18 @@ _pop_class:
|
|||||||
state->currentClass = state->currentClass->enclosing;
|
state->currentClass = state->currentClass->enclosing;
|
||||||
KrkCodeObject * makeclass = endCompiler(state);
|
KrkCodeObject * makeclass = endCompiler(state);
|
||||||
size_t indFunc = krk_addConstant(currentChunk(), OBJECT_VAL(makeclass));
|
size_t indFunc = krk_addConstant(currentChunk(), OBJECT_VAL(makeclass));
|
||||||
|
EMIT_OPERAND_OP(OP_CLOSURE, indFunc);
|
||||||
|
doUpvalues(state, &subcompiler, makeclass);
|
||||||
|
freeCompiler(&subcompiler);
|
||||||
|
|
||||||
RewindState afterFunction = {recordChunk(currentChunk()), krk_tellScanner(&state->scanner), state->parser};
|
RewindState afterFunction = {recordChunk(currentChunk()), krk_tellScanner(&state->scanner), state->parser};
|
||||||
|
|
||||||
size_t nameInd = nonidentifierTokenConstant(state, &classNameToken);
|
size_t nameInd = nonidentifierTokenConstant(state, &classNameToken);
|
||||||
|
EMIT_OPERAND_OP(OP_CONSTANT, nameInd);
|
||||||
|
|
||||||
krk_rewindScanner(&state->scanner, parameters.oldScanner);
|
krk_rewindScanner(&state->scanner, parameters.oldScanner);
|
||||||
state->parser = parameters.oldParser;
|
state->parser = parameters.oldParser;
|
||||||
|
|
||||||
EMIT_OPERAND_OP(OP_CLOSURE, indFunc);
|
|
||||||
doUpvalues(state, &subcompiler, makeclass);
|
|
||||||
freeCompiler(&subcompiler);
|
|
||||||
EMIT_OPERAND_OP(OP_CONSTANT, nameInd);
|
|
||||||
|
|
||||||
if (match(TOKEN_LEFT_PAREN)) {
|
if (match(TOKEN_LEFT_PAREN)) {
|
||||||
call(state, EXPR_CLASS_PARAMETERS, NULL);
|
call(state, EXPR_CLASS_PARAMETERS, NULL);
|
||||||
} else {
|
} else {
|
||||||
@ -1970,10 +1847,6 @@ static KrkToken decorator(struct GlobalState * state, size_t level, FunctionType
|
|||||||
if (identifiersEqual(&at_classmethod, &state->parser.current)) type = TYPE_CLASSMETHOD;
|
if (identifiersEqual(&at_classmethod, &state->parser.current)) type = TYPE_CLASSMETHOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level == 0 && inType == TYPE_FUNCTION && state->current->scopeDepth != 0) {
|
|
||||||
emitByte(OP_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
expression(state);
|
expression(state);
|
||||||
|
|
||||||
consume(TOKEN_EOL, "Expected end of line after decorator.");
|
consume(TOKEN_EOL, "Expected end of line after decorator.");
|
||||||
@ -1990,10 +1863,6 @@ static KrkToken decorator(struct GlobalState * state, size_t level, FunctionType
|
|||||||
if (type == TYPE_METHOD && funcName.length == 8 && !memcmp(funcName.start,"__init__",8)) {
|
if (type == TYPE_METHOD && funcName.length == 8 && !memcmp(funcName.start,"__init__",8)) {
|
||||||
type = TYPE_INIT;
|
type = TYPE_INIT;
|
||||||
}
|
}
|
||||||
if (type == TYPE_FUNCTION && state->current->scopeDepth > 0) {
|
|
||||||
declareVariable(state);
|
|
||||||
markInitialized(state);
|
|
||||||
}
|
|
||||||
function(state, type, blockWidth);
|
function(state, type, blockWidth);
|
||||||
} else if (match(TOKEN_ASYNC)) {
|
} else if (match(TOKEN_ASYNC)) {
|
||||||
if (!match(TOKEN_DEF)) {
|
if (!match(TOKEN_DEF)) {
|
||||||
@ -2002,10 +1871,6 @@ static KrkToken decorator(struct GlobalState * state, size_t level, FunctionType
|
|||||||
}
|
}
|
||||||
consume(TOKEN_IDENTIFIER, "Expected coroutine name after 'def'.");
|
consume(TOKEN_IDENTIFIER, "Expected coroutine name after 'def'.");
|
||||||
funcName = state->parser.previous;
|
funcName = state->parser.previous;
|
||||||
if (type == TYPE_FUNCTION && state->current->scopeDepth > 0) {
|
|
||||||
declareVariable(state);
|
|
||||||
markInitialized(state);
|
|
||||||
}
|
|
||||||
function(state, type == TYPE_METHOD ? TYPE_COROUTINE_METHOD : TYPE_COROUTINE, blockWidth);
|
function(state, type == TYPE_METHOD ? TYPE_COROUTINE_METHOD : TYPE_COROUTINE, blockWidth);
|
||||||
} else if (check(TOKEN_AT)) {
|
} else if (check(TOKEN_AT)) {
|
||||||
funcName = decorator(state, level+1, type);
|
funcName = decorator(state, level+1, type);
|
||||||
@ -2024,12 +1889,10 @@ static KrkToken decorator(struct GlobalState * state, size_t level, FunctionType
|
|||||||
|
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
if (inType == TYPE_FUNCTION) {
|
if (inType == TYPE_FUNCTION) {
|
||||||
if (state->current->scopeDepth == 0) {
|
state->parser.previous = funcName;
|
||||||
size_t ind = identifierConstant(state, &funcName);
|
declareVariable(state);
|
||||||
defineVariable(state, ind);
|
size_t ind = (state->current->scopeDepth > 0) ? 0 : identifierConstant(state, &funcName);
|
||||||
} else {
|
defineVariable(state, ind);
|
||||||
emitByte(OP_SWAP_POP);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
size_t ind = identifierConstant(state, &funcName);
|
size_t ind = identifierConstant(state, &funcName);
|
||||||
rememberClassProperty(state, ind);
|
rememberClassProperty(state, ind);
|
||||||
@ -2052,9 +1915,7 @@ static void emitLoop(struct GlobalState * state, int loopStart, uint8_t loopType
|
|||||||
emitByte(loopType);
|
emitByte(loopType);
|
||||||
|
|
||||||
int offset = currentChunk()->count - loopStart + ((loopType == OP_LOOP_ITER) ? -1 : 2);
|
int offset = currentChunk()->count - loopStart + ((loopType == OP_LOOP_ITER) ? -1 : 2);
|
||||||
if (offset > 0xFFFF) {
|
if (offset > 0xFFFF) error("Loop jump offset is too large for opcode.");
|
||||||
_emitOverlongJump(state, currentChunk()->count, offset);
|
|
||||||
}
|
|
||||||
emitBytes(offset >> 8, offset);
|
emitBytes(offset >> 8, offset);
|
||||||
|
|
||||||
/* Patch break statements */
|
/* Patch break statements */
|
||||||
@ -2131,6 +1992,9 @@ static void ifStatement(struct GlobalState * state) {
|
|||||||
|
|
||||||
if (state->parser.hadError) return;
|
if (state->parser.hadError) return;
|
||||||
|
|
||||||
|
int elseJump = emitJump(OP_JUMP);
|
||||||
|
patchJump(thenJump);
|
||||||
|
|
||||||
/* See if we have a matching else block */
|
/* See if we have a matching else block */
|
||||||
if (blockWidth == 0 || (check(TOKEN_INDENTATION) && (state->parser.current.length == blockWidth))) {
|
if (blockWidth == 0 || (check(TOKEN_INDENTATION) && (state->parser.current.length == blockWidth))) {
|
||||||
/* This is complicated */
|
/* This is complicated */
|
||||||
@ -2140,8 +2004,6 @@ static void ifStatement(struct GlobalState * state) {
|
|||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
if (match(TOKEN_ELSE) || check(TOKEN_ELIF)) {
|
if (match(TOKEN_ELSE) || check(TOKEN_ELIF)) {
|
||||||
int elseJump = emitJump(OP_JUMP);
|
|
||||||
patchJump(thenJump);
|
|
||||||
if (state->parser.current.type == TOKEN_ELIF || check(TOKEN_IF)) {
|
if (state->parser.current.type == TOKEN_ELIF || check(TOKEN_IF)) {
|
||||||
state->parser.previous = myPrevious;
|
state->parser.previous = myPrevious;
|
||||||
ifStatement(state); /* Keep nesting */
|
ifStatement(state); /* Keep nesting */
|
||||||
@ -2151,8 +2013,6 @@ static void ifStatement(struct GlobalState * state) {
|
|||||||
block(state,blockWidth,"else");
|
block(state,blockWidth,"else");
|
||||||
endScope(state);
|
endScope(state);
|
||||||
}
|
}
|
||||||
patchJump(elseJump);
|
|
||||||
return;
|
|
||||||
} else if (!check(TOKEN_EOF) && !check(TOKEN_EOL)) {
|
} else if (!check(TOKEN_EOF) && !check(TOKEN_EOL)) {
|
||||||
if (blockWidth) {
|
if (blockWidth) {
|
||||||
krk_ungetToken(&state->scanner, state->parser.current);
|
krk_ungetToken(&state->scanner, state->parser.current);
|
||||||
@ -2164,7 +2024,7 @@ static void ifStatement(struct GlobalState * state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
patchJump(thenJump);
|
patchJump(elseJump);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void patchBreaks(struct GlobalState * state, int loopStart) {
|
static void patchBreaks(struct GlobalState * state, int loopStart) {
|
||||||
@ -2178,8 +2038,8 @@ static void patchBreaks(struct GlobalState * state, int loopStart) {
|
|||||||
static void breakStatement(struct GlobalState * state) {
|
static void breakStatement(struct GlobalState * state) {
|
||||||
if (state->current->breakSpace < state->current->breakCount + 1) {
|
if (state->current->breakSpace < state->current->breakCount + 1) {
|
||||||
size_t old = state->current->breakSpace;
|
size_t old = state->current->breakSpace;
|
||||||
state->current->breakSpace = KRK_GROW_CAPACITY(old);
|
state->current->breakSpace = GROW_CAPACITY(old);
|
||||||
state->current->breaks = KRK_GROW_ARRAY(struct LoopExit,state->current->breaks,old,state->current->breakSpace);
|
state->current->breaks = GROW_ARRAY(struct LoopExit,state->current->breaks,old,state->current->breakSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->current->loopLocalCount != state->current->localCount) {
|
if (state->current->loopLocalCount != state->current->localCount) {
|
||||||
@ -2192,8 +2052,8 @@ static void breakStatement(struct GlobalState * state) {
|
|||||||
static void continueStatement(struct GlobalState * state) {
|
static void continueStatement(struct GlobalState * state) {
|
||||||
if (state->current->continueSpace < state->current->continueCount + 1) {
|
if (state->current->continueSpace < state->current->continueCount + 1) {
|
||||||
size_t old = state->current->continueSpace;
|
size_t old = state->current->continueSpace;
|
||||||
state->current->continueSpace = KRK_GROW_CAPACITY(old);
|
state->current->continueSpace = GROW_CAPACITY(old);
|
||||||
state->current->continues = KRK_GROW_ARRAY(struct LoopExit,state->current->continues,old,state->current->continueSpace);
|
state->current->continues = GROW_ARRAY(struct LoopExit,state->current->continues,old,state->current->continueSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->current->loopLocalCount != state->current->localCount) {
|
if (state->current->loopLocalCount != state->current->localCount) {
|
||||||
@ -2394,17 +2254,12 @@ static void tryStatement(struct GlobalState * state) {
|
|||||||
|
|
||||||
if (state->parser.hadError) return;
|
if (state->parser.hadError) return;
|
||||||
|
|
||||||
#define EXIT_JUMP_MAX 64
|
#define EXIT_JUMP_MAX 32
|
||||||
int exitJumps = 2;
|
int exitJumps = 1;
|
||||||
int exitJumpOffsets[EXIT_JUMP_MAX] = {0};
|
int exitJumpOffsets[EXIT_JUMP_MAX] = {0};
|
||||||
|
|
||||||
/* Jump possibly to `else` */
|
|
||||||
exitJumpOffsets[0] = emitJump(OP_JUMP);
|
exitJumpOffsets[0] = emitJump(OP_JUMP);
|
||||||
|
|
||||||
/* Except entry point; ENTER_EXCEPT jumps to `finally` or continues to
|
|
||||||
* first `except` expression test; may end up redundant if there is only an 'else'. */
|
|
||||||
patchJump(tryJump);
|
patchJump(tryJump);
|
||||||
exitJumpOffsets[1] = emitJump(OP_ENTER_EXCEPT);
|
|
||||||
|
|
||||||
int firstJump = 0;
|
int firstJump = 0;
|
||||||
int nextJump = -1;
|
int nextJump = -1;
|
||||||
@ -2420,6 +2275,7 @@ _anotherExcept:
|
|||||||
if (exitJumps && !firstJump && match(TOKEN_EXCEPT)) {
|
if (exitJumps && !firstJump && match(TOKEN_EXCEPT)) {
|
||||||
if (nextJump != -1) {
|
if (nextJump != -1) {
|
||||||
patchJump(nextJump);
|
patchJump(nextJump);
|
||||||
|
emitByte(OP_POP);
|
||||||
}
|
}
|
||||||
/* Match filter expression (should be class or tuple) */
|
/* Match filter expression (should be class or tuple) */
|
||||||
if (!check(TOKEN_COLON) && !check(TOKEN_AS)) {
|
if (!check(TOKEN_COLON) && !check(TOKEN_AS)) {
|
||||||
@ -2427,26 +2283,26 @@ _anotherExcept:
|
|||||||
} else {
|
} else {
|
||||||
emitByte(OP_NONE);
|
emitByte(OP_NONE);
|
||||||
}
|
}
|
||||||
nextJump = emitJump(OP_FILTER_EXCEPT);
|
emitByte(OP_FILTER_EXCEPT);
|
||||||
|
nextJump = emitJump(OP_JUMP_IF_FALSE_OR_POP);
|
||||||
|
|
||||||
/* Match 'as' to rename exception */
|
/* Match 'as' to rename exception */
|
||||||
size_t nameInd = 0;
|
|
||||||
if (match(TOKEN_AS)) {
|
if (match(TOKEN_AS)) {
|
||||||
consume(TOKEN_IDENTIFIER, "Expected identifier after 'as'.");
|
consume(TOKEN_IDENTIFIER, "Expected identifier after 'as'.");
|
||||||
state->current->locals[exceptionObject].name = state->parser.previous;
|
state->current->locals[exceptionObject].name = state->parser.previous;
|
||||||
/* `renameLocal` only introduces names for scoped debugging */
|
|
||||||
nameInd = renameLocal(state, exceptionObject, state->parser.previous);
|
|
||||||
} else {
|
} else {
|
||||||
state->current->locals[exceptionObject].name = syntheticToken("");
|
/* XXX Should we remove this now? */
|
||||||
|
state->current->locals[exceptionObject].name = syntheticToken("exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t nameInd = renameLocal(state, exceptionObject, state->current->locals[exceptionObject].name);
|
||||||
|
|
||||||
consume(TOKEN_COLON, "Expected ':' after 'except'.");
|
consume(TOKEN_COLON, "Expected ':' after 'except'.");
|
||||||
beginScope(state);
|
beginScope(state);
|
||||||
block(state,blockWidth,"except");
|
block(state,blockWidth,"except");
|
||||||
endScope(state);
|
endScope(state);
|
||||||
|
|
||||||
/* Remove scoped name */
|
state->current->codeobject->localNames[nameInd].deathday = (size_t)currentChunk()->count;
|
||||||
if (nameInd) state->current->codeobject->localNames[nameInd].deathday = (size_t)currentChunk()->count;
|
|
||||||
|
|
||||||
if (exitJumps < EXIT_JUMP_MAX) {
|
if (exitJumps < EXIT_JUMP_MAX) {
|
||||||
exitJumpOffsets[exitJumps++] = emitJump(OP_JUMP);
|
exitJumpOffsets[exitJumps++] = emitJump(OP_JUMP);
|
||||||
@ -2461,7 +2317,6 @@ _anotherExcept:
|
|||||||
patchJump(exitJumpOffsets[0]);
|
patchJump(exitJumpOffsets[0]);
|
||||||
firstJump = 1;
|
firstJump = 1;
|
||||||
emitByte(OP_TRY_ELSE);
|
emitByte(OP_TRY_ELSE);
|
||||||
state->current->locals[exceptionObject].name = syntheticToken("");
|
|
||||||
beginScope(state);
|
beginScope(state);
|
||||||
block(state, blockWidth, "else");
|
block(state, blockWidth, "else");
|
||||||
endScope(state);
|
endScope(state);
|
||||||
@ -2477,16 +2332,19 @@ _anotherExcept:
|
|||||||
for (int i = firstJump; i < exitJumps; ++i) {
|
for (int i = firstJump; i < exitJumps; ++i) {
|
||||||
patchJump(exitJumpOffsets[i]);
|
patchJump(exitJumpOffsets[i]);
|
||||||
}
|
}
|
||||||
if (nextJump != -1) {
|
size_t nameInd = renameLocal(state, exceptionObject, syntheticToken("__tmp"));
|
||||||
patchJump(nextJump);
|
|
||||||
}
|
|
||||||
emitByte(OP_BEGIN_FINALLY);
|
emitByte(OP_BEGIN_FINALLY);
|
||||||
exitJumps = 0;
|
exitJumps = 0;
|
||||||
state->current->locals[exceptionObject].name = syntheticToken("");
|
if (nextJump != -1) {
|
||||||
|
emitByte(OP_NONE);
|
||||||
|
patchJump(nextJump);
|
||||||
|
emitByte(OP_POP);
|
||||||
|
}
|
||||||
beginScope(state);
|
beginScope(state);
|
||||||
block(state,blockWidth,"finally");
|
block(state,blockWidth,"finally");
|
||||||
endScope(state);
|
endScope(state);
|
||||||
nextJump = -2;
|
nextJump = -2;
|
||||||
|
state->current->codeobject->localNames[nameInd].deathday = (size_t)currentChunk()->count;
|
||||||
emitByte(OP_END_FINALLY);
|
emitByte(OP_END_FINALLY);
|
||||||
} else if (!check(TOKEN_EOL) && !check(TOKEN_EOF)) {
|
} else if (!check(TOKEN_EOL) && !check(TOKEN_EOF)) {
|
||||||
krk_ungetToken(&state->scanner, state->parser.current);
|
krk_ungetToken(&state->scanner, state->parser.current);
|
||||||
@ -2504,8 +2362,10 @@ _anotherExcept:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextJump >= 0) {
|
if (nextJump >= 0) {
|
||||||
patchJump(nextJump);
|
|
||||||
emitByte(OP_BEGIN_FINALLY);
|
emitByte(OP_BEGIN_FINALLY);
|
||||||
|
emitByte(OP_NONE);
|
||||||
|
patchJump(nextJump);
|
||||||
|
emitByte(OP_POP);
|
||||||
emitByte(OP_END_FINALLY);
|
emitByte(OP_END_FINALLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2634,8 +2494,8 @@ static void fromImportStatement(struct GlobalState * state) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (match(TOKEN_DOT) || match(TOKEN_ELLIPSIS)) {
|
while (match(TOKEN_DOT)) {
|
||||||
leadingDots += state->parser.previous.length;
|
leadingDots++;
|
||||||
}
|
}
|
||||||
|
|
||||||
importModule(state, &startOfName, leadingDots);
|
importModule(state, &startOfName, leadingDots);
|
||||||
@ -3090,8 +2950,8 @@ static size_t addUpvalue(struct GlobalState * state, Compiler * compiler, ssize_
|
|||||||
}
|
}
|
||||||
if (upvalueCount + 1 > compiler->upvaluesSpace) {
|
if (upvalueCount + 1 > compiler->upvaluesSpace) {
|
||||||
size_t old = compiler->upvaluesSpace;
|
size_t old = compiler->upvaluesSpace;
|
||||||
compiler->upvaluesSpace = KRK_GROW_CAPACITY(old);
|
compiler->upvaluesSpace = GROW_CAPACITY(old);
|
||||||
compiler->upvalues = KRK_GROW_ARRAY(Upvalue,compiler->upvalues,old,compiler->upvaluesSpace);
|
compiler->upvalues = GROW_ARRAY(Upvalue,compiler->upvalues,old,compiler->upvaluesSpace);
|
||||||
}
|
}
|
||||||
compiler->upvalues[upvalueCount].isLocal = isLocal;
|
compiler->upvalues[upvalueCount].isLocal = isLocal;
|
||||||
compiler->upvalues[upvalueCount].index = index;
|
compiler->upvalues[upvalueCount].index = index;
|
||||||
@ -3855,8 +3715,6 @@ static void pstar(struct GlobalState * state, int exprType, RewindState *rewind)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void call(struct GlobalState * state, int exprType, RewindState *rewind) {
|
static void call(struct GlobalState * state, int exprType, RewindState *rewind) {
|
||||||
KrkToken left = rewind ? rewind->oldParser.current : state->parser.previous;
|
|
||||||
KrkToken this = state->parser.previous;
|
|
||||||
startEatingWhitespace();
|
startEatingWhitespace();
|
||||||
size_t argCount = 0, specialArgs = 0, keywordArgs = 0, seenKeywordUnpacking = 0;
|
size_t argCount = 0, specialArgs = 0, keywordArgs = 0, seenKeywordUnpacking = 0;
|
||||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||||
@ -3955,7 +3813,6 @@ static void call(struct GlobalState * state, int exprType, RewindState *rewind)
|
|||||||
} else {
|
} else {
|
||||||
EMIT_OPERAND_OP(OP_CALL, argCount);
|
EMIT_OPERAND_OP(OP_CALL, argCount);
|
||||||
}
|
}
|
||||||
writeExpressionLocation(&left,&state->parser.previous,&this,state);
|
|
||||||
|
|
||||||
invalidTarget(state, exprType, "function call");
|
invalidTarget(state, exprType, "function call");
|
||||||
}
|
}
|
||||||
@ -4126,7 +3983,7 @@ void _createAndBind_compilerClass(void) {
|
|||||||
* @return A compiled code object, or NULL on error.
|
* @return A compiled code object, or NULL on error.
|
||||||
* @exception SyntaxError if @p src could not be compiled.
|
* @exception SyntaxError if @p src could not be compiled.
|
||||||
*/
|
*/
|
||||||
KrkCodeObject * krk_compile(const char * src, const char * fileName) {
|
KrkCodeObject * krk_compile(const char * src, char * fileName) {
|
||||||
struct GlobalState * state = (void*)krk_newInstance(KRK_BASE_CLASS(CompilerState));
|
struct GlobalState * state = (void*)krk_newInstance(KRK_BASE_CLASS(CompilerState));
|
||||||
krk_push(OBJECT_VAL(state));
|
krk_push(OBJECT_VAL(state));
|
||||||
|
|
||||||
@ -4250,7 +4107,6 @@ ParseRule krk_parseRules[] = {
|
|||||||
RULE(FALSE, literal, NULL, PREC_NONE),
|
RULE(FALSE, literal, NULL, PREC_NONE),
|
||||||
RULE(NONE, literal, NULL, PREC_NONE),
|
RULE(NONE, literal, NULL, PREC_NONE),
|
||||||
RULE(TRUE, literal, NULL, PREC_NONE),
|
RULE(TRUE, literal, NULL, PREC_NONE),
|
||||||
RULE(ELLIPSIS, ellipsis, NULL, PREC_NONE),
|
|
||||||
RULE(YIELD, yield, NULL, PREC_NONE),
|
RULE(YIELD, yield, NULL, PREC_NONE),
|
||||||
RULE(AWAIT, await, NULL, PREC_NONE),
|
RULE(AWAIT, await, NULL, PREC_NONE),
|
||||||
RULE(LAMBDA, lambda, NULL, PREC_NONE),
|
RULE(LAMBDA, lambda, NULL, PREC_NONE),
|
||||||
|
521
src/debug.c
521
src/debug.c
@ -12,109 +12,6 @@
|
|||||||
#ifndef KRK_DISABLE_DEBUG
|
#ifndef KRK_DISABLE_DEBUG
|
||||||
#define NOOP (void)0
|
#define NOOP (void)0
|
||||||
|
|
||||||
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
|
|
||||||
#pragma GCC optimize ("Os")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STRING_DEBUG_TRUNCATE 50
|
|
||||||
|
|
||||||
void krk_printValueSafe(FILE * f, KrkValue printable) {
|
|
||||||
if (!IS_OBJECT(printable)) {
|
|
||||||
switch (KRK_VAL_TYPE(printable)) {
|
|
||||||
case KRK_VAL_INTEGER: fprintf(f, PRIkrk_int, AS_INTEGER(printable)); break;
|
|
||||||
case KRK_VAL_BOOLEAN: fprintf(f, "%s", AS_BOOLEAN(printable) ? "True" : "False"); break;
|
|
||||||
case KRK_VAL_NONE: fprintf(f, "None"); break;
|
|
||||||
case KRK_VAL_HANDLER:
|
|
||||||
switch (AS_HANDLER_TYPE(printable)) {
|
|
||||||
case OP_PUSH_TRY: fprintf(f, "{try->%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_PUSH_WITH: fprintf(f, "{with->%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_RAISE: fprintf(f, "{raise<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_FILTER_EXCEPT: fprintf(f, "{except<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_BEGIN_FINALLY: fprintf(f, "{finally<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_RETURN: fprintf(f, "{return<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_END_FINALLY: fprintf(f, "{end<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_EXIT_LOOP: fprintf(f, "{exit<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
case OP_RAISE_FROM: fprintf(f, "{reraise<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KRK_VAL_KWARGS: {
|
|
||||||
if (AS_INTEGER(printable) == KWARGS_SINGLE) {
|
|
||||||
fprintf(f, "{unpack single}");
|
|
||||||
} else if (AS_INTEGER(printable) == KWARGS_LIST) {
|
|
||||||
fprintf(f, "{unpack list}");
|
|
||||||
} else if (AS_INTEGER(printable) == KWARGS_DICT) {
|
|
||||||
fprintf(f, "{unpack dict}");
|
|
||||||
} else if (AS_INTEGER(printable) == KWARGS_NIL) {
|
|
||||||
fprintf(f, "{unpack nil}");
|
|
||||||
} else if (AS_INTEGER(printable) == KWARGS_UNSET) {
|
|
||||||
fprintf(f, "{unset default}");
|
|
||||||
} else {
|
|
||||||
fprintf(f, "{sentinel=" PRIkrk_int "}",AS_INTEGER(printable));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
#ifndef KRK_NO_FLOAT
|
|
||||||
if (IS_FLOATING(printable)) fprintf(f, "%.16g", AS_FLOATING(printable));
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (IS_STRING(printable)) {
|
|
||||||
fprintf(f, "'");
|
|
||||||
/*
|
|
||||||
* Print at most STRING_DEBUG_TRUNCATE characters, as bytes, escaping anything not ASCII.
|
|
||||||
* See also str.__repr__ which does something similar with escape sequences, but this
|
|
||||||
* is a dumber, safer, and slightly faster approach.
|
|
||||||
*/
|
|
||||||
for (size_t c = 0; c < AS_STRING(printable)->length && c < STRING_DEBUG_TRUNCATE; ++c) {
|
|
||||||
unsigned char byte = (unsigned char)AS_CSTRING(printable)[c];
|
|
||||||
switch (byte) {
|
|
||||||
case '\\': fprintf(f, "\\\\"); break;
|
|
||||||
case '\n': fprintf(f, "\\n"); break;
|
|
||||||
case '\r': fprintf(f, "\\r"); break;
|
|
||||||
case '\'': fprintf(f, "\\'"); break;
|
|
||||||
default: {
|
|
||||||
if (byte < ' ' || byte > '~') {
|
|
||||||
fprintf(f, "\\x%02x", byte);
|
|
||||||
} else {
|
|
||||||
fprintf(f, "%c", byte);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (AS_STRING(printable)->length > STRING_DEBUG_TRUNCATE) {
|
|
||||||
fprintf(f,"...");
|
|
||||||
}
|
|
||||||
fprintf(f,"'");
|
|
||||||
} else {
|
|
||||||
switch (AS_OBJECT(printable)->type) {
|
|
||||||
case KRK_OBJ_CODEOBJECT: fprintf(f, "<codeobject %s>", AS_codeobject(printable)->name ? AS_codeobject(printable)->name->chars : "?"); break;
|
|
||||||
case KRK_OBJ_CLASS: fprintf(f, "<class %s>", AS_CLASS(printable)->name ? AS_CLASS(printable)->name->chars : "?"); break;
|
|
||||||
case KRK_OBJ_INSTANCE: fprintf(f, "<instance of %s>", AS_INSTANCE(printable)->_class->name->chars); break;
|
|
||||||
case KRK_OBJ_NATIVE: fprintf(f, "<nativefn %s>", ((KrkNative*)AS_OBJECT(printable))->name); break;
|
|
||||||
case KRK_OBJ_CLOSURE: fprintf(f, "<function %s>", AS_CLOSURE(printable)->function->name->chars); break;
|
|
||||||
case KRK_OBJ_BYTES: fprintf(f, "<bytes of len %ld>", (long)AS_BYTES(printable)->length); break;
|
|
||||||
case KRK_OBJ_TUPLE: {
|
|
||||||
fprintf(f, "(");
|
|
||||||
for (size_t i = 0; i < AS_TUPLE(printable)->values.count; ++i) {
|
|
||||||
krk_printValueSafe(f, AS_TUPLE(printable)->values.values[i]);
|
|
||||||
if (i + 1 != AS_TUPLE(printable)->values.count) {
|
|
||||||
fprintf(f, ",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(f, ")");
|
|
||||||
} break;
|
|
||||||
case KRK_OBJ_BOUND_METHOD: fprintf(f, "<method %s>",
|
|
||||||
AS_BOUND_METHOD(printable)->method ? (
|
|
||||||
AS_BOUND_METHOD(printable)->method->type == KRK_OBJ_CLOSURE ? ((KrkClosure*)AS_BOUND_METHOD(printable)->method)->function->name->chars :
|
|
||||||
(AS_BOUND_METHOD(printable)->method->type == KRK_OBJ_NATIVE ? ((KrkNative*)AS_BOUND_METHOD(printable)->method)->name : "(unknown)")) : "(corrupt bound method)"); break;
|
|
||||||
default: fprintf(f, "<%s>", krk_typeName(printable)); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When tracing is enabled, we will present the elements on the stack with
|
* When tracing is enabled, we will present the elements on the stack with
|
||||||
* a safe printer; the format of values printed by krk_printValueSafe will
|
* a safe printer; the format of values printed by krk_printValueSafe will
|
||||||
@ -193,39 +90,20 @@ static inline const char * opcodeClean(const char * opc) {
|
|||||||
return &opc[3];
|
return &opc[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _overlong_jump(KrkCodeObject * func, size_t offset, size_t operand) {
|
|
||||||
for (size_t i = 0; i < func->overlongJumpsCount; ++i) {
|
|
||||||
if (func->overlongJumps[i].instructionOffset == offset) {
|
|
||||||
operand |= (int)func->overlongJumps[i].intendedTarget << 16;
|
|
||||||
size_t target = offset + 2 + operand;
|
|
||||||
if (func->overlongJumps[i].originalOpcode == OP_LOOP ||
|
|
||||||
func->overlongJumps[i].originalOpcode == OP_LOOP_ITER) {
|
|
||||||
target = offset + 2 - operand;
|
|
||||||
}
|
|
||||||
krk_tableSet(AS_DICT(func->jumpTargets), INTEGER_VAL(target), BOOLEAN_VAL(1));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int isJumpTarget(KrkCodeObject * func, size_t startPoint) {
|
static int isJumpTarget(KrkCodeObject * func, size_t startPoint) {
|
||||||
KrkChunk * chunk = &func->chunk;
|
KrkChunk * chunk = &func->chunk;
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
|
||||||
if (IS_NONE(func->jumpTargets)) {
|
|
||||||
func->jumpTargets = krk_dict_of(0,NULL,0);
|
|
||||||
#define SIMPLE(opc) case opc: size = 1; break;
|
#define SIMPLE(opc) case opc: size = 1; break;
|
||||||
#define CONSTANT(opc,more) case opc: { size_t constant _unused = chunk->code[offset + 1]; size = 2; more; break; } \
|
#define CONSTANT(opc,more) case opc: { size_t constant __attribute__((unused)) = chunk->code[offset + 1]; size = 2; more; break; } \
|
||||||
case opc ## _LONG: { size_t constant _unused = (chunk->code[offset + 1] << 16) | \
|
case opc ## _LONG: { size_t constant __attribute__((unused)) = (chunk->code[offset + 1] << 16) | \
|
||||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
||||||
#define OPERANDB(opc,more) case opc: { size = 2; more; break; }
|
#define OPERANDB(opc,more) case opc: { size = 2; more; break; }
|
||||||
#define OPERAND(opc,more) OPERANDB(opc,more) \
|
#define OPERAND(opc,more) OPERANDB(opc,more) \
|
||||||
case opc ## _LONG: { size = 4; more; break; }
|
case opc ## _LONG: { size = 4; more; break; }
|
||||||
#define JUMP(opc,sign) case opc: { uint16_t jump = (chunk->code[offset + 1] << 8) | (chunk->code[offset + 2]); \
|
#define JUMP(opc,sign) case opc: { uint16_t jump = (chunk->code[offset + 1] << 8) | (chunk->code[offset + 2]); \
|
||||||
krk_tableSet(AS_DICT(func->jumpTargets), INTEGER_VAL((size_t)(offset + 3 sign jump)), BOOLEAN_VAL(1)); \
|
if ((size_t)(offset + 3 sign jump) == startPoint) return 1; \
|
||||||
size = 3; break; }
|
size = 3; break; }
|
||||||
#define COMPLICATED(opc,more) case opc: size = 1; more; break;
|
|
||||||
#define OVERLONG_JUMP_MORE size += 2; _overlong_jump(func, offset+1, (chunk->code[offset + 1] << 8) | (chunk->code[offset + 2]))
|
|
||||||
#define CLOSURE_MORE \
|
#define CLOSURE_MORE \
|
||||||
KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
|
KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
|
||||||
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
||||||
@ -247,23 +125,16 @@ static int isJumpTarget(KrkCodeObject * func, size_t startPoint) {
|
|||||||
}
|
}
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
#undef SIMPLE
|
#undef SIMPLE
|
||||||
#undef OPERANDB
|
#undef OPERANDB
|
||||||
#undef OPERAND
|
#undef OPERAND
|
||||||
#undef CONSTANT
|
#undef CONSTANT
|
||||||
#undef JUMP
|
#undef JUMP
|
||||||
#undef COMPLICATED
|
|
||||||
#undef OVERLONG_JUMP_MORE
|
|
||||||
#undef CLOSURE_MORE
|
#undef CLOSURE_MORE
|
||||||
#undef LOCAL_MORE
|
#undef LOCAL_MORE
|
||||||
#undef EXPAND_ARGS_MORE
|
#undef EXPAND_ARGS_MORE
|
||||||
#undef FORMAT_VALUE_MORE
|
#undef FORMAT_VALUE_MORE
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_dict(func->jumpTargets)) return 0;
|
|
||||||
KrkValue garbage;
|
|
||||||
if (krk_tableGet(AS_DICT(func->jumpTargets), INTEGER_VAL(startPoint), &garbage)) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OPARGS FILE * f, const char * fullName, size_t * size, size_t * offset, KrkCodeObject * func, KrkChunk * chunk
|
#define OPARGS FILE * f, const char * fullName, size_t * size, size_t * offset, KrkCodeObject * func, KrkChunk * chunk
|
||||||
@ -303,41 +174,6 @@ static void _jump(OPARGS, int sign) {
|
|||||||
*size = 3;
|
*size = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _complicated(OPARGS, void (*more)(OPARGS)) {
|
|
||||||
_print_opcode(OPARG_VALS);
|
|
||||||
if (more) more(OPARG_VALS);
|
|
||||||
else *size = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SIMPLE(opc)
|
|
||||||
#define JUMP(opc,sign) case opc: fprintf(f, "(%s, to %zu)", opcodeClean(#opc), *offset + 3 sign current_jump); return;
|
|
||||||
#define OPERAND(opc,more)
|
|
||||||
#define CONSTANT(opc,more)
|
|
||||||
#define COMPLICATED(opc,more)
|
|
||||||
static void _overlong_jump_more(OPARGS) {
|
|
||||||
size_t current_jump = (chunk->code[*offset + 1] << 8) | (chunk->code[*offset + 2]);
|
|
||||||
*size = 3;
|
|
||||||
|
|
||||||
/* Now look it up */
|
|
||||||
for (size_t i = 0; i < func->overlongJumpsCount; ++i) {
|
|
||||||
if (*offset + 1 == (size_t)func->overlongJumps[i].instructionOffset) {
|
|
||||||
current_jump |= ((size_t)func->overlongJumps[i].intendedTarget << 16);
|
|
||||||
switch (func->overlongJumps[i].originalOpcode) {
|
|
||||||
#include "opcodes.h"
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(f,"(invalid destination)");
|
|
||||||
}
|
|
||||||
#undef SIMPLE
|
|
||||||
#undef OPERAND
|
|
||||||
#undef CONSTANT
|
|
||||||
#undef JUMP
|
|
||||||
#undef COMPLICATED
|
|
||||||
|
|
||||||
|
|
||||||
#undef NOOP
|
#undef NOOP
|
||||||
#define NOOP (NULL)
|
#define NOOP (NULL)
|
||||||
#define SIMPLE(opc) case opc: _simple(f,#opc,&size,&offset,func,chunk); break;
|
#define SIMPLE(opc) case opc: _simple(f,#opc,&size,&offset,func,chunk); break;
|
||||||
@ -346,9 +182,6 @@ static void _overlong_jump_more(OPARGS) {
|
|||||||
#define OPERAND(opc,more) case opc: _operand(f,#opc,&size,&offset,func,chunk,0,more); break; \
|
#define OPERAND(opc,more) case opc: _operand(f,#opc,&size,&offset,func,chunk,0,more); break; \
|
||||||
case opc ## _LONG: _operand(f,#opc "_LONG",&size,&offset,func,chunk,1,more); break;
|
case opc ## _LONG: _operand(f,#opc "_LONG",&size,&offset,func,chunk,1,more); break;
|
||||||
#define JUMP(opc,sign) case opc: _jump(f,#opc,&size,&offset,func,chunk,sign 1); break;
|
#define JUMP(opc,sign) case opc: _jump(f,#opc,&size,&offset,func,chunk,sign 1); break;
|
||||||
#define COMPLICATED(opc,more) case opc: _complicated(f,#opc,&size,&offset,func,chunk,more); break;
|
|
||||||
|
|
||||||
#define OVERLONG_JUMP_MORE _overlong_jump_more
|
|
||||||
|
|
||||||
#define CLOSURE_MORE _closure_more
|
#define CLOSURE_MORE _closure_more
|
||||||
|
|
||||||
@ -458,14 +291,34 @@ size_t krk_disassembleInstruction(FILE * f, KrkCodeObject * func, size_t offset)
|
|||||||
#undef OPERAND
|
#undef OPERAND
|
||||||
#undef CONSTANT
|
#undef CONSTANT
|
||||||
#undef JUMP
|
#undef JUMP
|
||||||
#undef COMPLICATED
|
|
||||||
#undef OVERLONG_JUMP_MORE
|
|
||||||
#undef CLOSURE_MORE
|
#undef CLOSURE_MORE
|
||||||
#undef LOCAL_MORE
|
#undef LOCAL_MORE
|
||||||
#undef EXPAND_ARGS_MORE
|
#undef EXPAND_ARGS_MORE
|
||||||
#undef FORMAT_VALUE_MORE
|
#undef FORMAT_VALUE_MORE
|
||||||
#undef NOOP
|
#undef NOOP
|
||||||
|
|
||||||
|
struct BreakpointEntry {
|
||||||
|
KrkCodeObject * inFunction;
|
||||||
|
size_t offset;
|
||||||
|
int flags;
|
||||||
|
uint8_t originalOpcode;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_BREAKPOINTS 32
|
||||||
|
struct DebuggerState {
|
||||||
|
int breakpointsCount;
|
||||||
|
KrkDebugCallback debuggerHook;
|
||||||
|
|
||||||
|
/* XXX This was previously thread-local; it probably should still be
|
||||||
|
* specific to an individual thread... but we don't really do
|
||||||
|
* much thread debugging, so... */
|
||||||
|
int repeatStack_top;
|
||||||
|
int repeatStack_bottom;
|
||||||
|
int thisWasForced;
|
||||||
|
|
||||||
|
struct BreakpointEntry breakpoints[MAX_BREAKPOINTS];
|
||||||
|
};
|
||||||
|
|
||||||
int krk_debug_addBreakpointCodeOffset(KrkCodeObject * target, size_t offset, int flags) {
|
int krk_debug_addBreakpointCodeOffset(KrkCodeObject * target, size_t offset, int flags) {
|
||||||
int index = vm.dbgState->breakpointsCount;
|
int index = vm.dbgState->breakpointsCount;
|
||||||
if (vm.dbgState->breakpointsCount == MAX_BREAKPOINTS) {
|
if (vm.dbgState->breakpointsCount == MAX_BREAKPOINTS) {
|
||||||
@ -538,6 +391,12 @@ int krk_debug_enableBreakpoint(int breakIndex) {
|
|||||||
vm.dbgState->breakpoints[breakIndex].inFunction->chunk.code[vm.dbgState->breakpoints[breakIndex].offset] = OP_BREAKPOINT;
|
vm.dbgState->breakpoints[breakIndex].inFunction->chunk.code[vm.dbgState->breakpoints[breakIndex].offset] = OP_BREAKPOINT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
KRK_Function(enablebreakpoint) {
|
||||||
|
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||||
|
if (krk_debug_enableBreakpoint(breakIndex))
|
||||||
|
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||||
|
return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
int krk_debug_disableBreakpoint(int breakIndex) {
|
int krk_debug_disableBreakpoint(int breakIndex) {
|
||||||
if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
|
if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
|
||||||
@ -549,6 +408,12 @@ int krk_debug_disableBreakpoint(int breakIndex) {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
KRK_Function(disablebreakpoint) {
|
||||||
|
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||||
|
if (krk_debug_disableBreakpoint(breakIndex))
|
||||||
|
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||||
|
return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
int krk_debug_removeBreakpoint(int breakIndex) {
|
int krk_debug_removeBreakpoint(int breakIndex) {
|
||||||
if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
|
if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
|
||||||
@ -560,6 +425,61 @@ int krk_debug_removeBreakpoint(int breakIndex) {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
KRK_Function(delbreakpoint) {
|
||||||
|
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||||
|
if (krk_debug_removeBreakpoint(breakIndex))
|
||||||
|
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||||
|
return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
KRK_Function(addbreakpoint) {
|
||||||
|
FUNCTION_TAKES_EXACTLY(2);
|
||||||
|
CHECK_ARG(1,int,krk_integer_type,lineNo);
|
||||||
|
|
||||||
|
int flags = KRK_BREAKPOINT_NORMAL;
|
||||||
|
|
||||||
|
if (hasKw) {
|
||||||
|
KrkValue flagsValue = NONE_VAL();
|
||||||
|
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("flags")), &flagsValue)) {
|
||||||
|
if (!IS_INTEGER(flagsValue))
|
||||||
|
return TYPE_ERROR(int,flagsValue);
|
||||||
|
flags = AS_INTEGER(flagsValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int result;
|
||||||
|
if (IS_STRING(argv[0])) {
|
||||||
|
result = krk_debug_addBreakpointFileLine(AS_STRING(argv[0]), lineNo, flags);
|
||||||
|
} else {
|
||||||
|
KrkCodeObject * target = NULL;
|
||||||
|
if (IS_CLOSURE(argv[0])) {
|
||||||
|
target = AS_CLOSURE(argv[0])->function;
|
||||||
|
} else if (IS_BOUND_METHOD(argv[0]) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))) {
|
||||||
|
target = AS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))->function;
|
||||||
|
} else if (IS_codeobject(argv[0])) {
|
||||||
|
target = AS_codeobject(argv[0]);
|
||||||
|
} else {
|
||||||
|
return TYPE_ERROR(function or method or filename,argv[0]);
|
||||||
|
}
|
||||||
|
/* Figure out what instruction this should be on */
|
||||||
|
size_t last = 0;
|
||||||
|
for (size_t i = 0; i < target->chunk.linesCount; ++i) {
|
||||||
|
if (target->chunk.lines[i].line > (size_t)lineNo) break;
|
||||||
|
if (target->chunk.lines[i].line == (size_t)lineNo) {
|
||||||
|
last = target->chunk.lines[i].startOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last = target->chunk.lines[i].startOffset;
|
||||||
|
}
|
||||||
|
result = krk_debug_addBreakpointCodeOffset(target,last,flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result < 0)
|
||||||
|
return krk_runtimeError(vm.exceptions->baseException, "Could not add breakpoint.");
|
||||||
|
|
||||||
|
return INTEGER_VAL(result);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Begin debugger utility functions.
|
* Begin debugger utility functions.
|
||||||
*
|
*
|
||||||
@ -696,46 +616,237 @@ int krk_debugBreakpointHandler(void) {
|
|||||||
return krk_debuggerHook(frame);
|
return krk_debuggerHook(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void krk_debug_init(void) {
|
/**
|
||||||
|
* dis.dis(object)
|
||||||
|
*/
|
||||||
|
KRK_Function(dis) {
|
||||||
|
FUNCTION_TAKES_EXACTLY(1);
|
||||||
|
|
||||||
|
if (IS_CLOSURE(argv[0])) {
|
||||||
|
KrkCodeObject * func = AS_CLOSURE(argv[0])->function;
|
||||||
|
krk_disassembleCodeObject(stdout, func, func->name ? func->name->chars : "<unnamed>");
|
||||||
|
} else if (IS_codeobject(argv[0])) {
|
||||||
|
krk_disassembleCodeObject(stdout, AS_codeobject(argv[0]), AS_codeobject(argv[0])->name ? AS_codeobject(argv[0])->name->chars : "<unnamed>");
|
||||||
|
} else if (IS_BOUND_METHOD(argv[0])) {
|
||||||
|
if (AS_BOUND_METHOD(argv[0])->method->type == KRK_OBJ_CLOSURE) {
|
||||||
|
KrkCodeObject * func = ((KrkClosure*)AS_BOUND_METHOD(argv[0])->method)->function;
|
||||||
|
const char * methodName = func->name ? func->name->chars : "<unnamed>";
|
||||||
|
const char * typeName = IS_CLASS(AS_BOUND_METHOD(argv[0])->receiver) ? AS_CLASS(AS_BOUND_METHOD(argv[0])->receiver)->name->chars : krk_typeName(AS_BOUND_METHOD(argv[0])->receiver);
|
||||||
|
size_t allocSize = strlen(methodName) + strlen(typeName) + 2;
|
||||||
|
char * tmp = malloc(allocSize);
|
||||||
|
snprintf(tmp, allocSize, "%s.%s", typeName, methodName);
|
||||||
|
krk_disassembleCodeObject(stdout, func, tmp);
|
||||||
|
free(tmp);
|
||||||
|
} else {
|
||||||
|
krk_runtimeError(vm.exceptions->typeError, "Can not disassemble built-in method of '%T'", AS_BOUND_METHOD(argv[0])->receiver);
|
||||||
|
}
|
||||||
|
} else if (IS_CLASS(argv[0])) {
|
||||||
|
KrkValue code;
|
||||||
|
if (krk_tableGet(&AS_CLASS(argv[0])->methods, OBJECT_VAL(S("__func__")), &code) && IS_CLOSURE(code)) {
|
||||||
|
KrkCodeObject * func = AS_CLOSURE(code)->function;
|
||||||
|
krk_disassembleCodeObject(stdout, func, AS_CLASS(argv[0])->name->chars);
|
||||||
|
}
|
||||||
|
/* TODO Methods! */
|
||||||
|
} else {
|
||||||
|
krk_runtimeError(vm.exceptions->typeError, "Don't know how to disassemble '%T'", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
KRK_Function(build) {
|
||||||
|
FUNCTION_TAKES_AT_LEAST(1);
|
||||||
|
FUNCTION_TAKES_AT_MOST(2);
|
||||||
|
CHECK_ARG(0,str,KrkString*,code);
|
||||||
|
char * fileName = "<source>";
|
||||||
|
if (argc > 1) {
|
||||||
|
CHECK_ARG(1,str,KrkString*,filename);
|
||||||
|
fileName = filename->chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unset module */
|
||||||
|
krk_push(OBJECT_VAL(krk_currentThread.module));
|
||||||
|
KrkInstance * module = krk_currentThread.module;
|
||||||
|
krk_currentThread.module = NULL;
|
||||||
|
KrkCodeObject * c = krk_compile(code->chars,fileName);
|
||||||
|
krk_currentThread.module = module;
|
||||||
|
krk_pop();
|
||||||
|
if (c) return OBJECT_VAL(c);
|
||||||
|
else return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NOOP (void)0
|
||||||
|
#define SIMPLE(opc) case opc: size = 1; break;
|
||||||
|
#define CONSTANT(opc,more) case opc: { constant = chunk->code[offset + 1]; size = 2; more; break; } \
|
||||||
|
case opc ## _LONG: { constant = (chunk->code[offset + 1] << 16) | \
|
||||||
|
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
||||||
|
#define OPERAND(opc,more) case opc: { operand = chunk->code[offset + 1]; size = 2; more; break; } \
|
||||||
|
case opc ## _LONG: { operand = (chunk->code[offset + 1] << 16) | \
|
||||||
|
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
||||||
|
#define JUMP(opc,sign) case opc: { jump = 0 sign ((chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])); \
|
||||||
|
size = 3; break; }
|
||||||
|
#define CLOSURE_MORE \
|
||||||
|
KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
|
||||||
|
size_t baseOffset = offset; \
|
||||||
|
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
||||||
|
int isLocal = chunk->code[baseOffset++ + size]; \
|
||||||
|
baseOffset++; \
|
||||||
|
if (isLocal & 2) { \
|
||||||
|
baseOffset += 2; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
size += baseOffset - offset;
|
||||||
|
#define EXPAND_ARGS_MORE
|
||||||
|
#define FORMAT_VALUE_MORE
|
||||||
|
#define LOCAL_MORE local = operand;
|
||||||
|
static KrkValue _examineInternal(KrkCodeObject* func) {
|
||||||
|
KrkValue output = krk_list_of(0,NULL,0);
|
||||||
|
krk_push(output);
|
||||||
|
|
||||||
|
KrkChunk * chunk = &func->chunk;
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < chunk->count) {
|
||||||
|
uint8_t opcode = chunk->code[offset];
|
||||||
|
size_t size = 0;
|
||||||
|
ssize_t constant = -1;
|
||||||
|
ssize_t jump = 0;
|
||||||
|
ssize_t operand = -1;
|
||||||
|
ssize_t local = -1;
|
||||||
|
switch (opcode) {
|
||||||
|
#include "opcodes.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
KrkTuple * newTuple = krk_newTuple(3);
|
||||||
|
krk_push(OBJECT_VAL(newTuple));
|
||||||
|
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(opcode);
|
||||||
|
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(size);
|
||||||
|
if (constant != -1) {
|
||||||
|
newTuple->values.values[newTuple->values.count++] = chunk->constants.values[constant];
|
||||||
|
} else if (jump != 0) {
|
||||||
|
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(jump);
|
||||||
|
} else if (local != -1) {
|
||||||
|
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand); /* Just in case */
|
||||||
|
for (size_t i = 0; i < func->localNameCount; ++i) {
|
||||||
|
if (func->localNames[i].id == (size_t)local && func->localNames[i].birthday <= offset && func->localNames[i].deathday >= offset) {
|
||||||
|
newTuple->values.values[newTuple->values.count-1] = OBJECT_VAL(func->localNames[i].name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (operand != -1) {
|
||||||
|
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand);
|
||||||
|
} else {
|
||||||
|
newTuple->values.values[newTuple->values.count++] = NONE_VAL();
|
||||||
|
}
|
||||||
|
krk_writeValueArray(AS_LIST(output), krk_peek(0));
|
||||||
|
krk_pop();
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return krk_pop();
|
||||||
|
}
|
||||||
|
KRK_Function(examine) {
|
||||||
|
FUNCTION_TAKES_EXACTLY(1);
|
||||||
|
CHECK_ARG(0,codeobject,KrkCodeObject*,func);
|
||||||
|
return _examineInternal(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SIMPLE
|
||||||
|
#undef OPERANDB
|
||||||
|
#undef OPERAND
|
||||||
|
#undef CONSTANT
|
||||||
|
#undef JUMP
|
||||||
|
#undef CLOSURE_MORE
|
||||||
|
#undef LOCAL_MORE
|
||||||
|
#undef EXPAND_ARGS_MORE
|
||||||
|
#undef FORMAT_VALUE_MORE
|
||||||
|
|
||||||
|
void krk_module_init_dis(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_attachNamedObject(&vm.modules, "dis", (KrkObj*)module);
|
||||||
|
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("dis"));
|
||||||
|
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||||
|
|
||||||
vm.dbgState = calloc(1, sizeof(struct DebuggerState));
|
vm.dbgState = calloc(1, sizeof(struct DebuggerState));
|
||||||
vm.dbgState->repeatStack_top = -1;
|
vm.dbgState->repeatStack_top = -1;
|
||||||
vm.dbgState->repeatStack_bottom = -1;
|
vm.dbgState->repeatStack_bottom = -1;
|
||||||
}
|
|
||||||
|
|
||||||
void krk_debug_addExpression(KrkCodeObject * codeobject, uint8_t start, uint8_t midStart, uint8_t midEnd, uint8_t end) {
|
KRK_DOC(module,
|
||||||
/* Traceback entries point to the last byte of an opcode, due to the way instruction fetch
|
"@brief Provides tools for disassembling bytecode.\n\n"
|
||||||
* advances the instruction pointer past all of the constituent operands of an opcode; as
|
"### Code Disassembly in Kuroko\n\n"
|
||||||
* such, our map is based on these last bytes and we need to look at the byte preceding
|
"The @c dis module contains functions for dealing with _code objects_ which "
|
||||||
* the current count when adding new entries. */
|
"represent the compiled bytecode of a Kuroko function. The bytecode compilation "
|
||||||
size_t offset = codeobject->chunk.count - 1;
|
"process is entirely static and bytecode analysis can be performed without calling "
|
||||||
|
"into the VM to run dynamic code.\n\n"
|
||||||
|
"### Debugger Breakpoints\n\n"
|
||||||
|
"Kuroko interpreters can provide a debugger hook through the C API's "
|
||||||
|
"@ref krk_debug_registerCallback() function. Breakpoints can be managed both "
|
||||||
|
"from the C API and from this module's @ref addbreakpoint, @ref delbreakpoint, "
|
||||||
|
"@ref enablebreakpoint, and @ref disablebreakpoint methods."
|
||||||
|
);
|
||||||
|
|
||||||
/* We can feasibly support offsets larger than UINT32_MAX on 64-bit platforms, though this
|
KRK_DOC(BIND_FUNC(module, dis),
|
||||||
* should never really happen. Just in case, avoid messing up our table with bad values. */
|
"@brief Disassemble an object.\n"
|
||||||
if (offset > UINT32_MAX) return;
|
"@arguments obj\n\n"
|
||||||
|
"Dumps a disassembly of the bytecode in the code object associated with @p obj. "
|
||||||
|
"If @p obj can not be disassembled, a @ref TypeError is raised.");
|
||||||
|
|
||||||
if (codeobject->expressionsCapacity < codeobject->expressionsCount + 1) {
|
KRK_DOC(BIND_FUNC(module, build),
|
||||||
size_t old = codeobject->expressionsCapacity;
|
"@brief Compile a string to a code object.\n"
|
||||||
codeobject->expressionsCapacity = KRK_GROW_CAPACITY(old);
|
"@arguments code\n\n"
|
||||||
codeobject->expressions = KRK_GROW_ARRAY(KrkExpressionsMap, codeobject->expressions, old, codeobject->expressionsCapacity);
|
"Compiles the string @p code and returns a code object. If a syntax "
|
||||||
}
|
"error is encountered, it will be raised.");
|
||||||
|
|
||||||
codeobject->expressions[codeobject->expressionsCount] = (KrkExpressionsMap){offset,start,midStart,midEnd,end};
|
KRK_DOC(BIND_FUNC(module, examine),
|
||||||
codeobject->expressionsCount++;
|
"@brief Convert a code object to a list of instructions.\n"
|
||||||
}
|
"@arguments func\n\n"
|
||||||
|
"Examines the code object @p func and returns a list representation of its instructions. "
|
||||||
|
"Each instruction entry is a tuple of the opcode, total instruction size in bytes, and "
|
||||||
|
"the operand of the argument, either as an integer for jump offsets, the actual value for "
|
||||||
|
"constant operands, or the name of a local or global variable if available.");
|
||||||
|
|
||||||
int krk_debug_expressionUnderline(const KrkCodeObject* codeobject, uint8_t* start, uint8_t* midStart, uint8_t* midEnd, uint8_t* end, size_t instruction) {
|
KRK_DOC(BIND_FUNC(module, addbreakpoint),
|
||||||
/* We could do binary search here, but as we only print these when an exception 'escapes',
|
"@brief Attach a breakpoint to a code object.\n"
|
||||||
* it's not really worth the optimization over a linear search per line in the traceback. */
|
"@arguments func, line\n\n"
|
||||||
for (size_t i = 0; i < codeobject->expressionsCount; ++i) {
|
"@p func may be a filename string, or a function, method, or code object. Returns "
|
||||||
if (codeobject->expressions[i].bytecodeOffset == instruction) {
|
"the new breakpoint index, or raises @ref Exception if a breakpoint code not be added.");
|
||||||
*start = codeobject->expressions[i].start;
|
|
||||||
*midStart = codeobject->expressions[i].midStart;
|
KRK_DOC(BIND_FUNC(module, delbreakpoint),
|
||||||
*midEnd = codeobject->expressions[i].midEnd;
|
"@brief Delete a breakpoint.\n"
|
||||||
*end = codeobject->expressions[i].end;
|
"@arguments handle\n\n"
|
||||||
return 1;
|
"Delete the breakpoint specified by @p handle, disabling it if it was enabled. "
|
||||||
}
|
"May raise @ref IndexError if @p handle is not a valid breakpoint handle.");
|
||||||
}
|
|
||||||
return 0;
|
KRK_DOC(BIND_FUNC(module, enablebreakpoint),
|
||||||
|
"@brief Enable a breakpoint.\n"
|
||||||
|
"@arguments handle\n\n"
|
||||||
|
"Enable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
||||||
|
"@p handle is not a valid breakpoint handle.");
|
||||||
|
|
||||||
|
KRK_DOC(BIND_FUNC(module, disablebreakpoint),
|
||||||
|
"@brief Disable a breakpoint.\n"
|
||||||
|
"@arguments handle\n\n"
|
||||||
|
"Disable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
||||||
|
"@p handle is not a valid breakpoint handle.");
|
||||||
|
|
||||||
|
krk_attachNamedValue(&module->fields, "BREAKPOINT_ONCE", INTEGER_VAL(KRK_BREAKPOINT_ONCE));
|
||||||
|
krk_attachNamedValue(&module->fields, "BREAKPOINT_REPEAT", INTEGER_VAL(KRK_BREAKPOINT_REPEAT));
|
||||||
|
|
||||||
|
#define OPCODE(opc) krk_attachNamedValue(&module->fields, #opc, INTEGER_VAL(opc));
|
||||||
|
#define SIMPLE(opc) OPCODE(opc)
|
||||||
|
#define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||||
|
#define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||||
|
#define JUMP(opc,sign) OPCODE(opc)
|
||||||
|
#include "opcodes.h"
|
||||||
|
#undef SIMPLE
|
||||||
|
#undef OPERANDB
|
||||||
|
#undef OPERAND
|
||||||
|
#undef CONSTANT
|
||||||
|
#undef JUMP
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -288,27 +288,12 @@ static void dumpInnerException(KrkValue exception, int depth) {
|
|||||||
}
|
}
|
||||||
if (line == lineNo) {
|
if (line == lineNo) {
|
||||||
fprintf(stderr," ");
|
fprintf(stderr," ");
|
||||||
unsigned short j = 1;
|
while (c == ' ') c = fgetc(f);
|
||||||
while (c == ' ' || c == '\t') {
|
|
||||||
c = fgetc(f);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
do {
|
do {
|
||||||
fputc(c, stderr);
|
fputc(c, stderr);
|
||||||
c = fgetc(f);
|
c = fgetc(f);
|
||||||
} while (!feof(f) && c > 0 && c != '\n');
|
} while (!feof(f) && c > 0 && c != '\n');
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
#ifndef KRK_DISABLE_DEBUG
|
|
||||||
uint8_t start, midStart, midEnd, end;
|
|
||||||
if (krk_debug_expressionUnderline(function, &start, &midStart, &midEnd, &end, instruction)) {
|
|
||||||
fprintf(stderr," ");
|
|
||||||
for (; j < start; ++j) fprintf(stderr," ");
|
|
||||||
for (; j < midStart; ++j) fprintf(stderr,"~");
|
|
||||||
for (; j < midEnd; ++j) fprintf(stderr, "^");
|
|
||||||
for (; j < end; ++j) fprintf(stderr,"~");
|
|
||||||
fprintf(stderr,"\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (!feof(f));
|
} while (!feof(f));
|
||||||
@ -476,7 +461,7 @@ KrkValue krk_runtimeError(KrkClass * type, const char * fmt, ...) {
|
|||||||
/* Allocate an exception object of the requested type. */
|
/* Allocate an exception object of the requested type. */
|
||||||
KrkInstance * exceptionObject = krk_newInstance(type);
|
KrkInstance * exceptionObject = krk_newInstance(type);
|
||||||
krk_push(OBJECT_VAL(exceptionObject));
|
krk_push(OBJECT_VAL(exceptionObject));
|
||||||
krk_attachNamedValue(&exceptionObject->fields, "arg", krk_valuesSame(msg,KWARGS_VAL(0)) ? finishStringBuilder(&sb) : msg);
|
krk_attachNamedValue(&exceptionObject->fields, "arg", msg == KWARGS_VAL(0) ? finishStringBuilder(&sb) : msg);
|
||||||
krk_attachNamedValue(&exceptionObject->fields, "__cause__", NONE_VAL());
|
krk_attachNamedValue(&exceptionObject->fields, "__cause__", NONE_VAL());
|
||||||
krk_attachNamedValue(&exceptionObject->fields, "__context__", NONE_VAL());
|
krk_attachNamedValue(&exceptionObject->fields, "__context__", NONE_VAL());
|
||||||
krk_pop();
|
krk_pop();
|
||||||
|
@ -14,11 +14,6 @@
|
|||||||
#include <kuroko/memory.h>
|
#include <kuroko/memory.h>
|
||||||
#include <kuroko/util.h>
|
#include <kuroko/util.h>
|
||||||
|
|
||||||
|
|
||||||
static KrkClass * fileio_File;
|
|
||||||
static KrkClass * fileio_BinaryFile;
|
|
||||||
static KrkClass * fileio_Directory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Object for a C `FILE*` stream.
|
* @brief Object for a C `FILE*` stream.
|
||||||
* @extends KrkInstance
|
* @extends KrkInstance
|
||||||
@ -29,10 +24,10 @@ struct File {
|
|||||||
int unowned;
|
int unowned;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IS_File(o) (krk_isInstanceOf(o, fileio_File))
|
#define IS_File(o) (krk_isInstanceOf(o, KRK_BASE_CLASS(File)))
|
||||||
#define AS_File(o) ((struct File*)AS_OBJECT(o))
|
#define AS_File(o) ((struct File*)AS_OBJECT(o))
|
||||||
|
|
||||||
#define IS_BinaryFile(o) (krk_isInstanceOf(o, fileio_BinaryFile))
|
#define IS_BinaryFile(o) (krk_isInstanceOf(o, KRK_BASE_CLASS(BinaryFile)))
|
||||||
#define AS_BinaryFile(o) ((struct File*)AS_OBJECT(o))
|
#define AS_BinaryFile(o) ((struct File*)AS_OBJECT(o))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,7 +39,7 @@ struct Directory {
|
|||||||
DIR * dirPtr;
|
DIR * dirPtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IS_Directory(o) (krk_isInstanceOf(o, fileio_Directory))
|
#define IS_Directory(o) (krk_isInstanceOf(o, KRK_BASE_CLASS(Directory)))
|
||||||
#define AS_Directory(o) ((struct Directory*)AS_OBJECT(o))
|
#define AS_Directory(o) ((struct Directory*)AS_OBJECT(o))
|
||||||
|
|
||||||
#define CURRENT_CTYPE struct File *
|
#define CURRENT_CTYPE struct File *
|
||||||
@ -82,7 +77,7 @@ KRK_Function(open) {
|
|||||||
if (!file) return krk_runtimeError(vm.exceptions->ioError, "open: failed to open file; system returned: %s", strerror(errno));
|
if (!file) return krk_runtimeError(vm.exceptions->ioError, "open: failed to open file; system returned: %s", strerror(errno));
|
||||||
|
|
||||||
/* Now let's build an object to hold it */
|
/* Now let's build an object to hold it */
|
||||||
KrkInstance * fileObject = krk_newInstance(isBinary ? fileio_BinaryFile : fileio_File);
|
KrkInstance * fileObject = krk_newInstance(isBinary ? KRK_BASE_CLASS(BinaryFile) : KRK_BASE_CLASS(File));
|
||||||
krk_push(OBJECT_VAL(fileObject));
|
krk_push(OBJECT_VAL(fileObject));
|
||||||
|
|
||||||
/* Let's put the filename in there somewhere... */
|
/* Let's put the filename in there somewhere... */
|
||||||
@ -98,7 +93,7 @@ KRK_Function(open) {
|
|||||||
|
|
||||||
#define BLOCK_SIZE 1024
|
#define BLOCK_SIZE 1024
|
||||||
|
|
||||||
KRK_Method(File,__repr__) {
|
KRK_Method(File,__str__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
KrkValue filename;
|
KrkValue filename;
|
||||||
KrkValue modestr;
|
KrkValue modestr;
|
||||||
@ -264,7 +259,7 @@ KRK_Method(File,__exit__) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void makeFileInstance(KrkInstance * module, const char name[], FILE * file, const char mode[]) {
|
static void makeFileInstance(KrkInstance * module, const char name[], FILE * file, const char mode[]) {
|
||||||
KrkInstance * fileObject = krk_newInstance(fileio_File);
|
KrkInstance * fileObject = krk_newInstance(KRK_BASE_CLASS(File));
|
||||||
krk_push(OBJECT_VAL(fileObject));
|
krk_push(OBJECT_VAL(fileObject));
|
||||||
KrkValue filename = OBJECT_VAL(krk_copyString(name,strlen(name)));
|
KrkValue filename = OBJECT_VAL(krk_copyString(name,strlen(name)));
|
||||||
krk_push(filename);
|
krk_push(filename);
|
||||||
@ -438,7 +433,7 @@ KRK_Function(opendir) {
|
|||||||
DIR * dir = opendir(path->chars);
|
DIR * dir = opendir(path->chars);
|
||||||
if (!dir) return krk_runtimeError(vm.exceptions->ioError, "opendir: %s", strerror(errno));
|
if (!dir) return krk_runtimeError(vm.exceptions->ioError, "opendir: %s", strerror(errno));
|
||||||
|
|
||||||
struct Directory * dirObj = (void *)krk_newInstance(fileio_Directory);
|
struct Directory * dirObj = (void *)krk_newInstance(KRK_BASE_CLASS(Directory));
|
||||||
krk_push(OBJECT_VAL(dirObj));
|
krk_push(OBJECT_VAL(dirObj));
|
||||||
|
|
||||||
krk_attachNamedValue(&dirObj->inst.fields, "path", OBJECT_VAL(path));
|
krk_attachNamedValue(&dirObj->inst.fields, "path", OBJECT_VAL(path));
|
||||||
@ -494,7 +489,11 @@ KRK_Method(Directory,__exit__) {
|
|||||||
return FUNC_NAME(Directory,close)(1,argv,0);
|
return FUNC_NAME(Directory,close)(1,argv,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Module(fileio) {
|
void krk_module_init_fileio(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_attachNamedObject(&vm.modules, "fileio", (KrkObj*)module);
|
||||||
|
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("fileio"));
|
||||||
|
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||||
KRK_DOC(module,
|
KRK_DOC(module,
|
||||||
"@brief Provides access to C <stdio> buffered file I/O functions.\n\n"
|
"@brief Provides access to C <stdio> buffered file I/O functions.\n\n"
|
||||||
"The @c fileio module provides classes and functions for reading "
|
"The @c fileio module provides classes and functions for reading "
|
||||||
@ -503,7 +502,7 @@ KRK_Module(fileio) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/* Define a class to represent files. (Should this be a helper method?) */
|
/* Define a class to represent files. (Should this be a helper method?) */
|
||||||
KrkClass * File = krk_makeClass(module, &fileio_File, "File", KRK_BASE_CLASS(object));
|
KrkClass * File = krk_makeClass(module, &KRK_BASE_CLASS(File), "File", KRK_BASE_CLASS(object));
|
||||||
KRK_DOC(File,"Interface to a buffered file stream.");
|
KRK_DOC(File,"Interface to a buffered file stream.");
|
||||||
File->allocSize = sizeof(struct File);
|
File->allocSize = sizeof(struct File);
|
||||||
File->_ongcsweep = _file_sweep;
|
File->_ongcsweep = _file_sweep;
|
||||||
@ -520,14 +519,15 @@ KRK_Module(fileio) {
|
|||||||
"Writes the contents of @p data to the stream.");
|
"Writes the contents of @p data to the stream.");
|
||||||
KRK_DOC(BIND_METHOD(File,close), "@brief Close the stream and flush any remaining buffered writes.");
|
KRK_DOC(BIND_METHOD(File,close), "@brief Close the stream and flush any remaining buffered writes.");
|
||||||
KRK_DOC(BIND_METHOD(File,flush), "@brief Flush unbuffered writes to the stream.");
|
KRK_DOC(BIND_METHOD(File,flush), "@brief Flush unbuffered writes to the stream.");
|
||||||
BIND_METHOD(File,__repr__);
|
BIND_METHOD(File,__str__);
|
||||||
KRK_DOC(BIND_METHOD(File,__init__), "@bsnote{%File objects can not be initialized using this constructor. "
|
KRK_DOC(BIND_METHOD(File,__init__), "@bsnote{%File objects can not be initialized using this constructor. "
|
||||||
"Use the <a class=\"el\" href=\"#open\">open()</a> function instead.}");
|
"Use the <a class=\"el\" href=\"#open\">open()</a> function instead.}");
|
||||||
BIND_METHOD(File,__enter__);
|
BIND_METHOD(File,__enter__);
|
||||||
BIND_METHOD(File,__exit__);
|
BIND_METHOD(File,__exit__);
|
||||||
|
krk_defineNative(&File->methods, "__repr__", FUNC_NAME(File,__str__));
|
||||||
krk_finalizeClass(File);
|
krk_finalizeClass(File);
|
||||||
|
|
||||||
KrkClass * BinaryFile = krk_makeClass(module, &fileio_BinaryFile, "BinaryFile", File);
|
KrkClass * BinaryFile = krk_makeClass(module, &KRK_BASE_CLASS(BinaryFile), "BinaryFile", File);
|
||||||
KRK_DOC(BinaryFile,
|
KRK_DOC(BinaryFile,
|
||||||
"Equivalent to @ref File but using @ref bytes instead of string @ref str."
|
"Equivalent to @ref File but using @ref bytes instead of string @ref str."
|
||||||
);
|
);
|
||||||
@ -537,7 +537,7 @@ KRK_Module(fileio) {
|
|||||||
BIND_METHOD(BinaryFile,write);
|
BIND_METHOD(BinaryFile,write);
|
||||||
krk_finalizeClass(BinaryFile);
|
krk_finalizeClass(BinaryFile);
|
||||||
|
|
||||||
KrkClass * Directory = krk_makeClass(module, &fileio_Directory, "Directory", KRK_BASE_CLASS(object));
|
KrkClass * Directory = krk_makeClass(module, &KRK_BASE_CLASS(Directory), "Directory", KRK_BASE_CLASS(object));
|
||||||
KRK_DOC(Directory,
|
KRK_DOC(Directory,
|
||||||
"Represents an opened file system directory."
|
"Represents an opened file system directory."
|
||||||
);
|
);
|
112
src/kuroko.c
112
src/kuroko.c
@ -33,10 +33,11 @@
|
|||||||
#define PROMPT_MAIN ">>> "
|
#define PROMPT_MAIN ">>> "
|
||||||
#define PROMPT_BLOCK " > "
|
#define PROMPT_BLOCK " > "
|
||||||
|
|
||||||
|
#define CALLGRIND_TMP_FILE "/tmp/kuroko.callgrind.tmp"
|
||||||
|
|
||||||
static int enableRline = 1;
|
static int enableRline = 1;
|
||||||
static int exitRepl = 0;
|
static int exitRepl = 0;
|
||||||
static int pasteEnabled = 0;
|
static int pasteEnabled = 0;
|
||||||
static int noColor = 0;
|
|
||||||
|
|
||||||
KRK_Function(exit) {
|
KRK_Function(exit) {
|
||||||
FUNCTION_TAKES_NONE();
|
FUNCTION_TAKES_NONE();
|
||||||
@ -127,16 +128,6 @@ KRK_Function(input) {
|
|||||||
return readLine(prompt, promptwidth, syntax);
|
return readLine(prompt, promptwidth, syntax);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printResult(FILE * file, KrkValue result) {
|
|
||||||
struct StringBuilder sb = {0};
|
|
||||||
if (!krk_pushStringBuilderFormat(&sb, noColor ? " => %R\n" : " \033[1;90m=> %R\033[0m\n", result)) {
|
|
||||||
krk_dumpTraceback();
|
|
||||||
} else {
|
|
||||||
fwrite(sb.bytes,1,sb.length,file);
|
|
||||||
}
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NO_RLINE
|
#ifndef NO_RLINE
|
||||||
/**
|
/**
|
||||||
* Given an object, find a property with the same name as a scanner token.
|
* Given an object, find a property with the same name as a scanner token.
|
||||||
@ -477,7 +468,9 @@ static int debuggerHook(KrkCallFrame * frame) {
|
|||||||
krk_pop();
|
krk_pop();
|
||||||
/* Call the compiled expression with no args. */
|
/* Call the compiled expression with no args. */
|
||||||
krk_push(krk_callStack(0));
|
krk_push(krk_callStack(0));
|
||||||
printResult(stderr, krk_peek(0));
|
fprintf(stderr, "=> ");
|
||||||
|
krk_printValue(stderr, krk_peek(0));
|
||||||
|
fprintf(stderr, "\n");
|
||||||
krk_pop();
|
krk_pop();
|
||||||
}
|
}
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
|
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
|
||||||
@ -751,6 +744,16 @@ static int compileFile(char * argv[], int flags, char * fileName) {
|
|||||||
return func == NULL;
|
return func == NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BUNDLE_LIBS
|
||||||
|
#define BUNDLED(name) do { \
|
||||||
|
extern KrkValue krk_module_onload_ ## name (); \
|
||||||
|
KrkValue moduleOut = krk_module_onload_ ## name (); \
|
||||||
|
krk_attachNamedValue(&vm.modules, # name, moduleOut); \
|
||||||
|
krk_attachNamedObject(&AS_INSTANCE(moduleOut)->fields, "__name__", (KrkObj*)krk_copyString(#name, sizeof(#name)-1)); \
|
||||||
|
krk_attachNamedValue(&AS_INSTANCE(moduleOut)->fields, "__file__", NONE_VAL()); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char * argv[]) {
|
int main(int argc, char * argv[]) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetConsoleOutputCP(65001);
|
SetConsoleOutputCP(65001);
|
||||||
@ -785,6 +788,11 @@ int main(int argc, char * argv[]) {
|
|||||||
/* Disassemble instructions as they are executed. */
|
/* Disassemble instructions as they are executed. */
|
||||||
flags |= KRK_THREAD_ENABLE_TRACING;
|
flags |= KRK_THREAD_ENABLE_TRACING;
|
||||||
break;
|
break;
|
||||||
|
case 'T': {
|
||||||
|
flags |= KRK_GLOBAL_CALLGRIND;
|
||||||
|
vm.callgrindFile = fopen(CALLGRIND_TMP_FILE,"w");
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'i':
|
case 'i':
|
||||||
inspectAfter = 1;
|
inspectAfter = 1;
|
||||||
break;
|
break;
|
||||||
@ -830,6 +838,7 @@ int main(int argc, char * argv[]) {
|
|||||||
" -r Disable complex line editing in the REPL.\n"
|
" -r Disable complex line editing in the REPL.\n"
|
||||||
" -R depth Set maximum recursion depth.\n"
|
" -R depth Set maximum recursion depth.\n"
|
||||||
" -t Disassemble instructions as they are exceuted.\n"
|
" -t Disassemble instructions as they are exceuted.\n"
|
||||||
|
" -T Write call trace file.\n"
|
||||||
" -C file Compile 'file', but do not execute it.\n"
|
" -C file Compile 'file', but do not execute it.\n"
|
||||||
" -M Print the default module import paths.\n"
|
" -M Print the default module import paths.\n"
|
||||||
" -S Enable single-step debugging.\n"
|
" -S Enable single-step debugging.\n"
|
||||||
@ -876,26 +885,20 @@ _finishArgs:
|
|||||||
/* Bind interrupt signal */
|
/* Bind interrupt signal */
|
||||||
bindSignalHandlers();
|
bindSignalHandlers();
|
||||||
|
|
||||||
#ifdef KRK_BUNDLE_LIBS
|
#ifdef BUNDLE_LIBS
|
||||||
/* Define KRK_BUNDLE_LIBS like "BUNDLE(os);BUNDLE(math);", etc. */
|
/* Add any other modules you want to include that are normally built as shared objects. */
|
||||||
#define BUNDLED(name) do { \
|
BUNDLED(math);
|
||||||
extern KrkValue krk_module_onload_ ## name (KrkString*); \
|
BUNDLED(socket);
|
||||||
KrkValue moduleOut = krk_module_onload_ ## name (NULL); \
|
BUNDLED(timeit);
|
||||||
krk_attachNamedValue(&vm.modules, # name, moduleOut); \
|
|
||||||
krk_attachNamedObject(&AS_INSTANCE(moduleOut)->fields, "__name__", (KrkObj*)krk_copyString(#name, sizeof(#name)-1)); \
|
|
||||||
krk_attachNamedValue(&AS_INSTANCE(moduleOut)->fields, "__file__", NONE_VAL()); \
|
|
||||||
} while (0)
|
|
||||||
KRK_BUNDLE_LIBS
|
|
||||||
#undef BUNDLED
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
KrkValue result = INTEGER_VAL(0);
|
KrkValue result = INTEGER_VAL(0);
|
||||||
|
|
||||||
char * env_KUROKOPATH = getenv("KUROKOPATH");
|
char * _KUROKOPATH = getenv("KUROKOPATH");
|
||||||
|
|
||||||
if (env_KUROKOPATH) {
|
if (_KUROKOPATH) {
|
||||||
/* Build a path by splitting */
|
/* Build a path by splitting */
|
||||||
krk_push(OBJECT_VAL(krk_copyString(env_KUROKOPATH,strlen(env_KUROKOPATH))));
|
krk_push(OBJECT_VAL(krk_copyString(_KUROKOPATH,strlen(_KUROKOPATH))));
|
||||||
krk_push(OBJECT_VAL(S(":")));
|
krk_push(OBJECT_VAL(S(":")));
|
||||||
|
|
||||||
/* Split into list */
|
/* Split into list */
|
||||||
@ -919,16 +922,12 @@ _finishArgs:
|
|||||||
krk_pop(); /* list */
|
krk_pop(); /* list */
|
||||||
}
|
}
|
||||||
|
|
||||||
char * env_NO_COLOR = getenv("NO_COLOR");
|
|
||||||
|
|
||||||
if (env_NO_COLOR && *env_NO_COLOR) noColor = 1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add general builtins that aren't part of the core VM.
|
* Add general builtins that aren't part of the core VM.
|
||||||
* This is where we provide @c input in particular.
|
* This is where we provide @c input in particular.
|
||||||
*/
|
*/
|
||||||
KRK_DOC(BIND_FUNC(vm.builtins,input), "@brief Read a line of input.\n"
|
KRK_DOC(BIND_FUNC(vm.builtins,input), "@brief Read a line of input.\n"
|
||||||
"@arguments prompt='',promptwidth=0,syntax=None\n\n"
|
"@arguments [prompt], promptwidth=None, syntax=None\n\n"
|
||||||
"Read a line of input from @c stdin. If the @c rline library is available, "
|
"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 "
|
"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. "
|
"a read ends with a line feed, which will be removed from the returned string. "
|
||||||
@ -942,7 +941,10 @@ _finishArgs:
|
|||||||
"provide color highlighting of the input line.");
|
"provide color highlighting of the input line.");
|
||||||
|
|
||||||
if (moduleAsMain) {
|
if (moduleAsMain) {
|
||||||
int out = !krk_importModule(AS_STRING(AS_LIST(argList)->values[0]), S("__main__"));
|
krk_push(OBJECT_VAL(krk_copyString("__main__",8)));
|
||||||
|
int out = !krk_importModule(
|
||||||
|
AS_STRING(AS_LIST(argList)->values[0]),
|
||||||
|
AS_STRING(krk_peek(0)));
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
|
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
|
||||||
krk_dumpTraceback();
|
krk_dumpTraceback();
|
||||||
krk_resetStack();
|
krk_resetStack();
|
||||||
@ -988,11 +990,12 @@ _finishArgs:
|
|||||||
* This module won't be imported by default, but it's still in
|
* This module won't be imported by default, but it's still in
|
||||||
* the modules list, so we can look for it there.
|
* the modules list, so we can look for it there.
|
||||||
*/
|
*/
|
||||||
if (vm.system) {
|
KrkValue systemModule;
|
||||||
|
if (krk_tableGet(&vm.modules, OBJECT_VAL(krk_copyString("kuroko",6)), &systemModule)) {
|
||||||
KrkValue version, buildenv, builddate;
|
KrkValue version, buildenv, builddate;
|
||||||
krk_tableGet_fast(&vm.system->fields, S("version"), &version);
|
krk_tableGet(&AS_INSTANCE(systemModule)->fields, OBJECT_VAL(krk_copyString("version",7)), &version);
|
||||||
krk_tableGet_fast(&vm.system->fields, S("buildenv"), &buildenv);
|
krk_tableGet(&AS_INSTANCE(systemModule)->fields, OBJECT_VAL(krk_copyString("buildenv",8)), &buildenv);
|
||||||
krk_tableGet_fast(&vm.system->fields, S("builddate"), &builddate);
|
krk_tableGet(&AS_INSTANCE(systemModule)->fields, OBJECT_VAL(krk_copyString("builddate",9)), &builddate);
|
||||||
|
|
||||||
fprintf(stdout, "Kuroko %s (%s) with %s\n",
|
fprintf(stdout, "Kuroko %s (%s) with %s\n",
|
||||||
AS_CSTRING(version), AS_CSTRING(builddate), AS_CSTRING(buildenv));
|
AS_CSTRING(version), AS_CSTRING(builddate), AS_CSTRING(buildenv));
|
||||||
@ -1003,7 +1006,7 @@ _finishArgs:
|
|||||||
while (!exitRepl) {
|
while (!exitRepl) {
|
||||||
size_t lineCapacity = 8;
|
size_t lineCapacity = 8;
|
||||||
size_t lineCount = 0;
|
size_t lineCount = 0;
|
||||||
char ** lines = KRK_ALLOCATE(char *, lineCapacity);
|
char ** lines = ALLOCATE(char *, lineCapacity);
|
||||||
size_t totalData = 0;
|
size_t totalData = 0;
|
||||||
int valid = 1;
|
int valid = 1;
|
||||||
char * allData = NULL;
|
char * allData = NULL;
|
||||||
@ -1089,8 +1092,8 @@ _finishArgs:
|
|||||||
if (lineCapacity < lineCount + 1) {
|
if (lineCapacity < lineCount + 1) {
|
||||||
/* If we need more space, grow as needed... */
|
/* If we need more space, grow as needed... */
|
||||||
size_t old = lineCapacity;
|
size_t old = lineCapacity;
|
||||||
lineCapacity = KRK_GROW_CAPACITY(old);
|
lineCapacity = GROW_CAPACITY(old);
|
||||||
lines = KRK_GROW_ARRAY(char *,lines,old,lineCapacity);
|
lines = GROW_ARRAY(char *,lines,old,lineCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = lineCount++;
|
int i = lineCount++;
|
||||||
@ -1159,13 +1162,26 @@ _finishArgs:
|
|||||||
#endif
|
#endif
|
||||||
free(lines[i]);
|
free(lines[i]);
|
||||||
}
|
}
|
||||||
KRK_FREE_ARRAY(char *, lines, lineCapacity);
|
FREE_ARRAY(char *, lines, lineCapacity);
|
||||||
|
|
||||||
if (valid) {
|
if (valid) {
|
||||||
KrkValue result = krk_interpret(allData, "<stdin>");
|
KrkValue result = krk_interpret(allData, "<stdin>");
|
||||||
if (!IS_NONE(result)) {
|
if (!IS_NONE(result)) {
|
||||||
krk_attachNamedValue(&vm.builtins->fields, "_", result);
|
krk_attachNamedValue(&vm.builtins->fields, "_", result);
|
||||||
printResult(stdout, result);
|
KrkClass * type = krk_getType(result);
|
||||||
|
const char * formatStr = " => %s\n";
|
||||||
|
if (type->_reprer) {
|
||||||
|
krk_push(result);
|
||||||
|
result = krk_callDirect(type->_reprer, 1);
|
||||||
|
} else if (type->_tostr) {
|
||||||
|
krk_push(result);
|
||||||
|
result = krk_callDirect(type->_tostr, 1);
|
||||||
|
}
|
||||||
|
if (!IS_STRING(result)) {
|
||||||
|
fprintf(stdout, " => Unable to produce representation for value.\n");
|
||||||
|
} else {
|
||||||
|
fprintf(stdout, formatStr, AS_CSTRING(result));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
krk_resetStack();
|
krk_resetStack();
|
||||||
free(allData);
|
free(allData);
|
||||||
@ -1175,6 +1191,20 @@ _finishArgs:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vm.globalFlags & KRK_GLOBAL_CALLGRIND) {
|
||||||
|
fclose(vm.callgrindFile);
|
||||||
|
vm.globalFlags &= ~(KRK_GLOBAL_CALLGRIND);
|
||||||
|
|
||||||
|
krk_resetStack();
|
||||||
|
krk_startModule("<callgrind>");
|
||||||
|
krk_attachNamedObject(&krk_currentThread.module->fields, "filename", (KrkObj*)S(CALLGRIND_TMP_FILE));
|
||||||
|
krk_interpret(
|
||||||
|
"from callgrind import processFile\n"
|
||||||
|
"import kuroko\n"
|
||||||
|
"import os\n"
|
||||||
|
"processFile(filename, os.getpid(), ' '.join(kuroko.argv))","<callgrind>");
|
||||||
|
}
|
||||||
|
|
||||||
krk_freeVM();
|
krk_freeVM();
|
||||||
|
|
||||||
if (IS_INTEGER(result)) return AS_INTEGER(result);
|
if (IS_INTEGER(result)) return AS_INTEGER(result);
|
||||||
|
@ -14,5 +14,5 @@
|
|||||||
* @param fileName Path name of the source file or a representative string like "<stdin>"
|
* @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.
|
* @return The code object resulting from the compilation, or NULL if compilation failed.
|
||||||
*/
|
*/
|
||||||
extern KrkCodeObject * krk_compile(const char * src, const char * fileName);
|
extern KrkCodeObject * krk_compile(const char * src, char * fileName);
|
||||||
|
|
||||||
|
@ -206,11 +206,6 @@ extern int krk_debug_examineBreakpoint(int breakIndex, KrkCodeObject ** funcOut,
|
|||||||
*/
|
*/
|
||||||
extern void krk_debug_dumpStack(FILE * f, KrkCallFrame * frame);
|
extern void krk_debug_dumpStack(FILE * f, KrkCallFrame * frame);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize debugger state. Call exactly once per VM.
|
|
||||||
*/
|
|
||||||
extern void krk_debug_init(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def KRK_BREAKPOINT_NORMAL
|
* @def KRK_BREAKPOINT_NORMAL
|
||||||
*
|
*
|
||||||
@ -238,55 +233,4 @@ extern void krk_debug_init(void);
|
|||||||
#define KRK_DEBUGGER_RAISE 3
|
#define KRK_DEBUGGER_RAISE 3
|
||||||
#define KRK_DEBUGGER_QUIT 4
|
#define KRK_DEBUGGER_QUIT 4
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add an expression mapping to the bytecode chunk.
|
|
||||||
* @memberof KrkCodeObject
|
|
||||||
*
|
|
||||||
* Associates a span of columns representing an expression with the
|
|
||||||
* current opcode offset. Used to supply debug information displayed
|
|
||||||
* in tracebacks with tildes and carets.
|
|
||||||
*
|
|
||||||
* @param codeobject Codeobject containing the instruction.
|
|
||||||
* @param start First column, 1-indexed, of tildes, left side of expression.
|
|
||||||
* @param midStart First column, 1-indexed, of carets, main token of expression.
|
|
||||||
* @param midEnd Last column, 1-indexed, of carets, main token of expression.
|
|
||||||
* @param end Last column, 1-indexed, of tildes, right side of expression.
|
|
||||||
*/
|
|
||||||
extern void krk_debug_addExpression(KrkCodeObject * codeobject, uint8_t start, uint8_t midStart, uint8_t midEnd, uint8_t end);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Extract expression mapping from chunk.
|
|
||||||
* @memberof KrkCodeObject
|
|
||||||
*
|
|
||||||
* Searches the debug information for the requested instruction to find
|
|
||||||
* an expression mapping and extracts the column values for underlining.
|
|
||||||
*
|
|
||||||
* @param codeobject Codeobject containing the instruction.
|
|
||||||
* @param start First column, 1-indexed, of tildes, left side of expression.
|
|
||||||
* @param midStart First column, 1-indexed, of carets, main token of expression.
|
|
||||||
* @param midEnd Last column, 1-indexed, of carets, main token of expression.
|
|
||||||
* @param end Last column, 1-indexed, of tildes, right side of expression.
|
|
||||||
* @param instruction Offset of the last byte of an opcode, as is stored in a traceback entry.
|
|
||||||
* @returns Non-zero if a mapping was found.
|
|
||||||
*/
|
|
||||||
extern int krk_debug_expressionUnderline(const KrkCodeObject * codeobject, uint8_t * start, uint8_t * midStart, uint8_t * endStart, uint8_t * end, size_t instruction);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -11,37 +11,24 @@
|
|||||||
typedef int64_t krk_integer_type;
|
typedef int64_t krk_integer_type;
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
# define KRK_PATH_SEP "/"
|
# define PATH_SEP "/"
|
||||||
# ifndef KRK_STATIC_ONLY
|
# ifndef KRK_STATIC_ONLY
|
||||||
# include <dlfcn.h>
|
# include <dlfcn.h>
|
||||||
# define krk_dlRefType void *
|
# define dlRefType void *
|
||||||
# define krk_dlSymType void *
|
# define dlSymType void *
|
||||||
# define krk_dlOpen(fileName) dlopen(fileName, RTLD_NOW)
|
# define dlOpen(fileName) dlopen(fileName, RTLD_NOW)
|
||||||
# define krk_dlSym(dlRef, handlerName) dlsym(dlRef,handlerName)
|
# define dlSym(dlRef, handlerName) dlsym(dlRef,handlerName)
|
||||||
# define krk_dlClose(dlRef) dlclose(dlRef)
|
# define dlClose(dlRef) dlclose(dlRef)
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
# define KRK_PATH_SEP "\\"
|
# define PATH_SEP "\\"
|
||||||
# ifndef KRK_STATIC_ONLY
|
# ifndef KRK_STATIC_ONLY
|
||||||
# define krk_dlRefType HINSTANCE
|
# define dlRefType HINSTANCE
|
||||||
# define krk_dlSymType FARPROC
|
# define dlSymType FARPROC
|
||||||
# define krk_dlOpen(fileName) LoadLibraryA(fileName)
|
# define dlOpen(fileName) LoadLibraryA(fileName)
|
||||||
# define krk_dlSym(dlRef, handlerName) GetProcAddress(dlRef, handlerName)
|
# define dlSym(dlRef, handlerName) GetProcAddress(dlRef, handlerName)
|
||||||
# define krk_dlClose(dlRef)
|
# define dlClose(dlRef)
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_MSC_VER) && !defined(__clang__)
|
|
||||||
#define KRK_NO_DOCUMENTATION 1
|
|
||||||
#define KRK_NO_GC_TRACING 1
|
|
||||||
typedef intptr_t ssize_t;
|
|
||||||
#pragma warning(disable : 4146) /* unary minus on unsigned */
|
|
||||||
#pragma warning(disable : 4996) /* sterror */
|
|
||||||
#pragma warning(disable : 4267) /* conversions to smaller types... */
|
|
||||||
#pragma warning(disable : 4244) /* conversions to smaller types... */
|
|
||||||
#include <math.h>
|
|
||||||
#define __builtin_floor floor
|
|
||||||
#define __builtin_unreachable abort
|
|
||||||
#define __builtin_expect(cond,expected) (cond)
|
|
||||||
#endif
|
|
||||||
|
@ -7,10 +7,13 @@
|
|||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
|
|
||||||
#define KRK_GROW_CAPACITY(c) ((c) < 8 ? 8 : (c) * 2)
|
#define GROW_CAPACITY(c) ((c) < 8 ? 8 : (c) * 2)
|
||||||
#define KRK_GROW_ARRAY(t,p,o,n) (t*)krk_reallocate(p,sizeof(t)*o,sizeof(t)*n)
|
#define GROW_ARRAY(t,p,o,n) (t*)krk_reallocate(p,sizeof(t)*o,sizeof(t)*n)
|
||||||
#define KRK_FREE_ARRAY(t,a,c) krk_reallocate(a,sizeof(t) * c, 0)
|
|
||||||
#define KRK_ALLOCATE(type, count) (type*)krk_reallocate(NULL,0,sizeof(type)*(count))
|
#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.
|
* @brief Resize an allocated heap object.
|
||||||
|
@ -133,27 +133,8 @@ typedef struct {
|
|||||||
KrkString * name; /**< @brief Name of the local */
|
KrkString * name; /**< @brief Name of the local */
|
||||||
} KrkLocalEntry;
|
} KrkLocalEntry;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Map entry of opcode offsets to expressions spans.
|
|
||||||
*
|
|
||||||
* Used for printing tracebacks with underlined expressions.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint32_t bytecodeOffset;
|
|
||||||
uint8_t start;
|
|
||||||
uint8_t midStart;
|
|
||||||
uint8_t midEnd;
|
|
||||||
uint8_t end;
|
|
||||||
} KrkExpressionsMap;
|
|
||||||
|
|
||||||
struct KrkInstance;
|
struct KrkInstance;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t instructionOffset; /**< @brief Instruction (operand offset) this jump target applies to */
|
|
||||||
uint16_t intendedTarget; /**< @brief High bytes of the intended target. */
|
|
||||||
uint8_t originalOpcode; /**< @brief Original jump opcode to execute. */
|
|
||||||
} KrkOverlongJump;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Code object.
|
* @brief Code object.
|
||||||
* @extends KrkObj
|
* @extends KrkObj
|
||||||
@ -176,13 +157,6 @@ typedef struct {
|
|||||||
size_t localNameCount; /**< @brief Number of entries in @ref localNames */
|
size_t localNameCount; /**< @brief Number of entries in @ref localNames */
|
||||||
KrkLocalEntry * localNames; /**< @brief Stores the names of local variables used in the function, for debugging */
|
KrkLocalEntry * localNames; /**< @brief Stores the names of local variables used in the function, for debugging */
|
||||||
KrkString * qualname; /**< @brief The dotted name of the function */
|
KrkString * qualname; /**< @brief The dotted name of the function */
|
||||||
size_t expressionsCapacity; /**< @brief Capacity of @ref expressions */
|
|
||||||
size_t expressionsCount; /**< @brief Number of entries in @ref expressions */
|
|
||||||
KrkExpressionsMap * expressions; /**< @brief Mapping of bytecode offsets to expression spans for debugging */
|
|
||||||
KrkValue jumpTargets; /**< @brief Possibly a set of jump targets... */
|
|
||||||
KrkOverlongJump * overlongJumps; /**< @brief Pessimal overlong jump container */
|
|
||||||
size_t overlongJumpsCapacity; /**< @brief Number of possible entries in pessimal jump table */
|
|
||||||
size_t overlongJumpsCount; /**< @brief Number of entries in pessimal jump table */
|
|
||||||
} KrkCodeObject;
|
} KrkCodeObject;
|
||||||
|
|
||||||
|
|
||||||
@ -385,7 +359,7 @@ struct DictValues {
|
|||||||
struct KrkModule {
|
struct KrkModule {
|
||||||
KrkInstance inst;
|
KrkInstance inst;
|
||||||
#ifndef KRK_STATIC_ONLY
|
#ifndef KRK_STATIC_ONLY
|
||||||
krk_dlRefType libHandle;
|
dlRefType libHandle;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,8 +111,6 @@ typedef enum {
|
|||||||
TOKEN_RETRY,
|
TOKEN_RETRY,
|
||||||
TOKEN_ERROR,
|
TOKEN_ERROR,
|
||||||
TOKEN_EOF,
|
TOKEN_EOF,
|
||||||
|
|
||||||
TOKEN_ELLIPSIS, /* ... */
|
|
||||||
} KrkTokenType;
|
} KrkTokenType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include "kuroko.h"
|
#include "kuroko.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include "threads.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief One (key,value) pair in a table.
|
* @brief One (key,value) pair in a table.
|
||||||
@ -26,11 +26,9 @@ typedef struct {
|
|||||||
* @brief Simple hash table of arbitrary keys to values.
|
* @brief Simple hash table of arbitrary keys to values.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t count; /**< Number of actual items in the dict. */
|
size_t count;
|
||||||
size_t capacity; /**< Size (in items) of each of the entries/indexes arrays */
|
size_t capacity;
|
||||||
size_t used; /**< Next insertion index in the entries array */
|
KrkTableEntry * entries;
|
||||||
KrkTableEntry * entries; /**< Key-value pairs, in insertion order (with KWARGS_VAL(0) gaps) */
|
|
||||||
ssize_t * indexes; /**< Actual hash map: indexes into the key-value pairs. */
|
|
||||||
} KrkTable;
|
} KrkTable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,6 +153,21 @@ extern int krk_tableDelete(KrkTable * table, KrkValue key);
|
|||||||
*/
|
*/
|
||||||
extern int krk_tableDeleteExact(KrkTable * table, KrkValue key);
|
extern int krk_tableDeleteExact(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.
|
* @brief Calculate the hash for a value.
|
||||||
* @memberof KrkValue
|
* @memberof KrkValue
|
||||||
|
@ -33,30 +33,6 @@
|
|||||||
#define _noexport
|
#define _noexport
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __has_attribute(unused)
|
|
||||||
# define _unused __attribute__((unused))
|
|
||||||
#else
|
|
||||||
# define _unused
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __has_attribute(hot)
|
|
||||||
# define _hot __attribute__((hot))
|
|
||||||
#else
|
|
||||||
# define _hot
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __has_attribute(cold)
|
|
||||||
# define _cold __attribute__((cold))
|
|
||||||
#else
|
|
||||||
# define _cold
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __has_attribute(nonnull)
|
|
||||||
# define _nonnull __attribute__((nonnull))
|
|
||||||
#else
|
|
||||||
# define _nonnull
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ADD_BASE_CLASS(obj, name, baseClass) krk_makeClass(vm.builtins, &obj, name, baseClass)
|
#define ADD_BASE_CLASS(obj, name, baseClass) krk_makeClass(vm.builtins, &obj, name, baseClass)
|
||||||
|
|
||||||
#define ATTRIBUTE_NOT_ASSIGNABLE() do { if (unlikely(argc != 1)) return krk_runtimeError(vm.exceptions->attributeError, "'%T' object has no attribute '%s'", \
|
#define ATTRIBUTE_NOT_ASSIGNABLE() do { if (unlikely(argc != 1)) return krk_runtimeError(vm.exceptions->attributeError, "'%T' object has no attribute '%s'", \
|
||||||
@ -94,11 +70,23 @@
|
|||||||
#define CHECK_ARG(i, type, ctype, name) \
|
#define CHECK_ARG(i, type, ctype, name) \
|
||||||
if (unlikely(argc < (i+1))) return NOT_ENOUGH_ARGS(name); \
|
if (unlikely(argc < (i+1))) return NOT_ENOUGH_ARGS(name); \
|
||||||
if (unlikely(!IS_ ## type (argv[i]))) return TYPE_ERROR(type,argv[i]); \
|
if (unlikely(!IS_ ## type (argv[i]))) return TYPE_ERROR(type,argv[i]); \
|
||||||
ctype name _unused = AS_ ## type (argv[i])
|
ctype name __attribute__((unused)) = AS_ ## type (argv[i])
|
||||||
|
|
||||||
#define FUNC_NAME(klass, name) _ ## klass ## _ ## name
|
#define FUNC_NAME(klass, name) _ ## klass ## _ ## name
|
||||||
#define FUNC_SIG(klass, name) _noexport KrkValue FUNC_NAME(klass,name) (int argc, const KrkValue argv[], int hasKw)
|
#define FUNC_SIG(klass, name) _noexport KrkValue FUNC_NAME(klass,name) (int argc, const KrkValue argv[], int hasKw)
|
||||||
|
|
||||||
|
/* These forms are deprecated. */
|
||||||
|
#define KRK_METHOD(klass, name, ...) FUNC_SIG(klass, name) { \
|
||||||
|
static __attribute__ ((unused)) const char* _method_name = # name; \
|
||||||
|
CHECK_ARG(0,klass,CURRENT_CTYPE,CURRENT_NAME); \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
return NONE_VAL(); }
|
||||||
|
|
||||||
|
#define KRK_FUNC(name,...) static KrkValue _krk_ ## name (int argc, const KrkValue argv[], int hasKw) { \
|
||||||
|
static __attribute__ ((unused)) const char* _method_name = # name; \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
return NONE_VAL(); }
|
||||||
|
|
||||||
/* This assumes you have a KrkInstance called `module` in the current scope. */
|
/* 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 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_METHOD(klass,method) krk_defineNative(&klass->methods, #method, _ ## klass ## _ ## method)
|
||||||
@ -365,18 +353,3 @@ extern int krk_pushStringBuilderFormat(struct StringBuilder * sb, const char * f
|
|||||||
extern KrkValue krk_stringFromFormat(const char * fmt, ...);
|
extern KrkValue krk_stringFromFormat(const char * fmt, ...);
|
||||||
extern int krk_long_to_int(KrkValue val, char size, void * out);
|
extern int krk_long_to_int(KrkValue val, char size, void * out);
|
||||||
extern int krk_isSubClass(const KrkClass * cls, const KrkClass * base);
|
extern int krk_isSubClass(const KrkClass * cls, const KrkClass * base);
|
||||||
|
|
||||||
#define KRK_Module_internal_name(name) \
|
|
||||||
_krk_module_onload_ ## name
|
|
||||||
#define KRK_Module_internal_sig(name) \
|
|
||||||
static inline void KRK_Module_internal_name(name) (KrkInstance * module, KrkString * runAs)
|
|
||||||
#define KRK_Module(name) \
|
|
||||||
KRK_Module_internal_sig(name); \
|
|
||||||
KrkValue krk_module_onload_ ## name (KrkString * runAs) { \
|
|
||||||
KrkInstance * module = krk_newInstance(KRK_BASE_CLASS(module)); \
|
|
||||||
krk_push(OBJECT_VAL(module)); \
|
|
||||||
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)runAs); \
|
|
||||||
KRK_Module_internal_name(name)(module, runAs); \
|
|
||||||
return krk_pop(); \
|
|
||||||
} \
|
|
||||||
KRK_Module_internal_sig(name)
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "kuroko.h"
|
#include "kuroko.h"
|
||||||
|
|
||||||
#ifndef KRK_NO_NAN_BOXING
|
|
||||||
/**
|
/**
|
||||||
* @brief Tag enum for basic value types.
|
* @brief Tag enum for basic value types.
|
||||||
*
|
*
|
||||||
@ -24,6 +23,28 @@ typedef enum {
|
|||||||
KRK_VAL_NOTIMPL = 0x7FFE,
|
KRK_VAL_NOTIMPL = 0x7FFE,
|
||||||
} KrkValueType;
|
} KrkValueType;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following poorly-named macros define bit patterns for identifying
|
||||||
|
* various boxed types.
|
||||||
|
*
|
||||||
|
* Boxing is done by first setting all of the bits of MASK_NAN. If all of
|
||||||
|
* these bits are set, a value is not a float. If any of them are not set,
|
||||||
|
* then a value is a float - and possibly a real NaN.
|
||||||
|
*
|
||||||
|
* Three other bits - one before and two after the MASK_NAN bits - determine
|
||||||
|
* what type the value actually is. KWARGS sets none of the identifying bits,
|
||||||
|
* NONE sets all of them.
|
||||||
|
*/
|
||||||
|
#define KRK_VAL_MASK_BOOLEAN ((uint64_t)0xFFFC000000000000) /* 1..1100 */
|
||||||
|
#define KRK_VAL_MASK_INTEGER ((uint64_t)0xFFFD000000000000) /* 1..1101 */
|
||||||
|
#define KRK_VAL_MASK_HANDLER ((uint64_t)0xFFFE000000000000) /* 1..1110 */
|
||||||
|
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000) /* 1..1111 */
|
||||||
|
#define KRK_VAL_MASK_KWARGS ((uint64_t)0x7FFC000000000000) /* 0..1100 */
|
||||||
|
#define KRK_VAL_MASK_OBJECT ((uint64_t)0x7FFD000000000000) /* 0..1101 */
|
||||||
|
#define KRK_VAL_MASK_NOTIMPL ((uint64_t)0x7FFE000000000000) /* 0..1110 */
|
||||||
|
#define KRK_VAL_MASK_NAN ((uint64_t)0x7FFC000000000000)
|
||||||
|
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct KrkValue
|
* @struct KrkValue
|
||||||
* @brief Stack reference or primative value.
|
* @brief Stack reference or primative value.
|
||||||
@ -40,32 +61,6 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
typedef uint64_t KrkValue;
|
typedef uint64_t KrkValue;
|
||||||
|
|
||||||
#define _krk_valuesSame(a,b) (a == b)
|
|
||||||
|
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* Tagged union, but without the union fun.
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
KRK_VAL_NONE = 0,
|
|
||||||
KRK_VAL_INTEGER = 1,
|
|
||||||
KRK_VAL_BOOLEAN = 2,
|
|
||||||
KRK_VAL_HANDLER = 4,
|
|
||||||
KRK_VAL_KWARGS = 8,
|
|
||||||
KRK_VAL_OBJECT = 16,
|
|
||||||
KRK_VAL_NOTIMPL = 32,
|
|
||||||
KRK_VAL_FLOATING = 64,
|
|
||||||
} KrkValueType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint64_t tag;
|
|
||||||
uint64_t val;
|
|
||||||
} KrkValue;
|
|
||||||
|
|
||||||
#define _krk_valuesSame(a,b) (memcmp(&(a),&(b),sizeof(KrkValue)) == 0)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Flexible vector of stack references.
|
* @brief Flexible vector of stack references.
|
||||||
*
|
*
|
||||||
@ -115,6 +110,42 @@ extern void krk_writeValueArray(KrkValueArray * array, KrkValue value);
|
|||||||
*/
|
*/
|
||||||
extern void krk_freeValueArray(KrkValueArray * array);
|
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.
|
* @brief Compare two values for equality.
|
||||||
* @memberof KrkValue
|
* @memberof KrkValue
|
||||||
@ -138,7 +169,7 @@ extern int krk_valuesEqual(KrkValue a, KrkValue b);
|
|||||||
*
|
*
|
||||||
* @return 1 if values represent the same object or value, 0 otherwise.
|
* @return 1 if values represent the same object or value, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
static inline int krk_valuesSame(KrkValue a, KrkValue b) { return _krk_valuesSame(a,b); }
|
extern int krk_valuesSame(KrkValue a, KrkValue b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Compare two values by identity, then by equality.
|
* @brief Compare two values by identity, then by equality.
|
||||||
@ -151,37 +182,12 @@ static inline int krk_valuesSame(KrkValue a, KrkValue b) { return _krk_valuesSam
|
|||||||
extern int krk_valuesSameOrEqual(KrkValue a, KrkValue b);
|
extern int krk_valuesSameOrEqual(KrkValue a, KrkValue b);
|
||||||
|
|
||||||
extern KrkValue krk_parse_int(const char * start, size_t width, unsigned int base);
|
extern KrkValue krk_parse_int(const char * start, size_t width, unsigned int base);
|
||||||
extern KrkValue krk_parse_float(const char* start, size_t width);
|
|
||||||
|
|
||||||
#ifndef KRK_NO_NAN_BOXING
|
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
KrkValue val;
|
KrkValue val;
|
||||||
double dbl;
|
double dbl;
|
||||||
} KrkValueDbl;
|
} KrkValueDbl;
|
||||||
|
|
||||||
/*
|
|
||||||
* The following poorly-named macros define bit patterns for identifying
|
|
||||||
* various boxed types.
|
|
||||||
*
|
|
||||||
* Boxing is done by first setting all of the bits of MASK_NAN. If all of
|
|
||||||
* these bits are set, a value is not a float. If any of them are not set,
|
|
||||||
* then a value is a float - and possibly a real NaN.
|
|
||||||
*
|
|
||||||
* Three other bits - one before and two after the MASK_NAN bits - determine
|
|
||||||
* what type the value actually is. KWARGS sets none of the identifying bits,
|
|
||||||
* NONE sets all of them.
|
|
||||||
*/
|
|
||||||
#define KRK_VAL_MASK_BOOLEAN ((uint64_t)0xFFFC000000000000) /* 1..1100 */
|
|
||||||
#define KRK_VAL_MASK_INTEGER ((uint64_t)0xFFFD000000000000) /* 1..1101 */
|
|
||||||
#define KRK_VAL_MASK_HANDLER ((uint64_t)0xFFFE000000000000) /* 1..1110 */
|
|
||||||
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000) /* 1..1111 */
|
|
||||||
#define KRK_VAL_MASK_KWARGS ((uint64_t)0x7FFC000000000000) /* 0..1100 */
|
|
||||||
#define KRK_VAL_MASK_OBJECT ((uint64_t)0x7FFD000000000000) /* 0..1101 */
|
|
||||||
#define KRK_VAL_MASK_NOTIMPL ((uint64_t)0x7FFE000000000000) /* 0..1110 */
|
|
||||||
#define KRK_VAL_MASK_NAN ((uint64_t)0x7FFC000000000000)
|
|
||||||
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
|
|
||||||
|
|
||||||
#ifdef KRK_SANITIZE_OBJECT_POINTERS
|
#ifdef KRK_SANITIZE_OBJECT_POINTERS
|
||||||
/**
|
/**
|
||||||
* Debugging tool for verifying we aren't trying to box NULL, which is not a valid object.
|
* Debugging tool for verifying we aren't trying to box NULL, which is not a valid object.
|
||||||
@ -207,13 +213,13 @@ static inline uintptr_t _krk_sanitize(uintptr_t input) {
|
|||||||
#define KRK_HEAP_TAG 0
|
#define KRK_HEAP_TAG 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NONE_VAL() ((KrkValue)(KRK_VAL_MASK_LOW | KRK_VAL_MASK_NONE))
|
#define NONE_VAL(value) ((KrkValue)(KRK_VAL_MASK_LOW | KRK_VAL_MASK_NONE))
|
||||||
#define NOTIMPL_VAL() ((KrkValue)(KRK_VAL_MASK_LOW | KRK_VAL_MASK_NOTIMPL))
|
#define NOTIMPL_VAL(value) ((KrkValue)(KRK_VAL_MASK_LOW | KRK_VAL_MASK_NOTIMPL))
|
||||||
#define BOOLEAN_VAL(value) ((KrkValue)(((uint64_t)(value) & KRK_VAL_MASK_LOW) | KRK_VAL_MASK_BOOLEAN))
|
#define BOOLEAN_VAL(value) ((KrkValue)(((uint64_t)(value) & KRK_VAL_MASK_LOW) | KRK_VAL_MASK_BOOLEAN))
|
||||||
#define INTEGER_VAL(value) ((KrkValue)(((uint64_t)(value) & KRK_VAL_MASK_LOW) | KRK_VAL_MASK_INTEGER))
|
#define INTEGER_VAL(value) ((KrkValue)(((uint64_t)(value) & KRK_VAL_MASK_LOW) | KRK_VAL_MASK_INTEGER))
|
||||||
#define KWARGS_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_KWARGS))
|
#define KWARGS_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_KWARGS))
|
||||||
#define OBJECT_VAL(value) ((KrkValue)((_krk_sanitize((uintptr_t)(value)) & KRK_VAL_MASK_LOW) | KRK_VAL_MASK_OBJECT))
|
#define OBJECT_VAL(value) ((KrkValue)((_krk_sanitize((uintptr_t)(value)) & KRK_VAL_MASK_LOW) | KRK_VAL_MASK_OBJECT))
|
||||||
#define HANDLER_VAL(ty,ta) ((KrkValue)((uint64_t)((((uint64_t)ty) << 32) | ((uint32_t)ta)) | KRK_VAL_MASK_HANDLER))
|
#define HANDLER_VAL(ty,ta) ((KrkValue)((uint32_t)((((uint16_t)ty) << 16) | ((uint16_t)ta)) | KRK_VAL_MASK_HANDLER))
|
||||||
#define FLOATING_VAL(value) (((KrkValueDbl){.dbl = (value)}).val)
|
#define FLOATING_VAL(value) (((KrkValueDbl){.dbl = (value)}).val)
|
||||||
|
|
||||||
#define KRK_VAL_TYPE(value) ((value) >> 48)
|
#define KRK_VAL_TYPE(value) ((value) >> 48)
|
||||||
@ -224,7 +230,7 @@ static inline uintptr_t _krk_sanitize(uintptr_t input) {
|
|||||||
#define AS_BOOLEAN(value) AS_INTEGER(value)
|
#define AS_BOOLEAN(value) AS_INTEGER(value)
|
||||||
|
|
||||||
#define AS_NOTIMPL(value) ((krk_integer_type)((value) & KRK_VAL_MASK_LOW))
|
#define AS_NOTIMPL(value) ((krk_integer_type)((value) & KRK_VAL_MASK_LOW))
|
||||||
#define AS_HANDLER(value) ((uint64_t)((value) & KRK_VAL_MASK_LOW))
|
#define AS_HANDLER(value) ((uint32_t)((value) & KRK_VAL_MASK_LOW))
|
||||||
#define AS_OBJECT(value) ((KrkObj*)(uintptr_t)(((value) & KRK_VAL_MASK_LOW) | KRK_HEAP_TAG))
|
#define AS_OBJECT(value) ((KrkObj*)(uintptr_t)(((value) & KRK_VAL_MASK_LOW) | KRK_HEAP_TAG))
|
||||||
#define AS_FLOATING(value) (((KrkValueDbl){.val = (value)}).dbl)
|
#define AS_FLOATING(value) (((KrkValueDbl){.val = (value)}).dbl)
|
||||||
|
|
||||||
@ -243,49 +249,8 @@ static inline uintptr_t _krk_sanitize(uintptr_t input) {
|
|||||||
/* ... and as we said above, if any of the MASK_NAN bits are unset, it's a float. */
|
/* ... and as we said above, if any of the MASK_NAN bits are unset, it's a float. */
|
||||||
#define IS_FLOATING(value) (((value) & KRK_VAL_MASK_NAN) != KRK_VAL_MASK_NAN)
|
#define IS_FLOATING(value) (((value) & KRK_VAL_MASK_NAN) != KRK_VAL_MASK_NAN)
|
||||||
|
|
||||||
#else
|
#define AS_HANDLER_TYPE(value) (AS_HANDLER(value) >> 16)
|
||||||
|
#define AS_HANDLER_TARGET(value) (AS_HANDLER(value) & 0xFFFF)
|
||||||
typedef union {
|
|
||||||
uint64_t val;
|
|
||||||
double dbl;
|
|
||||||
} KrkValueDbl;
|
|
||||||
|
|
||||||
#define NONE_VAL() ((KrkValue){KRK_VAL_NONE,-1})
|
|
||||||
#define NOTIMPL_VAL() ((KrkValue){KRK_VAL_NOTIMPL,0})
|
|
||||||
#define BOOLEAN_VAL(value) ((KrkValue){KRK_VAL_BOOLEAN,!!(value)})
|
|
||||||
#define INTEGER_VAL(value) ((KrkValue){KRK_VAL_INTEGER,((uint64_t)(value)) & 0xFFFFffffFFFFULL})
|
|
||||||
#define KWARGS_VAL(value) ((KrkValue){KRK_VAL_KWARGS,((uint32_t)(value))})
|
|
||||||
#define OBJECT_VAL(value) ((KrkValue){KRK_VAL_OBJECT,((uintptr_t)(value))})
|
|
||||||
#define HANDLER_VAL(ty,ta) ((KrkValue){KRK_VAL_HANDLER,((uint64_t)((((uint64_t)ty) << 32) | ((uint32_t)ta)))})
|
|
||||||
#define FLOATING_VAL(value) ((KrkValue){KRK_VAL_FLOATING,(((KrkValueDbl){.dbl = (value)}).val)})
|
|
||||||
|
|
||||||
#define KRK_VAL_TYPE(value) ((value).tag)
|
|
||||||
|
|
||||||
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000)
|
|
||||||
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
|
|
||||||
#define KRK_IX(value) ((uint64_t)((value).val & KRK_VAL_MASK_LOW))
|
|
||||||
#define KRK_SX(value) ((uint64_t)((value).val & 0x800000000000))
|
|
||||||
#define AS_INTEGER(value) ((krk_integer_type)(KRK_SX(value) ? (KRK_IX(value) | KRK_VAL_MASK_NONE) : (KRK_IX(value))))
|
|
||||||
#define AS_BOOLEAN(value) AS_INTEGER(value)
|
|
||||||
|
|
||||||
#define AS_HANDLER(value) ((uint64_t)((value)).val)
|
|
||||||
#define AS_OBJECT(value) ((KrkObj*)((uintptr_t)((value).val)))
|
|
||||||
#define AS_FLOATING(value) (((KrkValueDbl){.val = ((value)).val}).dbl)
|
|
||||||
|
|
||||||
#define IS_INTEGER(value) (!!(((value)).tag & (KRK_VAL_INTEGER|KRK_VAL_BOOLEAN)))
|
|
||||||
#define IS_BOOLEAN(value) (((value)).tag == KRK_VAL_BOOLEAN)
|
|
||||||
#define IS_NONE(value) (((value)).tag == KRK_VAL_NONE)
|
|
||||||
#define IS_HANDLER(value) (((value)).tag == KRK_VAL_HANDLER)
|
|
||||||
#define IS_OBJECT(value) (((value)).tag == KRK_VAL_OBJECT)
|
|
||||||
#define IS_KWARGS(value) (((value)).tag == KRK_VAL_KWARGS)
|
|
||||||
#define IS_NOTIMPL(value) (((value)).tag == KRK_VAL_NOTIMPL)
|
|
||||||
#define IS_FLOATING(value) (((value)).tag == KRK_VAL_FLOATING)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#define AS_HANDLER_TYPE(value) (AS_HANDLER(value) >> 32)
|
|
||||||
#define AS_HANDLER_TARGET(value) (AS_HANDLER(value) & 0xFFFFFFFF)
|
|
||||||
#define IS_HANDLER_TYPE(value,type) (IS_HANDLER(value) && AS_HANDLER_TYPE(value) == type)
|
#define IS_HANDLER_TYPE(value,type) (IS_HANDLER(value) && AS_HANDLER_TYPE(value) == type)
|
||||||
|
|
||||||
#define KWARGS_SINGLE (INT32_MAX)
|
#define KWARGS_SINGLE (INT32_MAX)
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
* for Kuroko, including initializing the VM and passing code to be interpreted.
|
* for Kuroko, including initializing the VM and passing code to be interpreted.
|
||||||
*/
|
*/
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include "kuroko.h"
|
#include "kuroko.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
@ -17,7 +19,7 @@
|
|||||||
* @def KRK_CALL_FRAMES_MAX
|
* @def KRK_CALL_FRAMES_MAX
|
||||||
* @brief Maximum depth of the call stack in managed-code function calls.
|
* @brief Maximum depth of the call stack in managed-code function calls.
|
||||||
*/
|
*/
|
||||||
#define KRK_CALL_FRAMES_MAX 1000
|
#define KRK_CALL_FRAMES_MAX 64
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def KRK_THREAD_SCRATCH_SIZE
|
* @def KRK_THREAD_SCRATCH_SIZE
|
||||||
@ -48,6 +50,9 @@ typedef struct {
|
|||||||
size_t outSlots; /**< Offset into the stack at which stackTop will be reset upon return */
|
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 */
|
KrkTable * globals; /**< Pointer to the attribute table containing valud global vairables for this call */
|
||||||
KrkValue globalsOwner; /**< Owner of the current globals context, to give to new closures. */
|
KrkValue globalsOwner; /**< Owner of the current globals context, to give to new closures. */
|
||||||
|
#ifndef KRK_NO_CALLGRIND
|
||||||
|
struct timespec in_time;
|
||||||
|
#endif
|
||||||
} KrkCallFrame;
|
} KrkCallFrame;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,13 +136,17 @@ struct BaseClasses {
|
|||||||
KrkClass * enumerateClass; /**< Yield pairs of indexes and values from an iterator. */
|
KrkClass * enumerateClass; /**< Yield pairs of indexes and values from an iterator. */
|
||||||
KrkClass * HelperClass; /**< Class implementation of 'help' object */
|
KrkClass * HelperClass; /**< Class implementation of 'help' object */
|
||||||
KrkClass * LicenseReaderClass; /**< Class implementation of 'license' object */
|
KrkClass * LicenseReaderClass; /**< Class implementation of 'license' object */
|
||||||
KrkClass * CompilerStateClass; /**< Compiler global state */
|
KrkClass * FileClass; /**< os.File */
|
||||||
KrkClass * CellClass; /**< Upvalue cell */
|
KrkClass * BinaryFileClass; /**< os.BinaryFile */
|
||||||
|
KrkClass * DirectoryClass; /**< os.Directory */
|
||||||
|
KrkClass * stat_resultClass; /**< stat.stat_result */
|
||||||
|
KrkClass * EnvironClass; /**< os._Environ */
|
||||||
KrkClass * setClass; /**< Unordered hashset */
|
KrkClass * setClass; /**< Unordered hashset */
|
||||||
KrkClass * setiteratorClass; /**< Iterator over values in a set */
|
KrkClass * setiteratorClass; /**< Iterator over values in a set */
|
||||||
KrkClass * ThreadClass; /**< Threading.Thread */
|
KrkClass * ThreadClass; /**< Threading.Thread */
|
||||||
KrkClass * LockClass; /**< Threading.Lock */
|
KrkClass * LockClass; /**< Threading.Lock */
|
||||||
KrkClass * ellipsisClass; /**< Type of the Ellipsis (...) singleton */
|
KrkClass * CompilerStateClass; /**< Compiler global state */
|
||||||
|
KrkClass * CellClass; /**< Upvalue cell */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,7 +172,6 @@ typedef struct KrkThreadState {
|
|||||||
KrkInstance * module; /**< The current module execution context. */
|
KrkInstance * module; /**< The current module execution context. */
|
||||||
KrkValue currentException; /**< When an exception is thrown, it is stored here. */
|
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. */
|
int flags; /**< Thread-local VM flags; each thread inherits the low byte of the global VM flags. */
|
||||||
unsigned int maximumCallDepth; /**< Maximum recursive call depth. */
|
|
||||||
KrkValue * stackMax; /**< End of allocated stack space. */
|
KrkValue * stackMax; /**< End of allocated stack space. */
|
||||||
|
|
||||||
KrkValue scratchSpace[KRK_THREAD_SCRATCH_SIZE]; /**< A place to store a few values to keep them from being prematurely GC'd. */
|
KrkValue scratchSpace[KRK_THREAD_SCRATCH_SIZE]; /**< A place to store a few values to keep them from being prematurely GC'd. */
|
||||||
@ -197,6 +205,8 @@ typedef struct KrkVM {
|
|||||||
KrkObj** grayStack; /**< Scan list */
|
KrkObj** grayStack; /**< Scan list */
|
||||||
|
|
||||||
KrkThreadState * threads; /**< Invasive linked list of all VM threads. */
|
KrkThreadState * threads; /**< Invasive linked list of all VM threads. */
|
||||||
|
FILE * callgrindFile; /**< File to write unprocessed callgrind data to. */
|
||||||
|
size_t maximumCallDepth; /**< Maximum recursive call depth. */
|
||||||
struct DebuggerState * dbgState; /**< Opaque debugger state pointer. */
|
struct DebuggerState * dbgState; /**< Opaque debugger state pointer. */
|
||||||
} KrkVM;
|
} KrkVM;
|
||||||
|
|
||||||
@ -213,15 +223,15 @@ typedef struct KrkVM {
|
|||||||
#define KRK_GLOBAL_ENABLE_STRESS_GC (1 << 8)
|
#define KRK_GLOBAL_ENABLE_STRESS_GC (1 << 8)
|
||||||
#define KRK_GLOBAL_GC_PAUSED (1 << 9)
|
#define KRK_GLOBAL_GC_PAUSED (1 << 9)
|
||||||
#define KRK_GLOBAL_CLEAN_OUTPUT (1 << 10)
|
#define KRK_GLOBAL_CLEAN_OUTPUT (1 << 10)
|
||||||
/* 11 is available again */
|
#define KRK_GLOBAL_CALLGRIND (1 << 11)
|
||||||
#define KRK_GLOBAL_REPORT_GC_COLLECTS (1 << 12)
|
#define KRK_GLOBAL_REPORT_GC_COLLECTS (1 << 12)
|
||||||
#define KRK_GLOBAL_THREADS (1 << 13)
|
#define KRK_GLOBAL_THREADS (1 << 13)
|
||||||
#define KRK_GLOBAL_NO_DEFAULT_MODULES (1 << 14)
|
#define KRK_GLOBAL_NO_DEFAULT_MODULES (1 << 14)
|
||||||
|
|
||||||
#ifndef KRK_DISABLE_THREADS
|
#ifndef KRK_DISABLE_THREADS
|
||||||
# define krk_threadLocal __thread
|
# define threadLocal __thread
|
||||||
#else
|
#else
|
||||||
# define krk_threadLocal
|
# define threadLocal
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -242,7 +252,7 @@ inline KrkThreadState * _macos_currentThread(void) {
|
|||||||
#elif !defined(KRK_DISABLE_THREADS) && ((defined(_WIN32) && !defined(KRKINLIB)) || defined(KRK_MEDIOCRE_TLS))
|
#elif !defined(KRK_DISABLE_THREADS) && ((defined(_WIN32) && !defined(KRKINLIB)) || defined(KRK_MEDIOCRE_TLS))
|
||||||
#define krk_currentThread (*krk_getCurrentThread())
|
#define krk_currentThread (*krk_getCurrentThread())
|
||||||
#else
|
#else
|
||||||
extern krk_threadLocal KrkThreadState krk_currentThread;
|
extern threadLocal KrkThreadState krk_currentThread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -311,7 +321,7 @@ extern void krk_resetStack(void);
|
|||||||
* indicate @c KRK_THREAD_HAS_EXCEPTION and @c krk_currentThread.currentException
|
* indicate @c KRK_THREAD_HAS_EXCEPTION and @c krk_currentThread.currentException
|
||||||
* should contain the raised exception value.
|
* should contain the raised exception value.
|
||||||
*/
|
*/
|
||||||
extern KrkValue krk_interpret(const char * src, const char * fromFile);
|
extern KrkValue krk_interpret(const char * src, char * fromFile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Load and run a source file and return when execution completes.
|
* @brief Load and run a source file and return when execution completes.
|
||||||
@ -325,7 +335,7 @@ extern KrkValue krk_interpret(const char * src, const char * fromFile);
|
|||||||
* @return As with @c krk_interpret, an object representing the newly created module,
|
* @return As with @c krk_interpret, an object representing the newly created module,
|
||||||
* or the final return value of the VM execution.
|
* or the final return value of the VM execution.
|
||||||
*/
|
*/
|
||||||
extern KrkValue krk_runfile(const char * fileName, const char * fromFile);
|
extern KrkValue krk_runfile(const char * fileName, char * fromFile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Push a stack value.
|
* @brief Push a stack value.
|
||||||
@ -985,6 +995,33 @@ extern int krk_delAttribute(KrkString * name);
|
|||||||
*/
|
*/
|
||||||
extern void krk_module_init_kuroko(void);
|
extern void krk_module_init_kuroko(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the built-in 'gc' module.
|
||||||
|
*/
|
||||||
|
extern void krk_module_init_gc(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the built-in 'time' module.
|
||||||
|
*/
|
||||||
|
extern void krk_module_init_time(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the built-in 'os' module.
|
||||||
|
*/
|
||||||
|
extern void krk_module_init_os(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the built-in 'fileio' module.
|
||||||
|
*/
|
||||||
|
extern void krk_module_init_fileio(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the built-in 'dis' module.
|
||||||
|
*
|
||||||
|
* Not available if KRK_DISABLE_DEBUG is set.
|
||||||
|
*/
|
||||||
|
extern void krk_module_init_dis(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize the built-in 'threading' module.
|
* @brief Initialize the built-in 'threading' module.
|
||||||
*
|
*
|
||||||
|
79
src/memory.c
79
src/memory.c
@ -1,4 +1,3 @@
|
|||||||
#include <time.h>
|
|
||||||
#include <kuroko/vm.h>
|
#include <kuroko/vm.h>
|
||||||
#include <kuroko/memory.h>
|
#include <kuroko/memory.h>
|
||||||
#include <kuroko/object.h>
|
#include <kuroko/object.h>
|
||||||
@ -8,8 +7,6 @@
|
|||||||
|
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
|
||||||
#define FREE_OBJECT(t,p) krk_reallocate(p,sizeof(t),0)
|
|
||||||
|
|
||||||
#if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
|
#if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
|
||||||
/**
|
/**
|
||||||
* Extensive Memory Debugging
|
* Extensive Memory Debugging
|
||||||
@ -21,7 +18,7 @@
|
|||||||
* the sizes of objects by both using the appropriate macros and by
|
* the sizes of objects by both using the appropriate macros and by
|
||||||
* ensuring the right sizes are passed to those macros. This is a very
|
* ensuring the right sizes are passed to those macros. This is a very
|
||||||
* easy thing to get wrong - allocate with @c malloc but free with the
|
* easy thing to get wrong - allocate with @c malloc but free with the
|
||||||
* @c KRK_FREE_ARRAY macros, for example, and the memory tracking now has
|
* @c FREE_ARRAY macros, for example, and the memory tracking now has
|
||||||
* a net negative, which may lead to underflowing. Use the right macros,
|
* a net negative, which may lead to underflowing. Use the right macros,
|
||||||
* but mix up sizes between allocation and deallocation, and we may have
|
* but mix up sizes between allocation and deallocation, and we may have
|
||||||
* a "leak" of bytes and garbage collection may happen more often than
|
* a "leak" of bytes and garbage collection may happen more often than
|
||||||
@ -205,9 +202,9 @@ static void freeObject(KrkObj * object) {
|
|||||||
switch (object->type) {
|
switch (object->type) {
|
||||||
case KRK_OBJ_STRING: {
|
case KRK_OBJ_STRING: {
|
||||||
KrkString * string = (KrkString*)object;
|
KrkString * string = (KrkString*)object;
|
||||||
KRK_FREE_ARRAY(char, string->chars, string->length + 1);
|
FREE_ARRAY(char, string->chars, string->length + 1);
|
||||||
if (string->codes && string->codes != string->chars) free(string->codes);
|
if (string->codes && string->codes != string->chars) free(string->codes);
|
||||||
FREE_OBJECT(KrkString, object);
|
FREE(KrkString, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_CODEOBJECT: {
|
case KRK_OBJ_CODEOBJECT: {
|
||||||
@ -215,26 +212,24 @@ static void freeObject(KrkObj * object) {
|
|||||||
krk_freeChunk(&function->chunk);
|
krk_freeChunk(&function->chunk);
|
||||||
krk_freeValueArray(&function->positionalArgNames);
|
krk_freeValueArray(&function->positionalArgNames);
|
||||||
krk_freeValueArray(&function->keywordArgNames);
|
krk_freeValueArray(&function->keywordArgNames);
|
||||||
KRK_FREE_ARRAY(KrkLocalEntry, function->localNames, function->localNameCount);
|
FREE_ARRAY(KrkLocalEntry, function->localNames, function->localNameCount);
|
||||||
KRK_FREE_ARRAY(KrkExpressionsMap, function->expressions, function->expressionsCapacity);
|
|
||||||
KRK_FREE_ARRAY(KrkOverlongJump, function->overlongJumps, function->overlongJumpsCapacity);
|
|
||||||
function->localNameCount = 0;
|
function->localNameCount = 0;
|
||||||
FREE_OBJECT(KrkCodeObject, object);
|
FREE(KrkCodeObject, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_NATIVE: {
|
case KRK_OBJ_NATIVE: {
|
||||||
FREE_OBJECT(KrkNative, object);
|
FREE(KrkNative, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_CLOSURE: {
|
case KRK_OBJ_CLOSURE: {
|
||||||
KrkClosure * closure = (KrkClosure*)object;
|
KrkClosure * closure = (KrkClosure*)object;
|
||||||
KRK_FREE_ARRAY(KrkUpvalue*,closure->upvalues,closure->upvalueCount);
|
FREE_ARRAY(KrkUpvalue*,closure->upvalues,closure->upvalueCount);
|
||||||
krk_freeTable(&closure->fields);
|
krk_freeTable(&closure->fields);
|
||||||
FREE_OBJECT(KrkClosure, object);
|
FREE(KrkClosure, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_UPVALUE: {
|
case KRK_OBJ_UPVALUE: {
|
||||||
FREE_OBJECT(KrkUpvalue, object);
|
FREE(KrkUpvalue, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_CLASS: {
|
case KRK_OBJ_CLASS: {
|
||||||
@ -244,7 +239,7 @@ static void freeObject(KrkObj * object) {
|
|||||||
if (_class->base) {
|
if (_class->base) {
|
||||||
krk_tableDeleteExact(&_class->base->subclasses, OBJECT_VAL(object));
|
krk_tableDeleteExact(&_class->base->subclasses, OBJECT_VAL(object));
|
||||||
}
|
}
|
||||||
FREE_OBJECT(KrkClass, object);
|
FREE(KrkClass, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_INSTANCE: {
|
case KRK_OBJ_INSTANCE: {
|
||||||
@ -257,18 +252,18 @@ static void freeObject(KrkObj * object) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_BOUND_METHOD:
|
case KRK_OBJ_BOUND_METHOD:
|
||||||
FREE_OBJECT(KrkBoundMethod, object);
|
FREE(KrkBoundMethod, object);
|
||||||
break;
|
break;
|
||||||
case KRK_OBJ_TUPLE: {
|
case KRK_OBJ_TUPLE: {
|
||||||
KrkTuple * tuple = (KrkTuple*)object;
|
KrkTuple * tuple = (KrkTuple*)object;
|
||||||
krk_freeValueArray(&tuple->values);
|
krk_freeValueArray(&tuple->values);
|
||||||
FREE_OBJECT(KrkTuple, object);
|
FREE(KrkTuple, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_BYTES: {
|
case KRK_OBJ_BYTES: {
|
||||||
KrkBytes * bytes = (KrkBytes*)object;
|
KrkBytes * bytes = (KrkBytes*)object;
|
||||||
KRK_FREE_ARRAY(uint8_t, bytes->bytes, bytes->length);
|
FREE_ARRAY(uint8_t, bytes->bytes, bytes->length);
|
||||||
FREE_OBJECT(KrkBytes, bytes);
|
FREE(KrkBytes, bytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -324,7 +319,7 @@ void krk_markObject(KrkObj * object) {
|
|||||||
object->flags |= KRK_OBJ_FLAGS_IS_MARKED;
|
object->flags |= KRK_OBJ_FLAGS_IS_MARKED;
|
||||||
|
|
||||||
if (vm.grayCapacity < vm.grayCount + 1) {
|
if (vm.grayCapacity < vm.grayCount + 1) {
|
||||||
vm.grayCapacity = KRK_GROW_CAPACITY(vm.grayCapacity);
|
vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
|
||||||
vm.grayStack = realloc(vm.grayStack, sizeof(KrkObj*) * vm.grayCapacity);
|
vm.grayStack = realloc(vm.grayStack, sizeof(KrkObj*) * vm.grayCapacity);
|
||||||
if (!vm.grayStack) exit(1);
|
if (!vm.grayStack) exit(1);
|
||||||
}
|
}
|
||||||
@ -367,7 +362,6 @@ static void blackenObject(KrkObj * object) {
|
|||||||
for (size_t i = 0; i < function->localNameCount; ++i) {
|
for (size_t i = 0; i < function->localNameCount; ++i) {
|
||||||
krk_markObject((KrkObj*)function->localNames[i].name);
|
krk_markObject((KrkObj*)function->localNames[i].name);
|
||||||
}
|
}
|
||||||
krk_markValue(function->jumpTargets);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_UPVALUE:
|
case KRK_OBJ_UPVALUE:
|
||||||
@ -442,7 +436,7 @@ static size_t sweep(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void krk_markTable(KrkTable * table) {
|
void krk_markTable(KrkTable * table) {
|
||||||
for (size_t i = 0; i < table->used; ++i) {
|
for (size_t i = 0; i < table->capacity; ++i) {
|
||||||
KrkTableEntry * entry = &table->entries[i];
|
KrkTableEntry * entry = &table->entries[i];
|
||||||
krk_markValue(entry->key);
|
krk_markValue(entry->key);
|
||||||
krk_markValue(entry->value);
|
krk_markValue(entry->value);
|
||||||
@ -450,7 +444,7 @@ void krk_markTable(KrkTable * table) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void tableRemoveWhite(KrkTable * table) {
|
static void tableRemoveWhite(KrkTable * table) {
|
||||||
for (size_t i = 0; i < table->used; ++i) {
|
for (size_t i = 0; i < table->capacity; ++i) {
|
||||||
KrkTableEntry * entry = &table->entries[i];
|
KrkTableEntry * entry = &table->entries[i];
|
||||||
if (IS_OBJECT(entry->key) && !((AS_OBJECT(entry->key))->flags & KRK_OBJ_FLAGS_IS_MARKED)) {
|
if (IS_OBJECT(entry->key) && !((AS_OBJECT(entry->key))->flags & KRK_OBJ_FLAGS_IS_MARKED)) {
|
||||||
krk_tableDeleteExact(table, entry->key);
|
krk_tableDeleteExact(table, entry->key);
|
||||||
@ -568,3 +562,42 @@ size_t krk_collectGarbage(void) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef KRK_NO_SYSTEM_MODULES
|
||||||
|
KRK_Function(collect) {
|
||||||
|
FUNCTION_TAKES_NONE();
|
||||||
|
if (&krk_currentThread != vm.threads) return krk_runtimeError(vm.exceptions->valueError, "only the main thread can do that");
|
||||||
|
return INTEGER_VAL(krk_collectGarbage());
|
||||||
|
}
|
||||||
|
|
||||||
|
KRK_Function(pause) {
|
||||||
|
FUNCTION_TAKES_NONE();
|
||||||
|
vm.globalFlags |= (KRK_GLOBAL_GC_PAUSED);
|
||||||
|
return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
KRK_Function(resume) {
|
||||||
|
FUNCTION_TAKES_NONE();
|
||||||
|
vm.globalFlags &= ~(KRK_GLOBAL_GC_PAUSED);
|
||||||
|
return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
void krk_module_init_gc(void) {
|
||||||
|
/**
|
||||||
|
* gc = module()
|
||||||
|
*
|
||||||
|
* Namespace for methods for controlling the garbage collector.
|
||||||
|
*/
|
||||||
|
KrkInstance * gcModule = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_attachNamedObject(&vm.modules, "gc", (KrkObj*)gcModule);
|
||||||
|
krk_attachNamedObject(&gcModule->fields, "__name__", (KrkObj*)S("gc"));
|
||||||
|
krk_attachNamedValue(&gcModule->fields, "__file__", NONE_VAL());
|
||||||
|
KRK_DOC(gcModule, "@brief Namespace containing methods for controlling the garbage collector.");
|
||||||
|
|
||||||
|
KRK_DOC(BIND_FUNC(gcModule,collect),
|
||||||
|
"@brief Triggers one cycle of garbage collection.");
|
||||||
|
KRK_DOC(BIND_FUNC(gcModule,pause),
|
||||||
|
"@brief Disables automatic garbage collection until @ref resume is called.");
|
||||||
|
KRK_DOC(BIND_FUNC(gcModule,resume),
|
||||||
|
"@brief Re-enable automatic garbage collection after it was stopped by @ref pause ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -62,10 +62,4 @@ SPECIAL_ATTRS(CHR, "__chr__")
|
|||||||
SPECIAL_ATTRS(ORD, "__ord__")
|
SPECIAL_ATTRS(ORD, "__ord__")
|
||||||
SPECIAL_ATTRS(FLOAT, "__float__")
|
SPECIAL_ATTRS(FLOAT, "__float__")
|
||||||
SPECIAL_ATTRS(STRSTRIP, " \t\n\r")
|
SPECIAL_ATTRS(STRSTRIP, " \t\n\r")
|
||||||
SPECIAL_ATTRS(HEX, "__hex__")
|
|
||||||
SPECIAL_ATTRS(OCT, "__oct__")
|
|
||||||
SPECIAL_ATTRS(BIN, "__bin__")
|
|
||||||
SPECIAL_ATTRS(ABS, "__abs__")
|
|
||||||
SPECIAL_ATTRS(FUNC, "__func__")
|
|
||||||
SPECIAL_ATTRS(BLDCLS, "__build_class__")
|
|
||||||
SPECIAL_ATTRS(MAIN, "__main__")
|
|
||||||
|
@ -1,315 +0,0 @@
|
|||||||
/**
|
|
||||||
* @brief Pairing heap.
|
|
||||||
* @file module__pheap.c
|
|
||||||
* @author K. Lange <klange@toaruos.org>
|
|
||||||
*
|
|
||||||
* A very simple pairing heap.
|
|
||||||
*
|
|
||||||
* Provides a min-heap with insert, peek, and pop.
|
|
||||||
*
|
|
||||||
* While heap entries may be mutable, care should be taken not to modify
|
|
||||||
* any values used for comparison, as the heap can not update ordering.
|
|
||||||
*
|
|
||||||
* This could likely be improved by the implementation of parent pointers,
|
|
||||||
* which can allow for elements of the heap other than the root to be
|
|
||||||
* removed or updated (removal + reinsertion), but that requires the
|
|
||||||
* ability to quickly reference specific elements - which requires the
|
|
||||||
* heap "nodes" to also be accessible or addressible in some way, such
|
|
||||||
* as by making them Kuroko objects. Such a change would likely result
|
|
||||||
* in performance impacts, so a parent-pointer pairing heap should be
|
|
||||||
* a separate class.
|
|
||||||
*
|
|
||||||
* The implementation here is based strongly on the pseudocode found in
|
|
||||||
* the Wikipedia article "Paring heap".
|
|
||||||
*/
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <kuroko/vm.h>
|
|
||||||
#include <kuroko/util.h>
|
|
||||||
|
|
||||||
static KrkClass * PHeapClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Heap node.
|
|
||||||
*
|
|
||||||
* Represents one element in the heap. Each element potentially has
|
|
||||||
* a pointer to more elements (a "right" or "next" pointer) and a
|
|
||||||
* pointer to a subheap (a "left" pointer).
|
|
||||||
*/
|
|
||||||
typedef struct PHeap PHeap;
|
|
||||||
struct PHeap {
|
|
||||||
struct PHeap_Obj * owner;
|
|
||||||
KrkValue value;
|
|
||||||
PHeap * subheaps; /* Left pointer to first child, if any. */
|
|
||||||
PHeap * next; /* Right pointer to next sibling, if any. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Heap comparator function.
|
|
||||||
*
|
|
||||||
* The heap is ostensibly a min-heap, but the comparison behavior is left
|
|
||||||
* entirely to the user. A comparator function should return true if the
|
|
||||||
* left (first) argument has priority (is less than) the right (second)
|
|
||||||
* argument, and 0 otherwise. This comparison must be consistent or the
|
|
||||||
* heap will not work correctly.
|
|
||||||
*
|
|
||||||
* Generally, this module only uses one comparison function: a wrapper that
|
|
||||||
* calls a Kuroko callable. As Kuroko callables may hold their own state,
|
|
||||||
* no facility was necessary to pass user data to the comparator - only
|
|
||||||
* the left and right heap nodes to compare are given.
|
|
||||||
*/
|
|
||||||
typedef int (*pheap_comparator_func)(PHeap *, PHeap *);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief meld - Combine two heaps.
|
|
||||||
*
|
|
||||||
* Combines two heaps and returns the result. The heaps are "destroyed" in the process.
|
|
||||||
*
|
|
||||||
* @param left One heap. Can be @c NULL for an empty heap.
|
|
||||||
* @param right The other heap. Can be @c NULL for an empty heap.
|
|
||||||
* @param comparator Function that should return true if @p left has priority (is less than) @p right.
|
|
||||||
* @returns A pointer to the new root, which will be either of @p left or @p right.
|
|
||||||
*/
|
|
||||||
static PHeap * pheap_meld(PHeap * left, PHeap * right, pheap_comparator_func comparator) {
|
|
||||||
/*
|
|
||||||
* If either of the heaps is "empty" (represented by NULL),
|
|
||||||
* then simply return the other one.
|
|
||||||
*/
|
|
||||||
if (!left) {
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
if (!right) {
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Otherwise, pull the 'smaller' of the two up and add the 'larger'
|
|
||||||
* to the front of the subheap list of the smaller one. We use
|
|
||||||
* intrusive lists within our Heap struct, so each Heap is also
|
|
||||||
* a List node (with a `next` pointer).
|
|
||||||
*/
|
|
||||||
if (comparator(left, right)) {
|
|
||||||
/* Turns `left` into Heap(left→value, right :: left→subheaps) */
|
|
||||||
if (left->subheaps) {
|
|
||||||
right->next = left->subheaps;
|
|
||||||
}
|
|
||||||
left->subheaps = right;
|
|
||||||
return left;
|
|
||||||
} else {
|
|
||||||
/* Turns `right` into Heap(right→value, left :: right→subheaps) */
|
|
||||||
if (right->subheaps) {
|
|
||||||
left->next = right->subheaps;
|
|
||||||
}
|
|
||||||
right->subheaps = left;
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief merge_pairs - Perform left-to-right/right-to-left merge on lists of subheaps.
|
|
||||||
*
|
|
||||||
* The core of the heap.
|
|
||||||
*
|
|
||||||
* @param list List of pairs to merge.
|
|
||||||
* @param comparator Comparator function as described in @c pheap_meld.
|
|
||||||
* @returns the resulting heap.
|
|
||||||
*/
|
|
||||||
static PHeap * pheap_merge_pairs(PHeap * list, pheap_comparator_func comparator) {
|
|
||||||
if (!list) {
|
|
||||||
/* An empty list is represented by NULL, and yields an empty Heap,
|
|
||||||
* which is also represented by NULL... */
|
|
||||||
return NULL;
|
|
||||||
} else if (list->next == NULL) {
|
|
||||||
/* If a list entry doesn't have a next, it has a size of one,
|
|
||||||
* and we can just return this heap directly. */
|
|
||||||
return list;
|
|
||||||
} else {
|
|
||||||
/* Otherwise we meld the first two, then meld them with the result of
|
|
||||||
* recursively melding the rest, which performs our left-right /
|
|
||||||
* right-left two-stage merge. */
|
|
||||||
PHeap * next = list->next;
|
|
||||||
list->next = NULL;
|
|
||||||
PHeap * rest = next->next;
|
|
||||||
next->next = NULL;
|
|
||||||
return pheap_meld(pheap_meld(list, next, comparator), pheap_merge_pairs(rest, comparator), comparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief delete_min - Remove the 'smallest' value from the heap.
|
|
||||||
*
|
|
||||||
* Removes the root node of the heap, rebalancing the remainder
|
|
||||||
* of the heap. Should only be used when the heap is not empty.
|
|
||||||
*
|
|
||||||
* @param heap Heap to remove the root of.
|
|
||||||
* @param comparator Comparator function as described in @c pheap_meld.
|
|
||||||
* @returns the resulting heap.
|
|
||||||
*/
|
|
||||||
static PHeap * pheap_delete_min(PHeap * heap, pheap_comparator_func comparator) {
|
|
||||||
PHeap * subs = heap->subheaps;
|
|
||||||
return pheap_merge_pairs(subs, comparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief visit_heap - Call a user function for every node in the heap.
|
|
||||||
*
|
|
||||||
* The function is called before recursing.
|
|
||||||
*
|
|
||||||
* @param heap Heap to walk.
|
|
||||||
* @param func Function to call.
|
|
||||||
* @param extra User data to pass to the function.
|
|
||||||
*/
|
|
||||||
static void pheap_visit_heap(PHeap * heap, void (*func)(PHeap *, void*), void* extra) {
|
|
||||||
if (!heap) return;
|
|
||||||
func(heap, extra);
|
|
||||||
pheap_visit_heap(heap->subheaps, func, extra);
|
|
||||||
pheap_visit_heap(heap->next, func, extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief visit_heap_after - Call a user function for every node in the heap.
|
|
||||||
*
|
|
||||||
* The function is called after recursing, so this is suitable for freeing the
|
|
||||||
* entirety of a heap.
|
|
||||||
*
|
|
||||||
* @param heap Heap to walk.
|
|
||||||
* @param func Function to call.
|
|
||||||
* @param extra User data to pass to the function.
|
|
||||||
*/
|
|
||||||
static void pheap_visit_heap_after(PHeap * heap, void (*func)(PHeap *, void*), void* extra) {
|
|
||||||
if (!heap) return;
|
|
||||||
pheap_visit_heap_after(heap->subheaps, func, extra);
|
|
||||||
pheap_visit_heap_after(heap->next, func, extra);
|
|
||||||
func(heap, extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PHeap_Obj {
|
|
||||||
KrkInstance inst;
|
|
||||||
KrkValue comparator;
|
|
||||||
PHeap * heap;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define IS_PHeap(o) (krk_isInstanceOf(o,PHeapClass))
|
|
||||||
#define AS_PHeap(o) ((struct PHeap_Obj*)AS_OBJECT(o))
|
|
||||||
#define CURRENT_CTYPE struct PHeap_Obj *
|
|
||||||
#define CURRENT_NAME self
|
|
||||||
|
|
||||||
KRK_Method(PHeap,__init__) {
|
|
||||||
KrkValue comparator;
|
|
||||||
if (!krk_parseArgs(".V:PHeap", (const char*[]){"comp"}, &comparator)) return NONE_VAL();
|
|
||||||
self->comparator = comparator;
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int run_comparator(PHeap * left, PHeap * right) {
|
|
||||||
assert(left->owner == right->owner);
|
|
||||||
krk_push(left->owner->comparator);
|
|
||||||
krk_push(left->value);
|
|
||||||
krk_push(right->value);
|
|
||||||
KrkValue result = krk_callStack(2);
|
|
||||||
if (!IS_BOOLEAN(result)) return 0;
|
|
||||||
return AS_BOOLEAN(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(PHeap,insert) {
|
|
||||||
KrkValue value;
|
|
||||||
if (!krk_parseArgs(".V",(const char*[]){"value"}, &value)) return NONE_VAL();
|
|
||||||
struct PHeap * node = calloc(sizeof(struct PHeap), 1);
|
|
||||||
node->owner = self;
|
|
||||||
node->value = value;
|
|
||||||
self->heap = pheap_meld(self->heap, node, run_comparator);
|
|
||||||
self->count += 1;
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(PHeap,peek) {
|
|
||||||
if (self->heap) return self->heap->value;
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(PHeap,pop) {
|
|
||||||
PHeap * old = self->heap;
|
|
||||||
if (!old) return krk_runtimeError(vm.exceptions->indexError, "pop from empty heap");
|
|
||||||
self->heap = pheap_delete_min(self->heap, run_comparator);
|
|
||||||
self->count -= 1;
|
|
||||||
KrkValue out = old->value;
|
|
||||||
free(old);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(PHeap,__bool__) {
|
|
||||||
return BOOLEAN_VAL(self->heap != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(PHeap,__len__) {
|
|
||||||
return INTEGER_VAL(self->count);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void run_visitor(PHeap * heap, void * visitor) {
|
|
||||||
krk_push(*(KrkValue*)visitor);
|
|
||||||
krk_push(heap->value);
|
|
||||||
krk_callStack(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(PHeap,visit) {
|
|
||||||
KrkValue func;
|
|
||||||
int after = 0;
|
|
||||||
if (!krk_parseArgs(".V|p",(const char*[]){"func","after"},
|
|
||||||
&func, &after)) return NONE_VAL();
|
|
||||||
|
|
||||||
(after ? pheap_visit_heap_after : pheap_visit_heap)(self->heap, run_visitor, &func);
|
|
||||||
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _scan_one(PHeap * heap, void * unused) {
|
|
||||||
krk_markValue(heap->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _pheap_scan(KrkInstance * _self) {
|
|
||||||
struct PHeap_Obj * self = (void*)_self;
|
|
||||||
krk_markValue(self->comparator);
|
|
||||||
pheap_visit_heap(self->heap, _scan_one, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _free_one(PHeap * heap, void * unused) {
|
|
||||||
free(heap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _pheap_sweep(KrkInstance * _self) {
|
|
||||||
struct PHeap_Obj * self = (void*)_self;
|
|
||||||
pheap_visit_heap_after(self->heap,_free_one, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(PHeap,comp) {
|
|
||||||
return self->comparator;
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Module(_pheap) {
|
|
||||||
KRK_DOC(module, "Pairing heap with simple insert and pop-min operations.");
|
|
||||||
|
|
||||||
KrkClass * PHeap = krk_makeClass(module, &PHeapClass, "PHeap", vm.baseClasses->objectClass);
|
|
||||||
KRK_DOC(PHeap,"Pairing heap with simple insert and pop-min operations.");
|
|
||||||
PHeap->allocSize = sizeof(struct PHeap_Obj);
|
|
||||||
PHeap->_ongcscan = _pheap_scan;
|
|
||||||
PHeap->_ongcsweep = _pheap_sweep;
|
|
||||||
|
|
||||||
KRK_DOC(BIND_METHOD(PHeap,__init__),
|
|
||||||
"@arguments comp\n\n"
|
|
||||||
"Create a new pairing heap governed by the given comparator function.");
|
|
||||||
KRK_DOC(BIND_METHOD(PHeap,insert),
|
|
||||||
"@arguments value\n\n"
|
|
||||||
"Insert a new element into the heap.");
|
|
||||||
KRK_DOC(BIND_METHOD(PHeap,peek),
|
|
||||||
"Retrieve the root (smallest) element of the heap, or None if it is empty.");
|
|
||||||
KRK_DOC(BIND_METHOD(PHeap,pop),
|
|
||||||
"Remove and return the root (smallest) element of the heap. If the heap is empty, IndexError is raised.");
|
|
||||||
BIND_METHOD(PHeap,__bool__);
|
|
||||||
BIND_METHOD(PHeap,__len__);
|
|
||||||
KRK_DOC(BIND_METHOD(PHeap,visit),
|
|
||||||
"@arguments func,after=False\n\n"
|
|
||||||
"Call a function for each element of the heap.");
|
|
||||||
BIND_PROP(PHeap,comp);
|
|
||||||
krk_finalizeClass(PHeapClass);
|
|
||||||
}
|
|
@ -1,376 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <kuroko/debug.h>
|
|
||||||
#include <kuroko/vm.h>
|
|
||||||
#include <kuroko/util.h>
|
|
||||||
#include <kuroko/compiler.h>
|
|
||||||
|
|
||||||
#include "../private.h"
|
|
||||||
#include "../opcode_enum.h"
|
|
||||||
|
|
||||||
#ifndef KRK_DISABLE_DEBUG
|
|
||||||
|
|
||||||
KRK_Function(enablebreakpoint) {
|
|
||||||
int breakIndex;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"breakpoint"}, &breakIndex)) return NONE_VAL();
|
|
||||||
if (krk_debug_enableBreakpoint(breakIndex))
|
|
||||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(disablebreakpoint) {
|
|
||||||
int breakIndex;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"breakpoint"}, &breakIndex)) return NONE_VAL();
|
|
||||||
if (krk_debug_disableBreakpoint(breakIndex))
|
|
||||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(delbreakpoint) {
|
|
||||||
int breakIndex;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"breakpoint"}, &breakIndex)) return NONE_VAL();
|
|
||||||
if (krk_debug_removeBreakpoint(breakIndex))
|
|
||||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(addbreakpoint) {
|
|
||||||
KrkValue func;
|
|
||||||
int lineNo;
|
|
||||||
int flags = KRK_BREAKPOINT_NORMAL;
|
|
||||||
if (!krk_parseArgs("Vi|i",(const char*[]){"func","lineno","flags"}, &func, &lineNo, &flags)) return NONE_VAL();
|
|
||||||
|
|
||||||
int result;
|
|
||||||
if (IS_STRING(func)) {
|
|
||||||
result = krk_debug_addBreakpointFileLine(AS_STRING(func), lineNo, flags);
|
|
||||||
} else {
|
|
||||||
KrkCodeObject * target = NULL;
|
|
||||||
if (IS_CLOSURE(func)) {
|
|
||||||
target = AS_CLOSURE(func)->function;
|
|
||||||
} else if (IS_BOUND_METHOD(func) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(func)->method))) {
|
|
||||||
target = AS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(func)->method))->function;
|
|
||||||
} else if (IS_codeobject(func)) {
|
|
||||||
target = AS_codeobject(func);
|
|
||||||
} else {
|
|
||||||
return TYPE_ERROR(function or method or filename,func);
|
|
||||||
}
|
|
||||||
/* Figure out what instruction this should be on */
|
|
||||||
size_t last = 0;
|
|
||||||
for (size_t i = 0; i < target->chunk.linesCount; ++i) {
|
|
||||||
if (target->chunk.lines[i].line > (size_t)lineNo) break;
|
|
||||||
if (target->chunk.lines[i].line == (size_t)lineNo) {
|
|
||||||
last = target->chunk.lines[i].startOffset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
last = target->chunk.lines[i].startOffset;
|
|
||||||
}
|
|
||||||
result = krk_debug_addBreakpointCodeOffset(target,last,flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result < 0)
|
|
||||||
return krk_runtimeError(vm.exceptions->baseException, "Could not add breakpoint.");
|
|
||||||
|
|
||||||
return INTEGER_VAL(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* dis.dis(object)
|
|
||||||
*/
|
|
||||||
KRK_Function(dis) {
|
|
||||||
KrkValue funcVal;
|
|
||||||
if (!krk_parseArgs("V",(const char*[]){"func"},&funcVal)) return NONE_VAL();
|
|
||||||
|
|
||||||
if (IS_CLOSURE(funcVal)) {
|
|
||||||
KrkCodeObject * func = AS_CLOSURE(funcVal)->function;
|
|
||||||
krk_disassembleCodeObject(stdout, func, func->name ? func->name->chars : "<unnamed>");
|
|
||||||
} else if (IS_codeobject(funcVal)) {
|
|
||||||
krk_disassembleCodeObject(stdout, AS_codeobject(funcVal), AS_codeobject(funcVal)->name ? AS_codeobject(funcVal)->name->chars : "<unnamed>");
|
|
||||||
} else if (IS_BOUND_METHOD(funcVal)) {
|
|
||||||
if (AS_BOUND_METHOD(funcVal)->method->type == KRK_OBJ_CLOSURE) {
|
|
||||||
KrkCodeObject * func = ((KrkClosure*)AS_BOUND_METHOD(funcVal)->method)->function;
|
|
||||||
const char * methodName = func->name ? func->name->chars : "<unnamed>";
|
|
||||||
const char * typeName = IS_CLASS(AS_BOUND_METHOD(funcVal)->receiver) ? AS_CLASS(AS_BOUND_METHOD(funcVal)->receiver)->name->chars : krk_typeName(AS_BOUND_METHOD(funcVal)->receiver);
|
|
||||||
size_t allocSize = strlen(methodName) + strlen(typeName) + 2;
|
|
||||||
char * tmp = malloc(allocSize);
|
|
||||||
snprintf(tmp, allocSize, "%s.%s", typeName, methodName);
|
|
||||||
krk_disassembleCodeObject(stdout, func, tmp);
|
|
||||||
free(tmp);
|
|
||||||
} else {
|
|
||||||
krk_runtimeError(vm.exceptions->typeError, "Can not disassemble built-in method of '%T'", AS_BOUND_METHOD(funcVal)->receiver);
|
|
||||||
}
|
|
||||||
} else if (IS_CLASS(funcVal)) {
|
|
||||||
KrkValue code;
|
|
||||||
if (krk_tableGet(&AS_CLASS(funcVal)->methods, vm.specialMethodNames[METHOD_FUNC], &code) && IS_CLOSURE(code)) {
|
|
||||||
KrkCodeObject * func = AS_CLOSURE(code)->function;
|
|
||||||
krk_disassembleCodeObject(stdout, func, AS_CLASS(funcVal)->name->chars);
|
|
||||||
}
|
|
||||||
/* TODO Methods! */
|
|
||||||
} else {
|
|
||||||
krk_runtimeError(vm.exceptions->typeError, "Don't know how to disassemble '%T'", funcVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(build) {
|
|
||||||
char * code;
|
|
||||||
char * fileName = "<source>";
|
|
||||||
if (!krk_parseArgs("s|s", (const char*[]){"code","filename"}, &code, &fileName)) return NONE_VAL();
|
|
||||||
|
|
||||||
/* Unset module */
|
|
||||||
krk_push(OBJECT_VAL(krk_currentThread.module));
|
|
||||||
KrkInstance * module = krk_currentThread.module;
|
|
||||||
krk_currentThread.module = NULL;
|
|
||||||
KrkCodeObject * c = krk_compile(code,fileName);
|
|
||||||
krk_currentThread.module = module;
|
|
||||||
krk_pop();
|
|
||||||
if (c) return OBJECT_VAL(c);
|
|
||||||
else return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NOOP (void)0
|
|
||||||
#define SIMPLE(opc) case opc: size = 1; break;
|
|
||||||
#define CONSTANT(opc,more) case opc: { constant = chunk->code[offset + 1]; size = 2; more; break; } \
|
|
||||||
case opc ## _LONG: { constant = (chunk->code[offset + 1] << 16) | \
|
|
||||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
|
||||||
#define OPERAND(opc,more) case opc: { operand = chunk->code[offset + 1]; size = 2; more; break; } \
|
|
||||||
case opc ## _LONG: { operand = (chunk->code[offset + 1] << 16) | \
|
|
||||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
|
||||||
#define JUMP(opc,sign) case opc: { jump = 0 sign ((chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])); \
|
|
||||||
size = 3; break; }
|
|
||||||
#define COMPLICATED(opc,more) case opc: size = 1; more; break;
|
|
||||||
#define OVERLONG_JUMP_MORE size = 3; jump = (chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])
|
|
||||||
#define CLOSURE_MORE \
|
|
||||||
KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
|
|
||||||
size_t baseOffset = offset; \
|
|
||||||
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
|
||||||
int isLocal = chunk->code[baseOffset++ + size]; \
|
|
||||||
baseOffset++; \
|
|
||||||
if (isLocal & 2) { \
|
|
||||||
baseOffset += 2; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
size += baseOffset - offset;
|
|
||||||
#define EXPAND_ARGS_MORE
|
|
||||||
#define FORMAT_VALUE_MORE
|
|
||||||
#define LOCAL_MORE local = operand;
|
|
||||||
static KrkValue _examineInternal(KrkCodeObject* func) {
|
|
||||||
KrkValue output = krk_list_of(0,NULL,0);
|
|
||||||
krk_push(output);
|
|
||||||
|
|
||||||
KrkChunk * chunk = &func->chunk;
|
|
||||||
size_t offset = 0;
|
|
||||||
while (offset < chunk->count) {
|
|
||||||
uint8_t opcode = chunk->code[offset];
|
|
||||||
size_t size = 0;
|
|
||||||
ssize_t constant = -1;
|
|
||||||
ssize_t jump = 0;
|
|
||||||
ssize_t operand = -1;
|
|
||||||
ssize_t local = -1;
|
|
||||||
switch (opcode) {
|
|
||||||
#include "opcodes.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
KrkTuple * newTuple = krk_newTuple(3);
|
|
||||||
krk_push(OBJECT_VAL(newTuple));
|
|
||||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(opcode);
|
|
||||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(size);
|
|
||||||
if (constant != -1) {
|
|
||||||
newTuple->values.values[newTuple->values.count++] = chunk->constants.values[constant];
|
|
||||||
} else if (jump != 0) {
|
|
||||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(jump);
|
|
||||||
} else if (local != -1) {
|
|
||||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand); /* Just in case */
|
|
||||||
for (size_t i = 0; i < func->localNameCount; ++i) {
|
|
||||||
if (func->localNames[i].id == (size_t)local && func->localNames[i].birthday <= offset && func->localNames[i].deathday >= offset) {
|
|
||||||
newTuple->values.values[newTuple->values.count-1] = OBJECT_VAL(func->localNames[i].name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (operand != -1) {
|
|
||||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand);
|
|
||||||
} else {
|
|
||||||
newTuple->values.values[newTuple->values.count++] = NONE_VAL();
|
|
||||||
}
|
|
||||||
krk_writeValueArray(AS_LIST(output), krk_peek(0));
|
|
||||||
krk_pop();
|
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return krk_pop();
|
|
||||||
}
|
|
||||||
KRK_Function(examine) {
|
|
||||||
KrkCodeObject * func;
|
|
||||||
if (!krk_parseArgs("O!",(const char*[]){"func"}, KRK_BASE_CLASS(codeobject), &func)) return NONE_VAL();
|
|
||||||
return _examineInternal(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef SIMPLE
|
|
||||||
#undef OPERANDB
|
|
||||||
#undef OPERAND
|
|
||||||
#undef CONSTANT
|
|
||||||
#undef JUMP
|
|
||||||
#undef COMPLICATED
|
|
||||||
#undef OVERLONG_JUMP_MORE
|
|
||||||
#undef CLOSURE_MORE
|
|
||||||
#undef LOCAL_MORE
|
|
||||||
#undef EXPAND_ARGS_MORE
|
|
||||||
#undef FORMAT_VALUE_MORE
|
|
||||||
|
|
||||||
KRK_Function(ip_to_expression) {
|
|
||||||
KrkValue func;
|
|
||||||
size_t ip;
|
|
||||||
|
|
||||||
if (!krk_parseArgs("VN",(const char*[]){"func","ip"}, &func, &ip)) return NONE_VAL();
|
|
||||||
|
|
||||||
KrkCodeObject * actual;
|
|
||||||
|
|
||||||
if (IS_CLOSURE(func)) actual = AS_CLOSURE(func)->function;
|
|
||||||
else if (IS_codeobject(func)) actual = AS_codeobject(func);
|
|
||||||
else if (IS_BOUND_METHOD(func) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(func)->method))) actual = ((KrkClosure*)AS_BOUND_METHOD(func)->method)->function;
|
|
||||||
else return krk_runtimeError(vm.exceptions->typeError, "func must be a managed function, method, or codeobject, not '%T'", func);
|
|
||||||
|
|
||||||
int lineNo = krk_lineNumber(&actual->chunk, ip);
|
|
||||||
uint8_t start, midStart, midEnd, end;
|
|
||||||
|
|
||||||
if (krk_debug_expressionUnderline(actual, &start, &midStart, &midEnd, &end, ip)) {
|
|
||||||
KrkTuple * out = krk_newTuple(5);
|
|
||||||
krk_push(OBJECT_VAL(out));
|
|
||||||
|
|
||||||
out->values.values[out->values.count++] = INTEGER_VAL(lineNo);
|
|
||||||
out->values.values[out->values.count++] = INTEGER_VAL(start);
|
|
||||||
out->values.values[out->values.count++] = INTEGER_VAL(midStart);
|
|
||||||
out->values.values[out->values.count++] = INTEGER_VAL(midEnd);
|
|
||||||
out->values.values[out->values.count++] = INTEGER_VAL(end);
|
|
||||||
|
|
||||||
return krk_pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
KRK_Module(dis) {
|
|
||||||
#ifndef KRK_DISABLE_DEBUG
|
|
||||||
KRK_DOC(module,
|
|
||||||
"@brief Provides tools for disassembling bytecode.\n\n"
|
|
||||||
"### Code Disassembly in Kuroko\n\n"
|
|
||||||
"The @c dis module contains functions for dealing with _code objects_ which "
|
|
||||||
"represent the compiled bytecode of a Kuroko function. The bytecode compilation "
|
|
||||||
"process is entirely static and bytecode analysis can be performed without calling "
|
|
||||||
"into the VM to run dynamic code.\n\n"
|
|
||||||
"### Debugger Breakpoints\n\n"
|
|
||||||
"Kuroko interpreters can provide a debugger hook through the C API's "
|
|
||||||
"@ref krk_debug_registerCallback() function. Breakpoints can be managed both "
|
|
||||||
"from the C API and from this module's @ref addbreakpoint, @ref delbreakpoint, "
|
|
||||||
"@ref enablebreakpoint, and @ref disablebreakpoint methods."
|
|
||||||
);
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, dis),
|
|
||||||
"@brief Disassemble an object.\n"
|
|
||||||
"@arguments obj\n\n"
|
|
||||||
"Dumps a disassembly of the bytecode in the code object associated with @p obj. "
|
|
||||||
"If @p obj can not be disassembled, a @ref TypeError is raised.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, build),
|
|
||||||
"@brief Compile a string to a code object.\n"
|
|
||||||
"@arguments code\n\n"
|
|
||||||
"Compiles the string @p code and returns a code object. If a syntax "
|
|
||||||
"error is encountered, it will be raised.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, examine),
|
|
||||||
"@brief Convert a code object to a list of instructions.\n"
|
|
||||||
"@arguments func\n\n"
|
|
||||||
"Examines the code object @p func and returns a list representation of its instructions. "
|
|
||||||
"Each instruction entry is a tuple of the opcode, total instruction size in bytes, and "
|
|
||||||
"the operand of the argument, either as an integer for jump offsets, the actual value for "
|
|
||||||
"constant operands, or the name of a local or global variable if available.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, addbreakpoint),
|
|
||||||
"@brief Attach a breakpoint to a code object.\n"
|
|
||||||
"@arguments func, line\n\n"
|
|
||||||
"@p func may be a filename string, or a function, method, or code object. Returns "
|
|
||||||
"the new breakpoint index, or raises @ref Exception if a breakpoint code not be added.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, delbreakpoint),
|
|
||||||
"@brief Delete a breakpoint.\n"
|
|
||||||
"@arguments handle\n\n"
|
|
||||||
"Delete the breakpoint specified by @p handle, disabling it if it was enabled. "
|
|
||||||
"May raise @ref IndexError if @p handle is not a valid breakpoint handle.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, enablebreakpoint),
|
|
||||||
"@brief Enable a breakpoint.\n"
|
|
||||||
"@arguments handle\n\n"
|
|
||||||
"Enable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
|
||||||
"@p handle is not a valid breakpoint handle.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, disablebreakpoint),
|
|
||||||
"@brief Disable a breakpoint.\n"
|
|
||||||
"@arguments handle\n\n"
|
|
||||||
"Disable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
|
||||||
"@p handle is not a valid breakpoint handle.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module, ip_to_expression),
|
|
||||||
"@brief Map an IP in a codeobject or function to an expression span.\n"
|
|
||||||
"@arguments func,ip\n\n"
|
|
||||||
"For various reasons, the instruction pointer @p ip must be the last byte of an opcode.");
|
|
||||||
|
|
||||||
krk_attachNamedValue(&module->fields, "BREAKPOINT_ONCE", INTEGER_VAL(KRK_BREAKPOINT_ONCE));
|
|
||||||
krk_attachNamedValue(&module->fields, "BREAKPOINT_REPEAT", INTEGER_VAL(KRK_BREAKPOINT_REPEAT));
|
|
||||||
|
|
||||||
#define OPCODE(opc) krk_attachNamedValue(&module->fields, #opc, INTEGER_VAL(opc));
|
|
||||||
#define SIMPLE(opc) OPCODE(opc)
|
|
||||||
#define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
|
||||||
#define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
|
||||||
#define JUMP(opc,sign) OPCODE(opc)
|
|
||||||
#define COMPLICATED(opc,more) OPCODE(opc)
|
|
||||||
#include "opcodes.h"
|
|
||||||
#undef SIMPLE
|
|
||||||
#undef OPERANDB
|
|
||||||
#undef OPERAND
|
|
||||||
#undef CONSTANT
|
|
||||||
#undef JUMP
|
|
||||||
#undef COMPLICATED
|
|
||||||
|
|
||||||
if (runAs && !strcmp(runAs->chars,"__main__")) {
|
|
||||||
/* Force `dis` into the module table early */
|
|
||||||
krk_attachNamedObject(&vm.modules, "dis", (KrkObj*)module);
|
|
||||||
/* Start executing additional code */
|
|
||||||
krk_startModule("_dis");
|
|
||||||
krk_interpret(
|
|
||||||
"import dis\n"
|
|
||||||
"def disrec(code, seen):\n"
|
|
||||||
" let next = [code]\n"
|
|
||||||
" while next:\n"
|
|
||||||
" let co = next[0]\n"
|
|
||||||
" next = next[1:]\n"
|
|
||||||
" dis.dis(co)\n"
|
|
||||||
" for inst,size,operand in dis.examine(co):\n"
|
|
||||||
" if isinstance(operand,codeobject) and operand not in seen and operand not in next:\n"
|
|
||||||
" next.append(operand)\n"
|
|
||||||
" if next:\n"
|
|
||||||
" print()\n"
|
|
||||||
"import kuroko\n"
|
|
||||||
"if (len(kuroko.argv) < 2):\n"
|
|
||||||
" print(\"Usage: kuroko -m dis FILE\")\n"
|
|
||||||
" return 1\n"
|
|
||||||
"import fileio\n"
|
|
||||||
"for file in kuroko.argv[1:]:\n"
|
|
||||||
" with fileio.open(file,'r') as f:\n"
|
|
||||||
" let result = dis.build(f.read(), file)\n"
|
|
||||||
" disrec(result,set())\n",
|
|
||||||
"_dis"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
krk_runtimeError(vm.exceptions->notImplementedError, "debugger support is disabled");
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
#include <kuroko/vm.h>
|
|
||||||
#include <kuroko/util.h>
|
|
||||||
|
|
||||||
KRK_Function(collect) {
|
|
||||||
FUNCTION_TAKES_NONE();
|
|
||||||
if (&krk_currentThread != vm.threads) return krk_runtimeError(vm.exceptions->valueError, "only the main thread can do that");
|
|
||||||
return INTEGER_VAL(krk_collectGarbage());
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(pause) {
|
|
||||||
FUNCTION_TAKES_NONE();
|
|
||||||
vm.globalFlags |= (KRK_GLOBAL_GC_PAUSED);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(resume) {
|
|
||||||
FUNCTION_TAKES_NONE();
|
|
||||||
vm.globalFlags &= ~(KRK_GLOBAL_GC_PAUSED);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Module(gc) {
|
|
||||||
KRK_DOC(module, "@brief Namespace containing methods for controlling the garbage collector.");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module,collect),
|
|
||||||
"@brief Triggers one cycle of garbage collection.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,pause),
|
|
||||||
"@brief Disables automatic garbage collection until @ref resume is called.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,resume),
|
|
||||||
"@brief Re-enable automatic garbage collection after it was stopped by @ref pause ");
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
#include <limits.h>
|
|
||||||
#include <locale.h>
|
|
||||||
|
|
||||||
#include <kuroko/vm.h>
|
|
||||||
#include <kuroko/util.h>
|
|
||||||
|
|
||||||
KRK_Function(setlocale) {
|
|
||||||
int category;
|
|
||||||
const char * locale = NULL;
|
|
||||||
if (!krk_parseArgs("i|z",(const char*[]){"category","locale"}, &category, &locale)) return NONE_VAL();
|
|
||||||
char * result = setlocale(category, locale);
|
|
||||||
if (!result) {
|
|
||||||
return krk_runtimeError(vm.exceptions->valueError, "unsupported locale setting or query failed");
|
|
||||||
}
|
|
||||||
return OBJECT_VAL(krk_copyString(result,strlen(result)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_grouping(KrkValue result, const char * keyname, const char * grouping) {
|
|
||||||
KrkValue out = krk_list_of(0,NULL,0);
|
|
||||||
krk_push(out);
|
|
||||||
|
|
||||||
const char * c = grouping;
|
|
||||||
|
|
||||||
/* If there is nothing here, return an empty list, otherwise return all
|
|
||||||
* entries including either a terminating NUL or a terminating CHAR_MAX */
|
|
||||||
if (*c) {
|
|
||||||
do {
|
|
||||||
krk_writeValueArray(AS_LIST(out), INTEGER_VAL(*c));
|
|
||||||
if (!*c || *c == CHAR_MAX) break;
|
|
||||||
c++;
|
|
||||||
} while (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
krk_attachNamedValue(AS_DICT(result), keyname, out);
|
|
||||||
krk_pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(localeconv) {
|
|
||||||
FUNCTION_TAKES_NONE();
|
|
||||||
|
|
||||||
struct lconv * lc = localeconv();
|
|
||||||
|
|
||||||
/* localeconv is defined to never fail... */
|
|
||||||
|
|
||||||
KrkValue result = krk_dict_of(0,NULL,0);
|
|
||||||
krk_push(result);
|
|
||||||
|
|
||||||
#define DO_DICT_STR(key) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)krk_copyString(lc-> key, strlen(lc-> key)))
|
|
||||||
#define DO_DICT_INT(key) krk_attachNamedValue(AS_DICT(result), #key, INTEGER_VAL(lc-> key))
|
|
||||||
|
|
||||||
DO_DICT_STR(decimal_point);
|
|
||||||
DO_DICT_STR(thousands_sep);
|
|
||||||
DO_DICT_STR(int_curr_symbol);
|
|
||||||
DO_DICT_STR(currency_symbol);
|
|
||||||
DO_DICT_STR(mon_decimal_point);
|
|
||||||
DO_DICT_STR(mon_thousands_sep);
|
|
||||||
DO_DICT_STR(positive_sign);
|
|
||||||
DO_DICT_STR(negative_sign);
|
|
||||||
|
|
||||||
DO_DICT_INT(int_frac_digits);
|
|
||||||
DO_DICT_INT(frac_digits);
|
|
||||||
DO_DICT_INT(p_cs_precedes);
|
|
||||||
DO_DICT_INT(p_sep_by_space);
|
|
||||||
DO_DICT_INT(n_cs_precedes);
|
|
||||||
DO_DICT_INT(n_sep_by_space);
|
|
||||||
DO_DICT_INT(p_sign_posn);
|
|
||||||
DO_DICT_INT(n_sign_posn);
|
|
||||||
|
|
||||||
/* 'grouping' and 'mon_grouping' aren't real strings */
|
|
||||||
do_grouping(result, "grouping", lc->grouping);
|
|
||||||
do_grouping(result, "mon_grouping", lc->mon_grouping);
|
|
||||||
|
|
||||||
#undef DO_DICT_STR
|
|
||||||
#undef DO_DICT_INT
|
|
||||||
|
|
||||||
return krk_pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Module(locale) {
|
|
||||||
KRK_DOC(module, "@brief Bindings for C locale functions");
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module,setlocale),
|
|
||||||
"@brief Set or query the C locale\n"
|
|
||||||
"@arguments category,locale=None\n\n"
|
|
||||||
"Set the locale used by various C functions.");
|
|
||||||
BIND_FUNC(module,localeconv);
|
|
||||||
|
|
||||||
#define DO_INT(name) krk_attachNamedValue(&module->fields, #name, INTEGER_VAL(name))
|
|
||||||
DO_INT(LC_ALL);
|
|
||||||
DO_INT(LC_COLLATE);
|
|
||||||
DO_INT(LC_CTYPE);
|
|
||||||
DO_INT(LC_MONETARY);
|
|
||||||
DO_INT(LC_NUMERIC);
|
|
||||||
DO_INT(LC_TIME);
|
|
||||||
/* LC_MESSAGES ? */
|
|
||||||
DO_INT(CHAR_MAX); /* Needed to understand grouping */
|
|
||||||
#undef DO_INT
|
|
||||||
|
|
||||||
}
|
|
@ -148,7 +148,10 @@ MATH_IS(isnan)
|
|||||||
|
|
||||||
#define bind(name) krk_defineNative(&module->fields, #name, _math_ ## name)
|
#define bind(name) krk_defineNative(&module->fields, #name, _math_ ## name)
|
||||||
|
|
||||||
KRK_Module(math) {
|
KrkValue krk_module_onload_math(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_push(OBJECT_VAL(module));
|
||||||
|
|
||||||
KRK_DOC(module, "@brief Provides access to floating-point mathematical functions from the system `libm`.");
|
KRK_DOC(module, "@brief Provides access to floating-point mathematical functions from the system `libm`.");
|
||||||
KRK_DOC(bind(ceil),
|
KRK_DOC(bind(ceil),
|
||||||
"@brief Returns the smallest integer value not less than the input.\n"
|
"@brief Returns the smallest integer value not less than the input.\n"
|
||||||
@ -268,4 +271,7 @@ KRK_Module(math) {
|
|||||||
krk_attachNamedValue(&module->fields, "e", FLOATING_VAL(M_E));
|
krk_attachNamedValue(&module->fields, "e", FLOATING_VAL(M_E));
|
||||||
krk_attachNamedValue(&module->fields, "inf", FLOATING_VAL(INFINITY));
|
krk_attachNamedValue(&module->fields, "inf", FLOATING_VAL(INFINITY));
|
||||||
krk_attachNamedValue(&module->fields, "nan", FLOATING_VAL(NAN));
|
krk_attachNamedValue(&module->fields, "nan", FLOATING_VAL(NAN));
|
||||||
|
|
||||||
|
krk_pop();
|
||||||
|
return OBJECT_VAL(module);
|
||||||
}
|
}
|
||||||
|
@ -58,11 +58,16 @@ KRK_Function(seed) {
|
|||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Module(random) {
|
KrkValue krk_module_onload_random(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_push(OBJECT_VAL(module));
|
||||||
|
|
||||||
KRK_DOC(module, "Functions for generating pseudo-random numbers.");
|
KRK_DOC(module, "Functions for generating pseudo-random numbers.");
|
||||||
|
|
||||||
BIND_FUNC(module, random);
|
BIND_FUNC(module, random);
|
||||||
BIND_FUNC(module, seed);
|
BIND_FUNC(module, seed);
|
||||||
|
|
||||||
FUNC_NAME(krk,seed)(0,NULL,0);
|
FUNC_NAME(krk,seed)(0,NULL,0);
|
||||||
|
|
||||||
|
return krk_pop();
|
||||||
}
|
}
|
||||||
|
@ -513,7 +513,10 @@ KRK_Method(socket,proto) {
|
|||||||
return INTEGER_VAL(self->proto);
|
return INTEGER_VAL(self->proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Module(socket) {
|
KrkValue krk_module_onload_socket(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_push(OBJECT_VAL(module));
|
||||||
|
|
||||||
KRK_DOC(module, "Lightweight wrapper around the standard Berkeley sockets interface.");
|
KRK_DOC(module, "Lightweight wrapper around the standard Berkeley sockets interface.");
|
||||||
|
|
||||||
KrkClass * socket = krk_makeClass(module, &SocketClass, "socket", vm.baseClasses->objectClass);
|
KrkClass * socket = krk_makeClass(module, &SocketClass, "socket", vm.baseClasses->objectClass);
|
||||||
@ -570,6 +573,7 @@ KRK_Module(socket) {
|
|||||||
BIND_PROP(socket,type);
|
BIND_PROP(socket,type);
|
||||||
BIND_PROP(socket,proto);
|
BIND_PROP(socket,proto);
|
||||||
|
|
||||||
|
krk_defineNative(&socket->methods,"__str__", FUNC_NAME(socket,__repr__));
|
||||||
krk_finalizeClass(SocketClass);
|
krk_finalizeClass(SocketClass);
|
||||||
|
|
||||||
BIND_FUNC(module, htons);
|
BIND_FUNC(module, htons);
|
||||||
@ -619,4 +623,6 @@ KRK_Module(socket) {
|
|||||||
krk_makeClass(module, &SocketError, "SocketError", vm.exceptions->baseException);
|
krk_makeClass(module, &SocketError, "SocketError", vm.exceptions->baseException);
|
||||||
KRK_DOC(SocketError, "Raised on faults from socket functions.");
|
KRK_DOC(SocketError, "Raised on faults from socket functions.");
|
||||||
krk_finalizeClass(SocketError);
|
krk_finalizeClass(SocketError);
|
||||||
|
|
||||||
|
return krk_pop();
|
||||||
}
|
}
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
#include <sys/stat.h>
|
|
||||||
#include <kuroko/vm.h>
|
|
||||||
#include <kuroko/util.h>
|
|
||||||
|
|
||||||
KRK_Function(S_ISBLK) {
|
|
||||||
int mode;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
|
||||||
return INTEGER_VAL(S_ISBLK(mode));
|
|
||||||
}
|
|
||||||
KRK_Function(S_ISCHR) {
|
|
||||||
int mode;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
|
||||||
return INTEGER_VAL(S_ISCHR(mode));
|
|
||||||
}
|
|
||||||
KRK_Function(S_ISDIR) {
|
|
||||||
int mode;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
|
||||||
return INTEGER_VAL(S_ISDIR(mode));
|
|
||||||
}
|
|
||||||
KRK_Function(S_ISFIFO) {
|
|
||||||
int mode;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
|
||||||
return INTEGER_VAL(S_ISFIFO(mode));
|
|
||||||
}
|
|
||||||
KRK_Function(S_ISREG) {
|
|
||||||
int mode;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
|
||||||
return INTEGER_VAL(S_ISREG(mode));
|
|
||||||
}
|
|
||||||
#ifndef _WIN32
|
|
||||||
KRK_Function(S_ISLNK) {
|
|
||||||
int mode;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
|
||||||
return INTEGER_VAL(S_ISLNK(mode));
|
|
||||||
}
|
|
||||||
KRK_Function(S_ISSOCK) {
|
|
||||||
int mode;
|
|
||||||
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
|
||||||
return INTEGER_VAL(S_ISSOCK(mode));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
KRK_Module(stat) {
|
|
||||||
KRK_DOC(module, "@brief Functions to check results from @ref stat calls.");
|
|
||||||
|
|
||||||
BIND_FUNC(module,S_ISBLK);
|
|
||||||
BIND_FUNC(module,S_ISCHR);
|
|
||||||
BIND_FUNC(module,S_ISDIR);
|
|
||||||
BIND_FUNC(module,S_ISFIFO);
|
|
||||||
BIND_FUNC(module,S_ISREG);
|
|
||||||
#ifndef _WIN32
|
|
||||||
BIND_FUNC(module,S_ISLNK);
|
|
||||||
BIND_FUNC(module,S_ISSOCK);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,276 +0,0 @@
|
|||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include <kuroko/vm.h>
|
|
||||||
#include <kuroko/value.h>
|
|
||||||
#include <kuroko/object.h>
|
|
||||||
#include <kuroko/util.h>
|
|
||||||
|
|
||||||
KRK_Function(sleep) {
|
|
||||||
FUNCTION_TAKES_EXACTLY(1);
|
|
||||||
|
|
||||||
if (!IS_INTEGER(argv[0]) && !IS_FLOATING(argv[0])) {
|
|
||||||
return TYPE_ERROR(int or float,argv[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int usecs = (IS_INTEGER(argv[0]) ? AS_INTEGER(argv[0]) :
|
|
||||||
(IS_FLOATING(argv[0]) ? AS_FLOATING(argv[0]) : 0)) *
|
|
||||||
1000000;
|
|
||||||
|
|
||||||
usleep(usecs);
|
|
||||||
|
|
||||||
return BOOLEAN_VAL(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(time) {
|
|
||||||
FUNCTION_TAKES_NONE();
|
|
||||||
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv,NULL);
|
|
||||||
|
|
||||||
double out = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
|
||||||
|
|
||||||
return FLOATING_VAL(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static KrkClass * struct_time;
|
|
||||||
|
|
||||||
struct struct_time_obj {
|
|
||||||
KrkInstance inst;
|
|
||||||
struct tm _value;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define IS_struct_time(o) (krk_isInstanceOf(o,struct_time))
|
|
||||||
#define AS_struct_time(o) ((struct struct_time_obj*)AS_OBJECT(o))
|
|
||||||
#define CURRENT_CTYPE struct struct_time_obj *
|
|
||||||
#define CURRENT_NAME self
|
|
||||||
|
|
||||||
KRK_Method(struct_time,__init__) {
|
|
||||||
KrkValue seq;
|
|
||||||
if (!krk_parseArgs(".V:struct_time", (const char *[]){"iterable"}, &seq)) return NONE_VAL();
|
|
||||||
if (!IS_TUPLE(seq) || AS_TUPLE(seq)->values.count != 9) return krk_runtimeError(vm.exceptions->notImplementedError, "sequence other than 9-tuple unsupported");
|
|
||||||
for (int i = 0; i < 9; ++i) {
|
|
||||||
if (!IS_INTEGER(AS_TUPLE(seq)->values.values[i])) return krk_runtimeError(vm.exceptions->valueError, "expected int, not %T", AS_TUPLE(seq)->values.values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self->_value.tm_year = AS_INTEGER(AS_TUPLE(seq)->values.values[0]) - 1900;
|
|
||||||
self->_value.tm_mon = AS_INTEGER(AS_TUPLE(seq)->values.values[1]) - 1;
|
|
||||||
self->_value.tm_mday = AS_INTEGER(AS_TUPLE(seq)->values.values[2]);
|
|
||||||
self->_value.tm_hour = AS_INTEGER(AS_TUPLE(seq)->values.values[3]);
|
|
||||||
self->_value.tm_min = AS_INTEGER(AS_TUPLE(seq)->values.values[4]);
|
|
||||||
self->_value.tm_sec = AS_INTEGER(AS_TUPLE(seq)->values.values[5]);
|
|
||||||
self->_value.tm_wday = (AS_INTEGER(AS_TUPLE(seq)->values.values[6])+6)%7;
|
|
||||||
self->_value.tm_yday = AS_INTEGER(AS_TUPLE(seq)->values.values[7]) - 1;
|
|
||||||
self->_value.tm_isdst = AS_INTEGER(AS_TUPLE(seq)->values.values[8]);
|
|
||||||
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(struct_time,tm_year) { return INTEGER_VAL(self->_value.tm_year + 1900); } /* struct tm is 1900-indexed, snakes are not */
|
|
||||||
KRK_Method(struct_time,tm_mon) { return INTEGER_VAL(self->_value.tm_mon + 1); } /* struct tm is 0-indexed, snakes are not */
|
|
||||||
KRK_Method(struct_time,tm_mday) { return INTEGER_VAL(self->_value.tm_mday); }
|
|
||||||
KRK_Method(struct_time,tm_hour) { return INTEGER_VAL(self->_value.tm_hour); }
|
|
||||||
KRK_Method(struct_time,tm_min) { return INTEGER_VAL(self->_value.tm_min); }
|
|
||||||
KRK_Method(struct_time,tm_sec) { return INTEGER_VAL(self->_value.tm_sec); }
|
|
||||||
KRK_Method(struct_time,tm_wday) { return INTEGER_VAL((self->_value.tm_wday+1)%7); } /* struct tm has Sunday = 0, but snakes use Monday = 0 */
|
|
||||||
KRK_Method(struct_time,tm_yday) { return INTEGER_VAL(self->_value.tm_yday+1); } /* struct tm is from 0, but snakes start from 1 */
|
|
||||||
KRK_Method(struct_time,tm_isdst) { return INTEGER_VAL(self->_value.tm_isdst); }
|
|
||||||
|
|
||||||
KRK_Method(struct_time,__repr__) {
|
|
||||||
return krk_stringFromFormat(
|
|
||||||
"time.struct_time(tm_year=%d, tm_mon=%d, tm_mday=%d, tm_hour=%d, tm_min=%d, "
|
|
||||||
"tm_sec=%d, tm_wday=%d, tm_yday=%d, tm_isdst=%d)",
|
|
||||||
self->_value.tm_year + 1900,
|
|
||||||
self->_value.tm_mon + 1,
|
|
||||||
self->_value.tm_mday,
|
|
||||||
self->_value.tm_hour,
|
|
||||||
self->_value.tm_min,
|
|
||||||
self->_value.tm_sec,
|
|
||||||
(self->_value.tm_wday + 1) % 7,
|
|
||||||
self->_value.tm_yday + 1,
|
|
||||||
self->_value.tm_isdst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_t time_or_now(int has_arg, long long secs) {
|
|
||||||
if (!has_arg) {
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv,NULL);
|
|
||||||
return (time_t)tv.tv_sec;
|
|
||||||
} else {
|
|
||||||
return (time_t)secs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tm_or_now(const struct struct_time_obj * t, struct tm * _time) {
|
|
||||||
if (t) {
|
|
||||||
memcpy(_time,&t->_value,sizeof(struct tm));
|
|
||||||
} else {
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv,NULL);
|
|
||||||
time_t time = tv.tv_sec;
|
|
||||||
localtime_r(&time,_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(localtime) {
|
|
||||||
int gave_seconds;
|
|
||||||
long long seconds;
|
|
||||||
if (!krk_parseArgs("|L?",(const char*[]){"seconds"},&gave_seconds, &seconds)) return NONE_VAL();
|
|
||||||
time_t time = time_or_now(gave_seconds, seconds);
|
|
||||||
|
|
||||||
/* Create a struct_time to store result in */
|
|
||||||
CURRENT_CTYPE out = (CURRENT_CTYPE)krk_newInstance(struct_time);
|
|
||||||
krk_push(OBJECT_VAL(out));
|
|
||||||
|
|
||||||
if (!localtime_r(&time, &out->_value)) return krk_runtimeError(vm.exceptions->valueError, "?");
|
|
||||||
|
|
||||||
return krk_pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static KrkValue krk_asctime(const struct tm *_time) {
|
|
||||||
/* asctime is normally locale-aware, but the snake function is not, so we do this manually */
|
|
||||||
static const char * monNames[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
|
|
||||||
static const char * dayNames[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
|
||||||
|
|
||||||
char buf[40] = {0};
|
|
||||||
|
|
||||||
/* The normal strftime string for this is %a %b %d %T %Y
|
|
||||||
* Day Mon DD HH:MM:SS YYYY */
|
|
||||||
snprintf(buf,39, "%s %s%3d %.2d:%.2d:%.2d %d",
|
|
||||||
dayNames[_time->tm_wday % 7],
|
|
||||||
monNames[_time->tm_mon % 12],
|
|
||||||
_time->tm_mday,
|
|
||||||
_time->tm_hour,
|
|
||||||
_time->tm_min,
|
|
||||||
_time->tm_sec,
|
|
||||||
_time->tm_year + 1900);
|
|
||||||
|
|
||||||
return OBJECT_VAL(krk_copyString(buf,strlen(buf)));
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(asctime) {
|
|
||||||
struct struct_time_obj * t = NULL;
|
|
||||||
if (!krk_parseArgs("|O!",(const char*[]){"t"},struct_time,&t)) return NONE_VAL();
|
|
||||||
struct tm _time;
|
|
||||||
tm_or_now(t,&_time);
|
|
||||||
return krk_asctime(&_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(ctime) {
|
|
||||||
int has_arg;
|
|
||||||
long long secs;
|
|
||||||
if (!krk_parseArgs("|L?",(const char*[]){"secs"},&has_arg,&secs)) return NONE_VAL();
|
|
||||||
time_t time = time_or_now(has_arg, secs);
|
|
||||||
struct tm _time;
|
|
||||||
if (!localtime_r(&time, &_time)) return krk_runtimeError(vm.exceptions->valueError, "?");
|
|
||||||
|
|
||||||
return krk_asctime(&_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(gmtime) {
|
|
||||||
int gave_seconds;
|
|
||||||
long long seconds;
|
|
||||||
if (!krk_parseArgs("|L?",(const char*[]){"secs"},&gave_seconds, &seconds)) return NONE_VAL();
|
|
||||||
time_t time = time_or_now(gave_seconds, seconds);
|
|
||||||
|
|
||||||
/* Create a struct_time to store result in */
|
|
||||||
CURRENT_CTYPE out = (CURRENT_CTYPE)krk_newInstance(struct_time);
|
|
||||||
krk_push(OBJECT_VAL(out));
|
|
||||||
|
|
||||||
if (!gmtime_r(&time, &out->_value)) return krk_runtimeError(vm.exceptions->valueError, "?");
|
|
||||||
|
|
||||||
return krk_pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(mktime) {
|
|
||||||
struct struct_time_obj * t;
|
|
||||||
if (!krk_parseArgs("O!",(const char*[]){"t"},struct_time,&t)) return NONE_VAL();
|
|
||||||
|
|
||||||
struct tm _time;
|
|
||||||
memcpy(&_time,&t->_value,sizeof(struct tm));
|
|
||||||
_time.tm_wday = -1;
|
|
||||||
time_t out = mktime(&_time);
|
|
||||||
if (out == -1 && _time.tm_wday == -1) return krk_runtimeError(vm.exceptions->valueError, "invalid argument to mktime");
|
|
||||||
return FLOATING_VAL(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(strftime) {
|
|
||||||
const char * format;
|
|
||||||
struct struct_time_obj * t = NULL;
|
|
||||||
if (!krk_parseArgs("s|O!",(const char*[]){"format","t"},&format,struct_time,&t)) return NONE_VAL();
|
|
||||||
struct tm _time;
|
|
||||||
tm_or_now(t,&_time);
|
|
||||||
|
|
||||||
/* strftime wants a buffer size, but we have no way of knowing. Following
|
|
||||||
* what CPython does, start from 1024 and try doubling until we reach
|
|
||||||
* the length of our format string * 256, and then give up. */
|
|
||||||
size_t fmt_len = strlen(format);
|
|
||||||
size_t size = 1024;
|
|
||||||
while (1) {
|
|
||||||
char * buf = malloc(size);
|
|
||||||
size_t ret = strftime(buf,size,format,&_time);
|
|
||||||
if (ret || size > fmt_len * 256) {
|
|
||||||
krk_push(OBJECT_VAL(krk_copyString(buf,ret)));
|
|
||||||
free(buf);
|
|
||||||
return krk_pop();
|
|
||||||
}
|
|
||||||
size *= 2;
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Module(time) {
|
|
||||||
KRK_DOC(module, "@brief Provides timekeeping functions.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,sleep), "@brief Pause execution of the current thread.\n"
|
|
||||||
"@arguments secs\n\n"
|
|
||||||
"Uses the system @c usleep() function to sleep for @p secs seconds, which may be a @ref float or @ref int. "
|
|
||||||
"The available precision is platform-dependent.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,time), "@brief Return the elapsed seconds since the system epoch.\n\n"
|
|
||||||
"Returns a @ref float representation of the number of seconds since the platform's epoch date. "
|
|
||||||
"On POSIX platforms, this is the number of seconds since 1 January 1970. "
|
|
||||||
"The precision of the return value is platform-dependent.");
|
|
||||||
|
|
||||||
krk_makeClass(module, &struct_time, "struct_time", KRK_BASE_CLASS(object));
|
|
||||||
struct_time->allocSize = sizeof(struct struct_time_obj);
|
|
||||||
KRK_DOC(struct_time, "Time value returned by various functions.");
|
|
||||||
KRK_DOC(BIND_METHOD(struct_time,__init__), "@arguments iterable: tuple\n\n"
|
|
||||||
"Create a @ref struct_time from a 9-tuple of @ref int values.\n"
|
|
||||||
"The format of @p iterable is `(tm_year,tm_mon,tm_mday,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst)`.");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_year), "Calendar year");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_mon), "Month, [1, 12]");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_mday), "Day of the month, [1, 31]");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_hour), "Clock hour, [0, 23]");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_min), "Clock minute, [0, 59]");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_sec), "Clock seconds, [0, 61] (maybe, due to leap seconds, depends on platform)");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_wday), "Day of week, [0, 6], 0 is Monday.");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_yday), "Day of year [1, 366]");
|
|
||||||
KRK_DOC(BIND_PROP(struct_time,tm_isdst), "0, 1, -1 for unknown");
|
|
||||||
BIND_METHOD(struct_time,__repr__);
|
|
||||||
krk_finalizeClass(struct_time);
|
|
||||||
|
|
||||||
KRK_DOC(BIND_FUNC(module,localtime), "@brief Convert seconds since epoch to local time.\n"
|
|
||||||
"@arguments seconds=time.time()\n\n"
|
|
||||||
"If @p seconds is not provided, the current @ref time is used.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,asctime), "@brief Convert time to string.\n"
|
|
||||||
"@arguments t=time.localtime()\n\n"
|
|
||||||
"If @p t is not provided, the current @ref localtime is used.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,ctime), "@brief Convert seconds since epoch to string.\n"
|
|
||||||
"@arguments secs=time.time()\n\n"
|
|
||||||
"If @p secs is not provided, the current @ref time is used.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,gmtime), "@brief Convert seconds since epoch to UTC time.\n"
|
|
||||||
"@arguments secs=time.time()\n\n"
|
|
||||||
"If @p secs is not provided, the current @ref time is used.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,mktime), "@brief Convert from local time to seconds since epoch.\n"
|
|
||||||
"@arguments t\n\n"
|
|
||||||
"For compatibility with @ref time a @ref float is returned.");
|
|
||||||
KRK_DOC(BIND_FUNC(module,strftime), "@brief Format time string with system function.\n"
|
|
||||||
"@arguments format,t=time.localtime()\n\n"
|
|
||||||
"Uses the system `strftime` C function to convert a @ref struct_time to a string.\n"
|
|
||||||
"If @p t is not provided, the current @ref localtime is used.");
|
|
||||||
}
|
|
||||||
|
|
@ -35,8 +35,14 @@ KRK_Function(timeit) {
|
|||||||
return FLOATING_VAL(after-before);
|
return FLOATING_VAL(after-before);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Module(timeit) {
|
KrkValue krk_module_onload_timeit(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_push(OBJECT_VAL(module));
|
||||||
|
|
||||||
KRK_DOC(module, "@brief Run functions very quickly without loop overhead from the interpreter.");
|
KRK_DOC(module, "@brief Run functions very quickly without loop overhead from the interpreter.");
|
||||||
|
|
||||||
BIND_FUNC(module,timeit);
|
BIND_FUNC(module,timeit);
|
||||||
|
|
||||||
|
krk_pop();
|
||||||
|
return OBJECT_VAL(module);
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,16 @@ KRK_Function(wcwidth) {
|
|||||||
return INTEGER_VAL(wcwidth(codepoint));
|
return INTEGER_VAL(wcwidth(codepoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Module(wcwidth) {
|
KrkValue krk_module_onload_wcwidth(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_push(OBJECT_VAL(module));
|
||||||
KRK_DOC(module, "Character widths.");
|
KRK_DOC(module, "Character widths.");
|
||||||
BIND_FUNC(module, wcwidth);
|
BIND_FUNC(module, wcwidth);
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return krk_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ KRK_Method(type,__file__) {
|
|||||||
return self->filename ? OBJECT_VAL(self->filename) : NONE_VAL();
|
return self->filename ? OBJECT_VAL(self->filename) : NONE_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(type,__repr__) {
|
KRK_Method(type,__str__) {
|
||||||
/* Determine if this class has a module */
|
/* Determine if this class has a module */
|
||||||
KrkValue module = NONE_VAL();
|
KrkValue module = NONE_VAL();
|
||||||
krk_tableGet(&self->methods, OBJECT_VAL(S("__module__")), &module);
|
krk_tableGet(&self->methods, OBJECT_VAL(S("__module__")), &module);
|
||||||
@ -185,42 +185,31 @@ KRK_Method(type,__call__) {
|
|||||||
return krk_runtimeError(vm.exceptions->typeError, "%S() can not be built", self->name);
|
return krk_runtimeError(vm.exceptions->typeError, "%S() can not be built", self->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
KrkValue result;
|
/* Push args */
|
||||||
if (self->_new->type == KRK_OBJ_NATIVE) {
|
int argCount = argc;
|
||||||
/* Fast call to native __new__ function. */
|
for (int i = 0; i < argc; ++i) {
|
||||||
result = ((KrkNative*)self->_new)->function(argc,argv,hasKw);
|
krk_push(argv[i]);
|
||||||
} else {
|
|
||||||
/* Slow call: Put arguments back on the stack with kwargs call format */
|
|
||||||
int argCount = argc;
|
|
||||||
for (int i = 0; i < argc; ++i) {
|
|
||||||
krk_push(argv[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasKw) {
|
|
||||||
argCount += 3;
|
|
||||||
krk_push(KWARGS_VAL(KWARGS_DICT));
|
|
||||||
krk_push(argv[argc]);
|
|
||||||
krk_push(KWARGS_VAL(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = krk_callDirect(self->_new, argCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If an exception happened in __new__, don't try to call __init__ even if the conditions would be right. */
|
if (hasKw) {
|
||||||
|
argCount += 3;
|
||||||
|
krk_push(KWARGS_VAL(KWARGS_DICT));
|
||||||
|
krk_push(argv[argc]);
|
||||||
|
krk_push(KWARGS_VAL(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
krk_push(krk_callDirect(self->_new, argCount));
|
||||||
|
|
||||||
|
/* Exception here */
|
||||||
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return NONE_VAL();
|
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return NONE_VAL();
|
||||||
|
|
||||||
if (self->_init != KRK_BASE_CLASS(object)->_init && self->_init != NULL && krk_isInstanceOf(result, self)) {
|
if (krk_isInstanceOf(krk_peek(0), self) && likely(self->_init != NULL)) {
|
||||||
/* Because we have to swap the class for the instance here, we can't do a fast call even
|
krk_push(krk_peek(0));
|
||||||
* if __init__ is a native function, so we're stuck with the slow approach... */
|
|
||||||
krk_push(result); /* once for safe keeping */
|
|
||||||
krk_push(result); /* once as an argument */
|
|
||||||
int argCount = argc;
|
|
||||||
for (int i = 0; i < argc - 1; ++i) {
|
for (int i = 0; i < argc - 1; ++i) {
|
||||||
krk_push(argv[i+1]);
|
krk_push(argv[i+1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasKw) {
|
if (hasKw) {
|
||||||
argCount += 3;
|
|
||||||
krk_push(KWARGS_VAL(KWARGS_DICT));
|
krk_push(KWARGS_VAL(KWARGS_DICT));
|
||||||
krk_push(argv[argc]);
|
krk_push(argv[argc]);
|
||||||
krk_push(KWARGS_VAL(1));
|
krk_push(KWARGS_VAL(1));
|
||||||
@ -231,10 +220,9 @@ KRK_Method(type,__call__) {
|
|||||||
fprintf(stderr, "Warning: Non-None result returned from %s.__init__\n",
|
fprintf(stderr, "Warning: Non-None result returned from %s.__init__\n",
|
||||||
self->name->chars);
|
self->name->chars);
|
||||||
}
|
}
|
||||||
return krk_pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return krk_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
_noexport
|
_noexport
|
||||||
@ -246,11 +234,12 @@ void _createAndBind_type(void) {
|
|||||||
BIND_PROP(type,__file__);
|
BIND_PROP(type,__file__);
|
||||||
BIND_PROP(type,__name__);
|
BIND_PROP(type,__name__);
|
||||||
|
|
||||||
BIND_METHOD(type,__repr__);
|
BIND_METHOD(type,__str__);
|
||||||
BIND_METHOD(type,__subclasses__);
|
BIND_METHOD(type,__subclasses__);
|
||||||
BIND_METHOD(type,__getitem__);
|
BIND_METHOD(type,__getitem__);
|
||||||
BIND_METHOD(type,__call__);
|
BIND_METHOD(type,__call__);
|
||||||
BIND_STATICMETHOD(type,__new__);
|
BIND_STATICMETHOD(type,__new__);
|
||||||
|
krk_defineNative(&type->methods,"__repr__",FUNC_NAME(type,__str__));
|
||||||
|
|
||||||
krk_finalizeClass(type);
|
krk_finalizeClass(type);
|
||||||
KRK_DOC(type, "Obtain the object representation of the class of an object.");
|
KRK_DOC(type, "Obtain the object representation of the class of an object.");
|
||||||
|
@ -304,7 +304,6 @@ KRK_Method(bytearray,__init__) {
|
|||||||
self->actual = OBJECT_VAL(krk_newBytes(AS_BYTES(argv[1])->length, AS_BYTES(argv[1])->bytes));
|
self->actual = OBJECT_VAL(krk_newBytes(AS_BYTES(argv[1])->length, AS_BYTES(argv[1])->bytes));
|
||||||
} else if (IS_INTEGER(argv[1])) {
|
} else if (IS_INTEGER(argv[1])) {
|
||||||
self->actual = OBJECT_VAL(krk_newBytes(AS_INTEGER(argv[1]),NULL));
|
self->actual = OBJECT_VAL(krk_newBytes(AS_INTEGER(argv[1]),NULL));
|
||||||
memset(AS_BYTES(self->actual)->bytes, 0, AS_BYTES(self->actual)->length);
|
|
||||||
} else {
|
} else {
|
||||||
return krk_runtimeError(vm.exceptions->valueError, "expected bytes");
|
return krk_runtimeError(vm.exceptions->valueError, "expected bytes");
|
||||||
}
|
}
|
||||||
@ -325,10 +324,15 @@ KRK_Method(bytearray,__repr__) {
|
|||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
struct StringBuilder sb = {0};
|
struct StringBuilder sb = {0};
|
||||||
pushStringBuilderStr(&sb, "bytearray(", 10);
|
pushStringBuilderStr(&sb, "bytearray(", 10);
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", self->actual)) {
|
|
||||||
krk_discardStringBuilder(&sb);
|
krk_push(self->actual);
|
||||||
|
KrkValue repred_bytes = krk_callDirect(vm.baseClasses->bytesClass->_reprer, 1);
|
||||||
|
if (!IS_STRING(repred_bytes)) {
|
||||||
|
/* Invalid repr of bytes? */
|
||||||
|
discardStringBuilder(&sb);
|
||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
}
|
}
|
||||||
|
pushStringBuilderStr(&sb, AS_STRING(repred_bytes)->chars, AS_STRING(repred_bytes)->length);
|
||||||
pushStringBuilder(&sb,')');
|
pushStringBuilder(&sb,')');
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
}
|
}
|
||||||
@ -432,6 +436,7 @@ void _createAndBind_bytesClass(void) {
|
|||||||
BIND_METHOD(bytes,__hash__);
|
BIND_METHOD(bytes,__hash__);
|
||||||
BIND_METHOD(bytes,decode);
|
BIND_METHOD(bytes,decode);
|
||||||
BIND_METHOD(bytes,join);
|
BIND_METHOD(bytes,join);
|
||||||
|
krk_defineNative(&bytes->methods,"__str__",FUNC_NAME(bytes,__repr__)); /* alias */
|
||||||
krk_finalizeClass(bytes);
|
krk_finalizeClass(bytes);
|
||||||
|
|
||||||
KrkClass * bytesiterator = ADD_BASE_CLASS(vm.baseClasses->bytesiteratorClass, "bytesiterator", vm.baseClasses->objectClass);
|
KrkClass * bytesiterator = ADD_BASE_CLASS(vm.baseClasses->bytesiteratorClass, "bytesiterator", vm.baseClasses->objectClass);
|
||||||
@ -456,5 +461,6 @@ void _createAndBind_bytesClass(void) {
|
|||||||
BIND_METHOD(bytearray,__eq__);
|
BIND_METHOD(bytearray,__eq__);
|
||||||
BIND_METHOD(bytearray,__iter__);
|
BIND_METHOD(bytearray,__iter__);
|
||||||
BIND_METHOD(bytearray,decode);
|
BIND_METHOD(bytearray,decode);
|
||||||
|
krk_defineNative(&bytearray->methods,"__str__",FUNC_NAME(bytearray,__repr__)); /* alias */
|
||||||
krk_finalizeClass(bytearray);
|
krk_finalizeClass(bytearray);
|
||||||
}
|
}
|
||||||
|
137
src/obj_dict.c
137
src/obj_dict.c
@ -4,14 +4,6 @@
|
|||||||
#include <kuroko/memory.h>
|
#include <kuroko/memory.h>
|
||||||
#include <kuroko/util.h>
|
#include <kuroko/util.h>
|
||||||
|
|
||||||
#if defined(__TINYC__) || (defined(_MSC_VER) && !defined(__clang__))
|
|
||||||
static int __builtin_clz(unsigned int x) {
|
|
||||||
int i = 31;
|
|
||||||
while (!(x & (1 << i)) && i >= 0) i--;
|
|
||||||
return 31-i;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposed method called to produce dictionaries from `{expr: expr, ...}` sequences in managed code.
|
* Exposed method called to produce dictionaries from `{expr: expr, ...}` sequences in managed code.
|
||||||
* Expects arguments as `key,value,key,value`...
|
* Expects arguments as `key,value,key,value`...
|
||||||
@ -21,15 +13,9 @@ KrkValue krk_dict_of(int argc, const KrkValue argv[], int hasKw) {
|
|||||||
KrkInstance * outDict = krk_newInstance(vm.baseClasses->dictClass);
|
KrkInstance * outDict = krk_newInstance(vm.baseClasses->dictClass);
|
||||||
krk_push(OBJECT_VAL(outDict));
|
krk_push(OBJECT_VAL(outDict));
|
||||||
krk_initTable(&((KrkDict*)outDict)->entries);
|
krk_initTable(&((KrkDict*)outDict)->entries);
|
||||||
if (argc) {
|
krk_tableAdjustCapacity(&((KrkDict*)outDict)->entries, argc);
|
||||||
size_t capacity = argc;
|
for (int ind = 0; ind < argc; ind += 2) {
|
||||||
size_t powerOfTwoCapacity = __builtin_clz(1) - __builtin_clz(capacity);
|
krk_tableSet(&((KrkDict*)outDict)->entries, argv[ind], argv[ind+1]);
|
||||||
if ((1UL << powerOfTwoCapacity) != capacity) powerOfTwoCapacity++;
|
|
||||||
capacity = (1UL << powerOfTwoCapacity);
|
|
||||||
krk_tableAdjustCapacity(&((KrkDict*)outDict)->entries, capacity);
|
|
||||||
for (int ind = 0; ind < argc; ind += 2) {
|
|
||||||
krk_tableSet(&((KrkDict*)outDict)->entries, argv[ind], argv[ind+1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return krk_pop();
|
return krk_pop();
|
||||||
}
|
}
|
||||||
@ -167,8 +153,8 @@ KRK_Method(dict,__len__) {
|
|||||||
|
|
||||||
KRK_Method(dict,__contains__) {
|
KRK_Method(dict,__contains__) {
|
||||||
METHOD_TAKES_EXACTLY(1);
|
METHOD_TAKES_EXACTLY(1);
|
||||||
KrkValue v;
|
KrkValue _unused;
|
||||||
return BOOLEAN_VAL(krk_tableGet(&self->entries, argv[1], &v));
|
return BOOLEAN_VAL(krk_tableGet(&self->entries, argv[1], &_unused));
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(dict,capacity) {
|
KRK_Method(dict,capacity) {
|
||||||
@ -188,21 +174,35 @@ KRK_Method(dict,__repr__) {
|
|||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
KrkTableEntry * entry = &self->entries.entries[i];
|
KrkTableEntry * entry = &self->entries.entries[i];
|
||||||
if (IS_KWARGS(entry->key)) continue;
|
if (IS_KWARGS(entry->key)) continue;
|
||||||
if (c) pushStringBuilderStr(&sb, ", ", 2);
|
if (c > 0) {
|
||||||
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
|
}
|
||||||
c++;
|
c++;
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", entry->key)) goto _error;
|
|
||||||
|
{
|
||||||
|
KrkClass * type = krk_getType(entry->key);
|
||||||
|
krk_push(entry->key);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pushStringBuilderStr(&sb, ": ", 2);
|
pushStringBuilderStr(&sb, ": ", 2);
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", entry->value)) goto _error;
|
|
||||||
|
{
|
||||||
|
KrkClass * type = krk_getType(entry->value);
|
||||||
|
krk_push(entry->value);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pushStringBuilder(&sb,'}');
|
pushStringBuilder(&sb,'}');
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(dict,copy) {
|
KRK_Method(dict,copy) {
|
||||||
@ -328,7 +328,7 @@ KRK_Method(dictitems,__iter__) {
|
|||||||
|
|
||||||
KRK_Method(dictitems,__call__) {
|
KRK_Method(dictitems,__call__) {
|
||||||
do {
|
do {
|
||||||
if (self->i >= AS_DICT(self->dict)->used) return argv[0];
|
if (self->i >= AS_DICT(self->dict)->capacity) return argv[0];
|
||||||
if (!IS_KWARGS(AS_DICT(self->dict)->entries[self->i].key)) {
|
if (!IS_KWARGS(AS_DICT(self->dict)->entries[self->i].key)) {
|
||||||
KrkTuple * outValue = krk_newTuple(2);
|
KrkTuple * outValue = krk_newTuple(2);
|
||||||
krk_push(OBJECT_VAL(outValue));
|
krk_push(OBJECT_VAL(outValue));
|
||||||
@ -350,27 +350,43 @@ KRK_Method(dictitems,__repr__) {
|
|||||||
pushStringBuilderStr(&sb,"dictitems([",11);
|
pushStringBuilderStr(&sb,"dictitems([",11);
|
||||||
|
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
size_t len = AS_DICT(self->dict)->used;
|
size_t len = AS_DICT(self->dict)->capacity;
|
||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
KrkTableEntry * entry = &AS_DICT(self->dict)->entries[i];
|
KrkTableEntry * entry = &AS_DICT(self->dict)->entries[i];
|
||||||
if (IS_KWARGS(entry->key)) continue;
|
if (IS_KWARGS(entry->key)) continue;
|
||||||
if (c) pushStringBuilderStr(&sb, ", ", 2);
|
if (c > 0) {
|
||||||
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
|
}
|
||||||
c++;
|
c++;
|
||||||
|
|
||||||
pushStringBuilder(&sb,'(');
|
pushStringBuilder(&sb,'(');
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", entry->key)) goto _error;
|
|
||||||
|
{
|
||||||
|
KrkClass * type = krk_getType(entry->key);
|
||||||
|
krk_push(entry->key);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pushStringBuilderStr(&sb, ", ", 2);
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", entry->value)) goto _error;
|
|
||||||
|
{
|
||||||
|
KrkClass * type = krk_getType(entry->value);
|
||||||
|
krk_push(entry->value);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pushStringBuilder(&sb,')');
|
pushStringBuilder(&sb,')');
|
||||||
}
|
}
|
||||||
|
|
||||||
pushStringBuilderStr(&sb,"])",2);
|
pushStringBuilderStr(&sb,"])",2);
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef CURRENT_CTYPE
|
#undef CURRENT_CTYPE
|
||||||
@ -397,7 +413,7 @@ KRK_Method(dictkeys,__iter__) {
|
|||||||
KRK_Method(dictkeys,__call__) {
|
KRK_Method(dictkeys,__call__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
do {
|
do {
|
||||||
if (self->i >= AS_DICT(self->dict)->used) return argv[0];
|
if (self->i >= AS_DICT(self->dict)->capacity) return argv[0];
|
||||||
if (!IS_KWARGS(AS_DICT(self->dict)->entries[self->i].key)) {
|
if (!IS_KWARGS(AS_DICT(self->dict)->entries[self->i].key)) {
|
||||||
krk_push(AS_DICT(self->dict)->entries[self->i].key);
|
krk_push(AS_DICT(self->dict)->entries[self->i].key);
|
||||||
self->i++;
|
self->i++;
|
||||||
@ -415,23 +431,28 @@ KRK_Method(dictkeys,__repr__) {
|
|||||||
pushStringBuilderStr(&sb,"dictkeys([",10);
|
pushStringBuilderStr(&sb,"dictkeys([",10);
|
||||||
|
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
size_t len = AS_DICT(self->dict)->used;
|
size_t len = AS_DICT(self->dict)->capacity;
|
||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
KrkTableEntry * entry = &AS_DICT(self->dict)->entries[i];
|
KrkTableEntry * entry = &AS_DICT(self->dict)->entries[i];
|
||||||
if (IS_KWARGS(entry->key)) continue;
|
if (IS_KWARGS(entry->key)) continue;
|
||||||
if (c) pushStringBuilderStr(&sb, ", ", 2);
|
if (c > 0) {
|
||||||
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
|
}
|
||||||
c++;
|
c++;
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", entry->key)) goto _error;
|
|
||||||
|
{
|
||||||
|
KrkClass * type = krk_getType(entry->key);
|
||||||
|
krk_push(entry->key);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pushStringBuilderStr(&sb,"])",2);
|
pushStringBuilderStr(&sb,"])",2);
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef CURRENT_CTYPE
|
#undef CURRENT_CTYPE
|
||||||
@ -458,7 +479,7 @@ KRK_Method(dictvalues,__iter__) {
|
|||||||
KRK_Method(dictvalues,__call__) {
|
KRK_Method(dictvalues,__call__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
do {
|
do {
|
||||||
if (self->i >= AS_DICT(self->dict)->used) return argv[0];
|
if (self->i >= AS_DICT(self->dict)->capacity) return argv[0];
|
||||||
if (!IS_KWARGS(AS_DICT(self->dict)->entries[self->i].key)) {
|
if (!IS_KWARGS(AS_DICT(self->dict)->entries[self->i].key)) {
|
||||||
krk_push(AS_DICT(self->dict)->entries[self->i].value);
|
krk_push(AS_DICT(self->dict)->entries[self->i].value);
|
||||||
self->i++;
|
self->i++;
|
||||||
@ -476,23 +497,28 @@ KRK_Method(dictvalues,__repr__) {
|
|||||||
pushStringBuilderStr(&sb,"dictvalues([",12);
|
pushStringBuilderStr(&sb,"dictvalues([",12);
|
||||||
|
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
size_t len = AS_DICT(self->dict)->used;
|
size_t len = AS_DICT(self->dict)->capacity;
|
||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
KrkTableEntry * entry = &AS_DICT(self->dict)->entries[i];
|
KrkTableEntry * entry = &AS_DICT(self->dict)->entries[i];
|
||||||
if (IS_KWARGS(entry->key)) continue;
|
if (IS_KWARGS(entry->key)) continue;
|
||||||
if (c) pushStringBuilderStr(&sb, ", ", 2);
|
if (c > 0) {
|
||||||
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
|
}
|
||||||
c++;
|
c++;
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", entry->value)) goto _error;
|
|
||||||
|
{
|
||||||
|
KrkClass * type = krk_getType(entry->value);
|
||||||
|
krk_push(entry->value);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pushStringBuilderStr(&sb,"])",2);
|
pushStringBuilderStr(&sb,"])",2);
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_noexport
|
_noexport
|
||||||
@ -521,6 +547,7 @@ void _createAndBind_dictClass(void) {
|
|||||||
BIND_METHOD(dict,setdefault);
|
BIND_METHOD(dict,setdefault);
|
||||||
BIND_METHOD(dict,update);
|
BIND_METHOD(dict,update);
|
||||||
krk_defineNative(&dict->methods, "__iter__", FUNC_NAME(dict,keys));
|
krk_defineNative(&dict->methods, "__iter__", FUNC_NAME(dict,keys));
|
||||||
|
krk_defineNative(&dict->methods, "__str__", FUNC_NAME(dict,__repr__));
|
||||||
krk_defineNative(&dict->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
|
krk_defineNative(&dict->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
|
||||||
krk_attachNamedValue(&dict->methods, "__hash__", NONE_VAL());
|
krk_attachNamedValue(&dict->methods, "__hash__", NONE_VAL());
|
||||||
krk_finalizeClass(dict);
|
krk_finalizeClass(dict);
|
||||||
|
@ -137,7 +137,7 @@ KRK_Method(function,_ip_to_line) {
|
|||||||
return INTEGER_VAL(line);
|
return INTEGER_VAL(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(function,__repr__) {
|
KRK_Method(function,__str__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
|
|
||||||
/* Do we have a qualified name? */
|
/* Do we have a qualified name? */
|
||||||
@ -215,7 +215,7 @@ KRK_Method(codeobject,__name__) {
|
|||||||
return self->name ? OBJECT_VAL(self->name) : OBJECT_VAL(S(""));
|
return self->name ? OBJECT_VAL(self->name) : OBJECT_VAL(S(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(codeobject,__repr__) {
|
KRK_Method(codeobject,__str__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
KrkValue s = FUNC_NAME(codeobject,__name__)(1,argv,0);
|
KrkValue s = FUNC_NAME(codeobject,__name__)(1,argv,0);
|
||||||
if (!IS_STRING(s)) return NONE_VAL();
|
if (!IS_STRING(s)) return NONE_VAL();
|
||||||
@ -303,10 +303,6 @@ KRK_Method(codeobject,__args__) {
|
|||||||
return OBJECT_VAL(tuple);
|
return OBJECT_VAL(tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(codeobject,__file__) {
|
|
||||||
ATTRIBUTE_NOT_ASSIGNABLE();
|
|
||||||
return self->chunk.filename ? OBJECT_VAL(self->chunk.filename) : OBJECT_VAL(S(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef CURRENT_CTYPE
|
#undef CURRENT_CTYPE
|
||||||
#define CURRENT_CTYPE KrkBoundMethod*
|
#define CURRENT_CTYPE KrkBoundMethod*
|
||||||
@ -334,7 +330,7 @@ KRK_Method(method,_ip_to_line) {
|
|||||||
return IS_function(OBJECT_VAL(self->method)) ? FUNC_NAME(function,_ip_to_line)(2,(KrkValue[]){OBJECT_VAL(self->method),argv[1]},0) : OBJECT_VAL(S("?"));
|
return IS_function(OBJECT_VAL(self->method)) ? FUNC_NAME(function,_ip_to_line)(2,(KrkValue[]){OBJECT_VAL(self->method),argv[1]},0) : OBJECT_VAL(S("?"));
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(method,__repr__) {
|
KRK_Method(method,__str__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
KrkValue s = FUNC_NAME(method,__qualname__)(1,argv,0);
|
KrkValue s = FUNC_NAME(method,__qualname__)(1,argv,0);
|
||||||
if (!IS_STRING(s)) s = FUNC_NAME(method,__name__)(1,argv,0);
|
if (!IS_STRING(s)) s = FUNC_NAME(method,__name__)(1,argv,0);
|
||||||
@ -383,7 +379,7 @@ KRK_Method(method,__func__) {
|
|||||||
|
|
||||||
KRK_Method(method,__self__) {
|
KRK_Method(method,__self__) {
|
||||||
ATTRIBUTE_NOT_ASSIGNABLE();
|
ATTRIBUTE_NOT_ASSIGNABLE();
|
||||||
return self->receiver;
|
return OBJECT_VAL(self->receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(staticmethod) {
|
KRK_Function(staticmethod) {
|
||||||
@ -408,7 +404,7 @@ void _createAndBind_functionClass(void) {
|
|||||||
codeobject->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
codeobject->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
codeobject->allocSize = 0;
|
codeobject->allocSize = 0;
|
||||||
BIND_STATICMETHOD(codeobject,__new__);
|
BIND_STATICMETHOD(codeobject,__new__);
|
||||||
BIND_METHOD(codeobject,__repr__);
|
BIND_METHOD(codeobject,__str__);
|
||||||
BIND_METHOD(codeobject,_ip_to_line);
|
BIND_METHOD(codeobject,_ip_to_line);
|
||||||
BIND_PROP(codeobject,__constants__);
|
BIND_PROP(codeobject,__constants__);
|
||||||
BIND_PROP(codeobject,__name__);
|
BIND_PROP(codeobject,__name__);
|
||||||
@ -419,14 +415,14 @@ void _createAndBind_functionClass(void) {
|
|||||||
BIND_PROP(codeobject,co_posonlyargcount);
|
BIND_PROP(codeobject,co_posonlyargcount);
|
||||||
BIND_PROP(codeobject,__locals__);
|
BIND_PROP(codeobject,__locals__);
|
||||||
BIND_PROP(codeobject,__args__);
|
BIND_PROP(codeobject,__args__);
|
||||||
BIND_PROP(codeobject,__file__);
|
krk_defineNative(&codeobject->methods, "__repr__", FUNC_NAME(codeobject,__str__));
|
||||||
krk_finalizeClass(codeobject);
|
krk_finalizeClass(codeobject);
|
||||||
|
|
||||||
KrkClass * function = ADD_BASE_CLASS(vm.baseClasses->functionClass, "function", vm.baseClasses->objectClass);
|
KrkClass * function = ADD_BASE_CLASS(vm.baseClasses->functionClass, "function", vm.baseClasses->objectClass);
|
||||||
function->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
function->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
function->allocSize = 0;
|
function->allocSize = 0;
|
||||||
BIND_STATICMETHOD(function,__new__);
|
BIND_STATICMETHOD(function,__new__);
|
||||||
BIND_METHOD(function,__repr__);
|
BIND_METHOD(function,__str__);
|
||||||
BIND_METHOD(function,_ip_to_line);
|
BIND_METHOD(function,_ip_to_line);
|
||||||
BIND_PROP(function,__doc__);
|
BIND_PROP(function,__doc__);
|
||||||
BIND_PROP(function,__name__);
|
BIND_PROP(function,__name__);
|
||||||
@ -437,6 +433,7 @@ void _createAndBind_functionClass(void) {
|
|||||||
BIND_PROP(function,__code__);
|
BIND_PROP(function,__code__);
|
||||||
BIND_PROP(function,__globals__);
|
BIND_PROP(function,__globals__);
|
||||||
BIND_PROP(function,__closure__);
|
BIND_PROP(function,__closure__);
|
||||||
|
krk_defineNative(&function->methods, "__repr__", FUNC_NAME(function,__str__));
|
||||||
krk_defineNative(&function->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
|
krk_defineNative(&function->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
|
||||||
krk_finalizeClass(function);
|
krk_finalizeClass(function);
|
||||||
|
|
||||||
@ -444,7 +441,7 @@ void _createAndBind_functionClass(void) {
|
|||||||
method->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
method->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
method->allocSize = 0;
|
method->allocSize = 0;
|
||||||
BIND_STATICMETHOD(method,__new__);
|
BIND_STATICMETHOD(method,__new__);
|
||||||
BIND_METHOD(method,__repr__);
|
BIND_METHOD(method,__str__);
|
||||||
BIND_METHOD(method,_ip_to_line);
|
BIND_METHOD(method,_ip_to_line);
|
||||||
BIND_PROP(method,__doc__);
|
BIND_PROP(method,__doc__);
|
||||||
BIND_PROP(method,__name__);
|
BIND_PROP(method,__name__);
|
||||||
@ -455,6 +452,7 @@ void _createAndBind_functionClass(void) {
|
|||||||
BIND_PROP(method,__self__);
|
BIND_PROP(method,__self__);
|
||||||
BIND_PROP(method,__func__);
|
BIND_PROP(method,__func__);
|
||||||
BIND_PROP(method,__code__);
|
BIND_PROP(method,__code__);
|
||||||
|
krk_defineNative(&method->methods, "__repr__", FUNC_NAME(method,__str__));
|
||||||
krk_finalizeClass(method);
|
krk_finalizeClass(method);
|
||||||
|
|
||||||
BUILTIN_FUNCTION("staticmethod", FUNC_NAME(krk,staticmethod), "A static method does not take an implicit self or cls argument.");
|
BUILTIN_FUNCTION("staticmethod", FUNC_NAME(krk,staticmethod), "A static method does not take an implicit self or cls argument.");
|
||||||
|
@ -272,5 +272,6 @@ void _createAndBind_generatorClass(void) {
|
|||||||
BIND_METHOD(generator,__finish__);
|
BIND_METHOD(generator,__finish__);
|
||||||
BIND_METHOD(generator,send);
|
BIND_METHOD(generator,send);
|
||||||
BIND_PROP(generator,gi_running);
|
BIND_PROP(generator,gi_running);
|
||||||
|
krk_defineNative(&generator->methods, "__str__", FUNC_NAME(generator,__repr__));
|
||||||
krk_finalizeClass(generator);
|
krk_finalizeClass(generator);
|
||||||
}
|
}
|
||||||
|
375
src/obj_list.c
375
src/obj_list.c
@ -34,7 +34,7 @@ KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw) {
|
|||||||
|
|
||||||
if (argc) {
|
if (argc) {
|
||||||
AS_LIST(outList)->capacity = argc;
|
AS_LIST(outList)->capacity = argc;
|
||||||
AS_LIST(outList)->values = KRK_GROW_ARRAY(KrkValue, AS_LIST(outList)->values, 0, argc);
|
AS_LIST(outList)->values = GROW_ARRAY(KrkValue, AS_LIST(outList)->values, 0, argc);
|
||||||
memcpy(AS_LIST(outList)->values, argv, sizeof(KrkValue) * argc);
|
memcpy(AS_LIST(outList)->values, argv, sizeof(KrkValue) * argc);
|
||||||
AS_LIST(outList)->count = argc;
|
AS_LIST(outList)->count = argc;
|
||||||
}
|
}
|
||||||
@ -138,7 +138,15 @@ KRK_Method(list,__repr__) {
|
|||||||
pushStringBuilder(&sb, '[');
|
pushStringBuilder(&sb, '[');
|
||||||
pthread_rwlock_rdlock(&self->rwlock);
|
pthread_rwlock_rdlock(&self->rwlock);
|
||||||
for (size_t i = 0; i < self->values.count; ++i) {
|
for (size_t i = 0; i < self->values.count; ++i) {
|
||||||
if (!krk_pushStringBuilderFormat(&sb,"%R",self->values.values[i])) goto _error;
|
/* repr(self[i]) */
|
||||||
|
KrkClass * type = krk_getType(self->values.values[i]);
|
||||||
|
krk_push(self->values.values[i]);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
|
||||||
if (i + 1 < self->values.count) {
|
if (i + 1 < self->values.count) {
|
||||||
pushStringBuilderStr(&sb, ", ", 2);
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
}
|
}
|
||||||
@ -148,20 +156,14 @@ KRK_Method(list,__repr__) {
|
|||||||
pushStringBuilder(&sb,']');
|
pushStringBuilder(&sb,']');
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
pthread_rwlock_unlock(&self->rwlock);
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _list_extend_callback(void * context, const KrkValue * values, size_t count) {
|
static int _list_extend_callback(void * context, const KrkValue * values, size_t count) {
|
||||||
KrkValueArray * positionals = context;
|
KrkValueArray * positionals = context;
|
||||||
if (positionals->count + count > positionals->capacity) {
|
if (positionals->count + count > positionals->capacity) {
|
||||||
size_t old = positionals->capacity;
|
size_t old = positionals->capacity;
|
||||||
positionals->capacity = (count == 1) ? KRK_GROW_CAPACITY(old) : (positionals->count + count);
|
positionals->capacity = (count == 1) ? GROW_CAPACITY(old) : (positionals->count + count);
|
||||||
positionals->values = KRK_GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
|
positionals->values = GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
@ -304,7 +306,7 @@ KRK_Method(list,__delitem__) {
|
|||||||
METHOD_TAKES_EXACTLY(1);
|
METHOD_TAKES_EXACTLY(1);
|
||||||
|
|
||||||
if (IS_INTEGER(argv[1])) {
|
if (IS_INTEGER(argv[1])) {
|
||||||
FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],argv[1]},0);
|
FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],INTEGER_VAL(argv[1])},0);
|
||||||
} else if (IS_slice(argv[1])) {
|
} else if (IS_slice(argv[1])) {
|
||||||
KRK_SLICER(argv[1],self->values.count) {
|
KRK_SLICER(argv[1],self->values.count) {
|
||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
@ -414,338 +416,36 @@ KRK_Method(list,copy) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief In-place reverse a value array. */
|
|
||||||
static void reverse_values(KrkValue * values, size_t n) {
|
|
||||||
KrkValue * end = values + n - 1;
|
|
||||||
while (values < end) {
|
|
||||||
krk_currentThread.scratchSpace[0] = *values;
|
|
||||||
*values = *end;
|
|
||||||
*end = krk_currentThread.scratchSpace[0];
|
|
||||||
values++;
|
|
||||||
end--;
|
|
||||||
}
|
|
||||||
krk_currentThread.scratchSpace[0] = NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(list,reverse) {
|
KRK_Method(list,reverse) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
pthread_rwlock_wrlock(&self->rwlock);
|
pthread_rwlock_wrlock(&self->rwlock);
|
||||||
if (self->values.count > 1) reverse_values(self->values.values, self->values.count);
|
for (size_t i = 0; i < (self->values.count) / 2; i++) {
|
||||||
|
KrkValue tmp = self->values.values[i];
|
||||||
|
self->values.values[i] = self->values.values[self->values.count-i-1];
|
||||||
|
self->values.values[self->values.count-i-1] = tmp;
|
||||||
|
}
|
||||||
pthread_rwlock_unlock(&self->rwlock);
|
pthread_rwlock_unlock(&self->rwlock);
|
||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SortSlice {
|
static int _list_sorter(const void * _a, const void * _b) {
|
||||||
KrkValue * keys;
|
KrkValue a = *(KrkValue*)_a;
|
||||||
KrkValue * values;
|
KrkValue b = *(KrkValue*)_b;
|
||||||
};
|
|
||||||
|
|
||||||
struct SliceAndPower {
|
/* Avoid actually calling the sort function if there's an active exception */
|
||||||
struct SortSlice begin;
|
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return -1;
|
||||||
size_t power;
|
KrkValue ltComp = krk_operator_lt(a,b);
|
||||||
};
|
if (IS_NONE(ltComp) || (IS_BOOLEAN(ltComp) && AS_BOOLEAN(ltComp))) return -1;
|
||||||
|
KrkValue gtComp = krk_operator_gt(a,b);
|
||||||
struct Run {
|
if (IS_NONE(gtComp) || (IS_BOOLEAN(gtComp) && AS_BOOLEAN(gtComp))) return 1;
|
||||||
struct SortSlice start;
|
return 0;
|
||||||
struct SortSlice end;
|
|
||||||
size_t power;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @brief s++ */
|
|
||||||
static inline void slice_advance(struct SortSlice * slice) {
|
|
||||||
slice->keys++;
|
|
||||||
if (slice->values) slice->values++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief s-- */
|
|
||||||
static inline void slice_decrement(struct SortSlice * slice) {
|
|
||||||
slice->keys--;
|
|
||||||
if (slice->values) slice->values--;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* @brief s + 1 */
|
|
||||||
static struct SortSlice slice_next(struct SortSlice slice) {
|
|
||||||
return (struct SortSlice){slice.keys + 1, slice.values ? slice.values + 1 : NULL};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief s + n */
|
|
||||||
static struct SortSlice slice_plus(struct SortSlice slice, ssize_t n) {
|
|
||||||
return (struct SortSlice){slice.keys + n, slice.values ? slice.values + n : NULL};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Copy start-end to buffer */
|
|
||||||
static void copy_slice(struct SortSlice start, struct SortSlice end, struct SortSlice buffer) {
|
|
||||||
while (start.keys != end.keys) {
|
|
||||||
*buffer.keys = *start.keys;
|
|
||||||
if (buffer.values) *buffer.values = *start.values;
|
|
||||||
slice_advance(&start);
|
|
||||||
slice_advance(&buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Very strictly a < b */
|
|
||||||
static int _list_sorter(KrkValue a, KrkValue b) {
|
|
||||||
KrkValue comp = krk_operator_lt(a,b);
|
|
||||||
return (IS_NONE(comp) || (IS_BOOLEAN(comp) && AS_BOOLEAN(comp)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief While next is strictly < current, advance current */
|
|
||||||
static struct SortSlice powersort_strictlyDecreasingPrefix(struct SortSlice begin, struct SortSlice end) {
|
|
||||||
while (begin.keys + 1 < end.keys && _list_sorter(*(begin.keys + 1), *begin.keys)) slice_advance(&begin);
|
|
||||||
return slice_next(begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief While next is greater than or equal to current, advance current */
|
|
||||||
static struct SortSlice powersort_weaklyIncreasingPrefix(struct SortSlice begin, struct SortSlice end) {
|
|
||||||
while (begin.keys + 1 < end.keys && !_list_sorter(*(begin.keys + 1), *begin.keys)) slice_advance(&begin);
|
|
||||||
return slice_next(begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Extend a run to the right
|
|
||||||
*
|
|
||||||
* Returns a slice pointing at the end of the run after extended it to the right.
|
|
||||||
* The resulting run consists of strictly ordered (a <= b, b > a) entries. We also
|
|
||||||
* handle reverse runs by reversing them in-place.
|
|
||||||
*
|
|
||||||
* @param begin Start of run
|
|
||||||
* @param end End of available input to scan; always end of list.
|
|
||||||
* @returns Slice pointing to end of run
|
|
||||||
*/
|
|
||||||
static struct SortSlice powersort_extend_and_reverse_right(struct SortSlice begin, struct SortSlice end) {
|
|
||||||
struct SortSlice j = begin;
|
|
||||||
if (j.keys == end.keys) return j;
|
|
||||||
if (j.keys + 1 == end.keys) return slice_next(j);
|
|
||||||
if (_list_sorter(*slice_next(j).keys, *j.keys)) {
|
|
||||||
/* If next is strictly less than current, begin a reversed chain; we already know
|
|
||||||
* we can advance by one, so do that before continuing to save a comparison. */
|
|
||||||
j = powersort_strictlyDecreasingPrefix(slice_next(begin), end);
|
|
||||||
reverse_values(begin.keys, j.keys - begin.keys);
|
|
||||||
if (begin.values) reverse_values(begin.values, j.values - begin.values);
|
|
||||||
} else {
|
|
||||||
/* Weakly increasing means j+1 >= j; continue with that chain*/
|
|
||||||
j = powersort_weaklyIncreasingPrefix(slice_next(begin), end);
|
|
||||||
}
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate power.
|
|
||||||
*
|
|
||||||
* I'll be honest here, I don't really know what this does; it's from the reference impl.
|
|
||||||
* and described in the paper.
|
|
||||||
*/
|
|
||||||
static size_t powersort_power(size_t begin, size_t end, size_t beginA, size_t beginB, size_t endB) {
|
|
||||||
size_t n = end - begin;
|
|
||||||
unsigned long l = beginA - begin + beginB - begin;
|
|
||||||
unsigned long r = beginB - begin + endB - begin;
|
|
||||||
size_t common = 0;
|
|
||||||
int digitA = l >= n;
|
|
||||||
int digitB = r >= n;
|
|
||||||
while (digitA == digitB) {
|
|
||||||
common++;
|
|
||||||
if (digitA) {
|
|
||||||
l -= n;
|
|
||||||
r -= n;
|
|
||||||
}
|
|
||||||
l <<= 1;
|
|
||||||
r <<= 1;
|
|
||||||
digitA = l >= n;
|
|
||||||
digitB = r >= n;
|
|
||||||
}
|
|
||||||
return common + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Merge neighboring runs.
|
|
||||||
*
|
|
||||||
* Merges the neighboring, sorted runs [left, mid) and [mid, right) using the provided
|
|
||||||
* buffer space. Specifically, the smaller of the two runs is copied to the buffer, and
|
|
||||||
* then merging occurs in-place.
|
|
||||||
*
|
|
||||||
* @param left Start of the first run
|
|
||||||
* @param mid End of first run, start of second run
|
|
||||||
* @param right End of second run
|
|
||||||
* @param buffer Scratch space
|
|
||||||
*/
|
|
||||||
static void powersort_merge(struct SortSlice left, struct SortSlice mid, struct SortSlice right, struct SortSlice buffer) {
|
|
||||||
size_t n1 = mid.keys - left.keys;
|
|
||||||
size_t n2 = right.keys - mid.keys;
|
|
||||||
|
|
||||||
if (n1 <= n2) {
|
|
||||||
copy_slice(left, mid, buffer);
|
|
||||||
struct SortSlice c1 = buffer, e1 = slice_plus(buffer, n1);
|
|
||||||
struct SortSlice c2 = mid, e2 = right, o = left;
|
|
||||||
|
|
||||||
while (c1.keys < e1.keys && c2.keys < e2.keys) {
|
|
||||||
if (!_list_sorter(*c2.keys, *c1.keys)) {
|
|
||||||
*o.keys = *c1.keys;
|
|
||||||
if (o.values) *o.values = *c1.values;
|
|
||||||
slice_advance(&c1);
|
|
||||||
} else {
|
|
||||||
*o.keys = *c2.keys;
|
|
||||||
if (o.values) *o.values = *c2.values;
|
|
||||||
slice_advance(&c2);
|
|
||||||
}
|
|
||||||
slice_advance(&o);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (c1.keys < e1.keys) {
|
|
||||||
*o.keys = *c1.keys;
|
|
||||||
if (o.values) *o.values = *c1.values;
|
|
||||||
slice_advance(&c1);
|
|
||||||
slice_advance(&o);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
copy_slice(mid, right, buffer);
|
|
||||||
|
|
||||||
struct SortSlice c1 = slice_plus(mid, -1), s1 = left, o = slice_plus(right, -1);
|
|
||||||
struct SortSlice c2 = slice_plus(buffer, n2 - 1), s2 = buffer;
|
|
||||||
|
|
||||||
while (c1.keys >= s1.keys && c2.keys >= s2.keys) {
|
|
||||||
if (!_list_sorter(*c2.keys, *c1.keys)) {
|
|
||||||
*o.keys = *c2.keys;
|
|
||||||
if (o.values) *o.values = *c2.values;
|
|
||||||
slice_decrement(&c2);
|
|
||||||
} else {
|
|
||||||
*o.keys = *c1.keys;
|
|
||||||
if (o.values) *o.values = *c1.values;
|
|
||||||
slice_decrement(&c1);
|
|
||||||
}
|
|
||||||
slice_decrement(&o);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (c2.keys >= s2.keys) {
|
|
||||||
*o.keys = *c2.keys;
|
|
||||||
if (o.values) *o.values = *c2.values;
|
|
||||||
slice_decrement(&c2);
|
|
||||||
slice_decrement(&o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Powersort - merge-sort sorted runs
|
|
||||||
*
|
|
||||||
* This is an implementation of Munro-Wild Powersort from the paper at:
|
|
||||||
* @ref https://www.wild-inter.net/publications/html/munro-wild-2018.pdf.html
|
|
||||||
*
|
|
||||||
* The reference implementation was also a helpful thing to study, and much
|
|
||||||
* of the iteration and merging is based on its use of C++ iterators:
|
|
||||||
* @ref https://github.com/sebawild/powersort
|
|
||||||
*
|
|
||||||
* There's no fancy extensions or improvements here, just the plain approach
|
|
||||||
* set out in the paper, which is probably good enough for us? That means no
|
|
||||||
* extending short runs to a minimum run length, no fancy node power calcs,
|
|
||||||
* just a short bit of extending and merging.
|
|
||||||
*
|
|
||||||
* If the key function raises an exception, no sorting will be attempted
|
|
||||||
* and the exception from the key function will be raised immediately.
|
|
||||||
*
|
|
||||||
* If the values to be sorted can not compare with __lt__, an exception
|
|
||||||
* should be thrown eventually, but the entire list may still be scanned
|
|
||||||
* and the resulting state is undefined.
|
|
||||||
*
|
|
||||||
* @param list List to sort in-place.
|
|
||||||
* @param key Key function, or None to sort values directly.
|
|
||||||
* @param reverse Sort direction, 0 for normal (a[0] <= b[0], etc.), 1 for reversed.
|
|
||||||
*/
|
|
||||||
static void powersort(KrkList * list, KrkValue key, int reverse) {
|
|
||||||
size_t n = list->values.count;
|
|
||||||
struct SortSlice slice = {list->values.values, NULL};
|
|
||||||
|
|
||||||
/* If there is a key function, create a separate array to store
|
|
||||||
* the resulting key values; shove it in a tuple so we can keep
|
|
||||||
* those key values from being garbage collected. */
|
|
||||||
if (!IS_NONE(key)) {
|
|
||||||
KrkTuple * _keys = krk_newTuple(n);
|
|
||||||
krk_push(OBJECT_VAL(_keys));
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
|
||||||
krk_push(key);
|
|
||||||
krk_push(list->values.values[i]);
|
|
||||||
_keys->values.values[i] = krk_callStack(1);
|
|
||||||
_keys->values.count++;
|
|
||||||
|
|
||||||
/* If the key function threw an exception, bail early. */
|
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _end_sort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* values are secondary, keys are what actually gets sorted */
|
|
||||||
slice.values = slice.keys;
|
|
||||||
slice.keys = _keys->values.values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We handle reverse sort by reversing, sorting normally, and then reversing again */
|
|
||||||
if (reverse) {
|
|
||||||
reverse_values(slice.keys, n);
|
|
||||||
if (slice.values) reverse_values(slice.values, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Supposedly the absolute maximum for this is strictly less than the number of bits
|
|
||||||
* we can fit in a size_t, so 64 ought to cover us until someone tries porting Kuroko
|
|
||||||
* to one of the 128-bit architectures, but even then I don't think we can handle
|
|
||||||
* holding that many values in a list to begin with.
|
|
||||||
*
|
|
||||||
* stack[0] should always be empty. */
|
|
||||||
struct SliceAndPower stack[64] = {0};
|
|
||||||
int top = 0;
|
|
||||||
|
|
||||||
/* Buffer space for the merges. We shouldn't need anywhere close to this much space,
|
|
||||||
* but best to be safe, and we're already allocating a bunch of space for key tuples */
|
|
||||||
KrkTuple * bufferSpace = krk_newTuple(slice.values ? (n * 2) : n);
|
|
||||||
krk_push(OBJECT_VAL(bufferSpace));
|
|
||||||
for (size_t i = 0; i < bufferSpace->values.capacity; ++i) bufferSpace->values.values[bufferSpace->values.count++] = NONE_VAL();
|
|
||||||
struct SortSlice buffer = {&bufferSpace->values.values[0], slice.values ? &bufferSpace->values.values[n] : NULL};
|
|
||||||
|
|
||||||
/* This just take the role of the C++ iterators in the reference implementaiton */
|
|
||||||
struct SortSlice begin = {slice.keys, slice.values};
|
|
||||||
struct SortSlice end = {slice.keys + n, slice.values ? slice.values + n : NULL};
|
|
||||||
|
|
||||||
/* Our first run starts from the left and extends as far as it can. */
|
|
||||||
struct Run a = {begin, powersort_extend_and_reverse_right(begin,end), 0};
|
|
||||||
|
|
||||||
while (a.end.keys < end.keys) {
|
|
||||||
/* Our next run is whatever is after that, assuming the initial run isn't the whole list. */
|
|
||||||
struct Run b = {a.end, powersort_extend_and_reverse_right(a.end, end), 0};
|
|
||||||
/* I don't really understand the power part of powersort, but whatever. */
|
|
||||||
a.power = powersort_power(0, n, a.start.keys - begin.keys, b.start.keys - begin.keys, b.end.keys - begin.keys);
|
|
||||||
|
|
||||||
/* While the stack has things with higher power, merge them into a */
|
|
||||||
while (stack[top].power > a.power) {
|
|
||||||
struct SliceAndPower top_run = stack[top--];
|
|
||||||
powersort_merge(top_run.begin, a.start, a.end, buffer);
|
|
||||||
a.start = top_run.begin;
|
|
||||||
}
|
|
||||||
/* Put a on top of the stack, and then replace a with b */
|
|
||||||
stack[++top] = (struct SliceAndPower){a.start, a.power};
|
|
||||||
a = (struct Run){b.start, b.end, 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* While there are things in the stack (excluding the empty 0 slot), merge them into the last a */
|
|
||||||
while (top > 0) {
|
|
||||||
struct SliceAndPower top_run = stack[top--];
|
|
||||||
powersort_merge(top_run.begin, a.start, end, buffer);
|
|
||||||
a.start = top_run.begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
krk_pop(); /* tuple with buffer space */
|
|
||||||
_end_sort:
|
|
||||||
if (!IS_NONE(key)) krk_pop(); /* keys tuple */
|
|
||||||
|
|
||||||
/* If we reversed at the start, reverse again now as the list is forward-sorted */
|
|
||||||
if (reverse) reverse_values(list->values.values, n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(list,sort) {
|
KRK_Method(list,sort) {
|
||||||
KrkValue key = NONE_VAL();
|
METHOD_TAKES_NONE();
|
||||||
int reverse = 0;
|
|
||||||
if (!krk_parseArgs(".|$Vp", (const char*[]){"key","reverse"}, &key, &reverse)) return NONE_VAL();
|
|
||||||
|
|
||||||
if (self->values.count < 2) return NONE_VAL();
|
|
||||||
|
|
||||||
pthread_rwlock_wrlock(&self->rwlock);
|
pthread_rwlock_wrlock(&self->rwlock);
|
||||||
powersort(self, key, reverse);
|
qsort(self->values.values, self->values.count, sizeof(KrkValue), _list_sorter);
|
||||||
pthread_rwlock_unlock(&self->rwlock);
|
pthread_rwlock_unlock(&self->rwlock);
|
||||||
|
|
||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
@ -821,7 +521,7 @@ KRK_Method(listiterator,__init__) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FUNC_SIG(listiterator,__call__) {
|
FUNC_SIG(listiterator,__call__) {
|
||||||
static _unused const char* _method_name = "__call__";
|
static __attribute__ ((unused)) const char* _method_name = "__call__";
|
||||||
if (unlikely((argc != 1))) goto _bad;
|
if (unlikely((argc != 1))) goto _bad;
|
||||||
if (unlikely(!IS_OBJECT(argv[0]))) goto _bad;
|
if (unlikely(!IS_OBJECT(argv[0]))) goto _bad;
|
||||||
if (unlikely(AS_INSTANCE(argv[0])->_class != vm.baseClasses->listiteratorClass)) goto _bad;
|
if (unlikely(AS_INSTANCE(argv[0])->_class != vm.baseClasses->listiteratorClass)) goto _bad;
|
||||||
@ -844,18 +544,18 @@ _bad:
|
|||||||
goto _maybeGood;
|
goto _maybeGood;
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(sorted) {
|
static KrkValue _sorted(int argc, const KrkValue argv[], int hasKw) {
|
||||||
if (argc < 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","sorted","at least",1,"",argc);
|
if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","sorted","exactly",1,"",argc);
|
||||||
KrkValue listOut = krk_list_of(0,NULL,0);
|
KrkValue listOut = krk_list_of(0,NULL,0);
|
||||||
krk_push(listOut);
|
krk_push(listOut);
|
||||||
FUNC_NAME(list,extend)(2,(KrkValue[]){listOut,argv[0]},0);
|
FUNC_NAME(list,extend)(2,(KrkValue[]){listOut,argv[0]},0);
|
||||||
if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
|
if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
|
||||||
FUNC_NAME(list,sort)(1,(KrkValue[]){listOut,hasKw ? argv[1] : NONE_VAL(), hasKw ? argv[2] : NONE_VAL()},hasKw);
|
FUNC_NAME(list,sort)(1,&listOut,0);
|
||||||
if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
|
if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
|
||||||
return krk_pop();
|
return krk_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(reversed) {
|
static KrkValue _reversed(int argc, const KrkValue argv[], int hasKw) {
|
||||||
/* FIXME The Python reversed() function produces an iterator and only works for things with indexing or a __reversed__ method;
|
/* FIXME The Python reversed() function produces an iterator and only works for things with indexing or a __reversed__ method;
|
||||||
* Building a list and reversing it like we do here is not correct! */
|
* Building a list and reversing it like we do here is not correct! */
|
||||||
if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","reversed","exactly",1,"",argc);
|
if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","reversed","exactly",1,"",argc);
|
||||||
@ -940,16 +640,17 @@ void _createAndBind_listClass(void) {
|
|||||||
"@brief Sort the contents of a list.\n\n"
|
"@brief Sort the contents of a list.\n\n"
|
||||||
"Performs an in-place sort of the elements in the list, returning @c None as a gentle reminder "
|
"Performs an in-place sort of the elements in the list, returning @c None as a gentle reminder "
|
||||||
"that the sort is in-place. If a sorted copy is desired, use @ref sorted instead.");
|
"that the sort is in-place. If a sorted copy is desired, use @ref sorted instead.");
|
||||||
|
krk_defineNative(&list->methods, "__str__", FUNC_NAME(list,__repr__));
|
||||||
krk_defineNative(&list->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
|
krk_defineNative(&list->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
|
||||||
krk_attachNamedValue(&list->methods, "__hash__", NONE_VAL());
|
krk_attachNamedValue(&list->methods, "__hash__", NONE_VAL());
|
||||||
krk_finalizeClass(list);
|
krk_finalizeClass(list);
|
||||||
KRK_DOC(list, "Mutable sequence of arbitrary values.");
|
KRK_DOC(list, "Mutable sequence of arbitrary values.");
|
||||||
|
|
||||||
BUILTIN_FUNCTION("sorted", FUNC_NAME(krk,sorted),
|
BUILTIN_FUNCTION("sorted", _sorted,
|
||||||
"@brief Return a sorted representation of an iterable.\n"
|
"@brief Return a sorted representation of an iterable.\n"
|
||||||
"@arguments iterable\n\n"
|
"@arguments iterable\n\n"
|
||||||
"Creates a new, sorted list from the elements of @p iterable.");
|
"Creates a new, sorted list from the elements of @p iterable.");
|
||||||
BUILTIN_FUNCTION("reversed", FUNC_NAME(krk,reversed),
|
BUILTIN_FUNCTION("reversed", _reversed,
|
||||||
"@brief Return a reversed representation of an iterable.\n"
|
"@brief Return a reversed representation of an iterable.\n"
|
||||||
"@arguments iterable\n\n"
|
"@arguments iterable\n\n"
|
||||||
"Creates a new, reversed list from the elements of @p iterable.");
|
"Creates a new, reversed list from the elements of @p iterable.");
|
||||||
|
1308
src/obj_long.c
1308
src/obj_long.c
File diff suppressed because it is too large
Load Diff
@ -23,13 +23,7 @@ KRK_StaticMethod(int,__new__) {
|
|||||||
int has_base = 0;
|
int has_base = 0;
|
||||||
int base = 10;
|
int base = 10;
|
||||||
|
|
||||||
/* Most common case */
|
if (!krk_parseArgs("O|V?i?:int", (const char*[]){"cls","x","base"},
|
||||||
if (!hasKw && argc == 2) {
|
|
||||||
x = argv[1];
|
|
||||||
goto _just_x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!krk_parseArgs("O|V?i?:int", (const char*[]){"","","base"},
|
|
||||||
&cls, &has_x, &x, &has_base, &base)) return NONE_VAL();
|
&cls, &has_x, &x, &has_base, &base)) return NONE_VAL();
|
||||||
|
|
||||||
if (has_base && (base < 2 || base > 36) && base != 0) {
|
if (has_base && (base < 2 || base > 36) && base != 0) {
|
||||||
@ -48,13 +42,6 @@ KRK_StaticMethod(int,__new__) {
|
|||||||
return krk_runtimeError(vm.exceptions->typeError, "can not convert non-str with explicit base");
|
return krk_runtimeError(vm.exceptions->typeError, "can not convert non-str with explicit base");
|
||||||
}
|
}
|
||||||
|
|
||||||
_just_x:
|
|
||||||
|
|
||||||
if (IS_INTEGER(x)) return INTEGER_VAL(AS_INTEGER(x));
|
|
||||||
#ifndef KRK_NO_FLOAT
|
|
||||||
if (IS_FLOATING(x)) return krk_int_from_float(AS_FLOATING(x));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (IS_STRING(x)) {
|
if (IS_STRING(x)) {
|
||||||
KrkValue result = krk_parse_int(AS_CSTRING(x), AS_STRING(x)->length, base);
|
KrkValue result = krk_parse_int(AS_CSTRING(x), AS_STRING(x)->length, base);
|
||||||
if (IS_NONE(result)) {
|
if (IS_NONE(result)) {
|
||||||
@ -64,11 +51,17 @@ _just_x:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IS_BOOLEAN(x)) return INTEGER_VAL(AS_INTEGER(x));
|
||||||
|
if (IS_INTEGER(x)) return x;
|
||||||
if (krk_isInstanceOf(x, KRK_BASE_CLASS(long))) return x;
|
if (krk_isInstanceOf(x, KRK_BASE_CLASS(long))) return x;
|
||||||
|
#ifndef KRK_NO_FLOAT
|
||||||
|
if (IS_FLOATING(x)) return krk_int_from_float(AS_FLOATING(x));
|
||||||
|
#endif
|
||||||
|
if (IS_BOOLEAN(x)) return INTEGER_VAL(AS_BOOLEAN(x));
|
||||||
return krk_runtimeError(vm.exceptions->typeError, "%s() argument must be a string or a number, not '%T'", "int", x);
|
return krk_runtimeError(vm.exceptions->typeError, "%s() argument must be a string or a number, not '%T'", "int", x);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(int,__repr__) {
|
KRK_Method(int,__str__) {
|
||||||
char tmp[100];
|
char tmp[100];
|
||||||
size_t l = snprintf(tmp, 100, PRIkrk_int, self);
|
size_t l = snprintf(tmp, 100, PRIkrk_int, self);
|
||||||
return OBJECT_VAL(krk_copyString(tmp, l));
|
return OBJECT_VAL(krk_copyString(tmp, l));
|
||||||
@ -243,7 +236,7 @@ KrkValue krk_doFormatString(const char * typeName, KrkString * format_spec, int
|
|||||||
if ((!positive || opts.sign == '+') && width > 1) width--;
|
if ((!positive || opts.sign == '+') && width > 1) width--;
|
||||||
|
|
||||||
int digits = 0;
|
int digits = 0;
|
||||||
int sepcount = (opts.sep == ',' || base == 10) ? 3 : 4;
|
int sepcount = opts.sep == ',' ? 3 : 4;
|
||||||
int more = 0;
|
int more = 0;
|
||||||
|
|
||||||
if (prepCallback) callback = prepCallback(abs, base);
|
if (prepCallback) callback = prepCallback(abs, base);
|
||||||
@ -624,94 +617,24 @@ KRK_StaticMethod(float,__new__) {
|
|||||||
return krk_runtimeError(vm.exceptions->typeError, "%s() argument must be a string or a number, not '%T'", "float", argv[1]);
|
return krk_runtimeError(vm.exceptions->typeError, "%s() argument must be a string or a number, not '%T'", "float", argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(float,__int__) { return krk_int_from_float(self); }
|
KRK_Method(float,__int__) { return INTEGER_VAL(self); }
|
||||||
KRK_Method(float,__float__) { return argv[0]; }
|
KRK_Method(float,__float__) { return argv[0]; }
|
||||||
|
|
||||||
extern KrkValue krk_double_to_string(double,unsigned int,char,int,int);
|
static int isDigits(const char * c) {
|
||||||
KRK_Method(float,__repr__) {
|
while (*c) {
|
||||||
return krk_double_to_string(self,16,' ',0,0);
|
if (*c != '-' && (*c < '0' || *c > '9')) return 0;
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(float,__format__) {
|
KRK_Method(float,__str__) {
|
||||||
char * format_spec;
|
char tmp[100];
|
||||||
size_t format_spec_length;
|
size_t l = snprintf(tmp, 97, "%.16g", self);
|
||||||
if (!krk_parseArgs(".s#", (const char*[]){"format_spec"}, &format_spec, &format_spec_length)) return NONE_VAL();
|
if (!strstr(tmp,".") && isDigits(tmp)) {
|
||||||
|
l = snprintf(tmp,100,"%.16g.0",self);
|
||||||
struct ParsedFormatSpec opts = {0};
|
|
||||||
const char * spec = krk_parseCommonFormatSpec(&opts, format_spec, format_spec_length);
|
|
||||||
if (!spec) return NONE_VAL();
|
|
||||||
|
|
||||||
char formatter = 'g';
|
|
||||||
int digits = 16;
|
|
||||||
int forcedigits = opts.alt;
|
|
||||||
|
|
||||||
switch (*spec) {
|
|
||||||
case 0:
|
|
||||||
case 'g':
|
|
||||||
/* defaults */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'G':
|
|
||||||
formatter = 'G';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
case 'F':
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
digits = 6;
|
|
||||||
formatter = *spec;
|
|
||||||
forcedigits = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return krk_runtimeError(vm.exceptions->valueError,
|
|
||||||
"Unknown format code '%c' for object of type '%s'",
|
|
||||||
*spec,
|
|
||||||
"float");
|
|
||||||
}
|
}
|
||||||
|
return OBJECT_VAL(krk_copyString(tmp, l));
|
||||||
if (opts.sep) return krk_runtimeError(vm.exceptions->valueError, "unsupported option for float");
|
|
||||||
if (opts.hasPrecision) digits = opts.prec;
|
|
||||||
if (!opts.align) opts.align = '>';
|
|
||||||
|
|
||||||
KrkValue result = krk_double_to_string(self, digits, formatter, opts.sign == '+', forcedigits);
|
|
||||||
if (!IS_STRING(result) || !opts.width) return result;
|
|
||||||
|
|
||||||
krk_push(result);
|
|
||||||
|
|
||||||
/* Calculate how much padding we need to add. */
|
|
||||||
size_t avail = (size_t)opts.width > AS_STRING(result)->length ? (size_t)opts.width - AS_STRING(result)->length : 0;
|
|
||||||
|
|
||||||
/* If there's no available space for padding, just return the string we already have. */
|
|
||||||
if (!avail) return krk_pop();
|
|
||||||
|
|
||||||
struct StringBuilder sb = {0};
|
|
||||||
size_t before = 0;
|
|
||||||
size_t after = 0;
|
|
||||||
int hassign = 0;
|
|
||||||
if (opts.align == '<') {
|
|
||||||
after = avail;
|
|
||||||
} else if (opts.align == '>') {
|
|
||||||
before = avail;
|
|
||||||
} else if (opts.align == '^') {
|
|
||||||
after = avail / 2;
|
|
||||||
before = avail - after;
|
|
||||||
} else if (opts.align == '=') {
|
|
||||||
before = avail;
|
|
||||||
if (avail && AS_STRING(result)->length && (AS_CSTRING(result)[0] == '-' || AS_CSTRING(result)[0] == '+')) {
|
|
||||||
krk_pushStringBuilder(&sb, AS_CSTRING(result)[0]);
|
|
||||||
hassign = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill in padding with a new string builder. */
|
|
||||||
for (size_t i = 0; i < before; ++i) krk_pushStringBuilderStr(&sb, opts.fill, opts.fillSize);
|
|
||||||
krk_pushStringBuilderStr(&sb, AS_CSTRING(result) + hassign, AS_STRING(result)->length - hassign);
|
|
||||||
for (size_t i = 0; i < after; ++i) krk_pushStringBuilderStr(&sb, opts.fill, opts.fillSize);
|
|
||||||
|
|
||||||
krk_pop();
|
|
||||||
return krk_finishStringBuilder(&sb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(float,__eq__) {
|
KRK_Method(float,__eq__) {
|
||||||
@ -813,11 +736,6 @@ KRK_Method(float,__rfloordiv__) {
|
|||||||
KRK_Method(float,__pos__) {
|
KRK_Method(float,__pos__) {
|
||||||
return argv[0];
|
return argv[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
extern KrkValue krk_float_to_fraction(double d);
|
|
||||||
KRK_Method(float,as_integer_ratio) {
|
|
||||||
return krk_float_to_fraction(self);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#undef CURRENT_CTYPE
|
#undef CURRENT_CTYPE
|
||||||
@ -829,7 +747,7 @@ KRK_StaticMethod(bool,__new__) {
|
|||||||
return BOOLEAN_VAL(!krk_isFalsey(argv[1]));
|
return BOOLEAN_VAL(!krk_isFalsey(argv[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(bool,__repr__) {
|
KRK_Method(bool,__str__) {
|
||||||
return OBJECT_VAL((self ? S("True") : S("False")));
|
return OBJECT_VAL((self ? S("True") : S("False")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,7 +756,7 @@ KRK_Method(bool,__format__) {
|
|||||||
CHECK_ARG(1,str,KrkString*,format_spec);
|
CHECK_ARG(1,str,KrkString*,format_spec);
|
||||||
|
|
||||||
if (!format_spec->length) {
|
if (!format_spec->length) {
|
||||||
return FUNC_NAME(bool,__repr__)(argc,argv,hasKw);
|
return FUNC_NAME(bool,__str__)(argc,argv,hasKw);
|
||||||
} else {
|
} else {
|
||||||
return FUNC_NAME(int,__format__)(argc,argv,hasKw);
|
return FUNC_NAME(int,__format__)(argc,argv,hasKw);
|
||||||
}
|
}
|
||||||
@ -849,7 +767,7 @@ KRK_StaticMethod(NoneType,__new__) {
|
|||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(NoneType,__repr__) {
|
KRK_Method(NoneType,__str__) {
|
||||||
return OBJECT_VAL(S("None"));
|
return OBJECT_VAL(S("None"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,7 +789,7 @@ KRK_StaticMethod(NotImplementedType,__new__) {
|
|||||||
return NOTIMPL_VAL();
|
return NOTIMPL_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(NotImplementedType,__repr__) {
|
KRK_Method(NotImplementedType,__str__) {
|
||||||
return OBJECT_VAL(S("NotImplemented"));
|
return OBJECT_VAL(S("NotImplemented"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +818,7 @@ void _createAndBind_numericClasses(void) {
|
|||||||
_int->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
_int->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
_int->allocSize = 0;
|
_int->allocSize = 0;
|
||||||
BIND_STATICMETHOD(int,__new__);
|
BIND_STATICMETHOD(int,__new__);
|
||||||
BIND_METHOD(int,__repr__);
|
BIND_METHOD(int,__str__);
|
||||||
BIND_METHOD(int,__int__);
|
BIND_METHOD(int,__int__);
|
||||||
BIND_METHOD(int,__chr__);
|
BIND_METHOD(int,__chr__);
|
||||||
BIND_METHOD(int,__eq__);
|
BIND_METHOD(int,__eq__);
|
||||||
@ -937,6 +855,7 @@ void _createAndBind_numericClasses(void) {
|
|||||||
BIND_METHOD(int,__abs__);
|
BIND_METHOD(int,__abs__);
|
||||||
BIND_METHOD(int,__pos__);
|
BIND_METHOD(int,__pos__);
|
||||||
|
|
||||||
|
krk_defineNative(&_int->methods, "__repr__", FUNC_NAME(int,__str__));
|
||||||
krk_finalizeClass(_int);
|
krk_finalizeClass(_int);
|
||||||
KRK_DOC(_int, "Convert a number or string type to an integer representation.");
|
KRK_DOC(_int, "Convert a number or string type to an integer representation.");
|
||||||
|
|
||||||
@ -947,7 +866,7 @@ void _createAndBind_numericClasses(void) {
|
|||||||
BIND_STATICMETHOD(float,__new__);
|
BIND_STATICMETHOD(float,__new__);
|
||||||
BIND_METHOD(float,__int__);
|
BIND_METHOD(float,__int__);
|
||||||
BIND_METHOD(float,__float__);
|
BIND_METHOD(float,__float__);
|
||||||
BIND_METHOD(float,__repr__);
|
BIND_METHOD(float,__str__);
|
||||||
BIND_METHOD(float,__eq__);
|
BIND_METHOD(float,__eq__);
|
||||||
BIND_METHOD(float,__hash__);
|
BIND_METHOD(float,__hash__);
|
||||||
BIND_TRIPLET(float,add);
|
BIND_TRIPLET(float,add);
|
||||||
@ -962,8 +881,7 @@ void _createAndBind_numericClasses(void) {
|
|||||||
BIND_METHOD(float,__neg__);
|
BIND_METHOD(float,__neg__);
|
||||||
BIND_METHOD(float,__abs__);
|
BIND_METHOD(float,__abs__);
|
||||||
BIND_METHOD(float,__pos__);
|
BIND_METHOD(float,__pos__);
|
||||||
BIND_METHOD(float,__format__);
|
krk_defineNative(&_float->methods, "__repr__", FUNC_NAME(float,__str__));
|
||||||
BIND_METHOD(float,as_integer_ratio);
|
|
||||||
#endif
|
#endif
|
||||||
krk_finalizeClass(_float);
|
krk_finalizeClass(_float);
|
||||||
KRK_DOC(_float, "Convert a number or string type to a float representation.");
|
KRK_DOC(_float, "Convert a number or string type to a float representation.");
|
||||||
@ -971,8 +889,9 @@ void _createAndBind_numericClasses(void) {
|
|||||||
KrkClass * _bool = ADD_BASE_CLASS(vm.baseClasses->boolClass, "bool", vm.baseClasses->intClass);
|
KrkClass * _bool = ADD_BASE_CLASS(vm.baseClasses->boolClass, "bool", vm.baseClasses->intClass);
|
||||||
_bool->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
_bool->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
BIND_STATICMETHOD(bool,__new__);
|
BIND_STATICMETHOD(bool,__new__);
|
||||||
BIND_METHOD(bool,__repr__);
|
BIND_METHOD(bool,__str__);
|
||||||
BIND_METHOD(bool,__format__);
|
BIND_METHOD(bool,__format__);
|
||||||
|
krk_defineNative(&_bool->methods, "__repr__", FUNC_NAME(bool,__str__));
|
||||||
krk_finalizeClass(_bool);
|
krk_finalizeClass(_bool);
|
||||||
KRK_DOC(_bool, "Returns False if the argument is 'falsey', otherwise True.");
|
KRK_DOC(_bool, "Returns False if the argument is 'falsey', otherwise True.");
|
||||||
|
|
||||||
@ -980,18 +899,20 @@ void _createAndBind_numericClasses(void) {
|
|||||||
_NoneType->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
_NoneType->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
_NoneType->allocSize = 0;
|
_NoneType->allocSize = 0;
|
||||||
BIND_STATICMETHOD(NoneType, __new__);
|
BIND_STATICMETHOD(NoneType, __new__);
|
||||||
BIND_METHOD(NoneType, __repr__);
|
BIND_METHOD(NoneType, __str__);
|
||||||
BIND_METHOD(NoneType, __hash__);
|
BIND_METHOD(NoneType, __hash__);
|
||||||
BIND_METHOD(NoneType, __eq__);
|
BIND_METHOD(NoneType, __eq__);
|
||||||
|
krk_defineNative(&_NoneType->methods, "__repr__", FUNC_NAME(NoneType,__str__));
|
||||||
krk_finalizeClass(_NoneType);
|
krk_finalizeClass(_NoneType);
|
||||||
|
|
||||||
KrkClass * _NotImplementedType = ADD_BASE_CLASS(vm.baseClasses->notImplClass, "NotImplementedType", vm.baseClasses->objectClass);
|
KrkClass * _NotImplementedType = ADD_BASE_CLASS(vm.baseClasses->notImplClass, "NotImplementedType", vm.baseClasses->objectClass);
|
||||||
_NotImplementedType->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
_NotImplementedType->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
_NotImplementedType->allocSize = 0;
|
_NotImplementedType->allocSize = 0;
|
||||||
BIND_STATICMETHOD(NotImplementedType, __new__);
|
BIND_STATICMETHOD(NotImplementedType, __new__);
|
||||||
BIND_METHOD(NotImplementedType, __repr__);
|
BIND_METHOD(NotImplementedType, __str__);
|
||||||
BIND_METHOD(NotImplementedType, __hash__);
|
BIND_METHOD(NotImplementedType, __hash__);
|
||||||
BIND_METHOD(NotImplementedType, __eq__);
|
BIND_METHOD(NotImplementedType, __eq__);
|
||||||
|
krk_defineNative(&_NotImplementedType->methods, "__repr__", FUNC_NAME(NotImplementedType,__str__));
|
||||||
krk_finalizeClass(_NotImplementedType);
|
krk_finalizeClass(_NotImplementedType);
|
||||||
|
|
||||||
krk_attachNamedValue(&vm.builtins->fields, "NotImplemented", NOTIMPL_VAL());
|
krk_attachNamedValue(&vm.builtins->fields, "NotImplemented", NOTIMPL_VAL());
|
||||||
|
@ -63,13 +63,13 @@ KRK_Method(set,__init__) {
|
|||||||
|
|
||||||
KRK_Method(set,__contains__) {
|
KRK_Method(set,__contains__) {
|
||||||
METHOD_TAKES_EXACTLY(1);
|
METHOD_TAKES_EXACTLY(1);
|
||||||
KrkValue v;
|
KrkValue _unused;
|
||||||
return BOOLEAN_VAL(krk_tableGet(&self->entries, argv[1], &v));
|
return BOOLEAN_VAL(krk_tableGet(&self->entries, argv[1], &_unused));
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(set,__repr__) {
|
KRK_Method(set,__repr__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL(S("{...}"));
|
if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL("{...}");
|
||||||
if (!self->entries.capacity) return OBJECT_VAL(S("set()"));
|
if (!self->entries.capacity) return OBJECT_VAL(S("set()"));
|
||||||
((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
|
((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
|
||||||
struct StringBuilder sb = {0};
|
struct StringBuilder sb = {0};
|
||||||
@ -80,19 +80,22 @@ KRK_Method(set,__repr__) {
|
|||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
KrkTableEntry * entry = &self->entries.entries[i];
|
KrkTableEntry * entry = &self->entries.entries[i];
|
||||||
if (IS_KWARGS(entry->key)) continue;
|
if (IS_KWARGS(entry->key)) continue;
|
||||||
if (c) pushStringBuilderStr(&sb, ", ", 2);
|
if (c > 0) {
|
||||||
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
|
}
|
||||||
c++;
|
c++;
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", entry->key)) goto _error;
|
|
||||||
|
KrkClass * type = krk_getType(entry->key);
|
||||||
|
krk_push(entry->key);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pushStringBuilder(&sb,'}');
|
pushStringBuilder(&sb,'}');
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(set,__and__) {
|
KRK_Method(set,__and__) {
|
||||||
@ -195,11 +198,11 @@ KRK_Method(set,__eq__) {
|
|||||||
if (self->entries.count != them->entries.count)
|
if (self->entries.count != them->entries.count)
|
||||||
return BOOLEAN_VAL(0);
|
return BOOLEAN_VAL(0);
|
||||||
|
|
||||||
KrkValue v;
|
KrkValue _unused;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < self->entries.capacity; ++i) {
|
for (unsigned int i = 0; i < self->entries.capacity; ++i) {
|
||||||
if (IS_KWARGS(self->entries.entries[i].key)) continue;
|
if (IS_KWARGS(self->entries.entries[i].key)) continue;
|
||||||
if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
|
if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return BOOLEAN_VAL(1);
|
return BOOLEAN_VAL(1);
|
||||||
@ -213,10 +216,10 @@ KRK_Method(set,__lt__) {
|
|||||||
struct Set * them = AS_set(argv[1]);
|
struct Set * them = AS_set(argv[1]);
|
||||||
if (self->entries.count == them->entries.count)
|
if (self->entries.count == them->entries.count)
|
||||||
return BOOLEAN_VAL(0);
|
return BOOLEAN_VAL(0);
|
||||||
KrkValue v;
|
KrkValue _unused;
|
||||||
for (unsigned int i = 0; i < self->entries.capacity; ++i) {
|
for (unsigned int i = 0; i < self->entries.capacity; ++i) {
|
||||||
if (IS_KWARGS(self->entries.entries[i].key)) continue;
|
if (IS_KWARGS(self->entries.entries[i].key)) continue;
|
||||||
if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
|
if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
|
||||||
}
|
}
|
||||||
return BOOLEAN_VAL(1);
|
return BOOLEAN_VAL(1);
|
||||||
}
|
}
|
||||||
@ -227,10 +230,10 @@ KRK_Method(set,__le__) {
|
|||||||
if (!IS_set(argv[1]))
|
if (!IS_set(argv[1]))
|
||||||
return NOTIMPL_VAL();
|
return NOTIMPL_VAL();
|
||||||
struct Set * them = AS_set(argv[1]);
|
struct Set * them = AS_set(argv[1]);
|
||||||
KrkValue v;
|
KrkValue _unused;
|
||||||
for (unsigned int i = 0; i < self->entries.capacity; ++i) {
|
for (unsigned int i = 0; i < self->entries.capacity; ++i) {
|
||||||
if (IS_KWARGS(self->entries.entries[i].key)) continue;
|
if (IS_KWARGS(self->entries.entries[i].key)) continue;
|
||||||
if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
|
if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
|
||||||
}
|
}
|
||||||
return BOOLEAN_VAL(1);
|
return BOOLEAN_VAL(1);
|
||||||
}
|
}
|
||||||
@ -243,10 +246,10 @@ KRK_Method(set,__gt__) {
|
|||||||
struct Set * them = AS_set(argv[1]);
|
struct Set * them = AS_set(argv[1]);
|
||||||
if (self->entries.count == them->entries.count)
|
if (self->entries.count == them->entries.count)
|
||||||
return BOOLEAN_VAL(0);
|
return BOOLEAN_VAL(0);
|
||||||
KrkValue v;
|
KrkValue _unused;
|
||||||
for (unsigned int i = 0; i < them->entries.capacity; ++i) {
|
for (unsigned int i = 0; i < them->entries.capacity; ++i) {
|
||||||
if (IS_KWARGS(them->entries.entries[i].key)) continue;
|
if (IS_KWARGS(them->entries.entries[i].key)) continue;
|
||||||
if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
|
if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
|
||||||
}
|
}
|
||||||
return BOOLEAN_VAL(1);
|
return BOOLEAN_VAL(1);
|
||||||
}
|
}
|
||||||
@ -256,10 +259,10 @@ KRK_Method(set,__ge__) {
|
|||||||
if (!IS_set(argv[1]))
|
if (!IS_set(argv[1]))
|
||||||
return NOTIMPL_VAL();
|
return NOTIMPL_VAL();
|
||||||
struct Set * them = AS_set(argv[1]);
|
struct Set * them = AS_set(argv[1]);
|
||||||
KrkValue v;
|
KrkValue _unused;
|
||||||
for (unsigned int i = 0; i < them->entries.capacity; ++i) {
|
for (unsigned int i = 0; i < them->entries.capacity; ++i) {
|
||||||
if (IS_KWARGS(them->entries.entries[i].key)) continue;
|
if (IS_KWARGS(them->entries.entries[i].key)) continue;
|
||||||
if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
|
if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
|
||||||
}
|
}
|
||||||
return BOOLEAN_VAL(1);
|
return BOOLEAN_VAL(1);
|
||||||
}
|
}
|
||||||
@ -388,6 +391,7 @@ void _createAndBind_setClass(void) {
|
|||||||
"@brief Empty the set.\n\n"
|
"@brief Empty the set.\n\n"
|
||||||
"Removes all elements from the set, in-place.");
|
"Removes all elements from the set, in-place.");
|
||||||
BIND_METHOD(set,update);
|
BIND_METHOD(set,update);
|
||||||
|
krk_defineNative(&set->methods, "__str__", FUNC_NAME(set,__repr__));
|
||||||
krk_attachNamedValue(&set->methods, "__hash__", NONE_VAL());
|
krk_attachNamedValue(&set->methods, "__hash__", NONE_VAL());
|
||||||
krk_finalizeClass(set);
|
krk_finalizeClass(set);
|
||||||
|
|
||||||
|
@ -118,24 +118,37 @@ KRK_Method(slice,__init__) {
|
|||||||
|
|
||||||
KRK_Method(slice,__repr__) {
|
KRK_Method(slice,__repr__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL(S("slice(...)"));
|
if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL("slice(...)");
|
||||||
((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
|
((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
|
||||||
struct StringBuilder sb = {0};
|
struct StringBuilder sb = {0};
|
||||||
pushStringBuilderStr(&sb,"slice(",6);
|
pushStringBuilderStr(&sb,"slice(",6);
|
||||||
|
|
||||||
|
KrkClass * type;
|
||||||
|
KrkValue result;
|
||||||
|
|
||||||
/* start */
|
/* start */
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", self->start)) goto _error;
|
type = krk_getType(self->start);
|
||||||
|
krk_push(self->start);
|
||||||
|
result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
|
||||||
pushStringBuilderStr(&sb,", ",2);
|
pushStringBuilderStr(&sb,", ",2);
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", self->end)) goto _error;
|
|
||||||
|
/* end */
|
||||||
|
type = krk_getType(self->end);
|
||||||
|
krk_push(self->end);
|
||||||
|
result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
|
||||||
pushStringBuilderStr(&sb,", ",2);
|
pushStringBuilderStr(&sb,", ",2);
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", self->step)) goto _error;
|
|
||||||
|
/* step */
|
||||||
|
type = krk_getType(self->step);
|
||||||
|
krk_push(self->step);
|
||||||
|
result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
|
||||||
|
|
||||||
pushStringBuilder(&sb,')');
|
pushStringBuilder(&sb,')');
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(slice,start) {
|
KRK_Method(slice,start) {
|
||||||
@ -153,30 +166,9 @@ KRK_Method(slice,step) {
|
|||||||
return self->step;
|
return self->step;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef CURRENT_CTYPE
|
|
||||||
#define CURRENT_CTYPE KrkInstance *
|
|
||||||
#define IS_ellipsis(o) (krk_isInstanceOf(o,KRK_BASE_CLASS(ellipsis)))
|
|
||||||
#define AS_ellipsis(o) ((KrkInstance*)AS_INSTANCE(o))
|
|
||||||
|
|
||||||
KRK_StaticMethod(ellipsis,__new__) {
|
|
||||||
KrkClass * _class = NULL;
|
|
||||||
if (!krk_parseArgs("O!", (const char*[]){"cls"}, KRK_BASE_CLASS(type), &_class)) return NONE_VAL();
|
|
||||||
if (!krk_isSubClass(_class, KRK_BASE_CLASS(ellipsis))) {
|
|
||||||
return krk_runtimeError(vm.exceptions->typeError, "%S is not a subclass of %S", _class->name, KRK_BASE_CLASS(ellipsis)->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
KrkValue out;
|
|
||||||
if (!krk_tableGet_fast(&vm.builtins->fields, S("Ellipsis"), &out)) return krk_runtimeError(vm.exceptions->typeError, "Ellipsis is missing");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Method(ellipsis,__repr__) {
|
|
||||||
return OBJECT_VAL(S("Ellipsis"));
|
|
||||||
}
|
|
||||||
|
|
||||||
_noexport
|
_noexport
|
||||||
void _createAndBind_sliceClass(void) {
|
void _createAndBind_sliceClass(void) {
|
||||||
KrkClass * slice = ADD_BASE_CLASS(KRK_BASE_CLASS(slice), "slice", KRK_BASE_CLASS(object));
|
KrkClass * slice = ADD_BASE_CLASS(vm.baseClasses->sliceClass, "slice", vm.baseClasses->objectClass);
|
||||||
slice->allocSize = sizeof(struct KrkSlice);
|
slice->allocSize = sizeof(struct KrkSlice);
|
||||||
slice->_ongcscan = _slice_gcscan;
|
slice->_ongcscan = _slice_gcscan;
|
||||||
slice->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
slice->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
||||||
@ -185,14 +177,7 @@ void _createAndBind_sliceClass(void) {
|
|||||||
BIND_PROP(slice,start);
|
BIND_PROP(slice,start);
|
||||||
BIND_PROP(slice,end);
|
BIND_PROP(slice,end);
|
||||||
BIND_PROP(slice,step);
|
BIND_PROP(slice,step);
|
||||||
|
krk_defineNative(&slice->methods, "__str__", FUNC_NAME(slice,__repr__));
|
||||||
krk_attachNamedValue(&slice->methods, "__hash__", NONE_VAL());
|
krk_attachNamedValue(&slice->methods, "__hash__", NONE_VAL());
|
||||||
krk_finalizeClass(slice);
|
krk_finalizeClass(slice);
|
||||||
|
|
||||||
KrkClass * ellipsis = ADD_BASE_CLASS(KRK_BASE_CLASS(ellipsis), "ellipsis", KRK_BASE_CLASS(object));
|
|
||||||
krk_attachNamedObject(&vm.builtins->fields, "Ellipsis", (KrkObj*)krk_newInstance(ellipsis));
|
|
||||||
ellipsis->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
|
|
||||||
BIND_STATICMETHOD(ellipsis,__new__);
|
|
||||||
BIND_METHOD(ellipsis,__repr__);
|
|
||||||
krk_finalizeClass(ellipsis);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
103
src/obj_str.c
103
src/obj_str.c
@ -56,7 +56,7 @@ KRK_Method(str,__add__) {
|
|||||||
bl = them->length;
|
bl = them->length;
|
||||||
|
|
||||||
size_t length = al + bl;
|
size_t length = al + bl;
|
||||||
char * chars = KRK_ALLOCATE(char, length + 1);
|
char * chars = ALLOCATE(char, length + 1);
|
||||||
memcpy(chars, a, al);
|
memcpy(chars, a, al);
|
||||||
memcpy(chars + al, b, bl);
|
memcpy(chars + al, b, bl);
|
||||||
chars[length] = '\0';
|
chars[length] = '\0';
|
||||||
@ -102,7 +102,7 @@ KRK_Method(str,__int__) {
|
|||||||
KRK_Method(str,__float__) {
|
KRK_Method(str,__float__) {
|
||||||
METHOD_TAKES_NONE();
|
METHOD_TAKES_NONE();
|
||||||
#ifndef KRK_NO_FLOAT
|
#ifndef KRK_NO_FLOAT
|
||||||
return krk_parse_float(AS_CSTRING(argv[0]),AS_STRING(argv[0])->length);
|
return FLOATING_VAL(strtod(AS_CSTRING(argv[0]),NULL));
|
||||||
#else
|
#else
|
||||||
return krk_runtimeError(vm.exceptions->valueError, "no float support");
|
return krk_runtimeError(vm.exceptions->valueError, "no float support");
|
||||||
#endif
|
#endif
|
||||||
@ -825,47 +825,17 @@ KRK_Method(str,index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(str,startswith) {
|
KRK_Method(str,startswith) {
|
||||||
KrkString * substr;
|
METHOD_TAKES_EXACTLY(1); /* I know the Python versions of these take optional start, end... */
|
||||||
int start = 0;
|
CHECK_ARG(1,str,KrkString*,prefix);
|
||||||
int end = self->codesLength;
|
return BOOLEAN_VAL(substringMatch(self->chars,self->length,prefix->chars,prefix->length));
|
||||||
if (!krk_parseArgs(".O!|ii",(const char*[]){"prefix","start","end"}, KRK_BASE_CLASS(str), &substr, &start, &end)) {
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
WRAP_INDEX(start);
|
|
||||||
WRAP_INDEX(end);
|
|
||||||
|
|
||||||
krk_unicodeString(self);
|
|
||||||
krk_unicodeString(substr);
|
|
||||||
|
|
||||||
if (end < start || (size_t)(end - start) < substr->codesLength) return BOOLEAN_VAL(0);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < substr->codesLength; ++i) {
|
|
||||||
if (KRK_STRING_FAST(self, start + i) != KRK_STRING_FAST(substr,i)) return BOOLEAN_VAL(0);
|
|
||||||
}
|
|
||||||
return BOOLEAN_VAL(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(str,endswith) {
|
KRK_Method(str,endswith) {
|
||||||
KrkString * substr;
|
METHOD_TAKES_EXACTLY(1); /* I know the Python versions of these take optional start, end... */
|
||||||
int start = 0;
|
CHECK_ARG(1,str,KrkString*,suffix);
|
||||||
int end = self->codesLength;
|
if (suffix->length > self->length) return BOOLEAN_VAL(0);
|
||||||
if (!krk_parseArgs(".O!|ii",(const char*[]){"suffix","start","end"}, KRK_BASE_CLASS(str), &substr, &start, &end)) {
|
return BOOLEAN_VAL(substringMatch(self->chars + (self->length - suffix->length),
|
||||||
return NONE_VAL();
|
suffix->length, suffix->chars, suffix->length));
|
||||||
}
|
|
||||||
|
|
||||||
WRAP_INDEX(start);
|
|
||||||
WRAP_INDEX(end);
|
|
||||||
|
|
||||||
krk_unicodeString(self);
|
|
||||||
krk_unicodeString(substr);
|
|
||||||
|
|
||||||
if (end < start || (size_t)(end - start) < substr->codesLength) return BOOLEAN_VAL(0);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < substr->codesLength; ++i) {
|
|
||||||
if (KRK_STRING_FAST(self, (end - i - 1)) != KRK_STRING_FAST(substr,(substr->codesLength - i - 1))) return BOOLEAN_VAL(0);
|
|
||||||
}
|
|
||||||
return BOOLEAN_VAL(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -892,35 +862,33 @@ KRK_Method(str,__repr__) {
|
|||||||
pushStringBuilder(&sb, quote);
|
pushStringBuilder(&sb, quote);
|
||||||
|
|
||||||
for (char * c = AS_CSTRING(argv[0]); c < end; ++c) {
|
for (char * c = AS_CSTRING(argv[0]); c < end; ++c) {
|
||||||
unsigned char ch = *c;
|
switch (*c) {
|
||||||
int addSlash = 0;
|
|
||||||
switch (ch) {
|
|
||||||
/* XXX: Other non-printables should probably be escaped as well. */
|
/* XXX: Other non-printables should probably be escaped as well. */
|
||||||
case '\'': addSlash = (quote == ch); ch = '\''; break;
|
case '\\': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'\\'); break;
|
||||||
case '\"': addSlash = (quote == ch); ch = '\"'; break;
|
case '\'': if (quote == *c) { pushStringBuilder(&sb,'\\'); } pushStringBuilder(&sb,'\''); break;
|
||||||
case '\\': addSlash = 1; ch = '\\'; break;
|
case '\"': if (quote == *c) { pushStringBuilder(&sb,'\\'); } pushStringBuilder(&sb,'\"'); break;
|
||||||
case '\a': addSlash = 1; ch = 'a'; break;
|
case '\a': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'a'); break;
|
||||||
case '\b': addSlash = 1; ch = 'b'; break;
|
case '\b': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'b'); break;
|
||||||
case '\f': addSlash = 1; ch = 'f'; break;
|
case '\f': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'f'); break;
|
||||||
case '\n': addSlash = 1; ch = 'n'; break;
|
case '\n': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'n'); break;
|
||||||
case '\r': addSlash = 1; ch = 'r'; break;
|
case '\r': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'r'); break;
|
||||||
case '\t': addSlash = 1; ch = 't'; break;
|
case '\t': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'t'); break;
|
||||||
case '\v': addSlash = 1; ch = 'v'; break;
|
case '\v': pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'v'); break;
|
||||||
case 27: addSlash = 1; ch = '['; break;
|
case 27: pushStringBuilder(&sb,'\\'); pushStringBuilder(&sb,'['); break;
|
||||||
default:
|
default: {
|
||||||
if (ch < ' ' || ch == 0x7F) {
|
if ((unsigned char)*c < ' ' || (unsigned char)*c == 0x7F) {
|
||||||
pushStringBuilder(&sb,'\\');
|
pushStringBuilder(&sb,'\\');
|
||||||
pushStringBuilder(&sb,'x');
|
pushStringBuilder(&sb,'x');
|
||||||
char hex[3];
|
char hex[3];
|
||||||
snprintf(hex, 3, "%02x", (unsigned char)*c);
|
snprintf(hex, 3, "%02x", (unsigned char)*c);
|
||||||
pushStringBuilder(&sb,hex[0]);
|
pushStringBuilder(&sb,hex[0]);
|
||||||
pushStringBuilder(&sb,hex[1]);
|
pushStringBuilder(&sb,hex[1]);
|
||||||
continue;
|
} else {
|
||||||
|
pushStringBuilder(&sb,*c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (addSlash) krk_pushStringBuilder(&sb,'\\');
|
|
||||||
krk_pushStringBuilder(&sb,ch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pushStringBuilder(&sb, quote);
|
pushStringBuilder(&sb, quote);
|
||||||
@ -1082,8 +1050,8 @@ _corrupt:
|
|||||||
void krk_pushStringBuilder(struct StringBuilder * sb, char c) {
|
void krk_pushStringBuilder(struct StringBuilder * sb, char c) {
|
||||||
if (sb->capacity < sb->length + 1) {
|
if (sb->capacity < sb->length + 1) {
|
||||||
size_t old = sb->capacity;
|
size_t old = sb->capacity;
|
||||||
sb->capacity = KRK_GROW_CAPACITY(old);
|
sb->capacity = GROW_CAPACITY(old);
|
||||||
sb->bytes = KRK_GROW_ARRAY(char, sb->bytes, old, sb->capacity);
|
sb->bytes = GROW_ARRAY(char, sb->bytes, old, sb->capacity);
|
||||||
}
|
}
|
||||||
sb->bytes[sb->length++] = c;
|
sb->bytes[sb->length++] = c;
|
||||||
}
|
}
|
||||||
@ -1093,9 +1061,9 @@ void krk_pushStringBuilderStr(struct StringBuilder * sb, const char *str, size_t
|
|||||||
size_t prevcap = sb->capacity;
|
size_t prevcap = sb->capacity;
|
||||||
while (sb->capacity < sb->length + len) {
|
while (sb->capacity < sb->length + len) {
|
||||||
size_t old = sb->capacity;
|
size_t old = sb->capacity;
|
||||||
sb->capacity = KRK_GROW_CAPACITY(old);
|
sb->capacity = GROW_CAPACITY(old);
|
||||||
}
|
}
|
||||||
sb->bytes = KRK_GROW_ARRAY(char, sb->bytes, prevcap, sb->capacity);
|
sb->bytes = GROW_ARRAY(char, sb->bytes, prevcap, sb->capacity);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
sb->bytes[sb->length++] = *(str++);
|
sb->bytes[sb->length++] = *(str++);
|
||||||
@ -1103,7 +1071,7 @@ void krk_pushStringBuilderStr(struct StringBuilder * sb, const char *str, size_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void _freeStringBuilder(struct StringBuilder * sb) {
|
static void _freeStringBuilder(struct StringBuilder * sb) {
|
||||||
KRK_FREE_ARRAY(char,sb->bytes, sb->capacity);
|
FREE_ARRAY(char,sb->bytes, sb->capacity);
|
||||||
sb->bytes = NULL;
|
sb->bytes = NULL;
|
||||||
sb->length = 0;
|
sb->length = 0;
|
||||||
sb->capacity = 0;
|
sb->capacity = 0;
|
||||||
@ -1133,7 +1101,9 @@ int krk_pushStringBuilderFormatV(struct StringBuilder * sb, const char * fmt, va
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Bail on exception */
|
/* Bail on exception */
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) break;
|
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
++f;
|
++f;
|
||||||
|
|
||||||
@ -1225,8 +1195,6 @@ int krk_pushStringBuilderFormatV(struct StringBuilder * sb, const char * fmt, va
|
|||||||
krk_push(res);
|
krk_push(res);
|
||||||
if (IS_STRING(res)) {
|
if (IS_STRING(res)) {
|
||||||
pushStringBuilderStr(sb, AS_CSTRING(res), AS_STRING(res)->length);
|
pushStringBuilderStr(sb, AS_CSTRING(res), AS_STRING(res)->length);
|
||||||
} else if (!(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
|
|
||||||
krk_runtimeError(vm.exceptions->typeError, "__repr__ returned non-string (type %T)", res);
|
|
||||||
}
|
}
|
||||||
krk_pop();
|
krk_pop();
|
||||||
}
|
}
|
||||||
@ -1251,7 +1219,6 @@ int krk_pushStringBuilderFormatV(struct StringBuilder * sb, const char * fmt, va
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return 0;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ static int _tuple_init_callback(void * context, const KrkValue * values, size_t
|
|||||||
KrkValueArray * positionals = context;
|
KrkValueArray * positionals = context;
|
||||||
if (positionals->count + count > positionals->capacity) {
|
if (positionals->count + count > positionals->capacity) {
|
||||||
size_t old = positionals->capacity;
|
size_t old = positionals->capacity;
|
||||||
positionals->capacity = (count == 1) ? KRK_GROW_CAPACITY(old) : (positionals->count + count);
|
positionals->capacity = (count == 1) ? GROW_CAPACITY(old) : (positionals->count + count);
|
||||||
positionals->values = KRK_GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
|
positionals->values = GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
@ -148,8 +148,15 @@ KRK_Method(tuple,__repr__) {
|
|||||||
pushStringBuilder(&sb, '(');
|
pushStringBuilder(&sb, '(');
|
||||||
|
|
||||||
for (size_t i = 0; i < self->values.count; ++i) {
|
for (size_t i = 0; i < self->values.count; ++i) {
|
||||||
if (i) pushStringBuilderStr(&sb, ", ", 2);
|
KrkClass * type = krk_getType(self->values.values[i]);
|
||||||
if (!krk_pushStringBuilderFormat(&sb, "%R", self->values.values[i])) goto _error;
|
krk_push(self->values.values[i]);
|
||||||
|
KrkValue result = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (IS_STRING(result)) {
|
||||||
|
pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
|
||||||
|
}
|
||||||
|
if (i != self->values.count - 1) {
|
||||||
|
pushStringBuilderStr(&sb, ", ", 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->values.count == 1) {
|
if (self->values.count == 1) {
|
||||||
@ -159,11 +166,6 @@ KRK_Method(tuple,__repr__) {
|
|||||||
pushStringBuilder(&sb, ')');
|
pushStringBuilder(&sb, ')');
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
||||||
return finishStringBuilder(&sb);
|
return finishStringBuilder(&sb);
|
||||||
|
|
||||||
_error:
|
|
||||||
((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
|
|
||||||
krk_discardStringBuilder(&sb);
|
|
||||||
return NONE_VAL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(tuple,__add__) {
|
KRK_Method(tuple,__add__) {
|
||||||
@ -281,6 +283,7 @@ void _createAndBind_tupleClass(void) {
|
|||||||
BIND_METHOD(tuple,__hash__);
|
BIND_METHOD(tuple,__hash__);
|
||||||
BIND_METHOD(tuple,__add__);
|
BIND_METHOD(tuple,__add__);
|
||||||
BIND_METHOD(tuple,__mul__);
|
BIND_METHOD(tuple,__mul__);
|
||||||
|
krk_defineNative(&tuple->methods, "__str__", FUNC_NAME(tuple,__repr__));
|
||||||
krk_finalizeClass(tuple);
|
krk_finalizeClass(tuple);
|
||||||
|
|
||||||
ADD_BASE_CLASS(vm.baseClasses->tupleiteratorClass, "tupleiterator", vm.baseClasses->objectClass);
|
ADD_BASE_CLASS(vm.baseClasses->tupleiteratorClass, "tupleiterator", vm.baseClasses->objectClass);
|
||||||
|
20
src/object.c
20
src/object.c
@ -7,7 +7,6 @@
|
|||||||
#include <kuroko/value.h>
|
#include <kuroko/value.h>
|
||||||
#include <kuroko/vm.h>
|
#include <kuroko/vm.h>
|
||||||
#include <kuroko/table.h>
|
#include <kuroko/table.h>
|
||||||
#include <kuroko/threads.h>
|
|
||||||
|
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
|
||||||
@ -172,8 +171,6 @@ uint32_t krk_unicodeCodepoint(KrkString * string, size_t index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int krk_tableSetExact(KrkTable * table, KrkValue key, KrkValue value);
|
|
||||||
|
|
||||||
static KrkString * allocateString(char * chars, size_t length, uint32_t hash) {
|
static KrkString * allocateString(char * chars, size_t length, uint32_t hash) {
|
||||||
size_t codesLength = 0;
|
size_t codesLength = 0;
|
||||||
int type = checkString(chars,length,&codesLength);
|
int type = checkString(chars,length,&codesLength);
|
||||||
@ -189,7 +186,7 @@ static KrkString * allocateString(char * chars, size_t length, uint32_t hash) {
|
|||||||
string->codes = NULL;
|
string->codes = NULL;
|
||||||
if (type == KRK_OBJ_FLAGS_STRING_ASCII) string->codes = string->chars;
|
if (type == KRK_OBJ_FLAGS_STRING_ASCII) string->codes = string->chars;
|
||||||
krk_push(OBJECT_VAL(string));
|
krk_push(OBJECT_VAL(string));
|
||||||
krk_tableSetExact(&vm.strings, OBJECT_VAL(string), NONE_VAL());
|
krk_tableSet(&vm.strings, OBJECT_VAL(string), NONE_VAL());
|
||||||
krk_pop();
|
krk_pop();
|
||||||
_release_lock(_stringLock);
|
_release_lock(_stringLock);
|
||||||
return string;
|
return string;
|
||||||
@ -210,7 +207,7 @@ KrkString * krk_takeString(char * chars, size_t length) {
|
|||||||
_obtain_lock(_stringLock);
|
_obtain_lock(_stringLock);
|
||||||
KrkString * interned = krk_tableFindString(&vm.strings, chars, length, hash);
|
KrkString * interned = krk_tableFindString(&vm.strings, chars, length, hash);
|
||||||
if (interned != NULL) {
|
if (interned != NULL) {
|
||||||
free(chars); /* This string isn't owned by us yet, so free, not KRK_FREE_ARRAY */
|
free(chars); /* This string isn't owned by us yet, so free, not FREE_ARRAY */
|
||||||
_release_lock(_stringLock);
|
_release_lock(_stringLock);
|
||||||
return interned;
|
return interned;
|
||||||
}
|
}
|
||||||
@ -229,7 +226,7 @@ KrkString * krk_copyString(const char * chars, size_t length) {
|
|||||||
_release_lock(_stringLock);
|
_release_lock(_stringLock);
|
||||||
return interned;
|
return interned;
|
||||||
}
|
}
|
||||||
char * heapChars = KRK_ALLOCATE(char, length + 1);
|
char * heapChars = ALLOCATE(char, length + 1);
|
||||||
memcpy(heapChars, chars ? chars : "", length);
|
memcpy(heapChars, chars ? chars : "", length);
|
||||||
heapChars[length] = '\0';
|
heapChars[length] = '\0';
|
||||||
KrkString * result = allocateString(heapChars, length, hash);
|
KrkString * result = allocateString(heapChars, length, hash);
|
||||||
@ -242,7 +239,7 @@ KrkString * krk_takeStringVetted(char * chars, size_t length, size_t codesLength
|
|||||||
_obtain_lock(_stringLock);
|
_obtain_lock(_stringLock);
|
||||||
KrkString * interned = krk_tableFindString(&vm.strings, chars, length, hash);
|
KrkString * interned = krk_tableFindString(&vm.strings, chars, length, hash);
|
||||||
if (interned != NULL) {
|
if (interned != NULL) {
|
||||||
KRK_FREE_ARRAY(char, chars, length + 1);
|
FREE_ARRAY(char, chars, length + 1);
|
||||||
_release_lock(_stringLock);
|
_release_lock(_stringLock);
|
||||||
return interned;
|
return interned;
|
||||||
}
|
}
|
||||||
@ -255,7 +252,7 @@ KrkString * krk_takeStringVetted(char * chars, size_t length, size_t codesLength
|
|||||||
string->codes = NULL;
|
string->codes = NULL;
|
||||||
if (type == KRK_OBJ_FLAGS_STRING_ASCII) string->codes = string->chars;
|
if (type == KRK_OBJ_FLAGS_STRING_ASCII) string->codes = string->chars;
|
||||||
krk_push(OBJECT_VAL(string));
|
krk_push(OBJECT_VAL(string));
|
||||||
krk_tableSetExact(&vm.strings, OBJECT_VAL(string), NONE_VAL());
|
krk_tableSet(&vm.strings, OBJECT_VAL(string), NONE_VAL());
|
||||||
krk_pop();
|
krk_pop();
|
||||||
_release_lock(_stringLock);
|
_release_lock(_stringLock);
|
||||||
return string;
|
return string;
|
||||||
@ -274,7 +271,6 @@ KrkCodeObject * krk_newCodeObject(void) {
|
|||||||
krk_initValueArray(&codeobject->positionalArgNames);
|
krk_initValueArray(&codeobject->positionalArgNames);
|
||||||
krk_initValueArray(&codeobject->keywordArgNames);
|
krk_initValueArray(&codeobject->keywordArgNames);
|
||||||
krk_initChunk(&codeobject->chunk);
|
krk_initChunk(&codeobject->chunk);
|
||||||
codeobject->jumpTargets = NONE_VAL();
|
|
||||||
return codeobject;
|
return codeobject;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +284,7 @@ KrkNative * krk_newNative(NativeFn function, const char * name, int type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KrkClosure * krk_newClosure(KrkCodeObject * function, KrkValue globals) {
|
KrkClosure * krk_newClosure(KrkCodeObject * function, KrkValue globals) {
|
||||||
KrkUpvalue ** upvalues = KRK_ALLOCATE(KrkUpvalue*, function->upvalueCount);
|
KrkUpvalue ** upvalues = ALLOCATE(KrkUpvalue*, function->upvalueCount);
|
||||||
for (size_t i = 0; i < function->upvalueCount; ++i) {
|
for (size_t i = 0; i < function->upvalueCount; ++i) {
|
||||||
upvalues[i] = NULL;
|
upvalues[i] = NULL;
|
||||||
}
|
}
|
||||||
@ -359,7 +355,7 @@ KrkTuple * krk_newTuple(size_t length) {
|
|||||||
krk_initValueArray(&tuple->values);
|
krk_initValueArray(&tuple->values);
|
||||||
krk_push(OBJECT_VAL(tuple));
|
krk_push(OBJECT_VAL(tuple));
|
||||||
tuple->values.capacity = length;
|
tuple->values.capacity = length;
|
||||||
tuple->values.values = KRK_GROW_ARRAY(KrkValue,NULL,0,length);
|
tuple->values.values = GROW_ARRAY(KrkValue,NULL,0,length);
|
||||||
krk_pop();
|
krk_pop();
|
||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
@ -369,7 +365,7 @@ KrkBytes * krk_newBytes(size_t length, uint8_t * source) {
|
|||||||
bytes->length = length;
|
bytes->length = length;
|
||||||
bytes->bytes = NULL;
|
bytes->bytes = NULL;
|
||||||
krk_push(OBJECT_VAL(bytes));
|
krk_push(OBJECT_VAL(bytes));
|
||||||
bytes->bytes = KRK_ALLOCATE(uint8_t, length);
|
bytes->bytes = ALLOCATE(uint8_t, length);
|
||||||
bytes->obj.hash = -1;
|
bytes->obj.hash = -1;
|
||||||
if (source) {
|
if (source) {
|
||||||
memcpy(bytes->bytes, source, length);
|
memcpy(bytes->bytes, source, length);
|
||||||
|
@ -25,20 +25,16 @@ typedef enum {
|
|||||||
#define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
#define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||||
#define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
#define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||||
#define JUMP(opc,sign) OPCODE(opc)
|
#define JUMP(opc,sign) OPCODE(opc)
|
||||||
#define COMPLICATED(opc,more) OPCODE(opc)
|
|
||||||
#define CLOSURE_MORE
|
#define CLOSURE_MORE
|
||||||
#define EXPAND_ARGS_MORE
|
#define EXPAND_ARGS_MORE
|
||||||
#define FORMAT_VALUE_MORE
|
#define FORMAT_VALUE_MORE
|
||||||
#define LOCAL_MORE
|
#define LOCAL_MORE
|
||||||
#define OVERLONG_JUMP_MORE
|
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#undef SIMPLE
|
#undef SIMPLE
|
||||||
#undef OPERANDB
|
#undef OPERANDB
|
||||||
#undef OPERAND
|
#undef OPERAND
|
||||||
#undef CONSTANT
|
#undef CONSTANT
|
||||||
#undef JUMP
|
#undef JUMP
|
||||||
#undef COMPLICATED
|
|
||||||
#undef OVERLONG_JUMP_MORE
|
|
||||||
#undef CLOSURE_MORE
|
#undef CLOSURE_MORE
|
||||||
#undef LOCAL_MORE
|
#undef LOCAL_MORE
|
||||||
#undef EXPAND_ARGS_MORE
|
#undef EXPAND_ARGS_MORE
|
||||||
|
@ -49,7 +49,7 @@ SIMPLE(OP_RAISE_FROM)
|
|||||||
SIMPLE(OP_SHIFTLEFT)
|
SIMPLE(OP_SHIFTLEFT)
|
||||||
JUMP(OP_POP_JUMP_IF_FALSE,+)
|
JUMP(OP_POP_JUMP_IF_FALSE,+)
|
||||||
SIMPLE(OP_ANNOTATE)
|
SIMPLE(OP_ANNOTATE)
|
||||||
JUMP(OP_FILTER_EXCEPT,+)
|
SIMPLE(OP_FILTER_EXCEPT)
|
||||||
SIMPLE(OP_BITAND)
|
SIMPLE(OP_BITAND)
|
||||||
SIMPLE(OP_NONE)
|
SIMPLE(OP_NONE)
|
||||||
SIMPLE(OP_POP)
|
SIMPLE(OP_POP)
|
||||||
@ -120,7 +120,3 @@ SIMPLE(OP_SET_ADD_TOP)
|
|||||||
SIMPLE(OP_TUPLE_FROM_LIST)
|
SIMPLE(OP_TUPLE_FROM_LIST)
|
||||||
|
|
||||||
OPERAND(OP_UNPACK_EX,NOOP)
|
OPERAND(OP_UNPACK_EX,NOOP)
|
||||||
JUMP(OP_ENTER_EXCEPT,+)
|
|
||||||
SIMPLE(OP_SWAP_POP)
|
|
||||||
COMPLICATED(OP_OVERLONG_JUMP,OVERLONG_JUMP_MORE)
|
|
||||||
SIMPLE(OP_PUSH_BUILD_CLASS)
|
|
||||||
|
@ -23,9 +23,6 @@
|
|||||||
/* Did you know this is actually specified to not exist in a header? */
|
/* Did you know this is actually specified to not exist in a header? */
|
||||||
extern char ** environ;
|
extern char ** environ;
|
||||||
|
|
||||||
static KrkClass * os_Environ;
|
|
||||||
static KrkClass * os_stat_result;
|
|
||||||
|
|
||||||
#define DO_KEY(key) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)krk_copyString(buf. key, strlen(buf .key)))
|
#define DO_KEY(key) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)krk_copyString(buf. key, strlen(buf .key)))
|
||||||
#define S_KEY(key,val) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)val);
|
#define S_KEY(key,val) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)val);
|
||||||
|
|
||||||
@ -92,7 +89,7 @@ KRK_Function(uname) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define AS_Environ(o) (AS_INSTANCE(o))
|
#define AS_Environ(o) (AS_INSTANCE(o))
|
||||||
#define IS_Environ(o) (krk_isInstanceOf(o,os_Environ))
|
#define IS_Environ(o) (krk_isInstanceOf(o,KRK_BASE_CLASS(Environ)))
|
||||||
#define CURRENT_CTYPE KrkInstance*
|
#define CURRENT_CTYPE KrkInstance*
|
||||||
|
|
||||||
static int _setVar(KrkString * key, KrkString * val) {
|
static int _setVar(KrkString * key, KrkString * val) {
|
||||||
@ -144,7 +141,7 @@ KRK_Method(Environ,__delitem__) {
|
|||||||
|
|
||||||
static void _loadEnviron(KrkInstance * module) {
|
static void _loadEnviron(KrkInstance * module) {
|
||||||
/* Create a new class to subclass `dict` */
|
/* Create a new class to subclass `dict` */
|
||||||
KrkClass * Environ = krk_makeClass(module, &os_Environ, "_Environ", vm.baseClasses->dictClass);
|
KrkClass * Environ = krk_makeClass(module, &KRK_BASE_CLASS(Environ), "_Environ", vm.baseClasses->dictClass);
|
||||||
krk_attachNamedObject(&module->fields, "_Environ", (KrkObj*)Environ);
|
krk_attachNamedObject(&module->fields, "_Environ", (KrkObj*)Environ);
|
||||||
|
|
||||||
/* Add our set method that should also call dict's set method */
|
/* Add our set method that should also call dict's set method */
|
||||||
@ -556,7 +553,7 @@ KRK_Function(stat) {
|
|||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
|
return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
|
||||||
}
|
}
|
||||||
KrkInstance * out = krk_newInstance(os_stat_result);
|
KrkInstance * out = krk_newInstance(KRK_BASE_CLASS(stat_result));
|
||||||
krk_push(OBJECT_VAL(out));
|
krk_push(OBJECT_VAL(out));
|
||||||
|
|
||||||
SET(st_dev);
|
SET(st_dev);
|
||||||
@ -574,7 +571,7 @@ KRK_Function(stat) {
|
|||||||
}
|
}
|
||||||
#undef SET
|
#undef SET
|
||||||
|
|
||||||
#define IS_stat_result(o) (krk_isInstanceOf(o,os_stat_result))
|
#define IS_stat_result(o) (krk_isInstanceOf(o,KRK_BASE_CLASS(stat_result)))
|
||||||
#define AS_stat_result(o) AS_INSTANCE(o)
|
#define AS_stat_result(o) AS_INSTANCE(o)
|
||||||
#define CURRENT_NAME self
|
#define CURRENT_NAME self
|
||||||
|
|
||||||
@ -617,7 +614,49 @@ KRK_Method(stat_result,__repr__) {
|
|||||||
return krk_pop();
|
return krk_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Module(os) {
|
KRK_Function(S_ISBLK) {
|
||||||
|
int mode;
|
||||||
|
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
||||||
|
return INTEGER_VAL(S_ISBLK(mode));
|
||||||
|
}
|
||||||
|
KRK_Function(S_ISCHR) {
|
||||||
|
int mode;
|
||||||
|
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
||||||
|
return INTEGER_VAL(S_ISCHR(mode));
|
||||||
|
}
|
||||||
|
KRK_Function(S_ISDIR) {
|
||||||
|
int mode;
|
||||||
|
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
||||||
|
return INTEGER_VAL(S_ISDIR(mode));
|
||||||
|
}
|
||||||
|
KRK_Function(S_ISFIFO) {
|
||||||
|
int mode;
|
||||||
|
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
||||||
|
return INTEGER_VAL(S_ISFIFO(mode));
|
||||||
|
}
|
||||||
|
KRK_Function(S_ISREG) {
|
||||||
|
int mode;
|
||||||
|
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
||||||
|
return INTEGER_VAL(S_ISREG(mode));
|
||||||
|
}
|
||||||
|
#ifndef _WIN32
|
||||||
|
KRK_Function(S_ISLNK) {
|
||||||
|
int mode;
|
||||||
|
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
||||||
|
return INTEGER_VAL(S_ISLNK(mode));
|
||||||
|
}
|
||||||
|
KRK_Function(S_ISSOCK) {
|
||||||
|
int mode;
|
||||||
|
if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
|
||||||
|
return INTEGER_VAL(S_ISSOCK(mode));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void krk_module_init_os(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_attachNamedObject(&vm.modules, "os", (KrkObj*)module);
|
||||||
|
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("os"));
|
||||||
|
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||||
KRK_DOC(module, "@brief Provides access to low-level system operations.");
|
KRK_DOC(module, "@brief Provides access to low-level system operations.");
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -830,7 +869,7 @@ KRK_Module(os) {
|
|||||||
_loadEnviron(module);
|
_loadEnviron(module);
|
||||||
|
|
||||||
/* Nothing special */
|
/* Nothing special */
|
||||||
KrkClass * stat_result = krk_makeClass(module, &os_stat_result, "stat_result", vm.baseClasses->objectClass);
|
KrkClass * stat_result = krk_makeClass(module, &KRK_BASE_CLASS(stat_result), "stat_result", vm.baseClasses->objectClass);
|
||||||
BIND_METHOD(stat_result,__repr__);
|
BIND_METHOD(stat_result,__repr__);
|
||||||
krk_finalizeClass(stat_result);
|
krk_finalizeClass(stat_result);
|
||||||
|
|
||||||
@ -838,6 +877,23 @@ KRK_Module(os) {
|
|||||||
"@brief Get the status of a file\n"
|
"@brief Get the status of a file\n"
|
||||||
"@arguments path\n\n"
|
"@arguments path\n\n"
|
||||||
"Runs the @c stat system call on @p path. Returns a @ref stat_result.\n");
|
"Runs the @c stat system call on @p path. Returns a @ref stat_result.\n");
|
||||||
|
|
||||||
|
module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_attachNamedObject(&vm.modules, "stat", (KrkObj*)module);
|
||||||
|
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("stat"));
|
||||||
|
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||||
|
KRK_DOC(module,
|
||||||
|
"@brief Functions to check results from @ref stat calls.");
|
||||||
|
|
||||||
|
BIND_FUNC(module,S_ISBLK);
|
||||||
|
BIND_FUNC(module,S_ISCHR);
|
||||||
|
BIND_FUNC(module,S_ISDIR);
|
||||||
|
BIND_FUNC(module,S_ISFIFO);
|
||||||
|
BIND_FUNC(module,S_ISREG);
|
||||||
|
#ifndef _WIN32
|
||||||
|
BIND_FUNC(module,S_ISLNK);
|
||||||
|
BIND_FUNC(module,S_ISSOCK);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
154
src/parseargs.c
154
src/parseargs.c
@ -1,75 +1,29 @@
|
|||||||
/**
|
|
||||||
* @brief Function argument parser.
|
|
||||||
*
|
|
||||||
* Provides a simple interface for parsing arguments passed to native functions.
|
|
||||||
*
|
|
||||||
* This is similar to CPython's PyArg_ParseTupleAndKeywords, and many of the options
|
|
||||||
* work the same way (though with some exceptions). With the utilities provided here,
|
|
||||||
* C bindings can parse positional and keyword arguments, with automatic type checking
|
|
||||||
* and conversion to C types.
|
|
||||||
*/
|
|
||||||
#include <kuroko/vm.h>
|
#include <kuroko/vm.h>
|
||||||
#include <kuroko/util.h>
|
#include <kuroko/util.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Format a TypeError exception for an argument.
|
* For use with @c ! formats, collects a @c KrkClass* and compares if the arg
|
||||||
*
|
* is set. As a special case, the type may be @c NULL in which case failure is
|
||||||
* @param method_name Method name from parseVArgs, after possibly modification by `:`
|
* guaranteed; this allows the standard library to reference potentially
|
||||||
* @param expected Description of expected type; generally a type name, but maybe something like "str of length 1".
|
* uninitialized types (like fileio.File which may be uninitialized if the
|
||||||
* @param arg The value passed that failed the type check.
|
* module is not loaded, but may still need to be referenced as a potential
|
||||||
* @param argName Name of the argument. If NULL or zero-length, argument name is not included in the description.
|
* type in a function like @c print ).
|
||||||
*/
|
*/
|
||||||
_cold
|
static int matchType(const char * _method_name, KrkClass * type, KrkValue arg) {
|
||||||
static void raise_TypeError(const char * method_name, const char * expected, KrkValue arg, const char * argName) {
|
if (arg != KWARGS_VAL(0) && !krk_isInstanceOf(arg, type)) {
|
||||||
krk_runtimeError(vm.exceptions->typeError,
|
krk_runtimeError(vm.exceptions->typeError, "%s() expects %s, not '%T'",
|
||||||
"%s()%s%s expects %s, not '%T'",
|
_method_name, type ? type->name->chars : "unknown type", arg);
|
||||||
method_name, (argName && *argName) ? " argument " : "", (argName && *argName) ? argName : "",
|
return 0;
|
||||||
expected, arg);
|
}
|
||||||
}
|
return 1;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the method name to use for an error message.
|
|
||||||
*
|
|
||||||
* If the format string has a ':' it is taken as the start of an alternative method name
|
|
||||||
* to include in error messages. This may be useful when calling the macro version of
|
|
||||||
* @c krk_parseArgs in a @c __new__ or @c __init__ method.
|
|
||||||
*
|
|
||||||
* @param method_name Original method name passed to krk_parseArgs.
|
|
||||||
* @param fmt Pointer to somewhere in the format string up to the colon.
|
|
||||||
*/
|
|
||||||
_cold
|
|
||||||
static const char * methodName(const char * method_name, const char * fmt) {
|
|
||||||
const char * maybeColon = strchr(fmt, ':');
|
|
||||||
return maybeColon ? maybeColon + 1 : method_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Just to avoid repeating ourselves... */
|
|
||||||
#define _method_name (methodName(orig_method_name, fmt))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Extract arguments from kwargs dict, but keep references to them.
|
|
||||||
*
|
|
||||||
* Searches for @p argName in the @p kwargs dict. If found, extracts the value
|
|
||||||
* into @p out and stores a reference to it in @p refList and then deletes
|
|
||||||
* the original entry from @p kwargs.
|
|
||||||
*
|
|
||||||
* @param kwargs Original keyword args dictionary, which will be mutated.
|
|
||||||
* @param argName Argument name to search for.
|
|
||||||
* @param out Slot to place argument value in.
|
|
||||||
* @param refList List to store references for garbage collection.
|
|
||||||
* @returns Non-zero if the argument was not found.
|
|
||||||
*/
|
|
||||||
static int extractKwArg(KrkTable * kwargs, KrkString * argName, KrkValue * out, KrkValueArray * refList) {
|
|
||||||
if (!krk_tableGet_fast(kwargs, argName, out)) return 1;
|
|
||||||
krk_writeValueArray(refList, *out);
|
|
||||||
krk_tableDeleteExact(kwargs, OBJECT_VAL(argName));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validate and parse arguments to a function similar to how managed
|
* @brief Validate and parse arguments to a function similar to how managed
|
||||||
* function arguments are handled.
|
* function arguments are handled.
|
||||||
*
|
*
|
||||||
|
* This attempts to emulate CPythons' PyArg_ParseTupleAndKeywords.
|
||||||
|
*
|
||||||
* This works like a fancy scanf. We accept the original argument specification
|
* This works like a fancy scanf. We accept the original argument specification
|
||||||
* (argc,argv,hasKw), a format string, an array of argument names, and then var
|
* (argc,argv,hasKw), a format string, an array of argument names, and then var
|
||||||
* args that are generally pointers to where to stick results.
|
* args that are generally pointers to where to stick results.
|
||||||
@ -83,7 +37,7 @@ static int extractKwArg(KrkTable * kwargs, KrkString * argName, KrkValue * out,
|
|||||||
* @returns 1 on success, 0 on error.
|
* @returns 1 on success, 0 on error.
|
||||||
*/
|
*/
|
||||||
int krk_parseVArgs(
|
int krk_parseVArgs(
|
||||||
const char * orig_method_name,
|
const char * _method_name,
|
||||||
int argc, const KrkValue argv[], int hasKw,
|
int argc, const KrkValue argv[], int hasKw,
|
||||||
const char * fmt, const char ** names, va_list args) {
|
const char * fmt, const char ** names, va_list args) {
|
||||||
int iarg = 0; /**< Index into positional input arguments */
|
int iarg = 0; /**< Index into positional input arguments */
|
||||||
@ -91,6 +45,11 @@ int krk_parseVArgs(
|
|||||||
int required = 1; /**< Parser state, whether required arguments are being collected */
|
int required = 1; /**< Parser state, whether required arguments are being collected */
|
||||||
int acceptextrakws = 0; /**< Whether extra keyword args should produce an error (0) or not (1) */
|
int acceptextrakws = 0; /**< Whether extra keyword args should produce an error (0) or not (1) */
|
||||||
|
|
||||||
|
const char * maybeColon = strchr(fmt, ':');
|
||||||
|
if (maybeColon) {
|
||||||
|
_method_name = maybeColon + 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (*fmt == '.') {
|
if (*fmt == '.') {
|
||||||
/**
|
/**
|
||||||
* If the format string starts with `.` then argument processing skips the first argument
|
* If the format string starts with `.` then argument processing skips the first argument
|
||||||
@ -168,16 +127,27 @@ int krk_parseVArgs(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int wasPositional = 0;
|
||||||
KrkValue arg = KWARGS_VAL(0);
|
KrkValue arg = KWARGS_VAL(0);
|
||||||
|
krk_push(OBJECT_VAL(krk_copyString(names[oarg],strlen(names[oarg]))));
|
||||||
|
|
||||||
if (iarg < argc) {
|
if (iarg < argc) {
|
||||||
/* Positional arguments are pretty straightforward. */
|
/* Positional arguments are pretty straightforward. */
|
||||||
arg = argv[iarg];
|
arg = argv[iarg];
|
||||||
iarg++;
|
iarg++;
|
||||||
} else if ((required && !hasKw) || (hasKw && extractKwArg(AS_DICT(argv[argc]), krk_copyString(names[oarg],strlen(names[oarg])), &arg, AS_LIST(argv[argc+1])) && required)) {
|
wasPositional = 1;
|
||||||
|
} else if ((required && !hasKw) || (hasKw && !krk_tableGet_fast(AS_DICT(argv[argc]), AS_STRING(krk_peek(0)), &arg) && required)) {
|
||||||
/* If keyword argument lookup failed and this is not an optional argument, raise an exception. */
|
/* If keyword argument lookup failed and this is not an optional argument, raise an exception. */
|
||||||
krk_runtimeError(vm.exceptions->typeError, "%s() missing required positional argument: '%s'",
|
krk_runtimeError(vm.exceptions->typeError, "%s() missing required positional argument: '%S'",
|
||||||
_method_name, names[oarg]);
|
_method_name, AS_STRING(krk_peek(0)));
|
||||||
|
goto _error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasKw && krk_tableDelete(AS_DICT(argv[argc]), krk_peek(0)) && wasPositional) {
|
||||||
|
/* We remove all arguments from kwargs. If we got this argument from a positional argument,
|
||||||
|
* and it was found during deletion, we raise a multiple-defs exception. */
|
||||||
|
krk_runtimeError(vm.exceptions->typeError, "%s() got multiple values for argument '%S'",
|
||||||
|
_method_name, AS_STRING(krk_peek(0)));
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +158,7 @@ int krk_parseVArgs(
|
|||||||
* still want to have all the type checking and automatic parsing. */
|
* still want to have all the type checking and automatic parsing. */
|
||||||
fmt++;
|
fmt++;
|
||||||
int * out = va_arg(args, int*);
|
int * out = va_arg(args, int*);
|
||||||
*out = !krk_valuesSame(arg, KWARGS_VAL(0));
|
*out = arg != KWARGS_VAL(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*fmt == '!') {
|
if (*fmt == '!') {
|
||||||
@ -198,10 +168,7 @@ int krk_parseVArgs(
|
|||||||
* Maybe if you want @c p to only be a bool this could be useful? */
|
* Maybe if you want @c p to only be a bool this could be useful? */
|
||||||
fmt++;
|
fmt++;
|
||||||
KrkClass * type = va_arg(args, KrkClass*);
|
KrkClass * type = va_arg(args, KrkClass*);
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0)) && !krk_isInstanceOf(arg, type)) {
|
if (!matchType(_method_name, type, arg)) goto _error;
|
||||||
raise_TypeError(_method_name, type ? type->name->chars : "unknown type", arg, names[oarg]);
|
|
||||||
goto _error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (argtype) {
|
switch (argtype) {
|
||||||
@ -216,11 +183,11 @@ int krk_parseVArgs(
|
|||||||
*/
|
*/
|
||||||
case 'O': {
|
case 'O': {
|
||||||
KrkObj ** out = va_arg(args, KrkObj**);
|
KrkObj ** out = va_arg(args, KrkObj**);
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
if (IS_NONE(arg)) {
|
if (IS_NONE(arg)) {
|
||||||
*out = NULL;
|
*out = NULL;
|
||||||
} else if (!IS_OBJECT(arg)) {
|
} else if (!IS_OBJECT(arg)) {
|
||||||
raise_TypeError(_method_name, "heap object", arg, names[oarg]);
|
TYPE_ERROR(heap object,arg);
|
||||||
goto _error;
|
goto _error;
|
||||||
} else {
|
} else {
|
||||||
*out = AS_OBJECT(arg);
|
*out = AS_OBJECT(arg);
|
||||||
@ -240,7 +207,7 @@ int krk_parseVArgs(
|
|||||||
*/
|
*/
|
||||||
case 'V': {
|
case 'V': {
|
||||||
KrkValue * out = va_arg(args, KrkValue*);
|
KrkValue * out = va_arg(args, KrkValue*);
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
*out = arg;
|
*out = arg;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -260,15 +227,15 @@ int krk_parseVArgs(
|
|||||||
fmt++;
|
fmt++;
|
||||||
size = va_arg(args, size_t*);
|
size = va_arg(args, size_t*);
|
||||||
}
|
}
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
if (IS_NONE(arg)) {
|
if (arg == NONE_VAL()) {
|
||||||
*out = NULL;
|
*out = NULL;
|
||||||
if (size) *size = 0;
|
if (size) *size = 0;
|
||||||
} else if (IS_STRING(arg)) {
|
} else if (IS_STRING(arg)) {
|
||||||
*out = AS_CSTRING(arg);
|
*out = AS_CSTRING(arg);
|
||||||
if (size) *size = AS_STRING(arg)->length;
|
if (size) *size = AS_STRING(arg)->length;
|
||||||
} else {
|
} else {
|
||||||
raise_TypeError(_method_name, "str or None", arg, names[oarg]);
|
TYPE_ERROR(str or None,arg);
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,12 +252,12 @@ int krk_parseVArgs(
|
|||||||
fmt++;
|
fmt++;
|
||||||
size = va_arg(args, size_t*);
|
size = va_arg(args, size_t*);
|
||||||
}
|
}
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
if (IS_STRING(arg)) {
|
if (IS_STRING(arg)) {
|
||||||
*out = AS_CSTRING(arg);
|
*out = AS_CSTRING(arg);
|
||||||
if (size) *size = AS_STRING(arg)->length;
|
if (size) *size = AS_STRING(arg)->length;
|
||||||
} else {
|
} else {
|
||||||
raise_TypeError(_method_name, "str", arg, names[oarg]);
|
TYPE_ERROR(str,arg);
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,7 +273,7 @@ int krk_parseVArgs(
|
|||||||
* both for future compatibility and to make intent clear, but have no
|
* both for future compatibility and to make intent clear, but have no
|
||||||
* functional difference at this point.
|
* functional difference at this point.
|
||||||
*/
|
*/
|
||||||
#define NUMERIC(c,type) case c: { type * out = va_arg(args, type*); if (!krk_valuesSame(arg, KWARGS_VAL(0))) { if (!krk_long_to_int(arg, sizeof(type), out)) goto _error; } break; }
|
#define NUMERIC(c,type) case c: { type * out = va_arg(args, type*); if (arg != KWARGS_VAL(0)) { if (!krk_long_to_int(arg, sizeof(type), out)) goto _error; } break; }
|
||||||
NUMERIC('b',unsigned char)
|
NUMERIC('b',unsigned char)
|
||||||
NUMERIC('h',short)
|
NUMERIC('h',short)
|
||||||
NUMERIC('H',unsigned short)
|
NUMERIC('H',unsigned short)
|
||||||
@ -325,9 +292,9 @@ int krk_parseVArgs(
|
|||||||
*/
|
*/
|
||||||
case 'C': {
|
case 'C': {
|
||||||
int * out = va_arg(args, int*);
|
int * out = va_arg(args, int*);
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
if (!IS_STRING(arg) || AS_STRING(arg)->codesLength != 1) {
|
if (!IS_STRING(arg) || AS_STRING(arg)->codesLength != 1) {
|
||||||
raise_TypeError(_method_name, "str of length 1", arg, names[oarg]);
|
TYPE_ERROR(str of length 1,arg);
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
*out = krk_unicodeCodepoint(AS_STRING(arg),0);
|
*out = krk_unicodeCodepoint(AS_STRING(arg),0);
|
||||||
@ -341,13 +308,13 @@ int krk_parseVArgs(
|
|||||||
*/
|
*/
|
||||||
case 'f': {
|
case 'f': {
|
||||||
float * out = va_arg(args, float*);
|
float * out = va_arg(args, float*);
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
if (!IS_FLOATING(arg)) {
|
if (!IS_FLOATING(arg)) {
|
||||||
KrkClass * type = krk_getType(arg);
|
KrkClass * type = krk_getType(arg);
|
||||||
krk_push(arg);
|
krk_push(arg);
|
||||||
if (!krk_bindMethod(type, S("__float__"))) {
|
if (!krk_bindMethod(type, S("__float__"))) {
|
||||||
krk_pop();
|
krk_pop();
|
||||||
raise_TypeError(_method_name, "float", arg, names[oarg]);
|
TYPE_ERROR(float,arg);
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
arg = krk_callStack(0);
|
arg = krk_callStack(0);
|
||||||
@ -362,13 +329,13 @@ int krk_parseVArgs(
|
|||||||
*/
|
*/
|
||||||
case 'd': {
|
case 'd': {
|
||||||
double * out = va_arg(args, double*);
|
double * out = va_arg(args, double*);
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
if (!IS_FLOATING(arg)) {
|
if (!IS_FLOATING(arg)) {
|
||||||
KrkClass * type = krk_getType(arg);
|
KrkClass * type = krk_getType(arg);
|
||||||
krk_push(arg);
|
krk_push(arg);
|
||||||
if (!krk_bindMethod(type, S("__float__"))) {
|
if (!krk_bindMethod(type, S("__float__"))) {
|
||||||
krk_pop();
|
krk_pop();
|
||||||
raise_TypeError(_method_name, "float", arg, names[oarg]);
|
TYPE_ERROR(float,arg);
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
arg = krk_callStack(0);
|
arg = krk_callStack(0);
|
||||||
@ -392,7 +359,7 @@ int krk_parseVArgs(
|
|||||||
*/
|
*/
|
||||||
case 'p': {
|
case 'p': {
|
||||||
int * out = va_arg(args, int*);
|
int * out = va_arg(args, int*);
|
||||||
if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
|
if (arg != KWARGS_VAL(0)) {
|
||||||
*out = !krk_isFalsey(arg);
|
*out = !krk_isFalsey(arg);
|
||||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _error;
|
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _error;
|
||||||
}
|
}
|
||||||
@ -405,6 +372,7 @@ int krk_parseVArgs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
krk_pop();
|
||||||
oarg++;
|
oarg++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,15 +396,6 @@ int krk_parseVArgs(
|
|||||||
for (size_t i = 0; i < AS_DICT(argv[argc])->capacity; ++i) {
|
for (size_t i = 0; i < AS_DICT(argv[argc])->capacity; ++i) {
|
||||||
KrkTableEntry * entry = &AS_DICT(argv[argc])->entries[i];
|
KrkTableEntry * entry = &AS_DICT(argv[argc])->entries[i];
|
||||||
if (IS_STRING(entry->key)) {
|
if (IS_STRING(entry->key)) {
|
||||||
/* See if this was the name of an argument, which means it was already provided as a positional argument. */
|
|
||||||
for (int j = 0; j < oarg; ++j) {
|
|
||||||
if (*names[j] && strlen(names[j]) == AS_STRING(entry->key)->length && !strcmp(names[j], AS_CSTRING(entry->key))) {
|
|
||||||
krk_runtimeError(vm.exceptions->typeError, "%s() got multiple values for argument '%s'",
|
|
||||||
_method_name, names[j]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Otherwise just say it was unexpected. */
|
|
||||||
krk_runtimeError(vm.exceptions->typeError, "%s() got an unexpected keyword argument '%S'",
|
krk_runtimeError(vm.exceptions->typeError, "%s() got an unexpected keyword argument '%S'",
|
||||||
_method_name, AS_STRING(entry->key));
|
_method_name, AS_STRING(entry->key));
|
||||||
return 0;
|
return 0;
|
||||||
@ -447,6 +406,7 @@ int krk_parseVArgs(
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
_error:
|
_error:
|
||||||
|
krk_pop(); /* name of argument with error */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,12 +414,12 @@ _error:
|
|||||||
* @brief Variable argument version of @c krk_parseVArgs.
|
* @brief Variable argument version of @c krk_parseVArgs.
|
||||||
*/
|
*/
|
||||||
int krk_parseArgs_impl(
|
int krk_parseArgs_impl(
|
||||||
const char * method_name,
|
const char * _method_name,
|
||||||
int argc, const KrkValue argv[], int hasKw,
|
int argc, const KrkValue argv[], int hasKw,
|
||||||
const char * format, const char ** names, ...) {
|
const char * format, const char ** names, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, names);
|
va_start(args, names);
|
||||||
int result = krk_parseVArgs(method_name,argc,argv,hasKw,format,names,args);
|
int result = krk_parseVArgs(_method_name,argc,argv,hasKw,format,names,args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -67,28 +67,3 @@ struct ParsedFormatSpec {
|
|||||||
* This is the "sdbm" hash. I've been using it in various places for many years,
|
* This is the "sdbm" hash. I've been using it in various places for many years,
|
||||||
* and this specific version apparently traces to gawk. */
|
* and this specific version apparently traces to gawk. */
|
||||||
#define krk_hash_advance(hash,c) do { hash = (int)(c) + (hash << 6) + (hash << 16) - hash; } while (0)
|
#define krk_hash_advance(hash,c) do { hash = (int)(c) + (hash << 6) + (hash << 16) - hash; } while (0)
|
||||||
|
|
||||||
#ifndef KRK_DISABLE_DEBUG
|
|
||||||
#include <kuroko/debug.h>
|
|
||||||
struct BreakpointEntry {
|
|
||||||
KrkCodeObject * inFunction;
|
|
||||||
size_t offset;
|
|
||||||
int flags;
|
|
||||||
uint8_t originalOpcode;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAX_BREAKPOINTS 32
|
|
||||||
struct DebuggerState {
|
|
||||||
int breakpointsCount;
|
|
||||||
KrkDebugCallback debuggerHook;
|
|
||||||
|
|
||||||
/* XXX This was previously thread-local; it probably should still be
|
|
||||||
* specific to an individual thread... but we don't really do
|
|
||||||
* much thread debugging, so... */
|
|
||||||
int repeatStack_top;
|
|
||||||
int repeatStack_bottom;
|
|
||||||
int thisWasForced;
|
|
||||||
|
|
||||||
struct BreakpointEntry breakpoints[MAX_BREAKPOINTS];
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <kuroko/kuroko.h>
|
|
||||||
#include <kuroko/scanner.h>
|
#include <kuroko/scanner.h>
|
||||||
|
|
||||||
KrkScanner krk_initScanner(const char * src) {
|
KrkScanner krk_initScanner(const char * src) {
|
||||||
@ -180,12 +179,6 @@ static KrkToken number(KrkScanner * scanner, char c) {
|
|||||||
while (isDigit(peek(scanner))) advance(scanner);
|
while (isDigit(peek(scanner))) advance(scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peek(scanner) == 'e' || peek(scanner) == 'E') {
|
|
||||||
advance(scanner);
|
|
||||||
if (peek(scanner) == '+' || peek(scanner) == '-') advance(scanner);
|
|
||||||
while (isDigit(peek(scanner))) advance(scanner);
|
|
||||||
}
|
|
||||||
|
|
||||||
return makeToken(scanner, TOKEN_NUMBER);
|
return makeToken(scanner, TOKEN_NUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,9 +349,9 @@ KrkToken krk_scanToken(KrkScanner * scanner) {
|
|||||||
case '[': return makeToken(scanner, TOKEN_LEFT_SQUARE);
|
case '[': return makeToken(scanner, TOKEN_LEFT_SQUARE);
|
||||||
case ']': return makeToken(scanner, TOKEN_RIGHT_SQUARE);
|
case ']': return makeToken(scanner, TOKEN_RIGHT_SQUARE);
|
||||||
case ',': return makeToken(scanner, TOKEN_COMMA);
|
case ',': return makeToken(scanner, TOKEN_COMMA);
|
||||||
|
case '.': return makeToken(scanner, TOKEN_DOT);
|
||||||
case ';': return makeToken(scanner, TOKEN_SEMICOLON);
|
case ';': return makeToken(scanner, TOKEN_SEMICOLON);
|
||||||
case '~': return makeToken(scanner, TOKEN_TILDE);
|
case '~': return makeToken(scanner, TOKEN_TILDE);
|
||||||
case '.': return makeToken(scanner, peek(scanner) == '.' ? (peekNext(scanner,1) == '.' ? (advance(scanner), advance(scanner), TOKEN_ELLIPSIS) : TOKEN_DOT) : TOKEN_DOT);
|
|
||||||
|
|
||||||
case ':': return makeToken(scanner, match(scanner, '=') ? TOKEN_WALRUS : TOKEN_COLON);
|
case ':': return makeToken(scanner, match(scanner, '=') ? TOKEN_WALRUS : TOKEN_COLON);
|
||||||
case '!': return makeToken(scanner, match(scanner, '=') ? TOKEN_BANG_EQUAL : TOKEN_BANG);
|
case '!': return makeToken(scanner, match(scanner, '=') ? TOKEN_BANG_EQUAL : TOKEN_BANG);
|
||||||
|
45
src/sys.c
45
src/sys.c
@ -4,12 +4,12 @@
|
|||||||
#include <kuroko/util.h>
|
#include <kuroko/util.h>
|
||||||
|
|
||||||
#define KRK_VERSION_MAJOR 1
|
#define KRK_VERSION_MAJOR 1
|
||||||
#define KRK_VERSION_MINOR 5
|
#define KRK_VERSION_MINOR 4
|
||||||
#define KRK_VERSION_PATCH 0
|
#define KRK_VERSION_PATCH 0
|
||||||
#define KRK_VERSION_LEVEL 0xa
|
#define KRK_VERSION_LEVEL 0xf
|
||||||
#define KRK_VERSION_SERIAL 0x1
|
#define KRK_VERSION_SERIAL 0x0
|
||||||
|
|
||||||
#define KRK_VERSION_EXTRA_BASE "a1"
|
#define KRK_VERSION_EXTRA_BASE ""
|
||||||
|
|
||||||
#ifndef KRK_STATIC_ONLY
|
#ifndef KRK_STATIC_ONLY
|
||||||
#define KRK_VERSION_EXTRA KRK_VERSION_EXTRA_BASE
|
#define KRK_VERSION_EXTRA KRK_VERSION_EXTRA_BASE
|
||||||
@ -23,10 +23,6 @@
|
|||||||
# define KRK_BUILD_COMPILER "GCC " __VERSION__
|
# define KRK_BUILD_COMPILER "GCC " __VERSION__
|
||||||
#elif (defined(__clang__))
|
#elif (defined(__clang__))
|
||||||
# define KRK_BUILD_COMPILER "clang " __clang_version__
|
# define KRK_BUILD_COMPILER "clang " __clang_version__
|
||||||
#elif (defined(_MSC_VER) && !defined(__clang__))
|
|
||||||
# define KRK_ARG_STR(str) #str
|
|
||||||
# define KRK_ARG_LOL(s) KRK_ARG_STR(s)
|
|
||||||
# define KRK_BUILD_COMPILER "msvc " KRK_ARG_LOL(_MSC_FULL_VER)
|
|
||||||
#else
|
#else
|
||||||
# define KRK_BUILD_COMPILER ""
|
# define KRK_BUILD_COMPILER ""
|
||||||
#endif
|
#endif
|
||||||
@ -77,15 +73,12 @@ KRK_Function(getsizeof) {
|
|||||||
mySize += sizeof(uint8_t) * self->chunk.capacity;
|
mySize += sizeof(uint8_t) * self->chunk.capacity;
|
||||||
mySize += sizeof(KrkLineMap) * self->chunk.linesCapacity;
|
mySize += sizeof(KrkLineMap) * self->chunk.linesCapacity;
|
||||||
mySize += sizeof(KrkValue) * self->chunk.constants.capacity;
|
mySize += sizeof(KrkValue) * self->chunk.constants.capacity;
|
||||||
mySize += sizeof(KrkExpressionsMap) * self->expressionsCapacity;
|
|
||||||
/* requiredArgNames */
|
/* requiredArgNames */
|
||||||
mySize += sizeof(KrkValue) * self->positionalArgNames.capacity;
|
mySize += sizeof(KrkValue) * self->positionalArgNames.capacity;
|
||||||
/* keywordArgNames */
|
/* keywordArgNames */
|
||||||
mySize += sizeof(KrkValue) * self->keywordArgNames.capacity;
|
mySize += sizeof(KrkValue) * self->keywordArgNames.capacity;
|
||||||
/* Locals array */
|
/* Locals array */
|
||||||
mySize += sizeof(KrkLocalEntry) * self->localNameCount;
|
mySize += sizeof(KrkLocalEntry) * self->localNameCount;
|
||||||
/* Overlong jumps */
|
|
||||||
mySize += sizeof(KrkOverlongJump) * self->overlongJumpsCapacity;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_NATIVE: {
|
case KRK_OBJ_NATIVE: {
|
||||||
@ -107,13 +100,13 @@ KRK_Function(getsizeof) {
|
|||||||
case KRK_OBJ_CLASS: {
|
case KRK_OBJ_CLASS: {
|
||||||
KrkClass * self = AS_CLASS(argv[0]);
|
KrkClass * self = AS_CLASS(argv[0]);
|
||||||
mySize += sizeof(KrkClass);
|
mySize += sizeof(KrkClass);
|
||||||
mySize += (sizeof(KrkTableEntry) + sizeof(ssize_t)) * self->methods.capacity;
|
mySize += sizeof(KrkTableEntry) * self->methods.capacity;
|
||||||
mySize += (sizeof(KrkTableEntry) + sizeof(ssize_t)) * self->subclasses.capacity;
|
mySize += sizeof(KrkTableEntry) * self->subclasses.capacity;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KRK_OBJ_INSTANCE: {
|
case KRK_OBJ_INSTANCE: {
|
||||||
KrkInstance * self = AS_INSTANCE(argv[0]);
|
KrkInstance * self = AS_INSTANCE(argv[0]);
|
||||||
mySize += (sizeof(KrkTableEntry) + sizeof(ssize_t)) * self->fields.capacity;
|
mySize += sizeof(KrkTableEntry) * self->fields.capacity;
|
||||||
KrkClass * type = krk_getType(argv[0]);
|
KrkClass * type = krk_getType(argv[0]);
|
||||||
mySize += type->allocSize; /* All instance types have an allocSize set */
|
mySize += type->allocSize; /* All instance types have an allocSize set */
|
||||||
|
|
||||||
@ -121,7 +114,7 @@ KRK_Function(getsizeof) {
|
|||||||
if (krk_isInstanceOf(argv[0], vm.baseClasses->listClass)) {
|
if (krk_isInstanceOf(argv[0], vm.baseClasses->listClass)) {
|
||||||
mySize += sizeof(KrkValue) * AS_LIST(argv[0])->capacity;
|
mySize += sizeof(KrkValue) * AS_LIST(argv[0])->capacity;
|
||||||
} else if (krk_isInstanceOf(argv[0], vm.baseClasses->dictClass)) {
|
} else if (krk_isInstanceOf(argv[0], vm.baseClasses->dictClass)) {
|
||||||
mySize += (sizeof(KrkTableEntry) + sizeof(ssize_t)) * AS_DICT(argv[0])->capacity;
|
mySize += sizeof(KrkTableEntry) * AS_DICT(argv[0])->capacity;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -208,22 +201,6 @@ KRK_Function(members) {
|
|||||||
return krk_pop();
|
return krk_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_Function(set_recursion_depth) {
|
|
||||||
unsigned int maxdepth;
|
|
||||||
int quiet = 0;
|
|
||||||
if (!krk_parseArgs("I|p",(const char*[]){"maxdepth","quiet"},&maxdepth,&quiet)) return NONE_VAL();
|
|
||||||
if (krk_currentThread.exitOnFrame != 0) {
|
|
||||||
if (quiet) return BOOLEAN_VAL(0);
|
|
||||||
return krk_runtimeError(vm.exceptions->valueError, "Can not change recursion depth in this context.");
|
|
||||||
}
|
|
||||||
krk_setMaximumRecursionDepth(maxdepth);
|
|
||||||
return BOOLEAN_VAL(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
KRK_Function(get_recursion_depth) {
|
|
||||||
return INTEGER_VAL(krk_currentThread.maximumCallDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void krk_module_init_kuroko(void) {
|
void krk_module_init_kuroko(void) {
|
||||||
/**
|
/**
|
||||||
* kuroko = module()
|
* kuroko = module()
|
||||||
@ -273,12 +250,8 @@ void krk_module_init_kuroko(void) {
|
|||||||
"Obtain the memory representation of a stack value.");
|
"Obtain the memory representation of a stack value.");
|
||||||
KRK_DOC(BIND_FUNC(vm.system,members),
|
KRK_DOC(BIND_FUNC(vm.system,members),
|
||||||
"Obtain a copy of a dict of the direct members of an object.");
|
"Obtain a copy of a dict of the direct members of an object.");
|
||||||
KRK_DOC(BIND_FUNC(vm.system,set_recursion_depth),
|
|
||||||
"Change the maximum recursion depth of the current thread if possible.");
|
|
||||||
KRK_DOC(BIND_FUNC(vm.system,get_recursion_depth),
|
|
||||||
"Examine the maximum recursion depth of the current thread.");
|
|
||||||
krk_attachNamedObject(&vm.system->fields, "module", (KrkObj*)vm.baseClasses->moduleClass);
|
krk_attachNamedObject(&vm.system->fields, "module", (KrkObj*)vm.baseClasses->moduleClass);
|
||||||
krk_attachNamedObject(&vm.system->fields, "path_sep", (KrkObj*)S(KRK_PATH_SEP));
|
krk_attachNamedObject(&vm.system->fields, "path_sep", (KrkObj*)S(PATH_SEP));
|
||||||
KrkValue module_paths = krk_list_of(0,NULL,0);
|
KrkValue module_paths = krk_list_of(0,NULL,0);
|
||||||
krk_attachNamedValue(&vm.system->fields, "module_paths", module_paths);
|
krk_attachNamedValue(&vm.system->fields, "module_paths", module_paths);
|
||||||
krk_writeValueArray(AS_LIST(module_paths), OBJECT_VAL(S("./")));
|
krk_writeValueArray(AS_LIST(module_paths), OBJECT_VAL(S("./")));
|
||||||
|
261
src/table.c
261
src/table.c
@ -1,23 +1,4 @@
|
|||||||
/**
|
#include <stdio.h>
|
||||||
* @file table.c
|
|
||||||
* @brief Ordered hash map.
|
|
||||||
*
|
|
||||||
* The original table implementation was derived from CLox. CLox's
|
|
||||||
* tables only supported string keys, but we support arbitrary values,
|
|
||||||
* so long as they are hashable.
|
|
||||||
*
|
|
||||||
* This implementation maintains the same general API, but take its
|
|
||||||
* inspiration from CPython to keep insertion order. The "entries"
|
|
||||||
* table is still an array of key-value pairs, but no longer tracks
|
|
||||||
* the hash lookup for the map. Instead, the entries array keeps
|
|
||||||
* a strict insertion ordering, with deleted entries replaced with
|
|
||||||
* sentinel values representing gaps. A separate "indexes" table
|
|
||||||
* maps hash slots to their associated key-value pairs, or to -1
|
|
||||||
* or -2 to represent unused and tombstone slots, respectively.
|
|
||||||
*
|
|
||||||
* When resizing a table, the entries array is rewritten and gaps
|
|
||||||
* are removed. Simultaneously, the new index entries are populated.
|
|
||||||
*/
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <kuroko/kuroko.h>
|
#include <kuroko/kuroko.h>
|
||||||
#include <kuroko/object.h>
|
#include <kuroko/object.h>
|
||||||
@ -33,14 +14,11 @@
|
|||||||
void krk_initTable(KrkTable * table) {
|
void krk_initTable(KrkTable * table) {
|
||||||
table->count = 0;
|
table->count = 0;
|
||||||
table->capacity = 0;
|
table->capacity = 0;
|
||||||
table->used = 0;
|
|
||||||
table->entries = NULL;
|
table->entries = NULL;
|
||||||
table->indexes = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void krk_freeTable(KrkTable * table) {
|
void krk_freeTable(KrkTable * table) {
|
||||||
KRK_FREE_ARRAY(KrkTableEntry, table->entries, table->capacity);
|
FREE_ARRAY(KrkTableEntry, table->entries, table->capacity);
|
||||||
KRK_FREE_ARRAY(ssize_t, table->indexes, table->capacity);
|
|
||||||
krk_initTable(table);
|
krk_initTable(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +54,7 @@ inline int krk_hashValue(KrkValue value, uint32_t *hashOut) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (IS_CLASS(value)) {
|
if (IS_CLASS(value)) {
|
||||||
*hashOut = (uint32_t)((int)(intptr_t)AS_OBJECT(value));
|
*hashOut = INTEGER_VAL((int)(intptr_t)AS_OBJECT(value));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
_unhashable:
|
_unhashable:
|
||||||
@ -85,117 +63,106 @@ _unhashable:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline ssize_t krk_tableIndexKeyC(const KrkTableEntry * entries, const ssize_t * indexes, size_t capacity, KrkValue key, int (*comparator)(KrkValue,KrkValue)) {
|
KrkTableEntry * krk_findEntry(KrkTableEntry * entries, size_t capacity, KrkValue key) {
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
if (krk_hashValue(key, &index)) return -1;
|
if (krk_hashValue(key, &index)) {
|
||||||
index &= (capacity - 1);
|
return NULL;
|
||||||
|
}
|
||||||
ssize_t tombstone = -1;
|
index &= (capacity-1);
|
||||||
|
KrkTableEntry * tombstone = NULL;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (indexes[index] == -1) {
|
KrkTableEntry * entry = &entries[index];
|
||||||
return tombstone != -1 ? tombstone : index;
|
if (entry->key == KWARGS_VAL(0)) {
|
||||||
} else if (indexes[index] == -2) {
|
return tombstone != NULL ? tombstone : entry;
|
||||||
if (tombstone == index) return tombstone;
|
} else if (entry->key == KWARGS_VAL(1)) {
|
||||||
if (tombstone == -1) tombstone = index;
|
if (tombstone == entry) return tombstone;
|
||||||
} else if (comparator(entries[indexes[index]].key, key)) {
|
if (tombstone == NULL) tombstone = entry;
|
||||||
return index;
|
} else if (krk_valuesSameOrEqual(entry->key, key)) {
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
index = (index + 1) & (capacity - 1);
|
index = (index + 1) & (capacity-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t krk_tableIndexKey(const KrkTableEntry * entries, const ssize_t * indexes, size_t capacity, KrkValue key) {
|
KrkTableEntry * krk_findEntryExact(KrkTableEntry * entries, size_t capacity, KrkValue key) {
|
||||||
return krk_tableIndexKeyC(entries,indexes,capacity,key,krk_valuesSameOrEqual);
|
uint32_t index;
|
||||||
|
if (krk_hashValue(key, &index)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
index &= (capacity-1);
|
||||||
|
KrkTableEntry * tombstone = NULL;
|
||||||
|
for (;;) {
|
||||||
|
KrkTableEntry * entry = &entries[index];
|
||||||
|
if (entry->key == KWARGS_VAL(0)) {
|
||||||
|
return tombstone != NULL ? tombstone : entry;
|
||||||
|
} else if (entry->key == KWARGS_VAL(1)) {
|
||||||
|
if (tombstone == entry) return tombstone;
|
||||||
|
if (tombstone == NULL) tombstone = entry;
|
||||||
|
} else if (krk_valuesSame(entry->key, key)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
index = (index + 1) & (capacity-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t krk_tableIndexKeyExact(const KrkTableEntry * entries, const ssize_t * indexes, size_t capacity, KrkValue key) {
|
#ifdef __TINYC__
|
||||||
return krk_tableIndexKeyC(entries,indexes,capacity,key,krk_valuesSame);
|
int __builtin_clz(unsigned int x) {
|
||||||
|
int i = 31;
|
||||||
|
while (!(x & (1 << i)) && i >= 0) i--;
|
||||||
|
return 31-i;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void krk_tableAdjustCapacity(KrkTable * table, size_t capacity) {
|
void krk_tableAdjustCapacity(KrkTable * table, size_t capacity) {
|
||||||
KrkTableEntry * nentries = KRK_ALLOCATE(KrkTableEntry, capacity);
|
if (capacity) {
|
||||||
ssize_t * nindexes = KRK_ALLOCATE(ssize_t, capacity);
|
/* Fast power-of-two calculation */
|
||||||
|
size_t powerOfTwoCapacity = __builtin_clz(1) - __builtin_clz(capacity);
|
||||||
|
if ((1UL << powerOfTwoCapacity) != capacity) powerOfTwoCapacity++;
|
||||||
|
capacity = (1UL << powerOfTwoCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
KrkTableEntry * entries = ALLOCATE(KrkTableEntry, capacity);
|
||||||
for (size_t i = 0; i < capacity; ++i) {
|
for (size_t i = 0; i < capacity; ++i) {
|
||||||
nindexes[i] = -1;
|
entries[i].key = KWARGS_VAL(0);
|
||||||
nentries[i].key = KWARGS_VAL(0);
|
entries[i].value = KWARGS_VAL(0);
|
||||||
nentries[i].value = KWARGS_VAL(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in used entries */
|
table->count = 0;
|
||||||
const KrkTableEntry * e = table->entries;
|
for (size_t i = 0; i < table->capacity; ++i) {
|
||||||
for (size_t i = 0; i < table->count; ++i) {
|
KrkTableEntry * entry = &table->entries[i];
|
||||||
while (IS_KWARGS(e->key)) e++;
|
if (IS_KWARGS(entry->key)) continue;
|
||||||
memcpy(&nentries[i], e, sizeof(KrkTableEntry));
|
KrkTableEntry * dest = krk_findEntry(entries, capacity, entry->key);
|
||||||
ssize_t indexkey = krk_tableIndexKeyExact(nentries,nindexes,capacity, e->key);
|
dest->key = entry->key;
|
||||||
nindexes[indexkey] = i;
|
dest->value = entry->value;
|
||||||
e++;
|
table->count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Swap before freeing */
|
FREE_ARRAY(KrkTableEntry, table->entries, table->capacity);
|
||||||
KrkTableEntry * oldEntries = table->entries;
|
table->entries = entries;
|
||||||
table->entries = nentries;
|
|
||||||
KRK_FREE_ARRAY(KrkTableEntry, oldEntries, table->capacity);
|
|
||||||
|
|
||||||
ssize_t * oldIndexes = table->indexes;
|
|
||||||
table->indexes = nindexes;
|
|
||||||
KRK_FREE_ARRAY(ssize_t, oldIndexes, table->capacity);
|
|
||||||
|
|
||||||
/* Update table with new capacity and used count */
|
|
||||||
table->capacity = capacity;
|
table->capacity = capacity;
|
||||||
table->used = table->count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int krk_tableSet(KrkTable * table, KrkValue key, KrkValue value) {
|
int krk_tableSet(KrkTable * table, KrkValue key, KrkValue value) {
|
||||||
if (table->used + 1 > table->capacity * TABLE_MAX_LOAD) {
|
if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) {
|
||||||
size_t capacity = KRK_GROW_CAPACITY(table->capacity);
|
size_t capacity = GROW_CAPACITY(table->capacity);
|
||||||
krk_tableAdjustCapacity(table, capacity);
|
krk_tableAdjustCapacity(table, capacity);
|
||||||
}
|
}
|
||||||
|
KrkTableEntry * entry = krk_findEntry(table->entries, table->capacity, key);
|
||||||
ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
|
if (!entry) return 0;
|
||||||
if (index < 0) return 0;
|
int isNewKey = IS_KWARGS(entry->key);
|
||||||
KrkTableEntry * entry;
|
if (isNewKey) table->count++;
|
||||||
int isNew = table->indexes[index] < 0;
|
entry->key = key;
|
||||||
if (isNew) {
|
|
||||||
table->indexes[index] = table->used;
|
|
||||||
entry = &table->entries[table->used];
|
|
||||||
entry->key = key;
|
|
||||||
table->used++;
|
|
||||||
table->count++;
|
|
||||||
} else {
|
|
||||||
entry = &table->entries[table->indexes[index]];
|
|
||||||
}
|
|
||||||
entry->value = value;
|
entry->value = value;
|
||||||
return isNew;
|
return isNewKey;
|
||||||
}
|
|
||||||
|
|
||||||
int krk_tableSetExact(KrkTable * table, KrkValue key, KrkValue value) {
|
|
||||||
if (table->used + 1 > table->capacity * TABLE_MAX_LOAD) {
|
|
||||||
size_t capacity = KRK_GROW_CAPACITY(table->capacity);
|
|
||||||
krk_tableAdjustCapacity(table, capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t index = krk_tableIndexKeyExact(table->entries, table->indexes, table->capacity, key);
|
|
||||||
if (index < 0) return 0;
|
|
||||||
KrkTableEntry * entry;
|
|
||||||
int isNew = table->indexes[index] < 0;
|
|
||||||
if (isNew) {
|
|
||||||
table->indexes[index] = table->used;
|
|
||||||
entry = &table->entries[table->used];
|
|
||||||
entry->key = key;
|
|
||||||
table->used++;
|
|
||||||
table->count++;
|
|
||||||
} else {
|
|
||||||
entry = &table->entries[table->indexes[index]];
|
|
||||||
}
|
|
||||||
entry->value = value;
|
|
||||||
return isNew;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int krk_tableSetIfExists(KrkTable * table, KrkValue key, KrkValue value) {
|
int krk_tableSetIfExists(KrkTable * table, KrkValue key, KrkValue value) {
|
||||||
if (table->count == 0) return 0;
|
if (table->count == 0) return 0;
|
||||||
ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
|
KrkTableEntry * entry = krk_findEntry(table->entries, table->capacity, key);
|
||||||
if (index < 0 || table->indexes[index] < 0) return 0;
|
if (!entry) return 0;
|
||||||
table->entries[table->indexes[index]].value = value;
|
if (IS_KWARGS(entry->key)) return 0; /* Not found */
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,69 +177,75 @@ void krk_tableAddAll(KrkTable * from, KrkTable * to) {
|
|||||||
|
|
||||||
int krk_tableGet(KrkTable * table, KrkValue key, KrkValue * value) {
|
int krk_tableGet(KrkTable * table, KrkValue key, KrkValue * value) {
|
||||||
if (table->count == 0) return 0;
|
if (table->count == 0) return 0;
|
||||||
ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
|
KrkTableEntry * entry = krk_findEntry(table->entries, table->capacity, key);
|
||||||
if (index < 0 || table->indexes[index] < 0) return 0;
|
if (!entry || IS_KWARGS(entry->key)) {
|
||||||
*value = table->entries[table->indexes[index]].value;
|
return 0;
|
||||||
return 1;
|
} else {
|
||||||
|
*value = entry->value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int krk_tableGet_fast(KrkTable * table, KrkString * str, KrkValue * value) {
|
int krk_tableGet_fast(KrkTable * table, KrkString * str, KrkValue * value) {
|
||||||
if (table->count == 0) return 0;
|
if (unlikely(table->count == 0)) return 0;
|
||||||
uint32_t index = str->obj.hash & (table->capacity-1);
|
uint32_t index = str->obj.hash & (table->capacity-1);
|
||||||
|
KrkTableEntry * tombstone = NULL;
|
||||||
ssize_t tombstone = -1;
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (table->indexes[index] == -1) {
|
KrkTableEntry * entry = &table->entries[index];
|
||||||
|
if (entry->key == KWARGS_VAL(0)) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (table->indexes[index] == -2) {
|
} else if (entry->key == KWARGS_VAL(1)) {
|
||||||
if (tombstone == index) return 0;
|
if (tombstone == entry) return 0;
|
||||||
if (tombstone == -1) tombstone = index;
|
if (tombstone == NULL) tombstone = entry;
|
||||||
} else if (krk_valuesSame(table->entries[table->indexes[index]].key, OBJECT_VAL(str))) {
|
} else if (entry->key == OBJECT_VAL(str)) {
|
||||||
*value = table->entries[table->indexes[index]].value;
|
*value = entry->value;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
index = (index + 1) & (table->capacity - 1);
|
index = (index + 1) & (table->capacity-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int krk_tableDelete(KrkTable * table, KrkValue key) {
|
int krk_tableDelete(KrkTable * table, KrkValue key) {
|
||||||
if (table->count == 0) return 0;
|
if (table->count == 0) return 0;
|
||||||
ssize_t index = krk_tableIndexKey(table->entries, table->indexes, table->capacity, key);
|
KrkTableEntry * entry = krk_findEntry(table->entries, table->capacity, key);
|
||||||
if (index < 0 || table->indexes[index] < 0) return 0;
|
if (!entry || IS_KWARGS(entry->key)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
table->count--;
|
table->count--;
|
||||||
table->entries[table->indexes[index]].key = KWARGS_VAL(0);
|
entry->key = KWARGS_VAL(1);
|
||||||
table->entries[table->indexes[index]].value = KWARGS_VAL(0);
|
entry->value = KWARGS_VAL(0);
|
||||||
table->indexes[index] = -2;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int krk_tableDeleteExact(KrkTable * table, KrkValue key) {
|
int krk_tableDeleteExact(KrkTable * table, KrkValue key) {
|
||||||
if (table->count == 0) return 0;
|
if (table->count == 0) return 0;
|
||||||
ssize_t index = krk_tableIndexKeyExact(table->entries, table->indexes, table->capacity, key);
|
KrkTableEntry * entry = krk_findEntryExact(table->entries, table->capacity, key);
|
||||||
if (index < 0 || table->indexes[index] < 0) return 0;
|
if (!entry || IS_KWARGS(entry->key)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
table->count--;
|
table->count--;
|
||||||
table->entries[table->indexes[index]].key = KWARGS_VAL(0);
|
entry->key = KWARGS_VAL(1);
|
||||||
table->entries[table->indexes[index]].value = KWARGS_VAL(0);
|
entry->value = KWARGS_VAL(0);
|
||||||
table->indexes[index] = -2;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
KrkString * krk_tableFindString(KrkTable * table, const char * chars, size_t length, uint32_t hash) {
|
KrkString * krk_tableFindString(KrkTable * table, const char * chars, size_t length, uint32_t hash) {
|
||||||
if (table->count == 0) return NULL;
|
if (table->count == 0) return NULL;
|
||||||
uint32_t index = hash & (table->capacity - 1);
|
|
||||||
|
|
||||||
ssize_t tombstone = -1;
|
uint32_t index = hash & (table->capacity-1);
|
||||||
|
KrkTableEntry * tombstone = NULL;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (table->indexes[index] == -1) {
|
KrkTableEntry * entry = &table->entries[index];
|
||||||
|
if (entry->key == KWARGS_VAL(0)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (table->indexes[index] == -2) {
|
} else if (entry->key == KWARGS_VAL(1)) {
|
||||||
if (tombstone == index) return NULL;
|
if (tombstone == entry) return NULL;
|
||||||
if (tombstone == -1) tombstone = index;
|
if (tombstone == NULL) tombstone = entry;
|
||||||
} else if (AS_STRING(table->entries[table->indexes[index]].key)->length == length &&
|
} else if (AS_STRING(entry->key)->length == length &&
|
||||||
AS_OBJECT(table->entries[table->indexes[index]].key)->hash == hash &&
|
AS_OBJECT(entry->key)->hash == hash &&
|
||||||
memcmp(AS_STRING(table->entries[table->indexes[index]].key)->chars, chars, length) == 0) {
|
memcmp(AS_STRING(entry->key)->chars, chars, length) == 0) {
|
||||||
return AS_STRING(table->entries[table->indexes[index]].key);
|
return AS_STRING(entry->key);
|
||||||
}
|
}
|
||||||
index = (index + 1) & (table->capacity - 1);
|
index = (index + 1) & (table->capacity-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#ifndef KRK_DISABLE_THREADS
|
#ifndef KRK_DISABLE_THREADS
|
||||||
#include <kuroko/util.h>
|
#include <kuroko/util.h>
|
||||||
#include <kuroko/threads.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
@ -37,7 +36,6 @@ struct Thread {
|
|||||||
pid_t tid;
|
pid_t tid;
|
||||||
unsigned int started:1;
|
unsigned int started:1;
|
||||||
unsigned int alive:1;
|
unsigned int alive:1;
|
||||||
unsigned int maxrec;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,13 +61,11 @@ KRK_Function(current_thread) {
|
|||||||
|
|
||||||
static volatile int _threadLock = 0;
|
static volatile int _threadLock = 0;
|
||||||
static void * _startthread(void * _threadObj) {
|
static void * _startthread(void * _threadObj) {
|
||||||
struct Thread * self = _threadObj;
|
|
||||||
#if defined(__APPLE__) && defined(__aarch64__)
|
#if defined(__APPLE__) && defined(__aarch64__)
|
||||||
krk_forceThreadData();
|
krk_forceThreadData();
|
||||||
#endif
|
#endif
|
||||||
memset(&krk_currentThread, 0, sizeof(KrkThreadState));
|
memset(&krk_currentThread, 0, sizeof(KrkThreadState));
|
||||||
krk_currentThread.maximumCallDepth = self->maxrec;
|
krk_currentThread.frames = calloc(vm.maximumCallDepth,sizeof(KrkCallFrame));
|
||||||
krk_currentThread.frames = calloc(krk_currentThread.maximumCallDepth,sizeof(KrkCallFrame));
|
|
||||||
vm.globalFlags |= KRK_GLOBAL_THREADS;
|
vm.globalFlags |= KRK_GLOBAL_THREADS;
|
||||||
_obtain_lock(_threadLock);
|
_obtain_lock(_threadLock);
|
||||||
if (vm.threads->next) {
|
if (vm.threads->next) {
|
||||||
@ -79,6 +75,7 @@ static void * _startthread(void * _threadObj) {
|
|||||||
_release_lock(_threadLock);
|
_release_lock(_threadLock);
|
||||||
|
|
||||||
/* Get our run function */
|
/* Get our run function */
|
||||||
|
struct Thread * self = _threadObj;
|
||||||
self->threadState = &krk_currentThread;
|
self->threadState = &krk_currentThread;
|
||||||
self->tid = gettid();
|
self->tid = gettid();
|
||||||
|
|
||||||
@ -107,7 +104,7 @@ static void * _startthread(void * _threadObj) {
|
|||||||
}
|
}
|
||||||
_release_lock(_threadLock);
|
_release_lock(_threadLock);
|
||||||
|
|
||||||
KRK_FREE_ARRAY(size_t, krk_currentThread.stack, krk_currentThread.stackSize);
|
FREE_ARRAY(size_t, krk_currentThread.stack, krk_currentThread.stackSize);
|
||||||
free(krk_currentThread.frames);
|
free(krk_currentThread.frames);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -129,15 +126,13 @@ KRK_Method(Thread,join) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KRK_Method(Thread,start) {
|
KRK_Method(Thread,start) {
|
||||||
unsigned int maxrec = krk_currentThread.maximumCallDepth;
|
METHOD_TAKES_NONE();
|
||||||
if (!krk_parseArgs(".|I", (const char*[]){"maxrec"}, &maxrec)) return NONE_VAL();
|
|
||||||
|
|
||||||
if (self->started)
|
if (self->started)
|
||||||
return krk_runtimeError(KRK_EXC(ThreadError), "Thread has already been started.");
|
return krk_runtimeError(KRK_EXC(ThreadError), "Thread has already been started.");
|
||||||
|
|
||||||
self->started = 1;
|
self->started = 1;
|
||||||
self->alive = 1;
|
self->alive = 1;
|
||||||
self->maxrec = maxrec;
|
|
||||||
pthread_create(&self->nativeRef, NULL, _startthread, (void*)self);
|
pthread_create(&self->nativeRef, NULL, _startthread, (void*)self);
|
||||||
|
|
||||||
return argv[0];
|
return argv[0];
|
||||||
|
56
src/time.c
Normal file
56
src/time.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <kuroko/vm.h>
|
||||||
|
#include <kuroko/value.h>
|
||||||
|
#include <kuroko/object.h>
|
||||||
|
#include <kuroko/util.h>
|
||||||
|
|
||||||
|
KRK_Function(sleep) {
|
||||||
|
FUNCTION_TAKES_EXACTLY(1);
|
||||||
|
|
||||||
|
if (!IS_INTEGER(argv[0]) && !IS_FLOATING(argv[0])) {
|
||||||
|
return TYPE_ERROR(int or float,argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int usecs = (IS_INTEGER(argv[0]) ? AS_INTEGER(argv[0]) :
|
||||||
|
(IS_FLOATING(argv[0]) ? AS_FLOATING(argv[0]) : 0)) *
|
||||||
|
1000000;
|
||||||
|
|
||||||
|
usleep(usecs);
|
||||||
|
|
||||||
|
return BOOLEAN_VAL(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
KRK_Function(time) {
|
||||||
|
FUNCTION_TAKES_NONE();
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv,NULL);
|
||||||
|
|
||||||
|
double out = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
||||||
|
|
||||||
|
return FLOATING_VAL(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void krk_module_init_time(void) {
|
||||||
|
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||||
|
krk_attachNamedObject(&vm.modules, "time", (KrkObj*)module);
|
||||||
|
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("time"));
|
||||||
|
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||||
|
KRK_DOC(module, "@brief Provides timekeeping functions.");
|
||||||
|
KRK_DOC(BIND_FUNC(module,sleep), "@brief Pause execution of the current thread.\n"
|
||||||
|
"@arguments secs\n\n"
|
||||||
|
"Uses the system @c usleep() function to sleep for @p secs seconds, which may be a @ref float or @ref int. "
|
||||||
|
"The available precision is platform-dependent.");
|
||||||
|
KRK_DOC(BIND_FUNC(module,time), "@brief Return the elapsed seconds since the system epoch.\n\n"
|
||||||
|
"Returns a @ref float representation of the number of seconds since the platform's epoch date. "
|
||||||
|
"On POSIX platforms, this is the number of seconds since 1 January 1970. "
|
||||||
|
"The precision of the return value is platform-dependent.");
|
||||||
|
}
|
||||||
|
|
135
src/value.c
135
src/value.c
@ -17,8 +17,8 @@ void krk_initValueArray(KrkValueArray * array) {
|
|||||||
void krk_writeValueArray(KrkValueArray * array, KrkValue value) {
|
void krk_writeValueArray(KrkValueArray * array, KrkValue value) {
|
||||||
if (array->capacity < array->count + 1) {
|
if (array->capacity < array->count + 1) {
|
||||||
int old = array->capacity;
|
int old = array->capacity;
|
||||||
array->capacity = KRK_GROW_CAPACITY(old);
|
array->capacity = GROW_CAPACITY(old);
|
||||||
array->values = KRK_GROW_ARRAY(KrkValue, array->values, old, array->capacity);
|
array->values = GROW_ARRAY(KrkValue, array->values, old, array->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
array->values[array->count] = value;
|
array->values[array->count] = value;
|
||||||
@ -26,10 +26,131 @@ void krk_writeValueArray(KrkValueArray * array, KrkValue value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void krk_freeValueArray(KrkValueArray * array) {
|
void krk_freeValueArray(KrkValueArray * array) {
|
||||||
KRK_FREE_ARRAY(KrkValue, array->values, array->capacity);
|
FREE_ARRAY(KrkValue, array->values, array->capacity);
|
||||||
krk_initValueArray(array);
|
krk_initValueArray(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void krk_printValue(FILE * f, KrkValue printable) {
|
||||||
|
KrkClass * type = krk_getType(printable);
|
||||||
|
if (type->_tostr) {
|
||||||
|
krk_push(printable);
|
||||||
|
printable = krk_callDirect(type->_tostr, 1);
|
||||||
|
if (!IS_STRING(printable)) return;
|
||||||
|
fprintf(f, "%s", AS_CSTRING(printable));
|
||||||
|
} else if (type->_reprer) {
|
||||||
|
krk_push(printable);
|
||||||
|
printable = krk_callDirect(type->_reprer, 1);
|
||||||
|
if (!IS_STRING(printable)) return;
|
||||||
|
fprintf(f, "%s", AS_CSTRING(printable));
|
||||||
|
} else {
|
||||||
|
fprintf(f, "%s", krk_typeName(printable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STRING_DEBUG_TRUNCATE 50
|
||||||
|
|
||||||
|
void krk_printValueSafe(FILE * f, KrkValue printable) {
|
||||||
|
if (!IS_OBJECT(printable)) {
|
||||||
|
switch (KRK_VAL_TYPE(printable)) {
|
||||||
|
case KRK_VAL_INTEGER: fprintf(f, PRIkrk_int, AS_INTEGER(printable)); break;
|
||||||
|
case KRK_VAL_BOOLEAN: fprintf(f, "%s", AS_BOOLEAN(printable) ? "True" : "False"); break;
|
||||||
|
case KRK_VAL_NONE: fprintf(f, "None"); break;
|
||||||
|
case KRK_VAL_HANDLER:
|
||||||
|
switch (AS_HANDLER_TYPE(printable)) {
|
||||||
|
case OP_PUSH_TRY: fprintf(f, "{try->%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
case OP_PUSH_WITH: fprintf(f, "{with->%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
case OP_RAISE: fprintf(f, "{raise<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
case OP_FILTER_EXCEPT: fprintf(f, "{except<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
case OP_BEGIN_FINALLY: fprintf(f, "{finally<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
case OP_RETURN: fprintf(f, "{return<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
case OP_END_FINALLY: fprintf(f, "{end<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
case OP_EXIT_LOOP: fprintf(f, "{exit<-%d}", (int)AS_HANDLER_TARGET(printable)); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KRK_VAL_KWARGS: {
|
||||||
|
if (AS_INTEGER(printable) == KWARGS_SINGLE) {
|
||||||
|
fprintf(f, "{unpack single}");
|
||||||
|
} else if (AS_INTEGER(printable) == KWARGS_LIST) {
|
||||||
|
fprintf(f, "{unpack list}");
|
||||||
|
} else if (AS_INTEGER(printable) == KWARGS_DICT) {
|
||||||
|
fprintf(f, "{unpack dict}");
|
||||||
|
} else if (AS_INTEGER(printable) == KWARGS_NIL) {
|
||||||
|
fprintf(f, "{unpack nil}");
|
||||||
|
} else if (AS_INTEGER(printable) == KWARGS_UNSET) {
|
||||||
|
fprintf(f, "{unset default}");
|
||||||
|
} else {
|
||||||
|
fprintf(f, "{sentinel=" PRIkrk_int "}",AS_INTEGER(printable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
#ifndef KRK_NO_FLOAT
|
||||||
|
if (IS_FLOATING(printable)) fprintf(f, "%.16g", AS_FLOATING(printable));
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (IS_STRING(printable)) {
|
||||||
|
fprintf(f, "'");
|
||||||
|
/*
|
||||||
|
* Print at most STRING_DEBUG_TRUNCATE characters, as bytes, escaping anything not ASCII.
|
||||||
|
* See also str.__repr__ which does something similar with escape sequences, but this
|
||||||
|
* is a dumber, safer, and slightly faster approach.
|
||||||
|
*/
|
||||||
|
for (size_t c = 0; c < AS_STRING(printable)->length && c < STRING_DEBUG_TRUNCATE; ++c) {
|
||||||
|
unsigned char byte = (unsigned char)AS_CSTRING(printable)[c];
|
||||||
|
switch (byte) {
|
||||||
|
case '\\': fprintf(f, "\\\\"); break;
|
||||||
|
case '\n': fprintf(f, "\\n"); break;
|
||||||
|
case '\r': fprintf(f, "\\r"); break;
|
||||||
|
case '\'': fprintf(f, "\\'"); break;
|
||||||
|
default: {
|
||||||
|
if (byte < ' ' || byte > '~') {
|
||||||
|
fprintf(f, "\\x%02x", byte);
|
||||||
|
} else {
|
||||||
|
fprintf(f, "%c", byte);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (AS_STRING(printable)->length > STRING_DEBUG_TRUNCATE) {
|
||||||
|
fprintf(f,"...");
|
||||||
|
}
|
||||||
|
fprintf(f,"'");
|
||||||
|
} else {
|
||||||
|
switch (AS_OBJECT(printable)->type) {
|
||||||
|
case KRK_OBJ_CODEOBJECT: fprintf(f, "<codeobject %s>", AS_codeobject(printable)->name ? AS_codeobject(printable)->name->chars : "?"); break;
|
||||||
|
case KRK_OBJ_CLASS: fprintf(f, "<class %s>", AS_CLASS(printable)->name ? AS_CLASS(printable)->name->chars : "?"); break;
|
||||||
|
case KRK_OBJ_INSTANCE: fprintf(f, "<instance of %s>", AS_INSTANCE(printable)->_class->name->chars); break;
|
||||||
|
case KRK_OBJ_NATIVE: fprintf(f, "<nativefn %s>", ((KrkNative*)AS_OBJECT(printable))->name); break;
|
||||||
|
case KRK_OBJ_CLOSURE: fprintf(f, "<function %s>", AS_CLOSURE(printable)->function->name->chars); break;
|
||||||
|
case KRK_OBJ_BYTES: fprintf(f, "<bytes of len %ld>", (long)AS_BYTES(printable)->length); break;
|
||||||
|
case KRK_OBJ_TUPLE: {
|
||||||
|
fprintf(f, "(");
|
||||||
|
for (size_t i = 0; i < AS_TUPLE(printable)->values.count; ++i) {
|
||||||
|
krk_printValueSafe(f, AS_TUPLE(printable)->values.values[i]);
|
||||||
|
if (i + 1 != AS_TUPLE(printable)->values.count) {
|
||||||
|
fprintf(f, ",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(f, ")");
|
||||||
|
} break;
|
||||||
|
case KRK_OBJ_BOUND_METHOD: fprintf(f, "<method %s>",
|
||||||
|
AS_BOUND_METHOD(printable)->method ? (
|
||||||
|
AS_BOUND_METHOD(printable)->method->type == KRK_OBJ_CLOSURE ? ((KrkClosure*)AS_BOUND_METHOD(printable)->method)->function->name->chars :
|
||||||
|
(AS_BOUND_METHOD(printable)->method->type == KRK_OBJ_NATIVE ? ((KrkNative*)AS_BOUND_METHOD(printable)->method)->name : "(unknown)")) : "(corrupt bound method)"); break;
|
||||||
|
default: fprintf(f, "<%s>", krk_typeName(printable)); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identity really should be the simple...
|
||||||
|
*/
|
||||||
|
int krk_valuesSame(KrkValue a, KrkValue b) {
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int _krk_method_equivalence(KrkValue a, KrkValue b) {
|
static inline int _krk_method_equivalence(KrkValue a, KrkValue b) {
|
||||||
KrkClass * type = krk_getType(a);
|
KrkClass * type = krk_getType(a);
|
||||||
if (likely(type && type->_eq)) {
|
if (likely(type && type->_eq)) {
|
||||||
@ -62,7 +183,7 @@ static inline int _krk_same_type_equivalence(uint16_t valtype, KrkValue a, KrkVa
|
|||||||
case KRK_VAL_NOTIMPL:
|
case KRK_VAL_NOTIMPL:
|
||||||
case KRK_VAL_KWARGS:
|
case KRK_VAL_KWARGS:
|
||||||
case KRK_VAL_HANDLER:
|
case KRK_VAL_HANDLER:
|
||||||
return krk_valuesSame(a,b);
|
return a == b;
|
||||||
case KRK_VAL_OBJECT:
|
case KRK_VAL_OBJECT:
|
||||||
default:
|
default:
|
||||||
return _krk_method_equivalence(a,b);
|
return _krk_method_equivalence(a,b);
|
||||||
@ -92,9 +213,9 @@ static inline int _krk_diff_type_equivalence(uint16_t val_a, uint16_t val_b, Krk
|
|||||||
return _krk_method_equivalence(a,b);
|
return _krk_method_equivalence(a,b);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hot
|
__attribute__((hot))
|
||||||
int krk_valuesSameOrEqual(KrkValue a, KrkValue b) {
|
int krk_valuesSameOrEqual(KrkValue a, KrkValue b) {
|
||||||
if (krk_valuesSame(a,b)) return 1;
|
if (a == b) return 1;
|
||||||
uint16_t val_a = KRK_VAL_TYPE(a);
|
uint16_t val_a = KRK_VAL_TYPE(a);
|
||||||
uint16_t val_b = KRK_VAL_TYPE(b);
|
uint16_t val_b = KRK_VAL_TYPE(b);
|
||||||
return (val_a == val_b)
|
return (val_a == val_b)
|
||||||
@ -102,7 +223,7 @@ int krk_valuesSameOrEqual(KrkValue a, KrkValue b) {
|
|||||||
: _krk_diff_type_equivalence(val_a, val_b, a, b);
|
: _krk_diff_type_equivalence(val_a, val_b, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hot
|
__attribute__((hot))
|
||||||
int krk_valuesEqual(KrkValue a, KrkValue b) {
|
int krk_valuesEqual(KrkValue a, KrkValue b) {
|
||||||
uint16_t val_a = KRK_VAL_TYPE(a);
|
uint16_t val_a = KRK_VAL_TYPE(a);
|
||||||
uint16_t val_b = KRK_VAL_TYPE(b);
|
uint16_t val_b = KRK_VAL_TYPE(b);
|
||||||
|
55
src/vendor/rline.c
vendored
55
src/vendor/rline.c
vendored
@ -363,19 +363,19 @@ static void recalculate_tabs(line_t * line) {
|
|||||||
*/
|
*/
|
||||||
static const char * COLOR_FG = "@9";
|
static const char * COLOR_FG = "@9";
|
||||||
static const char * COLOR_BG = "@9";
|
static const char * COLOR_BG = "@9";
|
||||||
static const char * COLOR_ALT_FG = "@9";
|
static const char * COLOR_ALT_FG = "@5";
|
||||||
static const char * COLOR_ALT_BG = "@9";
|
static const char * COLOR_ALT_BG = "@9";
|
||||||
static const char * COLOR_KEYWORD = "@9";
|
static const char * COLOR_KEYWORD = "@4";
|
||||||
static const char * COLOR_STRING = "@9";
|
static const char * COLOR_STRING = "@2";
|
||||||
static const char * COLOR_COMMENT = "@9";
|
static const char * COLOR_COMMENT = "@5";
|
||||||
static const char * COLOR_TYPE = "@9";
|
static const char * COLOR_TYPE = "@3";
|
||||||
static const char * COLOR_PRAGMA = "@9";
|
static const char * COLOR_PRAGMA = "@1";
|
||||||
static const char * COLOR_NUMERAL = "@9";
|
static const char * COLOR_NUMERAL = "@1";
|
||||||
static const char * COLOR_RED = "@9";
|
static const char * COLOR_RED = "@1";
|
||||||
static const char * COLOR_GREEN = "@9";
|
static const char * COLOR_GREEN = "@2";
|
||||||
static const char * COLOR_ESCAPE = "@9";
|
static const char * COLOR_ESCAPE = "@2";
|
||||||
static const char * COLOR_SEARCH_FG = "@9";
|
static const char * COLOR_SEARCH_FG = "@0";
|
||||||
static const char * COLOR_SEARCH_BG = "@9";
|
static const char * COLOR_SEARCH_BG = "@3";
|
||||||
static const char * COLOR_ERROR_FG = "@9";
|
static const char * COLOR_ERROR_FG = "@9";
|
||||||
static const char * COLOR_ERROR_BG = "@9";
|
static const char * COLOR_ERROR_BG = "@9";
|
||||||
static const char * COLOR_BOLD = "@9";
|
static const char * COLOR_BOLD = "@9";
|
||||||
@ -560,7 +560,7 @@ static char * syn_krk_types[] = {
|
|||||||
"self", "super", /* implicit in a class method */
|
"self", "super", /* implicit in a class method */
|
||||||
"len", "str", "int", "float", "dir", "repr", /* global functions from __builtins__ */
|
"len", "str", "int", "float", "dir", "repr", /* global functions from __builtins__ */
|
||||||
"list","dict","range", /* builtin classes */
|
"list","dict","range", /* builtin classes */
|
||||||
"object","isinstance","type","tuple","reversed",
|
"object","exception","isinstance","type","tuple","reversed",
|
||||||
"print","set","any","all","bool","ord","chr","hex","oct","filter",
|
"print","set","any","all","bool","ord","chr","hex","oct","filter",
|
||||||
"sorted","bytes","getattr","sum","min","max","id","hash","map","bin",
|
"sorted","bytes","getattr","sum","min","max","id","hash","map","bin",
|
||||||
"enumerate","zip","setattr","property","staticmethod","classmethod",
|
"enumerate","zip","setattr","property","staticmethod","classmethod",
|
||||||
@ -672,11 +672,6 @@ static int paint_krk_numeral(struct syntax_state * state) {
|
|||||||
paint(1, FLAG_NUMERAL);
|
paint(1, FLAG_NUMERAL);
|
||||||
while (isdigit(charat())) paint(1, FLAG_NUMERAL);
|
while (isdigit(charat())) paint(1, FLAG_NUMERAL);
|
||||||
}
|
}
|
||||||
if (charat() == 'e' || charat() == 'E') {
|
|
||||||
paint(1, FLAG_NUMERAL);
|
|
||||||
if (charat() == '-' || charat() == '+') paint(1, FLAG_NUMERAL);
|
|
||||||
while (isdigit(charat())) paint(1, FLAG_NUMERAL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2016,27 +2011,29 @@ static int handle_escape(int * this_buf, int * timeout, int c) {
|
|||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
static unsigned int _INTR, _EOF;
|
static unsigned int _INTR, _EOF;
|
||||||
static struct termios old;
|
static struct termios old;
|
||||||
static void set_unbuffered(void) {
|
static void get_initial_termios(void) {
|
||||||
tcgetattr(STDOUT_FILENO, &old);
|
tcgetattr(STDOUT_FILENO, &old);
|
||||||
_INTR = old.c_cc[VINTR];
|
_INTR = old.c_cc[VINTR];
|
||||||
_EOF = old.c_cc[VEOF];
|
_EOF = old.c_cc[VEOF];
|
||||||
|
}
|
||||||
|
static void set_unbuffered(void) {
|
||||||
struct termios new = old;
|
struct termios new = old;
|
||||||
new.c_lflag &= (~ICANON & ~ECHO & ~ISIG);
|
new.c_lflag &= (~ICANON & ~ECHO & ~ISIG);
|
||||||
tcsetattr(STDOUT_FILENO, TCSADRAIN, &new);
|
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &new);
|
||||||
if (wcwidth(0x3042) != 2) setlocale(LC_CTYPE, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_buffered(void) {
|
static void set_buffered(void) {
|
||||||
tcsetattr(STDOUT_FILENO, TCSADRAIN, &old);
|
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &old);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static unsigned int _INTR = 3;
|
static unsigned int _INTR = 3;
|
||||||
static unsigned int _EOF = 4;
|
static unsigned int _EOF = 4;
|
||||||
|
static void get_initial_termios(void) {
|
||||||
|
}
|
||||||
static void set_unbuffered(void) {
|
static void set_unbuffered(void) {
|
||||||
/* Disables line input, echo, ^C processing, and a few others. */
|
/* Disables line input, echo, ^C processing, and a few others. */
|
||||||
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_VIRTUAL_TERMINAL_INPUT);
|
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_VIRTUAL_TERMINAL_INPUT);
|
||||||
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_WRAP_AT_EOL_OUTPUT);
|
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||||
setlocale(LC_CTYPE, "C.UTF-8");
|
|
||||||
}
|
}
|
||||||
static void set_buffered(void) {
|
static void set_buffered(void) {
|
||||||
/* These are the defaults */
|
/* These are the defaults */
|
||||||
@ -2432,6 +2429,12 @@ static int read_line(void) {
|
|||||||
* Read a line of text with interactive editing.
|
* Read a line of text with interactive editing.
|
||||||
*/
|
*/
|
||||||
int rline(char * buffer, int buf_size) {
|
int rline(char * buffer, int buf_size) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
#else
|
||||||
|
setlocale(LC_ALL, "C.UTF-8");
|
||||||
|
#endif
|
||||||
|
get_initial_termios();
|
||||||
set_unbuffered();
|
set_unbuffered();
|
||||||
get_size();
|
get_size();
|
||||||
|
|
||||||
@ -2439,12 +2442,8 @@ int rline(char * buffer, int buf_size) {
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
buf_size_max = buf_size;
|
buf_size_max = buf_size;
|
||||||
|
|
||||||
char * noColor = getenv("NO_COLOR");
|
|
||||||
char * theme = getenv("RLINE_THEME");
|
char * theme = getenv("RLINE_THEME");
|
||||||
if ((noColor && *noColor) || (theme && !strcmp(theme,"none"))) {
|
if (theme && !strcmp(theme,"sunsmoke")) { /* TODO bring back theme tables */
|
||||||
/* If NO_COLOR is set, or RLINE_THEME=none, set no theme.
|
|
||||||
* The default colors are all @9. */
|
|
||||||
} else if (theme && !strcmp(theme,"sunsmoke")) {
|
|
||||||
rline_exp_load_colorscheme_sunsmoke();
|
rline_exp_load_colorscheme_sunsmoke();
|
||||||
} else {
|
} else {
|
||||||
rline_exp_load_colorscheme_default();
|
rline_exp_load_colorscheme_default();
|
||||||
|
305
src/vm.c
305
src/vm.c
@ -2,6 +2,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
@ -66,6 +67,44 @@ void krk_forceThreadData(void) {
|
|||||||
#define krk_currentThread (*_macos_currentThread())
|
#define krk_currentThread (*_macos_currentThread())
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(KRK_NO_TRACING) && !defined(__EMSCRIPTEN__) && !defined(KRK_NO_CALLGRIND)
|
||||||
|
static void _frame_in(KrkCallFrame * frame) {
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &frame->in_time);
|
||||||
|
}
|
||||||
|
static void _frame_out(KrkCallFrame * frame) {
|
||||||
|
if (frame->closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_IS_GENERATOR) return;
|
||||||
|
|
||||||
|
KrkCallFrame * caller = krk_currentThread.frameCount > 1 ? &krk_currentThread.frames[krk_currentThread.frameCount-2] : NULL;
|
||||||
|
|
||||||
|
struct timespec outTime, diff;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &outTime);
|
||||||
|
diff.tv_sec = outTime.tv_sec - frame->in_time.tv_sec;
|
||||||
|
diff.tv_nsec = outTime.tv_nsec - frame->in_time.tv_nsec;
|
||||||
|
|
||||||
|
if (diff.tv_nsec < 0) {
|
||||||
|
diff.tv_sec--;
|
||||||
|
diff.tv_nsec += 1000000000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(vm.callgrindFile, "%s %s@%p %d %s %s@%p %d %lld.%.9ld\n",
|
||||||
|
caller ? (caller->closure->function->chunk.filename->chars) : "stdin",
|
||||||
|
caller ? (caller->closure->function->qualname ? caller->closure->function->qualname->chars : caller->closure->function->name->chars) : "(root)",
|
||||||
|
caller ? ((void*)caller->closure->function) : NULL,
|
||||||
|
caller ? ((int)krk_lineNumber(&caller->closure->function->chunk, caller->ip - caller->closure->function->chunk.code)) : 1,
|
||||||
|
frame->closure->function->chunk.filename->chars,
|
||||||
|
frame->closure->function->qualname ? frame->closure->function->qualname->chars : frame->closure->function->name->chars,
|
||||||
|
(void*)frame->closure->function,
|
||||||
|
(int)krk_lineNumber(&frame->closure->function->chunk, 0),
|
||||||
|
(long long)diff.tv_sec, diff.tv_nsec);
|
||||||
|
}
|
||||||
|
# define FRAME_IN(frame) if (unlikely(vm.globalFlags & KRK_GLOBAL_CALLGRIND)) { _frame_in(frame); }
|
||||||
|
# define FRAME_OUT(frame) if (unlikely(vm.globalFlags & KRK_GLOBAL_CALLGRIND)) { _frame_out(frame); }
|
||||||
|
#else
|
||||||
|
# define FRAME_IN(frame)
|
||||||
|
# define FRAME_OUT(frame)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In some threading configurations, particular on Windows,
|
* In some threading configurations, particular on Windows,
|
||||||
* we can't have executables reference our thread-local thread
|
* we can't have executables reference our thread-local thread
|
||||||
@ -94,14 +133,14 @@ void krk_resetStack(void) {
|
|||||||
void krk_growStack(void) {
|
void krk_growStack(void) {
|
||||||
size_t old = krk_currentThread.stackSize;
|
size_t old = krk_currentThread.stackSize;
|
||||||
size_t old_offset = krk_currentThread.stackTop - krk_currentThread.stack;
|
size_t old_offset = krk_currentThread.stackTop - krk_currentThread.stack;
|
||||||
size_t newsize = KRK_GROW_CAPACITY(old);
|
size_t newsize = GROW_CAPACITY(old);
|
||||||
if (krk_currentThread.flags & KRK_THREAD_DEFER_STACK_FREE) {
|
if (krk_currentThread.flags & KRK_THREAD_DEFER_STACK_FREE) {
|
||||||
KrkValue * newStack = KRK_GROW_ARRAY(KrkValue, NULL, 0, newsize);
|
KrkValue * newStack = GROW_ARRAY(KrkValue, NULL, 0, newsize);
|
||||||
memcpy(newStack, krk_currentThread.stack, sizeof(KrkValue) * old);
|
memcpy(newStack, krk_currentThread.stack, sizeof(KrkValue) * old);
|
||||||
krk_currentThread.stack = newStack;
|
krk_currentThread.stack = newStack;
|
||||||
krk_currentThread.flags &= ~(KRK_THREAD_DEFER_STACK_FREE);
|
krk_currentThread.flags &= ~(KRK_THREAD_DEFER_STACK_FREE);
|
||||||
} else {
|
} else {
|
||||||
krk_currentThread.stack = KRK_GROW_ARRAY(KrkValue, krk_currentThread.stack, old, newsize);
|
krk_currentThread.stack = GROW_ARRAY(KrkValue, krk_currentThread.stack, old, newsize);
|
||||||
}
|
}
|
||||||
krk_currentThread.stackSize = newsize;
|
krk_currentThread.stackSize = newsize;
|
||||||
krk_currentThread.stackTop = krk_currentThread.stack + old_offset;
|
krk_currentThread.stackTop = krk_currentThread.stack + old_offset;
|
||||||
@ -185,7 +224,7 @@ KrkClass * krk_makeClass(KrkInstance * module, KrkClass ** _class, const char *
|
|||||||
*
|
*
|
||||||
* For a class built by managed code, called by type.__new__
|
* For a class built by managed code, called by type.__new__
|
||||||
*/
|
*/
|
||||||
_nonnull
|
__attribute__((nonnull))
|
||||||
void krk_finalizeClass(KrkClass * _class) {
|
void krk_finalizeClass(KrkClass * _class) {
|
||||||
KrkValue tmp;
|
KrkValue tmp;
|
||||||
|
|
||||||
@ -208,7 +247,7 @@ void krk_finalizeClass(KrkClass * _class) {
|
|||||||
*entry->method = NULL;
|
*entry->method = NULL;
|
||||||
KrkClass * _base = _class;
|
KrkClass * _base = _class;
|
||||||
while (_base) {
|
while (_base) {
|
||||||
if (krk_tableGet_fast(&_base->methods, AS_STRING(vm.specialMethodNames[entry->index]), &tmp)) break;
|
if (krk_tableGet(&_base->methods, vm.specialMethodNames[entry->index], &tmp)) break;
|
||||||
_base = _base->base;
|
_base = _base->base;
|
||||||
}
|
}
|
||||||
if (_base && (IS_CLOSURE(tmp) || IS_NATIVE(tmp)) && (!(AS_OBJECT(tmp)->flags & KRK_OBJ_FLAGS_FUNCTION_IS_STATIC_METHOD) || entry->index == METHOD_NEW)) {
|
if (_base && (IS_CLOSURE(tmp) || IS_NATIVE(tmp)) && (!(AS_OBJECT(tmp)->flags & KRK_OBJ_FLAGS_FUNCTION_IS_STATIC_METHOD) || entry->index == METHOD_NEW)) {
|
||||||
@ -219,10 +258,6 @@ void krk_finalizeClass(KrkClass * _class) {
|
|||||||
if (_class->base && _class->_eq != _class->base->_eq) {
|
if (_class->base && _class->_eq != _class->base->_eq) {
|
||||||
if (_class->_hash == _class->base->_hash) {
|
if (_class->_hash == _class->base->_hash) {
|
||||||
_class->_hash = NULL;
|
_class->_hash = NULL;
|
||||||
KrkValue v;
|
|
||||||
if (!krk_tableGet_fast(&_class->methods, AS_STRING(vm.specialMethodNames[METHOD_HASH]), &v)) {
|
|
||||||
krk_tableSet(&_class->methods, vm.specialMethodNames[METHOD_HASH], NONE_VAL());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,8 +364,8 @@ static int _unpack_args(void * context, const KrkValue * values, size_t count) {
|
|||||||
KrkValueArray * positionals = context;
|
KrkValueArray * positionals = context;
|
||||||
if (positionals->count + count > positionals->capacity) {
|
if (positionals->count + count > positionals->capacity) {
|
||||||
size_t old = positionals->capacity;
|
size_t old = positionals->capacity;
|
||||||
positionals->capacity = (count == 1) ? KRK_GROW_CAPACITY(old) : (positionals->count + count);
|
positionals->capacity = (count == 1) ? GROW_CAPACITY(old) : (positionals->count + count);
|
||||||
positionals->values = KRK_GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
|
positionals->values = GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
@ -364,7 +399,7 @@ int krk_processComplexArguments(int argCount, KrkValueArray * positionals, KrkTa
|
|||||||
krk_runtimeError(vm.exceptions->typeError, "%s(): **expression value is not a dict.", name);
|
krk_runtimeError(vm.exceptions->typeError, "%s(): **expression value is not a dict.", name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < AS_DICT(value)->used; ++i) {
|
for (size_t i = 0; i < AS_DICT(value)->capacity; ++i) {
|
||||||
KrkTableEntry * entry = &AS_DICT(value)->entries[i];
|
KrkTableEntry * entry = &AS_DICT(value)->entries[i];
|
||||||
if (!IS_KWARGS(entry->key)) {
|
if (!IS_KWARGS(entry->key)) {
|
||||||
if (!IS_STRING(entry->key)) {
|
if (!IS_STRING(entry->key)) {
|
||||||
@ -424,7 +459,7 @@ static inline int _callManaged(KrkClosure * closure, int argCount, int returnDep
|
|||||||
/* Store scratch while we adjust; we can not make calls while using these scratch
|
/* Store scratch while we adjust; we can not make calls while using these scratch
|
||||||
* registers as they may be clobbered by a nested call to _callManaged. */
|
* registers as they may be clobbered by a nested call to _callManaged. */
|
||||||
krk_currentThread.scratchSpace[0] = myList;
|
krk_currentThread.scratchSpace[0] = myList;
|
||||||
krk_currentThread.scratchSpace[1] = myDict;
|
krk_currentThread.scratchSpace[1] = myList;
|
||||||
|
|
||||||
/* Pop three things, including the kwargs count */
|
/* Pop three things, including the kwargs count */
|
||||||
krk_pop(); /* dict */
|
krk_pop(); /* dict */
|
||||||
@ -478,7 +513,7 @@ static inline int _callManaged(KrkClosure * closure, int argCount, int returnDep
|
|||||||
krk_currentThread.scratchSpace[0] = NONE_VAL();
|
krk_currentThread.scratchSpace[0] = NONE_VAL();
|
||||||
|
|
||||||
/* Now place keyword arguments */
|
/* Now place keyword arguments */
|
||||||
for (size_t i = 0; i < keywords->used; ++i) {
|
for (size_t i = 0; i < keywords->capacity; ++i) {
|
||||||
KrkTableEntry * entry = &keywords->entries[i];
|
KrkTableEntry * entry = &keywords->entries[i];
|
||||||
if (!IS_KWARGS(entry->key)) {
|
if (!IS_KWARGS(entry->key)) {
|
||||||
KrkValue name = entry->key;
|
KrkValue name = entry->key;
|
||||||
@ -571,7 +606,7 @@ _finishKwarg:
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(krk_currentThread.frameCount == (size_t)krk_currentThread.maximumCallDepth)) {
|
if (unlikely(krk_currentThread.frameCount == vm.maximumCallDepth)) {
|
||||||
krk_runtimeError(vm.exceptions->baseException, "maximum recursion depth exceeded");
|
krk_runtimeError(vm.exceptions->baseException, "maximum recursion depth exceeded");
|
||||||
goto _errorAfterKeywords;
|
goto _errorAfterKeywords;
|
||||||
}
|
}
|
||||||
@ -583,6 +618,7 @@ _finishKwarg:
|
|||||||
frame->outSlots = frame->slots - returnDepth;
|
frame->outSlots = frame->slots - returnDepth;
|
||||||
frame->globalsOwner = closure->globalsOwner;
|
frame->globalsOwner = closure->globalsOwner;
|
||||||
frame->globals = closure->globalsTable;
|
frame->globals = closure->globalsTable;
|
||||||
|
FRAME_IN(frame);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
_errorDuringPositionals:
|
_errorDuringPositionals:
|
||||||
@ -614,7 +650,7 @@ inline KrkValue krk_callNativeOnStack(size_t argCount, const KrkValue *stackArgs
|
|||||||
KrkValue result = native(argCount, stackArgs, hasKw);
|
KrkValue result = native(argCount, stackArgs, hasKw);
|
||||||
|
|
||||||
if (unlikely(krk_currentThread.stack != stackBefore)) {
|
if (unlikely(krk_currentThread.stack != stackBefore)) {
|
||||||
KRK_FREE_ARRAY(KrkValue, stackBefore, sizeBefore);
|
FREE_ARRAY(KrkValue, stackBefore, sizeBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
krk_currentThread.flags &= ~(KRK_THREAD_DEFER_STACK_FREE);
|
krk_currentThread.flags &= ~(KRK_THREAD_DEFER_STACK_FREE);
|
||||||
@ -650,17 +686,12 @@ static inline int _callNative(KrkNative* callee, int argCount, int returnDepth)
|
|||||||
/* Write the dict into the list */
|
/* Write the dict into the list */
|
||||||
krk_writeValueArray(AS_LIST(myList), myDict);
|
krk_writeValueArray(AS_LIST(myList), myDict);
|
||||||
|
|
||||||
/* Also add a list for storing references that get removed from the kwargs dict during mutation. */
|
|
||||||
KrkValue refList = krk_list_of(0,NULL,0);
|
|
||||||
krk_push(refList);
|
|
||||||
krk_writeValueArray(AS_LIST(myList), refList);
|
|
||||||
|
|
||||||
/* Reduce the stack to just the list */
|
/* Reduce the stack to just the list */
|
||||||
krk_currentThread.stack[stackOffsetAfterCall] = myList;
|
krk_currentThread.stack[stackOffsetAfterCall] = myList;
|
||||||
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffsetAfterCall+1];
|
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffsetAfterCall+1];
|
||||||
|
|
||||||
/* Call with list as arguments */
|
/* Call with list as arguments */
|
||||||
result = native(AS_LIST(myList)->count-2, AS_LIST(myList)->values, 1);
|
result = native(AS_LIST(myList)->count-1, AS_LIST(myList)->values, 1);
|
||||||
} else {
|
} else {
|
||||||
result = krk_callNativeOnStack(argCount, krk_currentThread.stackTop - argCount, 0, native);
|
result = krk_callNativeOnStack(argCount, krk_currentThread.stackTop - argCount, 0, native);
|
||||||
}
|
}
|
||||||
@ -861,8 +892,13 @@ int krk_isFalsey(KrkValue value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void krk_setMaximumRecursionDepth(size_t maxDepth) {
|
void krk_setMaximumRecursionDepth(size_t maxDepth) {
|
||||||
krk_currentThread.maximumCallDepth = maxDepth;
|
vm.maximumCallDepth = maxDepth;
|
||||||
krk_currentThread.frames = realloc(krk_currentThread.frames, maxDepth * sizeof(KrkCallFrame));
|
|
||||||
|
KrkThreadState * thread = vm.threads;
|
||||||
|
while (thread) {
|
||||||
|
thread->frames = realloc(thread->frames, maxDepth * sizeof(KrkCallFrame));
|
||||||
|
thread = thread->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void krk_initVM(int flags) {
|
void krk_initVM(int flags) {
|
||||||
@ -871,11 +907,11 @@ void krk_initVM(int flags) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
vm.globalFlags = flags & 0xFF00;
|
vm.globalFlags = flags & 0xFF00;
|
||||||
|
vm.maximumCallDepth = KRK_CALL_FRAMES_MAX;
|
||||||
|
|
||||||
/* Reset current thread */
|
/* Reset current thread */
|
||||||
krk_resetStack();
|
krk_resetStack();
|
||||||
krk_currentThread.maximumCallDepth = KRK_CALL_FRAMES_MAX;
|
krk_currentThread.frames = calloc(vm.maximumCallDepth,sizeof(KrkCallFrame));
|
||||||
krk_currentThread.frames = calloc(krk_currentThread.maximumCallDepth,sizeof(KrkCallFrame));
|
|
||||||
krk_currentThread.flags = flags & 0x00FF;
|
krk_currentThread.flags = flags & 0x00FF;
|
||||||
krk_currentThread.module = NULL;
|
krk_currentThread.module = NULL;
|
||||||
vm.threads = &krk_currentThread;
|
vm.threads = &krk_currentThread;
|
||||||
@ -932,16 +968,19 @@ void krk_initVM(int flags) {
|
|||||||
if (!(vm.globalFlags & KRK_GLOBAL_NO_DEFAULT_MODULES)) {
|
if (!(vm.globalFlags & KRK_GLOBAL_NO_DEFAULT_MODULES)) {
|
||||||
#ifndef KRK_NO_SYSTEM_MODULES
|
#ifndef KRK_NO_SYSTEM_MODULES
|
||||||
krk_module_init_kuroko();
|
krk_module_init_kuroko();
|
||||||
|
krk_module_init_gc();
|
||||||
|
krk_module_init_time();
|
||||||
|
krk_module_init_os();
|
||||||
|
krk_module_init_fileio();
|
||||||
|
#endif
|
||||||
|
#ifndef KRK_DISABLE_DEBUG
|
||||||
|
krk_module_init_dis();
|
||||||
#endif
|
#endif
|
||||||
#ifndef KRK_DISABLE_THREADS
|
#ifndef KRK_DISABLE_THREADS
|
||||||
krk_module_init_threading();
|
krk_module_init_threading();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef KRK_DISABLE_DEBUG
|
|
||||||
krk_debug_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* The VM is now ready to start executing code. */
|
/* The VM is now ready to start executing code. */
|
||||||
krk_resetStack();
|
krk_resetStack();
|
||||||
@ -964,11 +1003,11 @@ void krk_freeVM(void) {
|
|||||||
while (krk_currentThread.next) {
|
while (krk_currentThread.next) {
|
||||||
KrkThreadState * thread = krk_currentThread.next;
|
KrkThreadState * thread = krk_currentThread.next;
|
||||||
krk_currentThread.next = thread->next;
|
krk_currentThread.next = thread->next;
|
||||||
KRK_FREE_ARRAY(size_t, thread->stack, thread->stackSize);
|
FREE_ARRAY(size_t, thread->stack, thread->stackSize);
|
||||||
free(thread->frames);
|
free(thread->frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
KRK_FREE_ARRAY(size_t, krk_currentThread.stack, krk_currentThread.stackSize);
|
FREE_ARRAY(size_t, krk_currentThread.stack, krk_currentThread.stackSize);
|
||||||
memset(&krk_vm,0,sizeof(krk_vm));
|
memset(&krk_vm,0,sizeof(krk_vm));
|
||||||
free(krk_currentThread.frames);
|
free(krk_currentThread.frames);
|
||||||
memset(&krk_currentThread,0,sizeof(KrkThreadState));
|
memset(&krk_currentThread,0,sizeof(KrkThreadState));
|
||||||
@ -1092,9 +1131,7 @@ static int handleException(void) {
|
|||||||
stackOffset >= exitSlot &&
|
stackOffset >= exitSlot &&
|
||||||
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_PUSH_TRY) &&
|
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_PUSH_TRY) &&
|
||||||
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_PUSH_WITH) &&
|
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_PUSH_WITH) &&
|
||||||
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_FILTER_EXCEPT) &&
|
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_FILTER_EXCEPT)
|
||||||
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_RAISE) &&
|
|
||||||
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_END_FINALLY)
|
|
||||||
; stackOffset--);
|
; stackOffset--);
|
||||||
if (stackOffset < exitSlot) {
|
if (stackOffset < exitSlot) {
|
||||||
if (exitSlot == 0) {
|
if (exitSlot == 0) {
|
||||||
@ -1193,7 +1230,7 @@ int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs, Kr
|
|||||||
/* Try .../path/__init__.krk */
|
/* Try .../path/__init__.krk */
|
||||||
krk_push(OBJECT_VAL(path));
|
krk_push(OBJECT_VAL(path));
|
||||||
krk_addObjects();
|
krk_addObjects();
|
||||||
krk_push(OBJECT_VAL(S(KRK_PATH_SEP "__init__.krk")));
|
krk_push(OBJECT_VAL(S(PATH_SEP "__init__.krk")));
|
||||||
krk_addObjects();
|
krk_addObjects();
|
||||||
fileName = AS_CSTRING(krk_peek(0));
|
fileName = AS_CSTRING(krk_peek(0));
|
||||||
if (stat(fileName,&statbuf) == 0) {
|
if (stat(fileName,&statbuf) == 0) {
|
||||||
@ -1203,7 +1240,7 @@ int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs, Kr
|
|||||||
|
|
||||||
/* Convert back to .-formatted */
|
/* Convert back to .-formatted */
|
||||||
krk_push(krk_valueGetAttribute(OBJECT_VAL(path), "replace"));
|
krk_push(krk_valueGetAttribute(OBJECT_VAL(path), "replace"));
|
||||||
krk_push(OBJECT_VAL(S(KRK_PATH_SEP)));
|
krk_push(OBJECT_VAL(S(PATH_SEP)));
|
||||||
krk_push(OBJECT_VAL(S(".")));
|
krk_push(OBJECT_VAL(S(".")));
|
||||||
krk_push(krk_callStack(2));
|
krk_push(krk_callStack(2));
|
||||||
KrkValue packageName = krk_peek(0);
|
KrkValue packageName = krk_peek(0);
|
||||||
@ -1281,12 +1318,11 @@ int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs, Kr
|
|||||||
krk_runfile(fileName,fileName);
|
krk_runfile(fileName,fileName);
|
||||||
*moduleOut = OBJECT_VAL(krk_currentThread.module);
|
*moduleOut = OBJECT_VAL(krk_currentThread.module);
|
||||||
krk_currentThread.module = enclosing;
|
krk_currentThread.module = enclosing;
|
||||||
if (!IS_OBJECT(*moduleOut) || (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
|
if (!IS_OBJECT(*moduleOut)) {
|
||||||
if (!(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
|
if (!(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
|
||||||
krk_runtimeError(vm.exceptions->importError,
|
krk_runtimeError(vm.exceptions->importError,
|
||||||
"Failed to load module '%S' from '%s'", runAs, fileName);
|
"Failed to load module '%S' from '%s'", runAs, fileName);
|
||||||
}
|
}
|
||||||
krk_tableDelete(&vm.modules, OBJECT_VAL(runAs));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1297,7 +1333,7 @@ int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs, Kr
|
|||||||
#ifndef KRK_STATIC_ONLY
|
#ifndef KRK_STATIC_ONLY
|
||||||
_sharedObject: (void)0;
|
_sharedObject: (void)0;
|
||||||
|
|
||||||
krk_dlRefType dlRef = krk_dlOpen(fileName);
|
dlRefType dlRef = dlOpen(fileName);
|
||||||
if (!dlRef) {
|
if (!dlRef) {
|
||||||
*moduleOut = NONE_VAL();
|
*moduleOut = NONE_VAL();
|
||||||
krk_runtimeError(vm.exceptions->importError,
|
krk_runtimeError(vm.exceptions->importError,
|
||||||
@ -1317,11 +1353,11 @@ int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs, Kr
|
|||||||
char * handlerName = AS_CSTRING(krk_peek(0));
|
char * handlerName = AS_CSTRING(krk_peek(0));
|
||||||
|
|
||||||
KrkValue (*moduleOnLoad)(KrkString * name);
|
KrkValue (*moduleOnLoad)(KrkString * name);
|
||||||
krk_dlSymType out = krk_dlSym(dlRef, handlerName);
|
dlSymType out = dlSym(dlRef, handlerName);
|
||||||
memcpy(&moduleOnLoad,&out,sizeof(out));
|
memcpy(&moduleOnLoad,&out,sizeof(out));
|
||||||
|
|
||||||
if (!moduleOnLoad) {
|
if (!moduleOnLoad) {
|
||||||
krk_dlClose(dlRef);
|
dlClose(dlRef);
|
||||||
*moduleOut = NONE_VAL();
|
*moduleOut = NONE_VAL();
|
||||||
krk_runtimeError(vm.exceptions->importError,
|
krk_runtimeError(vm.exceptions->importError,
|
||||||
"Failed to run module initialization method '%s' from shared object '%s'",
|
"Failed to run module initialization method '%s' from shared object '%s'",
|
||||||
@ -1333,7 +1369,7 @@ int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs, Kr
|
|||||||
|
|
||||||
*moduleOut = moduleOnLoad(runAs);
|
*moduleOut = moduleOnLoad(runAs);
|
||||||
if (!krk_isInstanceOf(*moduleOut, vm.baseClasses->moduleClass)) {
|
if (!krk_isInstanceOf(*moduleOut, vm.baseClasses->moduleClass)) {
|
||||||
krk_dlClose(dlRef);
|
dlClose(dlRef);
|
||||||
krk_runtimeError(vm.exceptions->importError,
|
krk_runtimeError(vm.exceptions->importError,
|
||||||
"Failed to load module '%S' from '%s'", runAs, fileName);
|
"Failed to load module '%S' from '%s'", runAs, fileName);
|
||||||
return 0;
|
return 0;
|
||||||
@ -1363,7 +1399,7 @@ int krk_loadModule(KrkString * path, KrkValue * moduleOut, KrkString * runAs, Kr
|
|||||||
if (runAs == S("__main__")) {
|
if (runAs == S("__main__")) {
|
||||||
/* Then let's use 'path' instead, and replace all the /'s with .'s... */
|
/* Then let's use 'path' instead, and replace all the /'s with .'s... */
|
||||||
krk_push(krk_valueGetAttribute(OBJECT_VAL(path), "replace"));
|
krk_push(krk_valueGetAttribute(OBJECT_VAL(path), "replace"));
|
||||||
krk_push(OBJECT_VAL(S(KRK_PATH_SEP)));
|
krk_push(OBJECT_VAL(S(PATH_SEP)));
|
||||||
krk_push(OBJECT_VAL(S(".")));
|
krk_push(OBJECT_VAL(S(".")));
|
||||||
krk_push(krk_callStack(2));
|
krk_push(krk_callStack(2));
|
||||||
} else {
|
} else {
|
||||||
@ -1477,7 +1513,7 @@ int krk_importModule(KrkString * name, KrkString * runAs) {
|
|||||||
pushStringBuilderStr(&sb, &name->chars[dots], name->length - dots);
|
pushStringBuilderStr(&sb, &name->chars[dots], name->length - dots);
|
||||||
}
|
}
|
||||||
|
|
||||||
krk_push(finishStringBuilder(&sb));
|
krk_push(OBJECT_VAL(finishStringBuilder(&sb)));
|
||||||
|
|
||||||
/* Now to try to import the fully qualified module path */
|
/* Now to try to import the fully qualified module path */
|
||||||
if (krk_importModule(AS_STRING(krk_peek(0)), AS_STRING(krk_peek(0)))) {
|
if (krk_importModule(AS_STRING(krk_peek(0)), AS_STRING(krk_peek(0)))) {
|
||||||
@ -1557,7 +1593,7 @@ int krk_importModule(KrkString * name, KrkString * runAs) {
|
|||||||
krk_currentThread.stack[argBase-1] = krk_pop();
|
krk_currentThread.stack[argBase-1] = krk_pop();
|
||||||
/* Now concatenate forward slash... */
|
/* Now concatenate forward slash... */
|
||||||
krk_push(krk_currentThread.stack[argBase+1]); /* Slash path */
|
krk_push(krk_currentThread.stack[argBase+1]); /* Slash path */
|
||||||
krk_push(OBJECT_VAL(S(KRK_PATH_SEP)));
|
krk_push(OBJECT_VAL(S(PATH_SEP)));
|
||||||
krk_addObjects();
|
krk_addObjects();
|
||||||
krk_currentThread.stack[argBase+1] = krk_pop();
|
krk_currentThread.stack[argBase+1] = krk_pop();
|
||||||
/* And now for the dot... */
|
/* And now for the dot... */
|
||||||
@ -1613,7 +1649,7 @@ static void clearCache(KrkClass * type) {
|
|||||||
type->cacheIndex = 0;
|
type->cacheIndex = 0;
|
||||||
for (size_t i = 0; i < type->subclasses.capacity; ++i) {
|
for (size_t i = 0; i < type->subclasses.capacity; ++i) {
|
||||||
KrkTableEntry * entry = &type->subclasses.entries[i];
|
KrkTableEntry * entry = &type->subclasses.entries[i];
|
||||||
if (krk_valuesSame(entry->key, KWARGS_VAL(0))) continue;
|
if (entry->key == KWARGS_VAL(0)) continue;
|
||||||
clearCache(AS_CLASS(entry->key));
|
clearCache(AS_CLASS(entry->key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1751,12 +1787,10 @@ static int valueGetProperty(KrkString * name) {
|
|||||||
krk_currentThread.stackTop[-2] = krk_currentThread.stackTop[-1];
|
krk_currentThread.stackTop[-2] = krk_currentThread.stackTop[-1];
|
||||||
krk_currentThread.stackTop--;
|
krk_currentThread.stackTop--;
|
||||||
return 1;
|
return 1;
|
||||||
case 1: {
|
case 1:
|
||||||
KrkValue o = OBJECT_VAL(krk_newBoundMethod(krk_currentThread.stackTop[-2], AS_OBJECT(krk_currentThread.stackTop[-1])));
|
krk_currentThread.stackTop[-2] = OBJECT_VAL(krk_newBoundMethod(krk_currentThread.stackTop[-2], AS_OBJECT(krk_currentThread.stackTop[-1])));
|
||||||
krk_currentThread.stackTop[-2] = o;
|
|
||||||
krk_currentThread.stackTop--;
|
krk_currentThread.stackTop--;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1874,19 +1908,16 @@ static int valueSetProperty(KrkString * name) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (IS_INSTANCE(owner)) {
|
if (IS_INSTANCE(owner)) {
|
||||||
KrkValue o = setAttr_wrapper(owner,type,&AS_INSTANCE(owner)->fields, name, value);
|
krk_currentThread.stackTop[-1] = setAttr_wrapper(owner,type,&AS_INSTANCE(owner)->fields, name, value);
|
||||||
krk_currentThread.stackTop[-1] = o;
|
|
||||||
} else if (IS_CLASS(owner)) {
|
} else if (IS_CLASS(owner)) {
|
||||||
KrkValue o = setAttr_wrapper(owner,type,&AS_CLASS(owner)->methods, name, value);
|
krk_currentThread.stackTop[-1] = setAttr_wrapper(owner,type,&AS_CLASS(owner)->methods, name, value);
|
||||||
krk_currentThread.stackTop[-1] = o;
|
|
||||||
if (name->length > 1 && name->chars[0] == '_' && name->chars[1] == '_') {
|
if (name->length > 1 && name->chars[0] == '_' && name->chars[1] == '_') {
|
||||||
krk_finalizeClass(AS_CLASS(owner));
|
krk_finalizeClass(AS_CLASS(owner));
|
||||||
} else {
|
} else {
|
||||||
clearCache(AS_CLASS(owner));
|
clearCache(AS_CLASS(owner));
|
||||||
}
|
}
|
||||||
} else if (IS_CLOSURE(owner)) {
|
} else if (IS_CLOSURE(owner)) {
|
||||||
KrkValue o = setAttr_wrapper(owner,type,&AS_CLOSURE(owner)->fields, name, value);
|
krk_currentThread.stackTop[-1] = setAttr_wrapper(owner,type,&AS_CLOSURE(owner)->fields, name, value);
|
||||||
krk_currentThread.stackTop[-1] = o;
|
|
||||||
} else {
|
} else {
|
||||||
if (_setDescriptor(owner,type,name,value)) {
|
if (_setDescriptor(owner,type,name,value)) {
|
||||||
krk_swap(1);
|
krk_swap(1);
|
||||||
@ -2082,7 +2113,7 @@ static KrkValue run(void) {
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (unlikely(krk_currentThread.flags & (KRK_THREAD_ENABLE_TRACING | KRK_THREAD_SINGLE_STEP | KRK_THREAD_SIGNALLED))) {
|
if (unlikely(krk_currentThread.flags & (KRK_THREAD_ENABLE_TRACING | KRK_THREAD_SINGLE_STEP | KRK_THREAD_SIGNALLED))) {
|
||||||
#if !defined(KRK_NO_TRACING) && !defined(KRK_DISABLE_DEBUG)
|
#ifndef KRK_NO_TRACING
|
||||||
if (krk_currentThread.flags & KRK_THREAD_ENABLE_TRACING) {
|
if (krk_currentThread.flags & KRK_THREAD_ENABLE_TRACING) {
|
||||||
krk_debug_dumpStack(stderr, frame);
|
krk_debug_dumpStack(stderr, frame);
|
||||||
krk_disassembleInstruction(stderr, frame->closure->function,
|
krk_disassembleInstruction(stderr, frame->closure->function,
|
||||||
@ -2111,17 +2142,16 @@ _resumeHook: (void)0;
|
|||||||
unsigned int OPERAND = 0;
|
unsigned int OPERAND = 0;
|
||||||
|
|
||||||
/* Only GCC lets us put these on empty statements; just hope clang doesn't start complaining */
|
/* Only GCC lets us put these on empty statements; just hope clang doesn't start complaining */
|
||||||
#if defined(__GNUC__) && !defined(__clang__)
|
#ifndef __clang__
|
||||||
# define FALLTHROUGH __attribute__((fallthrough));
|
# define FALLTHROUGH __attribute__((fallthrough));
|
||||||
#else
|
#else
|
||||||
# define FALLTHROUGH
|
# define FALLTHROUGH
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TWO_BYTE_OPERAND { OPERAND = OPERAND | (frame->ip[0] << 8) | frame->ip[1]; frame->ip += 2; }
|
#define TWO_BYTE_OPERAND { OPERAND = (frame->ip[0] << 8) | frame->ip[1]; frame->ip += 2; }
|
||||||
#define THREE_BYTE_OPERAND { OPERAND = (frame->ip[0] << 16) | (frame->ip[1] << 8); frame->ip += 2; } FALLTHROUGH
|
#define THREE_BYTE_OPERAND { OPERAND = (frame->ip[0] << 16) | (frame->ip[1] << 8); frame->ip += 2; } FALLTHROUGH
|
||||||
#define ONE_BYTE_OPERAND { OPERAND = (OPERAND & ~0xFF) | READ_BYTE(); }
|
#define ONE_BYTE_OPERAND { OPERAND = (OPERAND & ~0xFF) | READ_BYTE(); }
|
||||||
|
|
||||||
_switchEntry: (void)0;
|
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case OP_CLEANUP_WITH: {
|
case OP_CLEANUP_WITH: {
|
||||||
/* Top of stack is a HANDLER that should have had something loaded into it if it was still valid */
|
/* Top of stack is a HANDLER that should have had something loaded into it if it was still valid */
|
||||||
@ -2131,7 +2161,6 @@ _switchEntry: (void)0;
|
|||||||
KrkClass * type = krk_getType(contextManager);
|
KrkClass * type = krk_getType(contextManager);
|
||||||
krk_push(contextManager);
|
krk_push(contextManager);
|
||||||
if (AS_HANDLER_TYPE(handler) == OP_RAISE) {
|
if (AS_HANDLER_TYPE(handler) == OP_RAISE) {
|
||||||
krk_currentThread.stackTop[-2] = HANDLER_VAL(OP_CLEANUP_WITH,AS_HANDLER_TARGET(krk_peek(1)));
|
|
||||||
krk_push(OBJECT_VAL(krk_getType(exceptionObject)));
|
krk_push(OBJECT_VAL(krk_getType(exceptionObject)));
|
||||||
krk_push(exceptionObject);
|
krk_push(exceptionObject);
|
||||||
KrkValue tracebackEntries = NONE_VAL();
|
KrkValue tracebackEntries = NONE_VAL();
|
||||||
@ -2164,6 +2193,7 @@ _switchEntry: (void)0;
|
|||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
_finishReturn: (void)0;
|
_finishReturn: (void)0;
|
||||||
KrkValue result = krk_pop();
|
KrkValue result = krk_pop();
|
||||||
|
closeUpvalues(frame->slots);
|
||||||
/* See if this frame had a thing */
|
/* See if this frame had a thing */
|
||||||
int stackOffset;
|
int stackOffset;
|
||||||
for (stackOffset = (int)(krk_currentThread.stackTop - krk_currentThread.stack - 1);
|
for (stackOffset = (int)(krk_currentThread.stackTop - krk_currentThread.stack - 1);
|
||||||
@ -2173,14 +2203,13 @@ _finishReturn: (void)0;
|
|||||||
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset],OP_FILTER_EXCEPT)
|
!IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset],OP_FILTER_EXCEPT)
|
||||||
; stackOffset--);
|
; stackOffset--);
|
||||||
if (stackOffset >= (int)frame->slots) {
|
if (stackOffset >= (int)frame->slots) {
|
||||||
closeUpvalues(stackOffset);
|
|
||||||
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffset + 1];
|
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffset + 1];
|
||||||
frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0));
|
frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0));
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_RETURN,AS_HANDLER_TARGET(krk_peek(0)));
|
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_RETURN,AS_HANDLER_TARGET(krk_peek(0)));
|
||||||
krk_currentThread.stackTop[-2] = result;
|
krk_currentThread.stackTop[-2] = result;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
closeUpvalues(frame->slots);
|
FRAME_OUT(frame);
|
||||||
krk_currentThread.frameCount--;
|
krk_currentThread.frameCount--;
|
||||||
if (krk_currentThread.frameCount == 0) {
|
if (krk_currentThread.frameCount == 0) {
|
||||||
krk_pop();
|
krk_pop();
|
||||||
@ -2224,8 +2253,7 @@ _finishReturn: (void)0;
|
|||||||
case OP_TRUE: krk_push(BOOLEAN_VAL(1)); break;
|
case OP_TRUE: krk_push(BOOLEAN_VAL(1)); break;
|
||||||
case OP_FALSE: krk_push(BOOLEAN_VAL(0)); break;
|
case OP_FALSE: krk_push(BOOLEAN_VAL(0)); break;
|
||||||
case OP_UNSET: krk_push(KWARGS_VAL(0)); break;
|
case OP_UNSET: krk_push(KWARGS_VAL(0)); break;
|
||||||
case OP_NOT: krk_push(BOOLEAN_VAL(krk_isFalsey(krk_peek(0)))); /* fallthrough */
|
case OP_NOT: krk_currentThread.stackTop[-1] = BOOLEAN_VAL(krk_isFalsey(krk_peek(0))); break;
|
||||||
case OP_SWAP_POP: krk_swap(1); /* fallthrough */
|
|
||||||
case OP_POP: krk_pop(); break;
|
case OP_POP: krk_pop(); break;
|
||||||
|
|
||||||
case OP_INPLACE_ADD: INPLACE_BINARY_OP(add)
|
case OP_INPLACE_ADD: INPLACE_BINARY_OP(add)
|
||||||
@ -2283,6 +2311,33 @@ _finishReturn: (void)0;
|
|||||||
case OP_SWAP:
|
case OP_SWAP:
|
||||||
krk_swap(1);
|
krk_swap(1);
|
||||||
break;
|
break;
|
||||||
|
case OP_FILTER_EXCEPT: {
|
||||||
|
int isMatch = 0;
|
||||||
|
if (AS_HANDLER_TYPE(krk_peek(1)) == OP_RETURN) {
|
||||||
|
isMatch = 0;
|
||||||
|
} else if (AS_HANDLER_TYPE(krk_peek(1)) == OP_END_FINALLY) {
|
||||||
|
isMatch = 0;
|
||||||
|
} else if (AS_HANDLER_TYPE(krk_peek(1)) == OP_EXIT_LOOP) {
|
||||||
|
isMatch = 0;
|
||||||
|
} else if (IS_CLASS(krk_peek(0)) && krk_isInstanceOf(krk_peek(2), AS_CLASS(krk_peek(0)))) {
|
||||||
|
isMatch = 1;
|
||||||
|
} else if (IS_TUPLE(krk_peek(0))) {
|
||||||
|
for (size_t i = 0; i < AS_TUPLE(krk_peek(0))->values.count; ++i) {
|
||||||
|
if (IS_CLASS(AS_TUPLE(krk_peek(0))->values.values[i]) && krk_isInstanceOf(krk_peek(2), AS_CLASS(AS_TUPLE(krk_peek(0))->values.values[i]))) {
|
||||||
|
isMatch = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (IS_NONE(krk_peek(0))) {
|
||||||
|
isMatch = !IS_NONE(krk_peek(2));
|
||||||
|
}
|
||||||
|
if (isMatch) {
|
||||||
|
krk_currentThread.stackTop[-2] = HANDLER_VAL(OP_FILTER_EXCEPT,AS_HANDLER_TARGET(krk_peek(1)));
|
||||||
|
}
|
||||||
|
krk_pop();
|
||||||
|
krk_push(BOOLEAN_VAL(isMatch));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_TRY_ELSE: {
|
case OP_TRY_ELSE: {
|
||||||
if (IS_HANDLER(krk_peek(0))) {
|
if (IS_HANDLER(krk_peek(0))) {
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_FILTER_EXCEPT,AS_HANDLER_TARGET(krk_peek(0)));
|
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_FILTER_EXCEPT,AS_HANDLER_TARGET(krk_peek(0)));
|
||||||
@ -2291,16 +2346,10 @@ _finishReturn: (void)0;
|
|||||||
}
|
}
|
||||||
case OP_BEGIN_FINALLY: {
|
case OP_BEGIN_FINALLY: {
|
||||||
if (IS_HANDLER(krk_peek(0))) {
|
if (IS_HANDLER(krk_peek(0))) {
|
||||||
switch (AS_HANDLER_TYPE(krk_peek(0))) {
|
if (AS_HANDLER_TYPE(krk_peek(0)) == OP_PUSH_TRY) {
|
||||||
/* We either entered the @c finally without an exception, or the exception was handled by an @c except */
|
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_BEGIN_FINALLY,AS_HANDLER_TARGET(krk_peek(0)));
|
||||||
case OP_PUSH_TRY:
|
} else if (AS_HANDLER_TYPE(krk_peek(0)) == OP_FILTER_EXCEPT) {
|
||||||
case OP_FILTER_EXCEPT:
|
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_BEGIN_FINALLY,AS_HANDLER_TARGET(krk_peek(0)));
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_BEGIN_FINALLY,AS_HANDLER_TARGET(krk_peek(0)));
|
|
||||||
break;
|
|
||||||
/* We entered the @c finally without handling an exception. */
|
|
||||||
case OP_RAISE:
|
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_END_FINALLY,AS_HANDLER_TARGET(krk_peek(0)));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2409,40 +2458,18 @@ _finishReturn: (void)0;
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OP_OVERLONG_JUMP: {
|
|
||||||
/* Overlong jumps replace 2-byte operand jump instructions with a zero-operand instruction that
|
|
||||||
* slowly scans through a dumb table to find the intended jump target and opcode. */
|
|
||||||
for (size_t i = 0; i < frame->closure->function->overlongJumpsCount; ++i) {
|
|
||||||
if (frame->closure->function->overlongJumps[i].instructionOffset ==
|
|
||||||
(size_t)((char*)frame->ip - (char*)frame->closure->function->chunk.code)) {
|
|
||||||
OPERAND = (int)frame->closure->function->overlongJumps[i].intendedTarget << 16;
|
|
||||||
opcode = frame->closure->function->overlongJumps[i].originalOpcode;
|
|
||||||
goto _switchEntry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
krk_runtimeError(vm.exceptions->valueError, "bad jump");
|
|
||||||
goto _finishException;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OP_PUSH_BUILD_CLASS: {
|
|
||||||
KrkValue build_class = NONE_VAL();
|
|
||||||
krk_tableGet_fast(&vm.builtins->fields, AS_STRING(vm.specialMethodNames[METHOD_BLDCLS]), &build_class);
|
|
||||||
krk_push(build_class);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Two-byte operands
|
* Two-byte operands
|
||||||
*/
|
*/
|
||||||
case OP_JUMP_IF_FALSE_OR_POP: {
|
case OP_JUMP_IF_FALSE_OR_POP: {
|
||||||
TWO_BYTE_OPERAND;
|
TWO_BYTE_OPERAND;
|
||||||
if (krk_valuesSame(krk_peek(0), BOOLEAN_VAL(0)) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
|
if (krk_peek(0) == BOOLEAN_VAL(0) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
|
||||||
else krk_pop();
|
else krk_pop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OP_POP_JUMP_IF_FALSE: {
|
case OP_POP_JUMP_IF_FALSE: {
|
||||||
TWO_BYTE_OPERAND;
|
TWO_BYTE_OPERAND;
|
||||||
if (krk_valuesSame(krk_peek(0), BOOLEAN_VAL(0)) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
|
if (krk_peek(0) == BOOLEAN_VAL(0) || krk_isFalsey(krk_peek(0))) frame->ip += OPERAND;
|
||||||
krk_pop();
|
krk_pop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2530,7 +2557,7 @@ _finishReturn: (void)0;
|
|||||||
krk_push(iter);
|
krk_push(iter);
|
||||||
krk_push(krk_callStack(0));
|
krk_push(krk_callStack(0));
|
||||||
/* krk_valuesSame() */
|
/* krk_valuesSame() */
|
||||||
if (krk_valuesSame(iter, krk_peek(0))) frame->ip += OPERAND;
|
if (iter == krk_peek(0)) frame->ip += OPERAND;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OP_LOOP_ITER: {
|
case OP_LOOP_ITER: {
|
||||||
@ -2538,55 +2565,12 @@ _finishReturn: (void)0;
|
|||||||
KrkValue iter = krk_peek(0);
|
KrkValue iter = krk_peek(0);
|
||||||
krk_push(iter);
|
krk_push(iter);
|
||||||
krk_push(krk_callStack(0));
|
krk_push(krk_callStack(0));
|
||||||
if (!krk_valuesSame(iter, krk_peek(0))) frame->ip -= OPERAND;
|
if (iter != krk_peek(0)) frame->ip -= OPERAND;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OP_TEST_ARG: {
|
case OP_TEST_ARG: {
|
||||||
TWO_BYTE_OPERAND;
|
TWO_BYTE_OPERAND;
|
||||||
if (!krk_valuesSame(krk_pop(), KWARGS_VAL(0))) frame->ip += OPERAND;
|
if (krk_pop() != KWARGS_VAL(0)) frame->ip += OPERAND;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_FILTER_EXCEPT: {
|
|
||||||
TWO_BYTE_OPERAND;
|
|
||||||
/* "Pop exception to match with and jump if not a match" */
|
|
||||||
int isMatch = 0;
|
|
||||||
if (IS_CLASS(krk_peek(0)) && krk_isInstanceOf(krk_peek(2), AS_CLASS(krk_peek(0)))) {
|
|
||||||
isMatch = 1;
|
|
||||||
} else if (IS_TUPLE(krk_peek(0))) {
|
|
||||||
for (size_t i = 0; i < AS_TUPLE(krk_peek(0))->values.count; ++i) {
|
|
||||||
if (IS_CLASS(AS_TUPLE(krk_peek(0))->values.values[i]) && krk_isInstanceOf(krk_peek(2), AS_CLASS(AS_TUPLE(krk_peek(0))->values.values[i]))) {
|
|
||||||
isMatch = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (IS_NONE(krk_peek(0))) {
|
|
||||||
isMatch = !IS_NONE(krk_peek(2));
|
|
||||||
}
|
|
||||||
if (isMatch) {
|
|
||||||
/* If exception matched, set handler state. */
|
|
||||||
krk_currentThread.stackTop[-2] = HANDLER_VAL(OP_FILTER_EXCEPT,AS_HANDLER_TARGET(krk_peek(1)));
|
|
||||||
} else {
|
|
||||||
/* If exception did not match, jump to next 'except' or 'finally' */
|
|
||||||
frame->ip += OPERAND;
|
|
||||||
}
|
|
||||||
krk_pop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_ENTER_EXCEPT: {
|
|
||||||
TWO_BYTE_OPERAND;
|
|
||||||
switch (AS_HANDLER_TYPE(krk_peek(0))) {
|
|
||||||
case OP_RETURN:
|
|
||||||
case OP_END_FINALLY:
|
|
||||||
case OP_EXIT_LOOP:
|
|
||||||
frame->ip += OPERAND;
|
|
||||||
break;
|
|
||||||
case OP_RAISE_FROM:
|
|
||||||
/* Exception happened while in @c finally */
|
|
||||||
krk_pop(); /* handler */
|
|
||||||
krk_currentThread.currentException = krk_pop();
|
|
||||||
krk_currentThread.flags |= KRK_THREAD_HAS_EXCEPTION;
|
|
||||||
goto _finishException;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2913,7 +2897,8 @@ _finishReturn: (void)0;
|
|||||||
THREE_BYTE_OPERAND;
|
THREE_BYTE_OPERAND;
|
||||||
case OP_EXIT_LOOP: {
|
case OP_EXIT_LOOP: {
|
||||||
ONE_BYTE_OPERAND;
|
ONE_BYTE_OPERAND;
|
||||||
_finishPopBlock: (void)0;
|
_finishPopBlock:
|
||||||
|
closeUpvalues(frame->slots + OPERAND);
|
||||||
int stackOffset;
|
int stackOffset;
|
||||||
for (stackOffset = (int)(krk_currentThread.stackTop - krk_currentThread.stack - 1);
|
for (stackOffset = (int)(krk_currentThread.stackTop - krk_currentThread.stack - 1);
|
||||||
stackOffset >= (int)(frame->slots + OPERAND) &&
|
stackOffset >= (int)(frame->slots + OPERAND) &&
|
||||||
@ -2924,14 +2909,11 @@ _finishPopBlock: (void)0;
|
|||||||
|
|
||||||
/* Do the handler. */
|
/* Do the handler. */
|
||||||
if (stackOffset >= (int)(frame->slots + OPERAND)) {
|
if (stackOffset >= (int)(frame->slots + OPERAND)) {
|
||||||
closeUpvalues(stackOffset);
|
|
||||||
uint16_t popTarget = (frame->ip - frame->closure->function->chunk.code);
|
uint16_t popTarget = (frame->ip - frame->closure->function->chunk.code);
|
||||||
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffset + 1];
|
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffset + 1];
|
||||||
frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0));
|
frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0));
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_EXIT_LOOP, popTarget);
|
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_EXIT_LOOP, popTarget);
|
||||||
krk_currentThread.stackTop[-2] = INTEGER_VAL(OPERAND);
|
krk_currentThread.stackTop[-2] = INTEGER_VAL(OPERAND);
|
||||||
} else {
|
|
||||||
closeUpvalues(frame->slots + OPERAND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Continue normally */
|
/* Continue normally */
|
||||||
@ -3163,21 +3145,10 @@ _finishException:
|
|||||||
frame = &krk_currentThread.frames[krk_currentThread.frameCount - 1];
|
frame = &krk_currentThread.frames[krk_currentThread.frameCount - 1];
|
||||||
frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0));
|
frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0));
|
||||||
/* Stick the exception into the exception slot */
|
/* Stick the exception into the exception slot */
|
||||||
switch (AS_HANDLER_TYPE(krk_currentThread.stackTop[-1])) {
|
if (AS_HANDLER_TYPE(krk_currentThread.stackTop[-1])== OP_FILTER_EXCEPT) {
|
||||||
/* An exception happened while handling an exception */
|
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_END_FINALLY,AS_HANDLER_TARGET(krk_peek(0)));
|
||||||
case OP_RAISE:
|
} else {
|
||||||
case OP_FILTER_EXCEPT:
|
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_RAISE,AS_HANDLER_TARGET(krk_peek(0)));
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_END_FINALLY,AS_HANDLER_TARGET(krk_peek(0)));
|
|
||||||
break;
|
|
||||||
/* An exception happened while already in the @c finally block from handling
|
|
||||||
* another exception. Bail. */
|
|
||||||
case OP_END_FINALLY:
|
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_RAISE_FROM,AS_HANDLER_TARGET(krk_peek(0)));
|
|
||||||
break;
|
|
||||||
/* First exception in this chain. */
|
|
||||||
default:
|
|
||||||
krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_RAISE,AS_HANDLER_TARGET(krk_peek(0)));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
krk_currentThread.stackTop[-2] = krk_currentThread.currentException;
|
krk_currentThread.stackTop[-2] = krk_currentThread.currentException;
|
||||||
krk_currentThread.currentException = NONE_VAL();
|
krk_currentThread.currentException = NONE_VAL();
|
||||||
@ -3216,7 +3187,7 @@ KrkInstance * krk_startModule(const char * name) {
|
|||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
KrkValue krk_interpret(const char * src, const char * fromFile) {
|
KrkValue krk_interpret(const char * src, char * fromFile) {
|
||||||
KrkCodeObject * function = krk_compile(src, fromFile);
|
KrkCodeObject * function = krk_compile(src, fromFile);
|
||||||
if (!function) {
|
if (!function) {
|
||||||
if (!krk_currentThread.frameCount) handleException();
|
if (!krk_currentThread.frameCount) handleException();
|
||||||
@ -3233,7 +3204,7 @@ KrkValue krk_interpret(const char * src, const char * fromFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef KRK_NO_FILESYSTEM
|
#ifndef KRK_NO_FILESYSTEM
|
||||||
KrkValue krk_runfile(const char * fileName, const char * fromFile) {
|
KrkValue krk_runfile(const char * fileName, char * fromFile) {
|
||||||
FILE * f = fopen(fileName,"r");
|
FILE * f = fopen(fileName,"r");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
fprintf(stderr, "%s: could not open file '%s': %s\n", "kuroko", fileName, strerror(errno));
|
fprintf(stderr, "%s: could not open file '%s': %s\n", "kuroko", fileName, strerror(errno));
|
||||||
|
@ -1,389 +1,389 @@
|
|||||||
Checking {'eyr': '2027', 'hcl': '#602927', 'hgt': '186cm', 'byr': '1939', 'iyr': '2019', 'pid': '552194973', 'ecl': 'hzl'}
|
Checking {'hcl': '#602927', 'iyr': '2019', 'hgt': '186cm', 'byr': '1939', 'pid': '552194973', 'eyr': '2027', 'ecl': 'hzl'}
|
||||||
Checking {'pid': '657988073', 'eyr': '2020', 'byr': '1996', 'ecl': 'brn', 'hcl': '#866857', 'iyr': '2015', 'hgt': '164cm'}
|
Checking {'hcl': '#866857', 'iyr': '2015', 'hgt': '164cm', 'pid': '657988073', 'byr': '1996', 'eyr': '2020', 'ecl': 'brn'}
|
||||||
Checking {'hcl': '#fffffd', 'byr': '1951', 'cid': '321', 'iyr': '2017', 'eyr': '2022', 'ecl': 'brn', 'hgt': '62in', 'pid': '#6ef4e1'}
|
Checking {'eyr': '2022', 'hcl': '#fffffd', 'iyr': '2017', 'hgt': '62in', 'byr': '1951', 'pid': '#6ef4e1', 'ecl': 'brn', 'cid': '321'}
|
||||||
bad pid
|
bad pid
|
||||||
Checking {'eyr': '2025', 'iyr': '2011', 'byr': '1980', 'hcl': '#fffffd', 'cid': '129', 'pid': '420023864', 'hgt': '150cm', 'ecl': 'brn'}
|
Checking {'ecl': 'brn', 'hcl': '#fffffd', 'iyr': '2011', 'hgt': '150cm', 'byr': '1980', 'pid': '420023864', 'eyr': '2025', 'cid': '129'}
|
||||||
Checking {'eyr': '2029', 'hcl': '#ceb3a1', 'hgt': '187cm', 'byr': '1925', 'ecl': 'amb', 'pid': '223151011', 'iyr': '2016'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2016', 'hgt': '187cm', 'byr': '1925', 'pid': '223151011', 'eyr': '2029', 'ecl': 'amb'}
|
||||||
Checking {'hcl': '#cfa07d', 'ecl': 'brn', 'eyr': '2022', 'pid': '135392110', 'iyr': '2010', 'hgt': '190cm', 'byr': '1959'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2010', 'hgt': '190cm', 'pid': '135392110', 'byr': '1959', 'ecl': 'brn', 'eyr': '2022'}
|
||||||
Checking {'eyr': '2024', 'cid': '225', 'iyr': '2018', 'pid': '522856696', 'byr': '1961', 'hcl': '#a97842', 'ecl': 'grn'}
|
Checking {'ecl': 'grn', 'hcl': '#a97842', 'iyr': '2018', 'pid': '522856696', 'byr': '1961', 'eyr': '2024', 'cid': '225'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'eyr': '2024', 'byr': '1964', 'ecl': 'brn', 'iyr': '1976', 'hcl': '#866857', 'hgt': '190cm', 'pid': '562135232'}
|
Checking {'hcl': '#866857', 'iyr': '1976', 'hgt': '190cm', 'byr': '1964', 'pid': '562135232', 'eyr': '2024', 'ecl': 'brn'}
|
||||||
Bad issue year
|
Bad issue year
|
||||||
Checking {'hgt': '193cm', 'pid': '#6e4342', 'iyr': '2011', 'byr': '1936', 'cid': '296', 'hcl': 'z', 'ecl': '#3b8ed3', 'eyr': '2022'}
|
Checking {'eyr': '2022', 'hcl': 'z', 'iyr': '2011', 'hgt': '193cm', 'pid': '#6e4342', 'byr': '1936', 'cid': '296', 'ecl': '#3b8ed3'}
|
||||||
bad hair color
|
bad hair color
|
||||||
Checking {'cid': '154', 'byr': '1985', 'pid': '503255860', 'ecl': 'gry', 'eyr': '2023', 'hcl': '#efcc98', 'iyr': '2014'}
|
Checking {'ecl': 'gry', 'hcl': '#efcc98', 'iyr': '2014', 'byr': '1985', 'pid': '503255860', 'eyr': '2023', 'cid': '154'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'eyr': '2026', 'iyr': '2012', 'pid': '631051435', 'byr': '1986', 'ecl': 'amb', 'hgt': '154cm', 'hcl': '#341e13'}
|
Checking {'hcl': '#341e13', 'iyr': '2012', 'hgt': '154cm', 'pid': '631051435', 'byr': '1986', 'eyr': '2026', 'ecl': 'amb'}
|
||||||
Checking {'hcl': '#623a2f', 'pid': '318048681', 'ecl': 'brn', 'eyr': '2035', 'hgt': '155cm', 'cid': '179', 'byr': '1984', 'iyr': '2019'}
|
Checking {'eyr': '2035', 'hcl': '#623a2f', 'iyr': '2019', 'hgt': '155cm', 'pid': '318048681', 'byr': '1984', 'cid': '179', 'ecl': 'brn'}
|
||||||
Bad expire year
|
Bad expire year
|
||||||
Checking {'iyr': '2013', 'hcl': '#733820', 'eyr': '2024', 'hgt': '189cm', 'ecl': 'amb', 'byr': '1969', 'pid': '185953891'}
|
Checking {'hcl': '#733820', 'iyr': '2013', 'hgt': '189cm', 'byr': '1969', 'pid': '185953891', 'eyr': '2024', 'ecl': 'amb'}
|
||||||
Checking {'hcl': '#cfa07d', 'ecl': '#38f2a6', 'iyr': '2013', 'hgt': '61cm', 'eyr': '2021', 'byr': '2012', 'pid': '33668114'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2013', 'hgt': '61cm', 'byr': '2012', 'pid': '33668114', 'ecl': '#38f2a6', 'eyr': '2021'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'pid': '47030948', 'hcl': '4946ca', 'iyr': '2019', 'hgt': '189', 'ecl': '#1d136d', 'byr': '2013', 'eyr': '2024', 'cid': '51'}
|
Checking {'cid': '51', 'hcl': '4946ca', 'iyr': '2019', 'hgt': '189', 'pid': '47030948', 'byr': '2013', 'ecl': '#1d136d', 'eyr': '2024'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'ecl': 'grn', 'iyr': '2011', 'hgt': '162cm', 'byr': '1935', 'hcl': '#c0946f', 'pid': '883047970', 'cid': '51', 'eyr': '2020'}
|
Checking {'eyr': '2020', 'hcl': '#c0946f', 'iyr': '2011', 'hgt': '162cm', 'byr': '1935', 'pid': '883047970', 'ecl': 'grn', 'cid': '51'}
|
||||||
Checking {'pid': '013760919', 'iyr': '2018', 'byr': '1942', 'ecl': 'blu', 'hcl': '#623a2f', 'eyr': '2020', 'cid': '221', 'hgt': '155cm'}
|
Checking {'cid': '221', 'hcl': '#623a2f', 'iyr': '2018', 'hgt': '155cm', 'pid': '013760919', 'byr': '1942', 'ecl': 'blu', 'eyr': '2020'}
|
||||||
Checking {'hgt': '152cm', 'eyr': '2030', 'ecl': 'amb', 'iyr': '1986', 'hcl': '#7d3b0c', 'pid': '29797863', 'byr': '2000'}
|
Checking {'hcl': '#7d3b0c', 'iyr': '1986', 'hgt': '152cm', 'pid': '29797863', 'byr': '2000', 'eyr': '2030', 'ecl': 'amb'}
|
||||||
Bad issue year
|
Bad issue year
|
||||||
Checking {'hgt': '176cm', 'byr': '1995', 'pid': '546676799', 'iyr': '2013', 'ecl': 'brn', 'hcl': '#fffffd', 'eyr': '2023'}
|
Checking {'hcl': '#fffffd', 'iyr': '2013', 'hgt': '176cm', 'byr': '1995', 'pid': '546676799', 'ecl': 'brn', 'eyr': '2023'}
|
||||||
Checking {'byr': '1955', 'pid': '634493767', 'eyr': '2028', 'iyr': '2015', 'ecl': 'oth'}
|
Checking {'iyr': '2015', 'byr': '1955', 'pid': '634493767', 'eyr': '2028', 'ecl': 'oth'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hcl': '#7d3b0c', 'iyr': '2020', 'cid': '150', 'hgt': '174cm', 'pid': '893757190', 'eyr': '2027', 'ecl': 'oth', 'byr': '2002'}
|
Checking {'ecl': 'oth', 'hcl': '#7d3b0c', 'iyr': '2020', 'hgt': '174cm', 'pid': '893757190', 'byr': '2002', 'cid': '150', 'eyr': '2027'}
|
||||||
Checking {'eyr': '2029', 'pid': '790648045', 'byr': '1978', 'iyr': '2012', 'hcl': '#efcc98', 'ecl': 'blu', 'hgt': '66in', 'cid': '256'}
|
Checking {'cid': '256', 'hcl': '#efcc98', 'iyr': '2012', 'hgt': '66in', 'pid': '790648045', 'byr': '1978', 'eyr': '2029', 'ecl': 'blu'}
|
||||||
Checking {'iyr': '2020', 'byr': '1945', 'hgt': '155cm', 'cid': '209', 'eyr': '2027', 'hcl': '#0eeb2d', 'ecl': 'hzl', 'pid': '048725571'}
|
Checking {'ecl': 'hzl', 'hcl': '#0eeb2d', 'iyr': '2020', 'hgt': '155cm', 'byr': '1945', 'pid': '048725571', 'cid': '209', 'eyr': '2027'}
|
||||||
Checking {'hcl': '#cfa07d', 'byr': '2000', 'iyr': '2011', 'pid': '381372526', 'ecl': 'oth', 'eyr': '2023', 'hgt': '162cm'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2011', 'hgt': '162cm', 'byr': '2000', 'pid': '381372526', 'ecl': 'oth', 'eyr': '2023'}
|
||||||
Checking {'ecl': 'blu', 'pid': '544462408', 'eyr': '2030', 'hgt': '171cm', 'iyr': '2018', 'hcl': '#602927', 'byr': '1994'}
|
Checking {'hcl': '#602927', 'iyr': '2018', 'hgt': '171cm', 'pid': '544462408', 'byr': '1994', 'ecl': 'blu', 'eyr': '2030'}
|
||||||
Checking {'hcl': '#733820', 'iyr': '2011', 'hgt': '187cm', 'ecl': 'hzl', 'pid': '533405863', 'byr': '1962', 'cid': '266', 'eyr': '2025'}
|
Checking {'eyr': '2025', 'hcl': '#733820', 'iyr': '2011', 'hgt': '187cm', 'pid': '533405863', 'byr': '1962', 'ecl': 'hzl', 'cid': '266'}
|
||||||
Checking {'byr': '1975', 'hcl': '#b6652a', 'iyr': '2019', 'pid': '967013712', 'eyr': '2029', 'hgt': '155cm', 'ecl': 'oth'}
|
Checking {'hcl': '#b6652a', 'iyr': '2019', 'hgt': '155cm', 'byr': '1975', 'pid': '967013712', 'eyr': '2029', 'ecl': 'oth'}
|
||||||
Checking {'iyr': '2010', 'eyr': '2022', 'ecl': 'amb', 'pid': '052112145', 'byr': '1982', 'hgt': '190cm', 'hcl': '#b6652a'}
|
Checking {'hcl': '#b6652a', 'iyr': '2010', 'hgt': '190cm', 'pid': '052112145', 'byr': '1982', 'eyr': '2022', 'ecl': 'amb'}
|
||||||
Checking {'iyr': '2012', 'hgt': '183cm', 'hcl': '#b6652a', 'byr': '1950', 'ecl': 'hzl', 'pid': '946714779', 'eyr': '2030'}
|
Checking {'hcl': '#b6652a', 'iyr': '2012', 'hgt': '183cm', 'byr': '1950', 'pid': '946714779', 'ecl': 'hzl', 'eyr': '2030'}
|
||||||
Checking {'eyr': '2027', 'pid': '686010502', 'cid': '103', 'byr': '1993', 'hcl': '#ceb3a1', 'ecl': 'gry', 'hgt': '70in', 'iyr': '2018'}
|
Checking {'cid': '103', 'hcl': '#ceb3a1', 'iyr': '2018', 'hgt': '70in', 'pid': '686010502', 'byr': '1993', 'ecl': 'gry', 'eyr': '2027'}
|
||||||
Checking {'hcl': '#733820', 'iyr': '2012', 'hgt': '157cm', 'byr': '1976', 'eyr': '2030', 'ecl': 'gry'}
|
Checking {'hcl': '#733820', 'iyr': '2012', 'byr': '1976', 'hgt': '157cm', 'eyr': '2030', 'ecl': 'gry'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'hzl', 'byr': '1955', 'hgt': '180cm', 'iyr': '2017', 'eyr': '2022', 'hcl': '#6b5442', 'pid': '732940101'}
|
Checking {'hcl': '#6b5442', 'iyr': '2017', 'hgt': '180cm', 'byr': '1955', 'pid': '732940101', 'ecl': 'hzl', 'eyr': '2022'}
|
||||||
Checking {'cid': '299', 'hgt': '188cm', 'byr': '1924', 'ecl': 'oth', 'pid': '905274031', 'iyr': '2010', 'eyr': '2024', 'hcl': '#18171d'}
|
Checking {'eyr': '2024', 'hcl': '#18171d', 'iyr': '2010', 'hgt': '188cm', 'byr': '1924', 'pid': '905274031', 'cid': '299', 'ecl': 'oth'}
|
||||||
Checking {'eyr': '2024', 'hgt': '174cm', 'byr': '1999', 'iyr': '2013', 'pid': '021076124', 'hcl': '#7f450a', 'ecl': 'gry'}
|
Checking {'hcl': '#7f450a', 'iyr': '2013', 'hgt': '174cm', 'byr': '1999', 'pid': '021076124', 'eyr': '2024', 'ecl': 'gry'}
|
||||||
Checking {'hcl': '#866857', 'iyr': '2016', 'ecl': 'oth', 'hgt': '176cm', 'byr': '1940', 'pid': '398320693', 'eyr': '2026'}
|
Checking {'hcl': '#866857', 'iyr': '2016', 'hgt': '176cm', 'byr': '1940', 'pid': '398320693', 'ecl': 'oth', 'eyr': '2026'}
|
||||||
Checking {'hgt': '172cm', 'eyr': '2020', 'hcl': '#733820', 'iyr': '1931', 'ecl': '#a0c290', 'pid': '158cm'}
|
Checking {'hcl': '#733820', 'iyr': '1931', 'pid': '158cm', 'hgt': '172cm', 'eyr': '2020', 'ecl': '#a0c290'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'blu', 'iyr': '2018', 'eyr': '2025', 'hcl': '#341e13', 'byr': '1990', 'pid': '444561212', 'hgt': '182cm'}
|
Checking {'hcl': '#341e13', 'iyr': '2018', 'hgt': '182cm', 'byr': '1990', 'pid': '444561212', 'ecl': 'blu', 'eyr': '2025'}
|
||||||
Checking {'hcl': '#602927', 'hgt': '165cm', 'pid': '240732315', 'ecl': 'oth', 'eyr': '2023', 'byr': '1976'}
|
Checking {'hcl': '#602927', 'pid': '240732315', 'byr': '1976', 'hgt': '165cm', 'ecl': 'oth', 'eyr': '2023'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2016', 'ecl': 'brn', 'pid': '377612846', 'eyr': '2021', 'byr': '1967', 'hcl': '#733820', 'hgt': '153cm'}
|
Checking {'hcl': '#733820', 'iyr': '2016', 'hgt': '153cm', 'pid': '377612846', 'byr': '1967', 'ecl': 'brn', 'eyr': '2021'}
|
||||||
Checking {'hgt': '187cm', 'iyr': '2018', 'eyr': '2030', 'ecl': 'blu', 'byr': '1925', 'hcl': '#733820', 'cid': '114', 'pid': '207103786'}
|
Checking {'cid': '114', 'hcl': '#733820', 'iyr': '2018', 'hgt': '187cm', 'byr': '1925', 'pid': '207103786', 'eyr': '2030', 'ecl': 'blu'}
|
||||||
Checking {'hgt': '184cm', 'ecl': 'blu', 'cid': '111', 'iyr': '2018', 'pid': '361909532', 'eyr': '2025'}
|
Checking {'eyr': '2025', 'iyr': '2018', 'pid': '361909532', 'hgt': '184cm', 'ecl': 'blu', 'cid': '111'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'grn', 'byr': '1968', 'iyr': '2019', 'hgt': '184cm', 'pid': '381103495', 'hcl': '#7d3b0c', 'eyr': '2026'}
|
Checking {'hcl': '#7d3b0c', 'iyr': '2019', 'hgt': '184cm', 'byr': '1968', 'pid': '381103495', 'ecl': 'grn', 'eyr': '2026'}
|
||||||
Checking {'iyr': '2019', 'byr': '1945', 'pid': '727826617', 'hcl': '#01adfd', 'eyr': '2020', 'hgt': '151cm'}
|
Checking {'hcl': '#01adfd', 'iyr': '2019', 'byr': '1945', 'pid': '727826617', 'hgt': '151cm', 'eyr': '2020'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'cid': '280', 'iyr': '2011', 'hcl': '#efcc98', 'ecl': 'hzl', 'eyr': '2029', 'byr': '1924', 'hgt': '171cm', 'pid': '235809608'}
|
Checking {'ecl': 'hzl', 'hcl': '#efcc98', 'iyr': '2011', 'hgt': '171cm', 'byr': '1924', 'pid': '235809608', 'eyr': '2029', 'cid': '280'}
|
||||||
Checking {'hcl': '#602927', 'byr': '1973', 'pid': '599786261', 'eyr': '2029', 'hgt': '172cm', 'iyr': '2010', 'ecl': 'gry', 'cid': '97'}
|
Checking {'cid': '97', 'hcl': '#602927', 'iyr': '2010', 'hgt': '172cm', 'byr': '1973', 'pid': '599786261', 'eyr': '2029', 'ecl': 'gry'}
|
||||||
Checking {'iyr': '2017', 'eyr': '2027', 'pid': '768895320', 'hgt': '163cm', 'hcl': '#866857', 'byr': '1940', 'ecl': 'oth'}
|
Checking {'hcl': '#866857', 'iyr': '2017', 'hgt': '163cm', 'pid': '768895320', 'byr': '1940', 'eyr': '2027', 'ecl': 'oth'}
|
||||||
Checking {'hgt': '178cm', 'hcl': '#6b5442', 'iyr': '2013', 'byr': '1959', 'pid': '823221334'}
|
Checking {'hcl': '#6b5442', 'iyr': '2013', 'byr': '1959', 'pid': '823221334', 'hgt': '178cm'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2014', 'pid': '534201972', 'ecl': 'hzl', 'hgt': '150cm', 'hcl': '#da8af3', 'byr': '1945', 'cid': '263', 'eyr': '2024'}
|
Checking {'eyr': '2024', 'hcl': '#da8af3', 'iyr': '2014', 'hgt': '150cm', 'pid': '534201972', 'byr': '1945', 'ecl': 'hzl', 'cid': '263'}
|
||||||
Checking {'pid': '469575516', 'hgt': '189cm', 'byr': '1994', 'eyr': '2025', 'iyr': '2010', 'ecl': 'blu', 'hcl': '#efcc98', 'cid': '341'}
|
Checking {'cid': '341', 'hcl': '#efcc98', 'iyr': '2010', 'hgt': '189cm', 'pid': '469575516', 'byr': '1994', 'eyr': '2025', 'ecl': 'blu'}
|
||||||
Checking {'cid': '167', 'eyr': '2024', 'byr': '1999', 'pid': '797138561', 'hcl': '#888785', 'hgt': '60in', 'iyr': '2015'}
|
Checking {'hcl': '#888785', 'iyr': '2015', 'hgt': '60in', 'byr': '1999', 'pid': '797138561', 'cid': '167', 'eyr': '2024'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2014', 'hcl': '#866857', 'cid': '103', 'pid': '909549652', 'byr': '1967', 'hgt': '174cm', 'ecl': 'amb', 'eyr': '2023'}
|
Checking {'eyr': '2023', 'hcl': '#866857', 'iyr': '2014', 'hgt': '174cm', 'pid': '909549652', 'byr': '1967', 'cid': '103', 'ecl': 'amb'}
|
||||||
Checking {'pid': '813003671', 'ecl': 'oth', 'hgt': '61in', 'cid': '95', 'iyr': '2016', 'byr': '1995', 'eyr': '2027'}
|
Checking {'eyr': '2027', 'iyr': '2016', 'hgt': '61in', 'pid': '813003671', 'byr': '1995', 'ecl': 'oth', 'cid': '95'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'eyr': '2021', 'hcl': '#fffffd', 'pid': '000088706', 'iyr': '2014', 'ecl': 'blu', 'hgt': '166cm', 'byr': '1951'}
|
Checking {'hcl': '#fffffd', 'iyr': '2014', 'hgt': '166cm', 'pid': '000088706', 'byr': '1951', 'eyr': '2021', 'ecl': 'blu'}
|
||||||
Checking {'cid': '287', 'ecl': 'grn', 'byr': '1941', 'iyr': '2017', 'hcl': '#18171d', 'hgt': '162cm', 'pid': '511728076', 'eyr': '2022'}
|
Checking {'eyr': '2022', 'hcl': '#18171d', 'iyr': '2017', 'hgt': '162cm', 'byr': '1941', 'pid': '511728076', 'cid': '287', 'ecl': 'grn'}
|
||||||
Checking {'pid': '209898040', 'byr': '1968', 'eyr': '2025', 'hcl': '#18171d', 'ecl': 'brn', 'iyr': '2017', 'hgt': '191cm'}
|
Checking {'hcl': '#18171d', 'iyr': '2017', 'hgt': '191cm', 'pid': '209898040', 'byr': '1968', 'eyr': '2025', 'ecl': 'brn'}
|
||||||
Checking {'byr': '1932', 'hcl': 'z', 'hgt': '190cm', 'cid': '201', 'iyr': '2016', 'pid': '#02dfcc', 'ecl': '#6b9341', 'eyr': '2004'}
|
Checking {'eyr': '2004', 'hcl': 'z', 'iyr': '2016', 'hgt': '190cm', 'byr': '1932', 'pid': '#02dfcc', 'cid': '201', 'ecl': '#6b9341'}
|
||||||
Bad expire year
|
Bad expire year
|
||||||
Checking {'iyr': '2013', 'hcl': '#ceb3a1', 'hgt': '191cm', 'pid': '501799813', 'ecl': 'hzl', 'byr': '1993', 'eyr': '2020'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2013', 'hgt': '191cm', 'pid': '501799813', 'byr': '1993', 'ecl': 'hzl', 'eyr': '2020'}
|
||||||
Checking {'ecl': 'blu', 'cid': '315', 'pid': '897450687', 'hgt': '179cm', 'byr': '1984', 'eyr': '2029', 'iyr': '2012', 'hcl': '#a97842'}
|
Checking {'cid': '315', 'hcl': '#a97842', 'iyr': '2012', 'hgt': '179cm', 'pid': '897450687', 'byr': '1984', 'eyr': '2029', 'ecl': 'blu'}
|
||||||
Checking {'iyr': '2011', 'ecl': 'gry', 'hcl': '#6b5442', 'pid': '299193732', 'eyr': '2020', 'byr': '1945', 'hgt': '190in'}
|
Checking {'hcl': '#6b5442', 'iyr': '2011', 'hgt': '190in', 'pid': '299193732', 'byr': '1945', 'ecl': 'gry', 'eyr': '2020'}
|
||||||
bad height in inches: 190
|
bad height in inches: 190
|
||||||
Checking {'hgt': '158cm', 'eyr': '2022', 'pid': '090738381', 'byr': '1992', 'iyr': '2017', 'hcl': '#fffffd', 'ecl': 'oth'}
|
Checking {'hcl': '#fffffd', 'iyr': '2017', 'hgt': '158cm', 'pid': '090738381', 'byr': '1992', 'eyr': '2022', 'ecl': 'oth'}
|
||||||
Checking {'eyr': '2028', 'cid': '92', 'ecl': 'amb', 'hcl': '#573edf', 'pid': '765588435', 'iyr': '2016', 'hgt': '179cm', 'byr': '2002'}
|
Checking {'cid': '92', 'hcl': '#573edf', 'iyr': '2016', 'hgt': '179cm', 'pid': '765588435', 'byr': '2002', 'ecl': 'amb', 'eyr': '2028'}
|
||||||
Checking {'eyr': '2025', 'pid': '128081454', 'hcl': '#967d2f', 'hgt': '190cm', 'iyr': '2015', 'ecl': 'oth'}
|
Checking {'hcl': '#967d2f', 'iyr': '2015', 'pid': '128081454', 'hgt': '190cm', 'eyr': '2025', 'ecl': 'oth'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hgt': '189cm', 'eyr': '2025', 'ecl': 'gry', 'hcl': '#888785', 'byr': '1993', 'pid': '001825574', 'cid': '239', 'iyr': '2019'}
|
Checking {'cid': '239', 'hcl': '#888785', 'iyr': '2019', 'hgt': '189cm', 'byr': '1993', 'pid': '001825574', 'eyr': '2025', 'ecl': 'gry'}
|
||||||
Checking {'byr': '2013', 'pid': '0758189515', 'hcl': 'z', 'eyr': '2034', 'iyr': '1971', 'ecl': 'gry', 'hgt': '100'}
|
Checking {'hcl': 'z', 'iyr': '1971', 'hgt': '100', 'byr': '2013', 'pid': '0758189515', 'eyr': '2034', 'ecl': 'gry'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'eyr': '2026', 'byr': '1943', 'hcl': '#3638a2', 'iyr': '2011', 'pid': '539139386', 'hgt': '156cm', 'ecl': 'hzl'}
|
Checking {'hcl': '#3638a2', 'iyr': '2011', 'hgt': '156cm', 'byr': '1943', 'pid': '539139386', 'eyr': '2026', 'ecl': 'hzl'}
|
||||||
Checking {'eyr': '2030', 'pid': '016597738', 'iyr': '2017', 'hgt': '173cm', 'ecl': 'brn', 'hcl': '#733820', 'byr': '1956'}
|
Checking {'hcl': '#733820', 'iyr': '2017', 'hgt': '173cm', 'pid': '016597738', 'byr': '1956', 'eyr': '2030', 'ecl': 'brn'}
|
||||||
Checking {'eyr': '2028', 'iyr': '2018', 'pid': '822607758', 'hcl': '#cfa07d', 'ecl': 'brn', 'hgt': '167cm', 'byr': '1974'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2018', 'hgt': '167cm', 'pid': '822607758', 'byr': '1974', 'eyr': '2028', 'ecl': 'brn'}
|
||||||
Checking {'eyr': '2020', 'byr': '1980', 'hgt': '65in', 'iyr': '2020', 'ecl': 'oth', 'hcl': '#efcc98', 'pid': '397182705'}
|
Checking {'hcl': '#efcc98', 'iyr': '2020', 'hgt': '65in', 'byr': '1980', 'pid': '397182705', 'eyr': '2020', 'ecl': 'oth'}
|
||||||
Checking {'iyr': '2015', 'byr': '1954', 'hcl': '#ceb3a1', 'eyr': '2024', 'pid': '398087239'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2015', 'byr': '1954', 'pid': '398087239', 'eyr': '2024'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'cid': '256', 'hcl': '234fc4', 'ecl': 'zzz', 'hgt': '177in', 'eyr': '2027', 'iyr': '2015', 'pid': '159cm', 'byr': '2022'}
|
Checking {'ecl': 'zzz', 'hcl': '234fc4', 'iyr': '2015', 'hgt': '177in', 'pid': '159cm', 'byr': '2022', 'eyr': '2027', 'cid': '256'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'iyr': '2018', 'cid': '209', 'hcl': '#a928b0', 'byr': '1976', 'ecl': 'hzl', 'pid': '920448637', 'eyr': '2025', 'hgt': '158cm'}
|
Checking {'eyr': '2025', 'hcl': '#a928b0', 'iyr': '2018', 'hgt': '158cm', 'byr': '1976', 'pid': '920448637', 'cid': '209', 'ecl': 'hzl'}
|
||||||
Checking {'pid': '96925844', 'iyr': '2016', 'eyr': '2030', 'hcl': '#888785', 'ecl': 'gry', 'byr': '1984', 'cid': '223', 'hgt': '165cm'}
|
Checking {'cid': '223', 'hcl': '#888785', 'iyr': '2016', 'hgt': '165cm', 'pid': '96925844', 'byr': '1984', 'eyr': '2030', 'ecl': 'gry'}
|
||||||
bad pid
|
bad pid
|
||||||
Checking {'byr': '1964', 'pid': '831479208', 'hgt': '153cm', 'iyr': '2014', 'eyr': '2024', 'ecl': 'brn', 'hcl': '#18171d'}
|
Checking {'hcl': '#18171d', 'iyr': '2014', 'hgt': '153cm', 'byr': '1964', 'pid': '831479208', 'eyr': '2024', 'ecl': 'brn'}
|
||||||
Checking {'iyr': '2019', 'ecl': 'brn', 'hgt': '185cm', 'byr': '1958', 'hcl': '#ceb3a1', 'eyr': '2026', 'pid': '827043482'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2019', 'hgt': '185cm', 'byr': '1958', 'pid': '827043482', 'ecl': 'brn', 'eyr': '2026'}
|
||||||
Checking {'iyr': '2020', 'hgt': '67in', 'cid': '116', 'hcl': '#733820', 'ecl': 'blu', 'pid': '426593479', 'byr': '1922', 'eyr': '2026'}
|
Checking {'eyr': '2026', 'hcl': '#733820', 'iyr': '2020', 'hgt': '67in', 'pid': '426593479', 'byr': '1922', 'cid': '116', 'ecl': 'blu'}
|
||||||
Checking {'eyr': '2022', 'hcl': '#fffffd', 'cid': '330', 'pid': '951768959', 'byr': '1969', 'iyr': '2019', 'hgt': '156cm'}
|
Checking {'hcl': '#fffffd', 'iyr': '2019', 'hgt': '156cm', 'pid': '951768959', 'byr': '1969', 'eyr': '2022', 'cid': '330'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'eyr': '2030', 'ecl': 'oth', 'byr': '1929', 'hgt': '151cm', 'cid': '223', 'hcl': '#111544', 'pid': '083495633', 'iyr': '2019'}
|
Checking {'ecl': 'oth', 'hcl': '#111544', 'iyr': '2019', 'hgt': '151cm', 'byr': '1929', 'pid': '083495633', 'cid': '223', 'eyr': '2030'}
|
||||||
Checking {'eyr': '2025', 'iyr': '2016', 'ecl': 'blu', 'hgt': '166cm', 'byr': '1967', 'pid': '739606431'}
|
Checking {'iyr': '2016', 'byr': '1967', 'pid': '739606431', 'hgt': '166cm', 'eyr': '2025', 'ecl': 'blu'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'eyr': '2021', 'ecl': 'gry', 'hcl': '#ceb3a1', 'pid': '788420638', 'byr': '1922', 'iyr': '2020', 'hgt': '161cm'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2020', 'hgt': '161cm', 'pid': '788420638', 'byr': '1922', 'eyr': '2021', 'ecl': 'gry'}
|
||||||
Checking {'byr': '1956', 'eyr': '2025', 'hcl': '#888785', 'ecl': 'oth', 'pid': '705051840', 'hgt': '158cm'}
|
Checking {'hcl': '#888785', 'byr': '1956', 'pid': '705051840', 'hgt': '158cm', 'eyr': '2025', 'ecl': 'oth'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'pid': '047851403', 'byr': '1937', 'hcl': '#cfa07d', 'iyr': '2015', 'hgt': '192cm', 'eyr': '2025'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2015', 'pid': '047851403', 'byr': '1937', 'hgt': '192cm', 'eyr': '2025'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hgt': '178cm', 'cid': '194', 'iyr': '2019', 'byr': '1923', 'hcl': '#c0946f', 'pid': '411527076', 'ecl': 'gry', 'eyr': '2022'}
|
Checking {'eyr': '2022', 'hcl': '#c0946f', 'iyr': '2019', 'hgt': '178cm', 'byr': '1923', 'pid': '411527076', 'cid': '194', 'ecl': 'gry'}
|
||||||
Checking {'hgt': '186cm', 'iyr': '2014', 'byr': '1956', 'eyr': '2027', 'ecl': 'brn', 'pid': '976268893', 'hcl': '#341e13'}
|
Checking {'hcl': '#341e13', 'iyr': '2014', 'hgt': '186cm', 'byr': '1956', 'pid': '976268893', 'eyr': '2027', 'ecl': 'brn'}
|
||||||
Checking {'iyr': '2011', 'cid': '81', 'hcl': '#18171d', 'hgt': '183cm', 'byr': '1958', 'ecl': 'brn', 'eyr': '2025', 'pid': '389943720'}
|
Checking {'eyr': '2025', 'hcl': '#18171d', 'iyr': '2011', 'hgt': '183cm', 'byr': '1958', 'pid': '389943720', 'cid': '81', 'ecl': 'brn'}
|
||||||
Checking {'eyr': '2028', 'byr': '1972', 'ecl': 'amb', 'hcl': '#c0946f', 'pid': '593351635', 'hgt': '165cm'}
|
Checking {'hcl': '#c0946f', 'byr': '1972', 'pid': '593351635', 'hgt': '165cm', 'eyr': '2028', 'ecl': 'amb'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2012', 'byr': '1991', 'ecl': 'blu', 'hcl': '#341e13', 'hgt': '169cm', 'cid': '156', 'pid': '599766528'}
|
Checking {'hcl': '#341e13', 'iyr': '2012', 'hgt': '169cm', 'byr': '1991', 'pid': '599766528', 'ecl': 'blu', 'cid': '156'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '2029', 'hgt': '75cm', 'pid': '319443119', 'iyr': '2001', 'eyr': '2020', 'cid': '306', 'ecl': 'zzz'}
|
Checking {'ecl': 'zzz', 'iyr': '2001', 'hgt': '75cm', 'byr': '2029', 'pid': '319443119', 'eyr': '2020', 'cid': '306'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hcl': '#866857', 'cid': '273', 'ecl': 'grn', 'byr': '1948', 'hgt': '167cm', 'iyr': '2014', 'pid': '256331758', 'eyr': '2021'}
|
Checking {'eyr': '2021', 'hcl': '#866857', 'iyr': '2014', 'hgt': '167cm', 'byr': '1948', 'pid': '256331758', 'cid': '273', 'ecl': 'grn'}
|
||||||
Checking {'iyr': '2016', 'eyr': '2024', 'ecl': 'oth', 'hcl': '#733820', 'byr': '1977', 'pid': '423680717', 'hgt': '158cm', 'cid': '241'}
|
Checking {'cid': '241', 'hcl': '#733820', 'iyr': '2016', 'hgt': '158cm', 'byr': '1977', 'pid': '423680717', 'eyr': '2024', 'ecl': 'oth'}
|
||||||
Checking {'hcl': '#341e13', 'pid': '788619400', 'byr': '1954', 'ecl': 'hzl', 'cid': '153', 'hgt': '185cm', 'eyr': '2024', 'iyr': '2017'}
|
Checking {'eyr': '2024', 'hcl': '#341e13', 'iyr': '2017', 'hgt': '185cm', 'pid': '788619400', 'byr': '1954', 'ecl': 'hzl', 'cid': '153'}
|
||||||
Checking {'hcl': '#cfa07d', 'ecl': 'blu', 'hgt': '161cm', 'eyr': '2026', 'pid': '621023569', 'byr': '1928', 'iyr': '2016'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2016', 'hgt': '161cm', 'pid': '621023569', 'byr': '1928', 'ecl': 'blu', 'eyr': '2026'}
|
||||||
Checking {'cid': '91', 'byr': '2024', 'hgt': '168in', 'iyr': '1951', 'ecl': 'xry', 'eyr': '1979', 'hcl': 'aa8fc8', 'pid': '166cm'}
|
Checking {'ecl': 'xry', 'hcl': 'aa8fc8', 'iyr': '1951', 'hgt': '168in', 'byr': '2024', 'pid': '166cm', 'eyr': '1979', 'cid': '91'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'eyr': '2028', 'cid': '155', 'hgt': '159cm', 'byr': '1952', 'pid': '875326712', 'ecl': 'brn', 'iyr': '2012', 'hcl': '#18171d'}
|
Checking {'cid': '155', 'hcl': '#18171d', 'iyr': '2012', 'hgt': '159cm', 'byr': '1952', 'pid': '875326712', 'ecl': 'brn', 'eyr': '2028'}
|
||||||
Checking {'eyr': '2026', 'hcl': '#733820', 'byr': '1990', 'hgt': '163cm', 'ecl': 'amb', 'iyr': '2015', 'pid': '162682954'}
|
Checking {'hcl': '#733820', 'iyr': '2015', 'hgt': '163cm', 'byr': '1990', 'pid': '162682954', 'eyr': '2026', 'ecl': 'amb'}
|
||||||
Checking {'iyr': '2020', 'pid': '936952728', 'byr': '1969', 'ecl': 'brn', 'hgt': '151cm', 'hcl': '#c0946f', 'eyr': '2029'}
|
Checking {'hcl': '#c0946f', 'iyr': '2020', 'hgt': '151cm', 'pid': '936952728', 'byr': '1969', 'ecl': 'brn', 'eyr': '2029'}
|
||||||
Checking {'hgt': '189cm', 'pid': '132928469', 'ecl': 'amb', 'hcl': '#866857', 'byr': '1928', 'eyr': '2026', 'iyr': '2013'}
|
Checking {'hcl': '#866857', 'iyr': '2013', 'hgt': '189cm', 'pid': '132928469', 'byr': '1928', 'ecl': 'amb', 'eyr': '2026'}
|
||||||
Checking {'ecl': 'grn', 'pid': '185240766', 'iyr': '2012', 'hgt': '190cm', 'byr': '1952', 'hcl': '#623a2f', 'eyr': '2020'}
|
Checking {'hcl': '#623a2f', 'iyr': '2012', 'hgt': '190cm', 'pid': '185240766', 'byr': '1952', 'ecl': 'grn', 'eyr': '2020'}
|
||||||
Checking {'byr': '2021', 'eyr': '2026', 'hgt': '67cm', 'ecl': '#ef67e5', 'hcl': 'z', 'iyr': '1935', 'pid': '4900748653', 'cid': '64'}
|
Checking {'cid': '64', 'hcl': 'z', 'iyr': '1935', 'hgt': '67cm', 'byr': '2021', 'pid': '4900748653', 'eyr': '2026', 'ecl': '#ef67e5'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'hgt': '69in', 'hcl': '#7d3b0c', 'cid': '248', 'eyr': '2022', 'pid': '076116194', 'byr': '1979', 'ecl': 'gry', 'iyr': '2016'}
|
Checking {'ecl': 'gry', 'hcl': '#7d3b0c', 'iyr': '2016', 'hgt': '69in', 'pid': '076116194', 'byr': '1979', 'cid': '248', 'eyr': '2022'}
|
||||||
Checking {'byr': '1991', 'eyr': '2021', 'hgt': '180cm', 'iyr': '2020', 'cid': '127', 'ecl': 'blu', 'hcl': '#44e350'}
|
Checking {'cid': '127', 'hcl': '#44e350', 'iyr': '2020', 'hgt': '180cm', 'byr': '1991', 'ecl': 'blu', 'eyr': '2021'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '1954', 'hcl': '#733820', 'iyr': '2018', 'pid': '002868205', 'hgt': '150cm', 'ecl': 'brn', 'eyr': '2021'}
|
Checking {'hcl': '#733820', 'iyr': '2018', 'hgt': '150cm', 'byr': '1954', 'pid': '002868205', 'ecl': 'brn', 'eyr': '2021'}
|
||||||
Checking {'pid': '524531652', 'hcl': '#623a2f', 'cid': '80', 'ecl': 'amb', 'iyr': '2017', 'eyr': '2020', 'hgt': '170cm', 'byr': '1927'}
|
Checking {'ecl': 'amb', 'hcl': '#623a2f', 'iyr': '2017', 'hgt': '170cm', 'pid': '524531652', 'byr': '1927', 'eyr': '2020', 'cid': '80'}
|
||||||
Checking {'pid': '424660272', 'iyr': '2018', 'hcl': '#efcc98', 'eyr': '2021', 'cid': '238', 'hgt': '187cm', 'byr': '1970', 'ecl': 'blu'}
|
Checking {'ecl': 'blu', 'hcl': '#efcc98', 'iyr': '2018', 'hgt': '187cm', 'pid': '424660272', 'byr': '1970', 'eyr': '2021', 'cid': '238'}
|
||||||
Checking {'byr': '1923', 'pid': '946014113', 'cid': '273', 'iyr': '2013', 'hgt': '175cm', 'ecl': 'brn', 'hcl': '#602927', 'eyr': '2020'}
|
Checking {'eyr': '2020', 'hcl': '#602927', 'iyr': '2013', 'hgt': '175cm', 'byr': '1923', 'pid': '946014113', 'cid': '273', 'ecl': 'brn'}
|
||||||
Checking {'hcl': '#6b5442', 'cid': '88', 'ecl': 'gry', 'iyr': '2012', 'byr': '1929', 'hgt': '71in', 'eyr': '2022', 'pid': '581329373'}
|
Checking {'eyr': '2022', 'hcl': '#6b5442', 'iyr': '2012', 'hgt': '71in', 'byr': '1929', 'pid': '581329373', 'cid': '88', 'ecl': 'gry'}
|
||||||
Checking {'eyr': '1960', 'ecl': 'oth', 'iyr': '2017', 'pid': '022131529', 'cid': '79', 'hgt': '184', 'byr': '2005', 'hcl': '#6b5442'}
|
Checking {'ecl': 'oth', 'hcl': '#6b5442', 'iyr': '2017', 'hgt': '184', 'pid': '022131529', 'byr': '2005', 'cid': '79', 'eyr': '1960'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'eyr': '2030', 'pid': '422677836', 'iyr': '2011', 'hcl': '#fffffd', 'byr': '1925', 'hgt': '60in', 'ecl': 'gry'}
|
Checking {'hcl': '#fffffd', 'iyr': '2011', 'hgt': '60in', 'pid': '422677836', 'byr': '1925', 'eyr': '2030', 'ecl': 'gry'}
|
||||||
Checking {'cid': '325', 'hgt': '158cm', 'byr': '1971', 'ecl': 'hzl', 'pid': '517329528', 'hcl': '#18171d', 'eyr': '2026', 'iyr': '2011'}
|
Checking {'eyr': '2026', 'hcl': '#18171d', 'iyr': '2011', 'hgt': '158cm', 'byr': '1971', 'pid': '517329528', 'cid': '325', 'ecl': 'hzl'}
|
||||||
Checking {'byr': '1937', 'cid': '259', 'eyr': '2030', 'hgt': '176cm', 'ecl': 'blu', 'pid': '321795494', 'iyr': '2017'}
|
Checking {'eyr': '2030', 'iyr': '2017', 'hgt': '176cm', 'byr': '1937', 'pid': '321795494', 'ecl': 'blu', 'cid': '259'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'pid': '551525002', 'ecl': 'grn', 'eyr': '2026', 'iyr': '2013', 'cid': '230', 'hgt': '74in', 'hcl': '#cfa07d', 'byr': '1954'}
|
Checking {'eyr': '2026', 'hcl': '#cfa07d', 'iyr': '2013', 'hgt': '74in', 'pid': '551525002', 'byr': '1954', 'cid': '230', 'ecl': 'grn'}
|
||||||
Checking {'pid': '004366607', 'hcl': 'c39522', 'hgt': '66cm', 'ecl': '#21a3e9', 'eyr': '2024', 'cid': '139'}
|
Checking {'cid': '139', 'hcl': 'c39522', 'pid': '004366607', 'hgt': '66cm', 'ecl': '#21a3e9', 'eyr': '2024'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'xry', 'eyr': '2037', 'byr': '2016', 'iyr': '1994', 'cid': '98', 'pid': '522572315', 'hgt': '158cm', 'hcl': '0ee9d4'}
|
Checking {'eyr': '2037', 'hcl': '0ee9d4', 'iyr': '1994', 'hgt': '158cm', 'byr': '2016', 'pid': '522572315', 'cid': '98', 'ecl': 'xry'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'cid': '70', 'eyr': '2028', 'hgt': '179cm', 'pid': '073189127', 'ecl': 'grn', 'iyr': '2018', 'hcl': '#142217', 'byr': '1977'}
|
Checking {'eyr': '2028', 'hcl': '#142217', 'iyr': '2018', 'hgt': '179cm', 'pid': '073189127', 'byr': '1977', 'ecl': 'grn', 'cid': '70'}
|
||||||
Checking {'pid': '045852463', 'cid': '69', 'iyr': '2020', 'ecl': 'brn', 'byr': '1948', 'eyr': '2020', 'hgt': '64in', 'hcl': '#733820'}
|
Checking {'ecl': 'brn', 'hcl': '#733820', 'iyr': '2020', 'hgt': '64in', 'pid': '045852463', 'byr': '1948', 'eyr': '2020', 'cid': '69'}
|
||||||
Checking {'cid': '268', 'byr': '1970', 'eyr': '2025', 'hgt': '178cm', 'pid': '512594967', 'iyr': '2011', 'ecl': 'brn', 'hcl': '#733820'}
|
Checking {'ecl': 'brn', 'hcl': '#733820', 'iyr': '2011', 'hgt': '178cm', 'byr': '1970', 'pid': '512594967', 'cid': '268', 'eyr': '2025'}
|
||||||
Checking {'eyr': '2025', 'hcl': '#18171d', 'iyr': '2014', 'byr': '1950', 'hgt': '161cm', 'pid': '329927551'}
|
Checking {'hcl': '#18171d', 'iyr': '2014', 'byr': '1950', 'pid': '329927551', 'hgt': '161cm', 'eyr': '2025'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '1956', 'eyr': '2024', 'hgt': '163cm', 'pid': '965746490', 'hcl': '#a97842', 'ecl': 'brn', 'cid': '100', 'iyr': '2010'}
|
Checking {'cid': '100', 'hcl': '#a97842', 'iyr': '2010', 'hgt': '163cm', 'byr': '1956', 'pid': '965746490', 'eyr': '2024', 'ecl': 'brn'}
|
||||||
Checking {'cid': '112', 'eyr': '2027', 'pid': '864571411', 'hgt': '190cm', 'iyr': '2011', 'byr': '1962', 'ecl': 'grn', 'hcl': '#602927'}
|
Checking {'ecl': 'grn', 'hcl': '#602927', 'iyr': '2011', 'hgt': '190cm', 'pid': '864571411', 'byr': '1962', 'cid': '112', 'eyr': '2027'}
|
||||||
Checking {'ecl': 'gry', 'cid': '54', 'iyr': '2011', 'hcl': '#6b5442', 'byr': '1922', 'eyr': '2025', 'pid': '689641249', 'hgt': '159cm'}
|
Checking {'cid': '54', 'hcl': '#6b5442', 'iyr': '2011', 'hgt': '159cm', 'byr': '1922', 'pid': '689641249', 'eyr': '2025', 'ecl': 'gry'}
|
||||||
Checking {'eyr': '2028', 'cid': '323', 'iyr': '2020', 'hgt': '158cm', 'ecl': 'hzl', 'pid': '876082513', 'byr': '1941'}
|
Checking {'cid': '323', 'iyr': '2020', 'hgt': '158cm', 'pid': '876082513', 'byr': '1941', 'ecl': 'hzl', 'eyr': '2028'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hcl': '#18171d', 'hgt': '160cm', 'pid': '910116712', 'ecl': 'oth', 'iyr': '2014', 'byr': '1927', 'eyr': '2023', 'cid': '226'}
|
Checking {'cid': '226', 'hcl': '#18171d', 'iyr': '2014', 'hgt': '160cm', 'pid': '910116712', 'byr': '1927', 'ecl': 'oth', 'eyr': '2023'}
|
||||||
Checking {'iyr': '2030', 'hcl': '#602927', 'eyr': '2030', 'ecl': 'grn', 'cid': '183', 'hgt': '186cm', 'byr': '1963', 'pid': '706533329'}
|
Checking {'ecl': 'grn', 'hcl': '#602927', 'iyr': '2030', 'hgt': '186cm', 'byr': '1963', 'pid': '706533329', 'cid': '183', 'eyr': '2030'}
|
||||||
Bad issue year
|
Bad issue year
|
||||||
Checking {'cid': '279', 'eyr': '2026', 'iyr': '2015', 'byr': '1958', 'pid': '120633047', 'hgt': '150cm', 'ecl': 'hzl', 'hcl': '#866857'}
|
Checking {'ecl': 'hzl', 'hcl': '#866857', 'iyr': '2015', 'hgt': '150cm', 'byr': '1958', 'pid': '120633047', 'cid': '279', 'eyr': '2026'}
|
||||||
Checking {'iyr': '2019', 'byr': '1989', 'hcl': '#733820', 'ecl': 'hzl', 'pid': '470596304', 'hgt': '187cm', 'eyr': '2022'}
|
Checking {'hcl': '#733820', 'iyr': '2019', 'hgt': '187cm', 'byr': '1989', 'pid': '470596304', 'ecl': 'hzl', 'eyr': '2022'}
|
||||||
Checking {'eyr': '2027', 'hcl': '#888785', 'cid': '346', 'iyr': '2013', 'hgt': '167cm', 'ecl': 'hzl', 'byr': '1994', 'pid': '528844948'}
|
Checking {'cid': '346', 'hcl': '#888785', 'iyr': '2013', 'hgt': '167cm', 'byr': '1994', 'pid': '528844948', 'ecl': 'hzl', 'eyr': '2027'}
|
||||||
Checking {'hcl': '#fffffd', 'pid': '969181309', 'iyr': '2014', 'hgt': '192cm', 'eyr': '2025', 'byr': '1970', 'ecl': 'amb'}
|
Checking {'hcl': '#fffffd', 'iyr': '2014', 'hgt': '192cm', 'pid': '969181309', 'byr': '1970', 'eyr': '2025', 'ecl': 'amb'}
|
||||||
Checking {'eyr': '2026', 'iyr': '2012', 'hcl': '#341e13', 'ecl': 'oth', 'pid': '053348609', 'byr': '1931', 'hgt': '167cm'}
|
Checking {'hcl': '#341e13', 'iyr': '2012', 'hgt': '167cm', 'pid': '053348609', 'byr': '1931', 'eyr': '2026', 'ecl': 'oth'}
|
||||||
Checking {'eyr': '2029', 'ecl': 'grn', 'pid': '030276279', 'iyr': '2013', 'hgt': '182cm', 'hcl': '#fffffd', 'byr': '1967'}
|
Checking {'hcl': '#fffffd', 'iyr': '2013', 'hgt': '182cm', 'pid': '030276279', 'byr': '1967', 'eyr': '2029', 'ecl': 'grn'}
|
||||||
Checking {'byr': '1949', 'eyr': '2022', 'hcl': '#ceb3a1', 'iyr': '2016', 'ecl': 'oth', 'hgt': '177cm', 'cid': '224', 'pid': '745439371'}
|
Checking {'cid': '224', 'hcl': '#ceb3a1', 'iyr': '2016', 'hgt': '177cm', 'byr': '1949', 'pid': '745439371', 'eyr': '2022', 'ecl': 'oth'}
|
||||||
Checking {'iyr': '2016', 'byr': '1940', 'eyr': '2028', 'pid': '351021541', 'hcl': '#341e13', 'ecl': 'amb', 'hgt': '64in'}
|
Checking {'hcl': '#341e13', 'iyr': '2016', 'hgt': '64in', 'byr': '1940', 'pid': '351021541', 'eyr': '2028', 'ecl': 'amb'}
|
||||||
Checking {'cid': '309', 'hgt': '74in', 'pid': '698666542', 'hcl': '#866857', 'ecl': 'oth', 'byr': '1953', 'iyr': '2019', 'eyr': '2021'}
|
Checking {'eyr': '2021', 'hcl': '#866857', 'iyr': '2019', 'hgt': '74in', 'pid': '698666542', 'byr': '1953', 'cid': '309', 'ecl': 'oth'}
|
||||||
Checking {'iyr': '2013', 'byr': '1979', 'eyr': '2023', 'hgt': '186cm', 'ecl': 'brn', 'cid': '236', 'pid': '727367898', 'hcl': '#733820'}
|
Checking {'ecl': 'brn', 'hcl': '#733820', 'iyr': '2013', 'hgt': '186cm', 'byr': '1979', 'pid': '727367898', 'cid': '236', 'eyr': '2023'}
|
||||||
Checking {'hgt': '65cm', 'byr': '1956', 'eyr': '2025', 'pid': '371685442', 'iyr': '2016', 'ecl': 'oth', 'cid': '245', 'hcl': '#623a2f'}
|
Checking {'cid': '245', 'hcl': '#623a2f', 'iyr': '2016', 'hgt': '65cm', 'byr': '1956', 'pid': '371685442', 'eyr': '2025', 'ecl': 'oth'}
|
||||||
bad height in cm
|
bad height in cm
|
||||||
Checking {'hgt': '155cm', 'ecl': 'grn', 'hcl': '#888785', 'eyr': '2027', 'iyr': '2010', 'byr': '1927', 'pid': '916070590'}
|
Checking {'hcl': '#888785', 'iyr': '2010', 'hgt': '155cm', 'byr': '1927', 'pid': '916070590', 'ecl': 'grn', 'eyr': '2027'}
|
||||||
Checking {'hgt': '179cm', 'ecl': 'blu', 'hcl': '#866857', 'byr': '1993', 'iyr': '2019', 'cid': '332', 'eyr': '2022', 'pid': '354895012'}
|
Checking {'eyr': '2022', 'hcl': '#866857', 'iyr': '2019', 'hgt': '179cm', 'byr': '1993', 'pid': '354895012', 'ecl': 'blu', 'cid': '332'}
|
||||||
Checking {'iyr': '2029', 'hgt': '69cm', 'hcl': '#efcc98', 'pid': '179cm', 'cid': '216', 'byr': '2007', 'ecl': 'oth', 'eyr': '2025'}
|
Checking {'eyr': '2025', 'hcl': '#efcc98', 'iyr': '2029', 'hgt': '69cm', 'pid': '179cm', 'byr': '2007', 'cid': '216', 'ecl': 'oth'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'iyr': '1988', 'hgt': '187', 'hcl': 'z', 'ecl': '#30e67c', 'byr': '2020', 'pid': '225115160', 'eyr': '2037'}
|
Checking {'hcl': 'z', 'iyr': '1988', 'hgt': '187', 'byr': '2020', 'pid': '225115160', 'ecl': '#30e67c', 'eyr': '2037'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'eyr': '2021', 'iyr': '2011', 'hgt': '188cm', 'ecl': 'hzl', 'byr': '1965', 'pid': '455044780'}
|
Checking {'iyr': '2011', 'byr': '1965', 'pid': '455044780', 'hgt': '188cm', 'eyr': '2021', 'ecl': 'hzl'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'gry', 'pid': '750994177', 'byr': '2002', 'iyr': '2016', 'eyr': '2023', 'hgt': '61in', 'hcl': '#fffffd'}
|
Checking {'hcl': '#fffffd', 'iyr': '2016', 'hgt': '61in', 'pid': '750994177', 'byr': '2002', 'ecl': 'gry', 'eyr': '2023'}
|
||||||
Checking {'iyr': '2020', 'ecl': 'gry', 'pid': '304482618', 'hcl': '#18171d', 'eyr': '2027', 'byr': '1955', 'hgt': '177cm'}
|
Checking {'hcl': '#18171d', 'iyr': '2020', 'hgt': '177cm', 'pid': '304482618', 'byr': '1955', 'ecl': 'gry', 'eyr': '2027'}
|
||||||
Checking {'hgt': '187cm', 'byr': '1981', 'pid': '795201673', 'eyr': '2020', 'cid': '154', 'hcl': '#b6652a', 'iyr': '2017', 'ecl': 'oth'}
|
Checking {'ecl': 'oth', 'hcl': '#b6652a', 'iyr': '2017', 'hgt': '187cm', 'byr': '1981', 'pid': '795201673', 'eyr': '2020', 'cid': '154'}
|
||||||
Checking {'ecl': 'gry', 'byr': '1954', 'hgt': '151cm', 'iyr': '2019', 'cid': '101', 'eyr': '2026', 'hcl': '#cfa07d', 'pid': '930011749'}
|
Checking {'cid': '101', 'hcl': '#cfa07d', 'iyr': '2019', 'hgt': '151cm', 'byr': '1954', 'pid': '930011749', 'eyr': '2026', 'ecl': 'gry'}
|
||||||
Checking {'ecl': 'zzz', 'eyr': '1955', 'pid': '#d45ed4', 'cid': '338', 'iyr': '2030', 'hcl': 'z', 'byr': '1999'}
|
Checking {'eyr': '1955', 'hcl': 'z', 'iyr': '2030', 'pid': '#d45ed4', 'byr': '1999', 'cid': '338', 'ecl': 'zzz'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'eyr': '2020', 'pid': '861636258', 'hgt': '166cm', 'hcl': '#7d3b0c', 'ecl': 'brn', 'iyr': '2018', 'cid': '125', 'byr': '1958'}
|
Checking {'cid': '125', 'hcl': '#7d3b0c', 'iyr': '2018', 'hgt': '166cm', 'pid': '861636258', 'byr': '1958', 'eyr': '2020', 'ecl': 'brn'}
|
||||||
Checking {'iyr': '2014', 'eyr': '2022', 'hgt': '67', 'hcl': '#7d3b0c', 'ecl': 'brn', 'byr': '1935', 'pid': '409864761'}
|
Checking {'hcl': '#7d3b0c', 'iyr': '2014', 'hgt': '67', 'byr': '1935', 'pid': '409864761', 'eyr': '2022', 'ecl': 'brn'}
|
||||||
bad height generally
|
bad height generally
|
||||||
Checking {'hcl': '#866857', 'iyr': '2012', 'cid': '94', 'pid': '483584137', 'byr': '2000', 'ecl': 'blu', 'hgt': '178cm', 'eyr': '2022'}
|
Checking {'eyr': '2022', 'hcl': '#866857', 'iyr': '2012', 'hgt': '178cm', 'pid': '483584137', 'byr': '2000', 'cid': '94', 'ecl': 'blu'}
|
||||||
Checking {'byr': '1946', 'ecl': 'hzl', 'iyr': '2015', 'eyr': '2028', 'hgt': '184cm', 'hcl': '#602927', 'pid': '947292495'}
|
Checking {'hcl': '#602927', 'iyr': '2015', 'hgt': '184cm', 'byr': '1946', 'pid': '947292495', 'ecl': 'hzl', 'eyr': '2028'}
|
||||||
Checking {'byr': '1974', 'cid': '96', 'hgt': '59in', 'eyr': '2028', 'pid': '358779220', 'hcl': '#6b5442', 'ecl': 'gry', 'iyr': '2014'}
|
Checking {'ecl': 'gry', 'hcl': '#6b5442', 'iyr': '2014', 'hgt': '59in', 'byr': '1974', 'pid': '358779220', 'cid': '96', 'eyr': '2028'}
|
||||||
Checking {'hcl': '#61154f', 'byr': '1932', 'hgt': '167cm', 'ecl': 'brn', 'cid': '126', 'eyr': '2022'}
|
Checking {'eyr': '2022', 'hcl': '#61154f', 'byr': '1932', 'hgt': '167cm', 'ecl': 'brn', 'cid': '126'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'gry', 'iyr': '2014', 'hgt': '169cm', 'byr': '1926', 'eyr': '2020', 'hcl': '#866857', 'pid': '463772660'}
|
Checking {'hcl': '#866857', 'iyr': '2014', 'hgt': '169cm', 'byr': '1926', 'pid': '463772660', 'ecl': 'gry', 'eyr': '2020'}
|
||||||
Checking {'pid': '654733578', 'ecl': 'hzl', 'cid': '111', 'iyr': '2010', 'hcl': '#fffffd', 'eyr': '2024', 'hgt': '191cm', 'byr': '1943'}
|
Checking {'cid': '111', 'hcl': '#fffffd', 'iyr': '2010', 'hgt': '191cm', 'pid': '654733578', 'byr': '1943', 'eyr': '2024', 'ecl': 'hzl'}
|
||||||
Checking {'eyr': '2021', 'hgt': '74cm', 'hcl': '#c0946f', 'iyr': '2026', 'pid': '164776417', 'byr': '1977'}
|
Checking {'hcl': '#c0946f', 'iyr': '2026', 'pid': '164776417', 'byr': '1977', 'hgt': '74cm', 'eyr': '2021'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': '#6db74f', 'iyr': '1921', 'pid': '442332495', 'byr': '2018', 'hcl': 'z', 'cid': '101', 'eyr': '1949'}
|
Checking {'eyr': '1949', 'hcl': 'z', 'iyr': '1921', 'pid': '442332495', 'byr': '2018', 'ecl': '#6db74f', 'cid': '101'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'cid': '332', 'byr': '2022', 'ecl': 'blu', 'eyr': '2038', 'iyr': '1939', 'hcl': '518816', 'hgt': '191cm', 'pid': '10107923'}
|
Checking {'ecl': 'blu', 'hcl': '518816', 'iyr': '1939', 'hgt': '191cm', 'byr': '2022', 'pid': '10107923', 'eyr': '2038', 'cid': '332'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'pid': '168853141', 'iyr': '2010', 'eyr': '2021', 'byr': '1996', 'ecl': 'hzl', 'hgt': '183cm', 'hcl': '#733820'}
|
Checking {'hcl': '#733820', 'iyr': '2010', 'hgt': '183cm', 'pid': '168853141', 'byr': '1996', 'eyr': '2021', 'ecl': 'hzl'}
|
||||||
Checking {'hgt': '62in', 'pid': '556617728', 'hcl': '336a3b', 'eyr': '2023', 'byr': '2029', 'ecl': 'xry', 'iyr': '2016', 'cid': '89'}
|
Checking {'cid': '89', 'hcl': '336a3b', 'iyr': '2016', 'hgt': '62in', 'pid': '556617728', 'byr': '2029', 'eyr': '2023', 'ecl': 'xry'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'ecl': 'hzl', 'hcl': '#efcc98', 'iyr': '2020', 'cid': '297', 'hgt': '181cm', 'pid': '075811396', 'eyr': '2023', 'byr': '1960'}
|
Checking {'eyr': '2023', 'hcl': '#efcc98', 'iyr': '2020', 'hgt': '181cm', 'pid': '075811396', 'byr': '1960', 'ecl': 'hzl', 'cid': '297'}
|
||||||
Checking {'eyr': '2030', 'hcl': '#602927', 'ecl': 'brn', 'iyr': '2015', 'hgt': '75in', 'byr': '1995'}
|
Checking {'hcl': '#602927', 'iyr': '2015', 'byr': '1995', 'hgt': '75in', 'eyr': '2030', 'ecl': 'brn'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'grn', 'cid': '237', 'hcl': '#8936bb', 'hgt': '183cm', 'eyr': '2028', 'iyr': '2015', 'byr': '1998'}
|
Checking {'cid': '237', 'hcl': '#8936bb', 'iyr': '2015', 'hgt': '183cm', 'byr': '1998', 'eyr': '2028', 'ecl': 'grn'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'pid': '550427102', 'hgt': '67in', 'byr': '1991', 'ecl': 'gry', 'hcl': '#efcc98'}
|
Checking {'hcl': '#efcc98', 'pid': '550427102', 'byr': '1991', 'hgt': '67in', 'ecl': 'gry'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'cid': '274', 'hgt': '70cm', 'iyr': '2022', 'eyr': '1961', 'hcl': '00f05b', 'byr': '1948', 'ecl': 'gmt'}
|
Checking {'ecl': 'gmt', 'hcl': '00f05b', 'iyr': '2022', 'hgt': '70cm', 'byr': '1948', 'cid': '274', 'eyr': '1961'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'blu', 'iyr': '2018', 'hgt': '153cm', 'eyr': '2020', 'pid': '831302208', 'byr': '1927', 'hcl': '#18171d', 'cid': '150'}
|
Checking {'cid': '150', 'hcl': '#18171d', 'iyr': '2018', 'hgt': '153cm', 'pid': '831302208', 'byr': '1927', 'ecl': 'blu', 'eyr': '2020'}
|
||||||
Checking {'iyr': '2018', 'byr': '1973', 'hcl': '#ceb3a1', 'cid': '215', 'hgt': '192cm', 'pid': '770473271', 'eyr': '2027', 'ecl': 'blu'}
|
Checking {'ecl': 'blu', 'hcl': '#ceb3a1', 'iyr': '2018', 'hgt': '192cm', 'byr': '1973', 'pid': '770473271', 'cid': '215', 'eyr': '2027'}
|
||||||
Checking {'iyr': '2019', 'eyr': '2021', 'hcl': '#623a2f', 'ecl': 'hzl', 'hgt': '174cm', 'byr': '1962', 'pid': '589533254'}
|
Checking {'hcl': '#623a2f', 'iyr': '2019', 'hgt': '174cm', 'byr': '1962', 'pid': '589533254', 'eyr': '2021', 'ecl': 'hzl'}
|
||||||
Checking {'iyr': '2012', 'ecl': 'hzl', 'hgt': '184cm', 'cid': '292', 'byr': '1991', 'pid': '677889195', 'hcl': '#a97842'}
|
Checking {'hcl': '#a97842', 'iyr': '2012', 'hgt': '184cm', 'byr': '1991', 'pid': '677889195', 'ecl': 'hzl', 'cid': '292'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2010', 'pid': '#4f47c3', 'hgt': '154in', 'cid': '69', 'hcl': 'z', 'byr': '2022', 'ecl': '#e36a65'}
|
Checking {'hcl': 'z', 'iyr': '2010', 'hgt': '154in', 'pid': '#4f47c3', 'byr': '2022', 'cid': '69', 'ecl': '#e36a65'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': '#5ff50c', 'pid': '499582878', 'hcl': '#b6652a', 'hgt': '171cm', 'iyr': '2016', 'byr': '1930', 'eyr': '2024'}
|
Checking {'hcl': '#b6652a', 'iyr': '2016', 'hgt': '171cm', 'pid': '499582878', 'byr': '1930', 'ecl': '#5ff50c', 'eyr': '2024'}
|
||||||
bad eye color
|
bad eye color
|
||||||
Checking {'hgt': '159cm', 'byr': '1936', 'eyr': '2028', 'hcl': '#6b5442', 'ecl': 'amb', 'iyr': '2015', 'pid': '658019126'}
|
Checking {'hcl': '#6b5442', 'iyr': '2015', 'hgt': '159cm', 'byr': '1936', 'pid': '658019126', 'eyr': '2028', 'ecl': 'amb'}
|
||||||
Checking {'byr': '1928', 'pid': '599970280', 'eyr': '2026', 'ecl': 'grn', 'hgt': '158cm', 'hcl': '#18171d', 'cid': '239', 'iyr': '2013'}
|
Checking {'cid': '239', 'hcl': '#18171d', 'iyr': '2013', 'hgt': '158cm', 'byr': '1928', 'pid': '599970280', 'eyr': '2026', 'ecl': 'grn'}
|
||||||
Checking {'pid': '684820830', 'hgt': '182cm', 'ecl': 'oth', 'hcl': '#c0946f', 'eyr': '2023', 'iyr': '2018'}
|
Checking {'hcl': '#c0946f', 'iyr': '2018', 'pid': '684820830', 'hgt': '182cm', 'ecl': 'oth', 'eyr': '2023'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2019', 'ecl': 'blu', 'pid': '668361647', 'cid': '348', 'byr': '1952', 'hcl': '#602927', 'eyr': '2021', 'hgt': '71in'}
|
Checking {'eyr': '2021', 'hcl': '#602927', 'iyr': '2019', 'hgt': '71in', 'pid': '668361647', 'byr': '1952', 'ecl': 'blu', 'cid': '348'}
|
||||||
Checking {'hgt': '165cm', 'ecl': 'grn', 'eyr': '2030', 'iyr': '2010', 'pid': '256350027', 'hcl': '#7d5994', 'cid': '193', 'byr': '1947'}
|
Checking {'cid': '193', 'hcl': '#7d5994', 'iyr': '2010', 'hgt': '165cm', 'pid': '256350027', 'byr': '1947', 'ecl': 'grn', 'eyr': '2030'}
|
||||||
Checking {'hcl': '#602927', 'iyr': '2019', 'ecl': 'gry', 'eyr': '2029', 'byr': '1931', 'cid': '118', 'hgt': '153cm', 'pid': '911300650'}
|
Checking {'eyr': '2029', 'hcl': '#602927', 'iyr': '2019', 'hgt': '153cm', 'byr': '1931', 'pid': '911300650', 'cid': '118', 'ecl': 'gry'}
|
||||||
Checking {'eyr': '2025', 'hgt': '154cm', 'hcl': '#866857', 'iyr': '2016', 'ecl': 'grn', 'pid': '515526226', 'byr': '1936'}
|
Checking {'hcl': '#866857', 'iyr': '2016', 'hgt': '154cm', 'pid': '515526226', 'byr': '1936', 'eyr': '2025', 'ecl': 'grn'}
|
||||||
Checking {'hcl': '#623a2f', 'hgt': '160cm', 'iyr': '2019', 'ecl': 'oth', 'pid': '932621460', 'eyr': '2030', 'byr': '1990'}
|
Checking {'hcl': '#623a2f', 'iyr': '2019', 'hgt': '160cm', 'pid': '932621460', 'byr': '1990', 'ecl': 'oth', 'eyr': '2030'}
|
||||||
Checking {'hcl': '#623a2f', 'ecl': 'blu', 'eyr': '2027', 'cid': '277', 'pid': '662549708', 'byr': '1949', 'iyr': '2016', 'hgt': '176cm'}
|
Checking {'eyr': '2027', 'hcl': '#623a2f', 'iyr': '2016', 'hgt': '176cm', 'pid': '662549708', 'byr': '1949', 'cid': '277', 'ecl': 'blu'}
|
||||||
Checking {'iyr': '2010', 'eyr': '2021', 'byr': '1947', 'pid': '223603325', 'ecl': 'gry'}
|
Checking {'iyr': '2010', 'byr': '1947', 'pid': '223603325', 'eyr': '2021', 'ecl': 'gry'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'gry', 'pid': '145738978', 'eyr': '2029', 'iyr': '2020', 'hcl': '#733820', 'byr': '1949', 'hgt': '183cm'}
|
Checking {'hcl': '#733820', 'iyr': '2020', 'hgt': '183cm', 'pid': '145738978', 'byr': '1949', 'ecl': 'gry', 'eyr': '2029'}
|
||||||
Checking {'hgt': '63in', 'eyr': '2028', 'iyr': '2011', 'hcl': '#a97842', 'byr': '1941', 'ecl': 'gry', 'pid': '091089766'}
|
Checking {'hcl': '#a97842', 'iyr': '2011', 'hgt': '63in', 'byr': '1941', 'pid': '091089766', 'eyr': '2028', 'ecl': 'gry'}
|
||||||
Checking {'hcl': '#fffffd', 'cid': '275', 'hgt': '157cm', 'eyr': '2021', 'byr': '1978', 'ecl': 'hzl', 'iyr': '2020', 'pid': '242258232'}
|
Checking {'eyr': '2021', 'hcl': '#fffffd', 'iyr': '2020', 'hgt': '157cm', 'byr': '1978', 'pid': '242258232', 'ecl': 'hzl', 'cid': '275'}
|
||||||
Checking {'pid': '239061408', 'ecl': 'oth', 'eyr': '2023', 'iyr': '2011', 'hgt': '192cm', 'byr': '1949', 'hcl': '#733820', 'cid': '132'}
|
Checking {'cid': '132', 'hcl': '#733820', 'iyr': '2011', 'hgt': '192cm', 'pid': '239061408', 'byr': '1949', 'ecl': 'oth', 'eyr': '2023'}
|
||||||
Checking {'byr': '1954', 'hgt': '152cm', 'ecl': 'brn', 'pid': '667414305', 'eyr': '2021', 'cid': '282', 'iyr': '2014', 'hcl': '#341e13'}
|
Checking {'eyr': '2021', 'hcl': '#341e13', 'iyr': '2014', 'hgt': '152cm', 'byr': '1954', 'pid': '667414305', 'cid': '282', 'ecl': 'brn'}
|
||||||
Checking {'iyr': '2018', 'pid': '745564182', 'hgt': '186cm', 'eyr': '2028', 'hcl': '#7d3b0c', 'byr': '1935', 'ecl': 'gry'}
|
Checking {'hcl': '#7d3b0c', 'iyr': '2018', 'hgt': '186cm', 'pid': '745564182', 'byr': '1935', 'eyr': '2028', 'ecl': 'gry'}
|
||||||
Checking {'iyr': '2014', 'eyr': '2026', 'hcl': 'd26483', 'byr': '1972', 'pid': '611712147', 'hgt': '163cm', 'ecl': '#57d27c'}
|
Checking {'hcl': 'd26483', 'iyr': '2014', 'hgt': '163cm', 'byr': '1972', 'pid': '611712147', 'eyr': '2026', 'ecl': '#57d27c'}
|
||||||
bad hair color
|
bad hair color
|
||||||
Checking {'cid': '322', 'eyr': '2025', 'byr': '1937', 'iyr': '2020', 'ecl': 'blu', 'hcl': '#cfa07d', 'hgt': '158cm', 'pid': '150255302'}
|
Checking {'eyr': '2025', 'hcl': '#cfa07d', 'iyr': '2020', 'hgt': '158cm', 'byr': '1937', 'pid': '150255302', 'ecl': 'blu', 'cid': '322'}
|
||||||
Checking {'eyr': '2030', 'iyr': '2011', 'hcl': '#866857', 'byr': '1974', 'ecl': 'blu', 'pid': '755213661', 'hgt': '155cm', 'cid': '116'}
|
Checking {'cid': '116', 'hcl': '#866857', 'iyr': '2011', 'hgt': '155cm', 'byr': '1974', 'pid': '755213661', 'eyr': '2030', 'ecl': 'blu'}
|
||||||
Checking {'hcl': '#866857', 'pid': '679616797', 'ecl': 'gry', 'hgt': '166cm', 'byr': '1999', 'iyr': '2014', 'eyr': '2025'}
|
Checking {'hcl': '#866857', 'iyr': '2014', 'hgt': '166cm', 'pid': '679616797', 'byr': '1999', 'ecl': 'gry', 'eyr': '2025'}
|
||||||
Checking {'iyr': '2019', 'hcl': '#fffffd', 'hgt': '158cm', 'eyr': '2028', 'pid': '835993614', 'byr': '1920'}
|
Checking {'hcl': '#fffffd', 'iyr': '2019', 'pid': '835993614', 'byr': '1920', 'hgt': '158cm', 'eyr': '2028'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'brn', 'pid': '742320152', 'iyr': '2013', 'hgt': '151cm', 'eyr': '2025', 'cid': '63', 'byr': '1931', 'hcl': '#200aaa'}
|
Checking {'eyr': '2025', 'hcl': '#200aaa', 'iyr': '2013', 'hgt': '151cm', 'pid': '742320152', 'byr': '1931', 'cid': '63', 'ecl': 'brn'}
|
||||||
Checking {'byr': '1950', 'cid': '155', 'ecl': 'xry', 'hgt': '150cm', 'iyr': '2014', 'eyr': '2027', 'hcl': '#615954', 'pid': '596469710'}
|
Checking {'ecl': 'xry', 'hcl': '#615954', 'iyr': '2014', 'hgt': '150cm', 'byr': '1950', 'pid': '596469710', 'eyr': '2027', 'cid': '155'}
|
||||||
bad eye color
|
bad eye color
|
||||||
Checking {'byr': '1946', 'hgt': '166cm', 'ecl': 'gry', 'hcl': '#18171d', 'cid': '261', 'iyr': '2016', 'pid': '267318602', 'eyr': '2021'}
|
Checking {'eyr': '2021', 'hcl': '#18171d', 'iyr': '2016', 'hgt': '166cm', 'byr': '1946', 'pid': '267318602', 'ecl': 'gry', 'cid': '261'}
|
||||||
Checking {'ecl': 'gry', 'hgt': '185cm', 'iyr': '2013', 'pid': '092573029', 'eyr': '2023', 'byr': '1956', 'hcl': '#b6652a'}
|
Checking {'hcl': '#b6652a', 'iyr': '2013', 'hgt': '185cm', 'pid': '092573029', 'byr': '1956', 'ecl': 'gry', 'eyr': '2023'}
|
||||||
Checking {'byr': '1997', 'hgt': '172cm', 'eyr': '2021', 'iyr': '2014', 'pid': '337403043', 'ecl': 'blu', 'hcl': '#efcc98'}
|
Checking {'hcl': '#efcc98', 'iyr': '2014', 'hgt': '172cm', 'byr': '1997', 'pid': '337403043', 'eyr': '2021', 'ecl': 'blu'}
|
||||||
Checking {'hgt': '190cm', 'iyr': '2015', 'byr': '1949', 'eyr': '2023', 'pid': '230935940'}
|
Checking {'iyr': '2015', 'byr': '1949', 'pid': '230935940', 'hgt': '190cm', 'eyr': '2023'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '1980', 'hgt': '171cm', 'eyr': '2021', 'pid': '9435249395', 'ecl': 'oth', 'hcl': '#a97842', 'iyr': '2017'}
|
Checking {'hcl': '#a97842', 'iyr': '2017', 'hgt': '171cm', 'byr': '1980', 'pid': '9435249395', 'eyr': '2021', 'ecl': 'oth'}
|
||||||
bad pid
|
bad pid
|
||||||
Checking {'byr': '2011', 'hcl': '#b6652a', 'eyr': '2039', 'ecl': 'hzl', 'iyr': '1923', 'hgt': '186cm', 'pid': '239188418', 'cid': '93'}
|
Checking {'cid': '93', 'hcl': '#b6652a', 'iyr': '1923', 'hgt': '186cm', 'byr': '2011', 'pid': '239188418', 'eyr': '2039', 'ecl': 'hzl'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'pid': '791787662', 'cid': '51', 'ecl': 'gry', 'hcl': '#602927', 'hgt': '160cm', 'iyr': '2020', 'eyr': '2028', 'byr': '1975'}
|
Checking {'eyr': '2028', 'hcl': '#602927', 'iyr': '2020', 'hgt': '160cm', 'pid': '791787662', 'byr': '1975', 'cid': '51', 'ecl': 'gry'}
|
||||||
Checking {'ecl': 'amb', 'pid': '720900081', 'byr': '1978', 'hcl': '#a97842', 'hgt': '183cm', 'iyr': '2016', 'eyr': '2022'}
|
Checking {'hcl': '#a97842', 'iyr': '2016', 'hgt': '183cm', 'pid': '720900081', 'byr': '1978', 'ecl': 'amb', 'eyr': '2022'}
|
||||||
Checking {'eyr': '2027', 'hgt': '157cm', 'pid': '628454234', 'hcl': '#18171d', 'ecl': 'gry', 'iyr': '2017', 'cid': '345', 'byr': '1988'}
|
Checking {'cid': '345', 'hcl': '#18171d', 'iyr': '2017', 'hgt': '157cm', 'pid': '628454234', 'byr': '1988', 'eyr': '2027', 'ecl': 'gry'}
|
||||||
Checking {'byr': '1985', 'pid': '996422540', 'iyr': '2013', 'hgt': '66in', 'ecl': 'grn', 'eyr': '2020', 'hcl': '#341e13'}
|
Checking {'hcl': '#341e13', 'iyr': '2013', 'hgt': '66in', 'byr': '1985', 'pid': '996422540', 'ecl': 'grn', 'eyr': '2020'}
|
||||||
Checking {'eyr': '2022', 'pid': '186cm', 'cid': '214', 'iyr': '2017', 'hcl': '#866857', 'ecl': 'brn', 'byr': '1988', 'hgt': '161cm'}
|
Checking {'cid': '214', 'hcl': '#866857', 'iyr': '2017', 'hgt': '161cm', 'pid': '186cm', 'byr': '1988', 'ecl': 'brn', 'eyr': '2022'}
|
||||||
bad pid
|
bad pid
|
||||||
Checking {'iyr': '2019', 'eyr': '2025', 'pid': '752184592', 'hgt': '154cm', 'byr': '1966', 'hcl': '#18171d', 'cid': '119', 'ecl': 'grn'}
|
Checking {'ecl': 'grn', 'hcl': '#18171d', 'iyr': '2019', 'hgt': '154cm', 'pid': '752184592', 'byr': '1966', 'eyr': '2025', 'cid': '119'}
|
||||||
Checking {'hcl': '#b6652a', 'cid': '100', 'byr': '1974', 'pid': '477922277', 'eyr': '2024', 'ecl': 'grn', 'iyr': '2011', 'hgt': '59in'}
|
Checking {'eyr': '2024', 'hcl': '#b6652a', 'iyr': '2011', 'hgt': '59in', 'byr': '1974', 'pid': '477922277', 'ecl': 'grn', 'cid': '100'}
|
||||||
Checking {'iyr': '2013', 'ecl': 'brn', 'hgt': '184cm', 'eyr': '2023', 'byr': '1969', 'pid': '514127885', 'hcl': '#6b5442'}
|
Checking {'hcl': '#6b5442', 'iyr': '2013', 'hgt': '184cm', 'byr': '1969', 'pid': '514127885', 'ecl': 'brn', 'eyr': '2023'}
|
||||||
Checking {'hcl': '#cfa07d', 'iyr': '2020', 'byr': '1923', 'ecl': 'gry', 'hgt': '64in', 'eyr': '2029', 'cid': '111'}
|
Checking {'cid': '111', 'hcl': '#cfa07d', 'iyr': '2020', 'hgt': '64in', 'byr': '1923', 'ecl': 'gry', 'eyr': '2029'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '1921', 'hgt': '73in', 'pid': '971490088', 'iyr': '2016', 'eyr': '2025', 'hcl': '#866857', 'cid': '271', 'ecl': 'blu'}
|
Checking {'ecl': 'blu', 'hcl': '#866857', 'iyr': '2016', 'hgt': '73in', 'byr': '1921', 'pid': '971490088', 'eyr': '2025', 'cid': '271'}
|
||||||
Checking {'ecl': 'oth', 'eyr': '2023', 'iyr': '2019', 'hgt': '179cm', 'byr': '1953', 'pid': '226869705', 'hcl': '#602927', 'cid': '63'}
|
Checking {'cid': '63', 'hcl': '#602927', 'iyr': '2019', 'hgt': '179cm', 'byr': '1953', 'pid': '226869705', 'ecl': 'oth', 'eyr': '2023'}
|
||||||
Checking {'hgt': '175cm', 'ecl': 'hzl', 'iyr': '2010', 'byr': '1938', 'eyr': '2021', 'pid': '718683561', 'hcl': '#341e13'}
|
Checking {'hcl': '#341e13', 'iyr': '2010', 'hgt': '175cm', 'byr': '1938', 'pid': '718683561', 'ecl': 'hzl', 'eyr': '2021'}
|
||||||
Checking {'iyr': '2023', 'hgt': '189in', 'byr': '2030', 'pid': '171cm', 'ecl': '#447c00', 'hcl': 'z', 'eyr': '2022'}
|
Checking {'hcl': 'z', 'iyr': '2023', 'hgt': '189in', 'byr': '2030', 'pid': '171cm', 'ecl': '#447c00', 'eyr': '2022'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'ecl': 'blu', 'eyr': '2026', 'hgt': '191cm', 'iyr': '2020', 'hcl': '#888785', 'pid': '128824091', 'cid': '99', 'byr': '1982'}
|
Checking {'cid': '99', 'hcl': '#888785', 'iyr': '2020', 'hgt': '191cm', 'pid': '128824091', 'byr': '1982', 'ecl': 'blu', 'eyr': '2026'}
|
||||||
Checking {'eyr': '2026', 'pid': '333173949', 'iyr': '2017', 'ecl': 'oth', 'byr': '1928', 'hcl': '#fffffd', 'hgt': '151cm'}
|
Checking {'hcl': '#fffffd', 'iyr': '2017', 'hgt': '151cm', 'pid': '333173949', 'byr': '1928', 'eyr': '2026', 'ecl': 'oth'}
|
||||||
Checking {'hcl': '#6b5442', 'ecl': 'grn', 'byr': '1945', 'pid': '888990994', 'cid': '168', 'eyr': '2026', 'iyr': '2016', 'hgt': '158cm'}
|
Checking {'cid': '168', 'hcl': '#6b5442', 'iyr': '2016', 'hgt': '158cm', 'byr': '1945', 'pid': '888990994', 'eyr': '2026', 'ecl': 'grn'}
|
||||||
Checking {'ecl': 'grn', 'iyr': '2013', 'byr': '1931', 'pid': '716975878', 'hgt': '168cm', 'hcl': '#cfa07d', 'eyr': '2023'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2013', 'hgt': '168cm', 'byr': '1931', 'pid': '716975878', 'ecl': 'grn', 'eyr': '2023'}
|
||||||
Checking {'pid': '815050555', 'ecl': 'blu', 'hgt': '161cm', 'hcl': '#888785', 'eyr': '2025', 'iyr': '2020', 'byr': '1980'}
|
Checking {'hcl': '#888785', 'iyr': '2020', 'hgt': '161cm', 'pid': '815050555', 'byr': '1980', 'ecl': 'blu', 'eyr': '2025'}
|
||||||
Checking {'pid': '470039281', 'byr': '1967', 'iyr': '2017', 'ecl': 'gry', 'eyr': '2021', 'hgt': '171cm', 'hcl': '#7d3b0c'}
|
Checking {'hcl': '#7d3b0c', 'iyr': '2017', 'hgt': '171cm', 'pid': '470039281', 'byr': '1967', 'ecl': 'gry', 'eyr': '2021'}
|
||||||
Checking {'hcl': '#bdf8d6', 'iyr': '2018', 'byr': '1954', 'eyr': '2030', 'ecl': 'blu', 'hgt': '184cm', 'pid': '694267794'}
|
Checking {'hcl': '#bdf8d6', 'iyr': '2018', 'hgt': '184cm', 'byr': '1954', 'pid': '694267794', 'eyr': '2030', 'ecl': 'blu'}
|
||||||
Checking {'hcl': '#cfa07d', 'byr': '1971', 'ecl': 'brn', 'iyr': '2016', 'hgt': '167cm', 'eyr': '2027', 'pid': '237865320'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2016', 'hgt': '167cm', 'byr': '1971', 'pid': '237865320', 'ecl': 'brn', 'eyr': '2027'}
|
||||||
Checking {'byr': '1921', 'eyr': '2028', 'iyr': '2014', 'pid': '186145415', 'cid': '215', 'ecl': 'oth', 'hgt': '176cm', 'hcl': '#a97842'}
|
Checking {'cid': '215', 'hcl': '#a97842', 'iyr': '2014', 'hgt': '176cm', 'byr': '1921', 'pid': '186145415', 'ecl': 'oth', 'eyr': '2028'}
|
||||||
Checking {'pid': '925805272', 'hcl': '#7d3b0c', 'eyr': '2030', 'hgt': '65in', 'ecl': 'blu'}
|
Checking {'hcl': '#7d3b0c', 'pid': '925805272', 'hgt': '65in', 'eyr': '2030', 'ecl': 'blu'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '1992', 'cid': '278', 'ecl': 'oth', 'hgt': '65in', 'hcl': '#c0946f', 'iyr': '2013', 'pid': '092712496', 'eyr': '2024'}
|
Checking {'eyr': '2024', 'hcl': '#c0946f', 'iyr': '2013', 'hgt': '65in', 'byr': '1992', 'pid': '092712496', 'cid': '278', 'ecl': 'oth'}
|
||||||
Checking {'hgt': '151cm', 'iyr': '2018', 'hcl': '#18171d', 'byr': '1971', 'pid': '599220575', 'cid': '321', 'eyr': '2030', 'ecl': 'brn'}
|
Checking {'ecl': 'brn', 'hcl': '#18171d', 'iyr': '2018', 'hgt': '151cm', 'byr': '1971', 'pid': '599220575', 'cid': '321', 'eyr': '2030'}
|
||||||
Checking {'byr': '1956', 'iyr': '2016', 'hcl': '#b6652a', 'pid': '109381754', 'ecl': 'hzl', 'cid': '233'}
|
Checking {'hcl': '#b6652a', 'iyr': '2016', 'byr': '1956', 'pid': '109381754', 'ecl': 'hzl', 'cid': '233'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2015', 'byr': '1988', 'hcl': '#866857', 'ecl': 'amb', 'pid': '274656754', 'hgt': '152cm', 'eyr': '2022'}
|
Checking {'hcl': '#866857', 'iyr': '2015', 'hgt': '152cm', 'byr': '1988', 'pid': '274656754', 'ecl': 'amb', 'eyr': '2022'}
|
||||||
Checking {'eyr': '2028', 'ecl': 'amb', 'cid': '285', 'byr': '1947', 'hgt': '186cm', 'pid': '165847317', 'hcl': '#733820', 'iyr': '2013'}
|
Checking {'ecl': 'amb', 'hcl': '#733820', 'iyr': '2013', 'hgt': '186cm', 'byr': '1947', 'pid': '165847317', 'cid': '285', 'eyr': '2028'}
|
||||||
Checking {'hcl': '#866857', 'pid': '601229952', 'eyr': '2023', 'ecl': 'brn', 'hgt': '191cm', 'cid': '183'}
|
Checking {'cid': '183', 'hcl': '#866857', 'pid': '601229952', 'hgt': '191cm', 'eyr': '2023', 'ecl': 'brn'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hgt': '191cm', 'iyr': '2018', 'hcl': '#b50bab', 'byr': '1936', 'eyr': '2025', 'pid': '422563929', 'ecl': 'oth'}
|
Checking {'hcl': '#b50bab', 'iyr': '2018', 'hgt': '191cm', 'byr': '1936', 'pid': '422563929', 'eyr': '2025', 'ecl': 'oth'}
|
||||||
Checking {'ecl': 'gry', 'eyr': '2025', 'hgt': '181cm', 'hcl': '#a97842', 'byr': '1971', 'iyr': '2010', 'pid': '267796608'}
|
Checking {'hcl': '#a97842', 'iyr': '2010', 'hgt': '181cm', 'byr': '1971', 'pid': '267796608', 'ecl': 'gry', 'eyr': '2025'}
|
||||||
Checking {'hcl': '#0fd3b0', 'eyr': '2030', 'iyr': '2014', 'ecl': 'oth', 'pid': '606512017', 'hgt': '173cm', 'cid': '301', 'byr': '1999'}
|
Checking {'cid': '301', 'hcl': '#0fd3b0', 'iyr': '2014', 'hgt': '173cm', 'pid': '606512017', 'byr': '1999', 'eyr': '2030', 'ecl': 'oth'}
|
||||||
Checking {'byr': '1937', 'cid': '277', 'pid': '148179917', 'hgt': '179cm', 'hcl': '#602927', 'iyr': '2018', 'eyr': '2029', 'ecl': 'grn'}
|
Checking {'ecl': 'grn', 'hcl': '#602927', 'iyr': '2018', 'hgt': '179cm', 'byr': '1937', 'pid': '148179917', 'cid': '277', 'eyr': '2029'}
|
||||||
Checking {'hcl': '#7d3b0c', 'byr': '1960', 'ecl': 'hzl', 'hgt': '162cm', 'iyr': '2015', 'pid': '014246579', 'eyr': '2023'}
|
Checking {'hcl': '#7d3b0c', 'iyr': '2015', 'hgt': '162cm', 'byr': '1960', 'pid': '014246579', 'ecl': 'hzl', 'eyr': '2023'}
|
||||||
Checking {'ecl': 'blu', 'eyr': '2020', 'iyr': '2011', 'byr': '1955', 'hcl': '#777876', 'hgt': '188cm', 'pid': '988764375'}
|
Checking {'hcl': '#777876', 'iyr': '2011', 'hgt': '188cm', 'byr': '1955', 'pid': '988764375', 'ecl': 'blu', 'eyr': '2020'}
|
||||||
Checking {'iyr': '2012', 'ecl': 'amb', 'hcl': '#18171d', 'pid': '524961020', 'byr': '1983', 'hgt': '173cm', 'eyr': '2028'}
|
Checking {'hcl': '#18171d', 'iyr': '2012', 'hgt': '173cm', 'pid': '524961020', 'byr': '1983', 'ecl': 'amb', 'eyr': '2028'}
|
||||||
Checking {'hgt': '153cm', 'hcl': '#efcc98', 'pid': '127759635', 'iyr': '2019', 'byr': '1932', 'ecl': 'hzl', 'eyr': '2020'}
|
Checking {'hcl': '#efcc98', 'iyr': '2019', 'hgt': '153cm', 'pid': '127759635', 'byr': '1932', 'ecl': 'hzl', 'eyr': '2020'}
|
||||||
Checking {'eyr': '2025', 'pid': '421725637', 'ecl': 'gry', 'iyr': '2013', 'hcl': '#c0946f', 'byr': '1971'}
|
Checking {'hcl': '#c0946f', 'iyr': '2013', 'pid': '421725637', 'byr': '1971', 'eyr': '2025', 'ecl': 'gry'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2015', 'pid': '654033544', 'cid': '176', 'hgt': '163cm', 'byr': '1923', 'ecl': 'brn', 'hcl': '#866857'}
|
Checking {'hcl': '#866857', 'iyr': '2015', 'hgt': '163cm', 'pid': '654033544', 'byr': '1923', 'cid': '176', 'ecl': 'brn'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hcl': '#623a2f', 'byr': '2013', 'hgt': '76cm', 'ecl': '#5cd4a8', 'iyr': '2007', 'eyr': '2035', 'cid': '128', 'pid': '122621229'}
|
Checking {'cid': '128', 'hcl': '#623a2f', 'iyr': '2007', 'hgt': '76cm', 'byr': '2013', 'pid': '122621229', 'ecl': '#5cd4a8', 'eyr': '2035'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'hcl': '#927794', 'iyr': '2019', 'byr': '1964', 'hgt': '158cm', 'ecl': 'oth', 'pid': '269737193', 'eyr': '2025'}
|
Checking {'hcl': '#927794', 'iyr': '2019', 'hgt': '158cm', 'byr': '1964', 'pid': '269737193', 'ecl': 'oth', 'eyr': '2025'}
|
||||||
Checking {'iyr': '2014', 'cid': '181', 'ecl': 'blu', 'byr': '1949', 'hcl': '#341e13', 'eyr': '2026', 'pid': '120077363', 'hgt': '174cm'}
|
Checking {'ecl': 'blu', 'hcl': '#341e13', 'iyr': '2014', 'hgt': '174cm', 'byr': '1949', 'pid': '120077363', 'eyr': '2026', 'cid': '181'}
|
||||||
Checking {'cid': '161', 'eyr': '2024', 'hcl': 'z', 'pid': '638178037', 'hgt': '151cm', 'byr': '1920', 'ecl': 'oth', 'iyr': '2011'}
|
Checking {'ecl': 'oth', 'hcl': 'z', 'iyr': '2011', 'hgt': '151cm', 'pid': '638178037', 'byr': '1920', 'cid': '161', 'eyr': '2024'}
|
||||||
bad hair color
|
bad hair color
|
||||||
Checking {'ecl': 'brn', 'eyr': '2023', 'hgt': '161cm', 'hcl': '#a97842', 'byr': '1977', 'cid': '79', 'pid': '177001463', 'iyr': '2014'}
|
Checking {'eyr': '2023', 'hcl': '#a97842', 'iyr': '2014', 'hgt': '161cm', 'byr': '1977', 'pid': '177001463', 'cid': '79', 'ecl': 'brn'}
|
||||||
Checking {'byr': '1938', 'eyr': '1967', 'ecl': 'grn', 'pid': '302413712', 'hcl': '#888785', 'hgt': '183cm', 'iyr': '2010'}
|
Checking {'hcl': '#888785', 'iyr': '2010', 'hgt': '183cm', 'byr': '1938', 'pid': '302413712', 'eyr': '1967', 'ecl': 'grn'}
|
||||||
Bad expire year
|
Bad expire year
|
||||||
Checking {'hgt': '164cm', 'byr': '1955', 'pid': '772380994', 'iyr': '2015', 'hcl': '#c0946f', 'eyr': '2025', 'ecl': 'amb'}
|
Checking {'hcl': '#c0946f', 'iyr': '2015', 'hgt': '164cm', 'byr': '1955', 'pid': '772380994', 'eyr': '2025', 'ecl': 'amb'}
|
||||||
Checking {'hcl': '#602927', 'eyr': '2021', 'byr': '1924', 'hgt': '171cm', 'cid': '161', 'ecl': 'amb', 'iyr': '2019'}
|
Checking {'cid': '161', 'hcl': '#602927', 'iyr': '2019', 'hgt': '171cm', 'byr': '1924', 'ecl': 'amb', 'eyr': '2021'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hgt': '119', 'hcl': '7d1404', 'eyr': '1957', 'pid': '143311761', 'iyr': '2027', 'byr': '1939', 'ecl': '#de1d21'}
|
Checking {'hcl': '7d1404', 'iyr': '2027', 'hgt': '119', 'pid': '143311761', 'byr': '1939', 'eyr': '1957', 'ecl': '#de1d21'}
|
||||||
Bad issue year
|
Bad issue year
|
||||||
Checking {'hgt': '182cm', 'iyr': '2015', 'ecl': 'blu', 'cid': '205', 'pid': '136552613', 'hcl': '#ceb3a1', 'byr': '1992'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2015', 'hgt': '182cm', 'pid': '136552613', 'byr': '1992', 'ecl': 'blu', 'cid': '205'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'ecl': 'blu', 'iyr': '2013', 'cid': '54', 'byr': '1998', 'hcl': 'z', 'hgt': '172cm', 'eyr': '2034', 'pid': '#ec3c3a'}
|
Checking {'eyr': '2034', 'hcl': 'z', 'iyr': '2013', 'hgt': '172cm', 'byr': '1998', 'pid': '#ec3c3a', 'ecl': 'blu', 'cid': '54'}
|
||||||
Bad expire year
|
Bad expire year
|
||||||
Checking {'byr': '1975', 'ecl': 'blu', 'eyr': '2025', 'hcl': '#623a2f', 'pid': '358585328', 'iyr': '2012'}
|
Checking {'hcl': '#623a2f', 'iyr': '2012', 'byr': '1975', 'pid': '358585328', 'ecl': 'blu', 'eyr': '2025'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'pid': '282306278', 'byr': '1958', 'ecl': 'grn', 'hgt': '190cm', 'iyr': '2020', 'hcl': '#18171d', 'cid': '276', 'eyr': '2024'}
|
Checking {'eyr': '2024', 'hcl': '#18171d', 'iyr': '2020', 'hgt': '190cm', 'pid': '282306278', 'byr': '1958', 'ecl': 'grn', 'cid': '276'}
|
||||||
Checking {'byr': '1955', 'eyr': '2028', 'iyr': '2017', 'hgt': '177cm', 'ecl': 'grn', 'pid': '111002386', 'hcl': '#6b5442'}
|
Checking {'hcl': '#6b5442', 'iyr': '2017', 'hgt': '177cm', 'byr': '1955', 'pid': '111002386', 'eyr': '2028', 'ecl': 'grn'}
|
||||||
Checking {'eyr': '2026', 'pid': '694088201', 'hcl': '#866857', 'hgt': '169cm', 'byr': '1957', 'ecl': 'amb', 'iyr': '2018', 'cid': '109'}
|
Checking {'cid': '109', 'hcl': '#866857', 'iyr': '2018', 'hgt': '169cm', 'pid': '694088201', 'byr': '1957', 'eyr': '2026', 'ecl': 'amb'}
|
||||||
Checking {'hgt': '171cm', 'ecl': 'blu', 'iyr': '2013', 'eyr': '2021', 'hcl': '#6b5442', 'byr': '1965', 'pid': '268169550'}
|
Checking {'hcl': '#6b5442', 'iyr': '2013', 'hgt': '171cm', 'byr': '1965', 'pid': '268169550', 'ecl': 'blu', 'eyr': '2021'}
|
||||||
Checking {'pid': '803092066', 'eyr': '2023', 'iyr': '2010', 'hcl': '#a97842', 'ecl': 'grn', 'byr': '1956', 'hgt': '191cm', 'cid': '173'}
|
Checking {'cid': '173', 'hcl': '#a97842', 'iyr': '2010', 'hgt': '191cm', 'pid': '803092066', 'byr': '1956', 'eyr': '2023', 'ecl': 'grn'}
|
||||||
Checking {'eyr': '2024', 'iyr': '2012', 'byr': '1991', 'ecl': 'gry', 'pid': '946620993', 'hgt': '190cm', 'hcl': '#b6652a', 'cid': '181'}
|
Checking {'cid': '181', 'hcl': '#b6652a', 'iyr': '2012', 'hgt': '190cm', 'byr': '1991', 'pid': '946620993', 'eyr': '2024', 'ecl': 'gry'}
|
||||||
Checking {'iyr': '2019', 'hcl': '#cfa07d', 'cid': '75', 'hgt': '175cm', 'eyr': '2022', 'pid': '062548271', 'ecl': 'oth'}
|
Checking {'ecl': 'oth', 'hcl': '#cfa07d', 'iyr': '2019', 'hgt': '175cm', 'pid': '062548271', 'cid': '75', 'eyr': '2022'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'cid': '262', 'byr': '1956', 'iyr': '2014', 'pid': '860561420', 'hcl': '#888785', 'hgt': '176cm'}
|
Checking {'hcl': '#888785', 'iyr': '2014', 'byr': '1956', 'pid': '860561420', 'hgt': '176cm', 'cid': '262'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'hcl': '#efcc98', 'hgt': '188cm', 'pid': '828180303', 'iyr': '2013', 'byr': '1932', 'ecl': 'gry', 'eyr': '2028'}
|
Checking {'hcl': '#efcc98', 'iyr': '2013', 'hgt': '188cm', 'pid': '828180303', 'byr': '1932', 'ecl': 'gry', 'eyr': '2028'}
|
||||||
Checking {'eyr': '2029', 'hcl': '#341e13', 'byr': '1992', 'ecl': 'brn', 'iyr': '2012', 'cid': '292', 'pid': '644391775', 'hgt': '150cm'}
|
Checking {'ecl': 'brn', 'hcl': '#341e13', 'iyr': '2012', 'hgt': '150cm', 'byr': '1992', 'pid': '644391775', 'cid': '292', 'eyr': '2029'}
|
||||||
Checking {'ecl': 'grn', 'hgt': '182cm', 'eyr': '2026', 'hcl': '#ceb3a1', 'byr': '1982', 'pid': '625704144', 'iyr': '2013'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2013', 'hgt': '182cm', 'byr': '1982', 'pid': '625704144', 'ecl': 'grn', 'eyr': '2026'}
|
||||||
Checking {'byr': '1926', 'iyr': '2013', 'ecl': 'brn', 'hcl': '#812218', 'eyr': '2025', 'hgt': '150cm', 'pid': '610910806'}
|
Checking {'hcl': '#812218', 'iyr': '2013', 'hgt': '150cm', 'byr': '1926', 'pid': '610910806', 'ecl': 'brn', 'eyr': '2025'}
|
||||||
Checking {'hgt': '61in', 'byr': '1926', 'iyr': '2017', 'eyr': '2020', 'ecl': 'oth', 'hcl': '#623a2f', 'pid': '347974562'}
|
Checking {'hcl': '#623a2f', 'iyr': '2017', 'hgt': '61in', 'byr': '1926', 'pid': '347974562', 'eyr': '2020', 'ecl': 'oth'}
|
||||||
Checking {'hgt': '185cm', 'eyr': '2023', 'ecl': 'blu', 'byr': '1940', 'pid': '123961293', 'iyr': '2014', 'hcl': '#a97842'}
|
Checking {'hcl': '#a97842', 'iyr': '2014', 'hgt': '185cm', 'byr': '1940', 'pid': '123961293', 'eyr': '2023', 'ecl': 'blu'}
|
||||||
Checking {'iyr': '2011', 'byr': '1984', 'hgt': '172cm', 'eyr': '2020', 'hcl': '#692e6c', 'ecl': 'grn', 'pid': '342962046'}
|
Checking {'hcl': '#692e6c', 'iyr': '2011', 'hgt': '172cm', 'byr': '1984', 'pid': '342962046', 'eyr': '2020', 'ecl': 'grn'}
|
||||||
Checking {'ecl': 'blu', 'hcl': '#b08932', 'pid': '343331979', 'iyr': '2019', 'hgt': '193cm', 'byr': '1985', 'cid': '269', 'eyr': '2023'}
|
Checking {'eyr': '2023', 'hcl': '#b08932', 'iyr': '2019', 'hgt': '193cm', 'pid': '343331979', 'byr': '1985', 'ecl': 'blu', 'cid': '269'}
|
||||||
Checking {'hcl': '#fffffd', 'ecl': 'blu', 'pid': '483091240', 'iyr': '2011', 'eyr': '2022', 'byr': '1988'}
|
Checking {'hcl': '#fffffd', 'iyr': '2011', 'pid': '483091240', 'byr': '1988', 'ecl': 'blu', 'eyr': '2022'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'iyr': '2019', 'hgt': '177cm', 'pid': '516533115', 'cid': '294', 'ecl': 'amb', 'hcl': '#ceb3a1', 'byr': '1922'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2019', 'hgt': '177cm', 'pid': '516533115', 'byr': '1922', 'cid': '294', 'ecl': 'amb'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '1965', 'eyr': '2023', 'hcl': '#cfa07d', 'iyr': '2013', 'hgt': '193cm', 'ecl': 'grn', 'pid': '931305875'}
|
Checking {'hcl': '#cfa07d', 'iyr': '2013', 'hgt': '193cm', 'byr': '1965', 'pid': '931305875', 'eyr': '2023', 'ecl': 'grn'}
|
||||||
Checking {'ecl': 'hzl', 'byr': '1944', 'hgt': '164cm', 'hcl': '#fffffd', 'iyr': '2019', 'eyr': '2029', 'pid': '141532765', 'cid': '209'}
|
Checking {'cid': '209', 'hcl': '#fffffd', 'iyr': '2019', 'hgt': '164cm', 'byr': '1944', 'pid': '141532765', 'ecl': 'hzl', 'eyr': '2029'}
|
||||||
Checking {'iyr': '2013', 'hgt': '189cm', 'ecl': 'brn', 'pid': '604140631', 'eyr': '2022', 'hcl': '#ceb3a1', 'byr': '1935'}
|
Checking {'hcl': '#ceb3a1', 'iyr': '2013', 'hgt': '189cm', 'pid': '604140631', 'byr': '1935', 'ecl': 'brn', 'eyr': '2022'}
|
||||||
Checking {'ecl': 'amb', 'hcl': '#888785', 'byr': '1959', 'cid': '287', 'pid': '849438430', 'hgt': '152cm', 'eyr': '2027', 'iyr': '2020'}
|
Checking {'eyr': '2027', 'hcl': '#888785', 'iyr': '2020', 'hgt': '152cm', 'byr': '1959', 'pid': '849438430', 'ecl': 'amb', 'cid': '287'}
|
||||||
Checking {'byr': '1988', 'hcl': '#623a2f', 'eyr': '2029', 'ecl': 'brn', 'iyr': '2018', 'hgt': '167cm', 'pid': '470443459'}
|
Checking {'hcl': '#623a2f', 'iyr': '2018', 'hgt': '167cm', 'byr': '1988', 'pid': '470443459', 'eyr': '2029', 'ecl': 'brn'}
|
||||||
Checking {'cid': '276', 'hcl': '#341e13', 'eyr': '2021', 'iyr': '2012', 'ecl': 'hzl', 'hgt': '175cm', 'byr': '2027', 'pid': '271833606'}
|
Checking {'eyr': '2021', 'hcl': '#341e13', 'iyr': '2012', 'hgt': '175cm', 'byr': '2027', 'pid': '271833606', 'ecl': 'hzl', 'cid': '276'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'eyr': '2027', 'iyr': '2010', 'pid': '970527839', 'hgt': '164cm', 'byr': '1974', 'hcl': '#623a2f', 'ecl': 'amb'}
|
Checking {'hcl': '#623a2f', 'iyr': '2010', 'hgt': '164cm', 'pid': '970527839', 'byr': '1974', 'eyr': '2027', 'ecl': 'amb'}
|
||||||
Checking {'eyr': '2020', 'ecl': 'grn', 'byr': '1932', 'iyr': '2013', 'pid': '104193512', 'hcl': '#c0946f'}
|
Checking {'hcl': '#c0946f', 'iyr': '2013', 'byr': '1932', 'pid': '104193512', 'eyr': '2020', 'ecl': 'grn'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'eyr': '2030', 'byr': '1982', 'hgt': '65in', 'ecl': 'blu', 'hcl': '#623a2f', 'pid': '570953460', 'iyr': '2020'}
|
Checking {'hcl': '#623a2f', 'iyr': '2020', 'hgt': '65in', 'byr': '1982', 'pid': '570953460', 'eyr': '2030', 'ecl': 'blu'}
|
||||||
Checking {'eyr': '2020', 'byr': '1922', 'hcl': '#602927', 'pid': '803264417', 'iyr': '2019', 'ecl': 'grn', 'hgt': '169cm'}
|
Checking {'hcl': '#602927', 'iyr': '2019', 'hgt': '169cm', 'byr': '1922', 'pid': '803264417', 'eyr': '2020', 'ecl': 'grn'}
|
||||||
Checking {'ecl': 'amb', 'hcl': '#866857', 'byr': '1963', 'hgt': '170cm', 'eyr': '2028', 'pid': '762546796', 'iyr': '2017'}
|
Checking {'hcl': '#866857', 'iyr': '2017', 'hgt': '170cm', 'byr': '1963', 'pid': '762546796', 'ecl': 'amb', 'eyr': '2028'}
|
||||||
Checking {'eyr': '2035', 'pid': '54291174', 'cid': '184', 'iyr': '1980', 'hgt': '176cm', 'ecl': 'gry', 'hcl': '#733820', 'byr': '1974'}
|
Checking {'cid': '184', 'hcl': '#733820', 'iyr': '1980', 'hgt': '176cm', 'pid': '54291174', 'byr': '1974', 'ecl': 'gry', 'eyr': '2035'}
|
||||||
Bad issue year
|
Bad issue year
|
||||||
Checking {'iyr': '2013', 'hcl': '#c0946f', 'pid': '408646971', 'hgt': '63in', 'cid': '84', 'byr': '1951', 'ecl': 'amb', 'eyr': '2028'}
|
Checking {'eyr': '2028', 'hcl': '#c0946f', 'iyr': '2013', 'hgt': '63in', 'pid': '408646971', 'byr': '1951', 'cid': '84', 'ecl': 'amb'}
|
||||||
Checking {'eyr': '2021', 'hcl': '#6b5442', 'byr': '1994', 'hgt': '170cm', 'ecl': 'amb', 'pid': '348959147', 'iyr': '2013'}
|
Checking {'hcl': '#6b5442', 'iyr': '2013', 'hgt': '170cm', 'byr': '1994', 'pid': '348959147', 'eyr': '2021', 'ecl': 'amb'}
|
||||||
Checking {'byr': '1957', 'ecl': 'hzl', 'hgt': '156cm', 'pid': '890752588', 'eyr': '2025', 'hcl': 'e7c520', 'cid': '199', 'iyr': '2017'}
|
Checking {'cid': '199', 'hcl': 'e7c520', 'iyr': '2017', 'hgt': '156cm', 'byr': '1957', 'pid': '890752588', 'ecl': 'hzl', 'eyr': '2025'}
|
||||||
bad hair color
|
bad hair color
|
||||||
Checking {'hgt': '169cm', 'iyr': '2016', 'cid': '180', 'hcl': '#733820', 'eyr': '2024', 'ecl': 'hzl', 'pid': '661114936', 'byr': '1928'}
|
Checking {'eyr': '2024', 'hcl': '#733820', 'iyr': '2016', 'hgt': '169cm', 'pid': '661114936', 'byr': '1928', 'ecl': 'hzl', 'cid': '180'}
|
||||||
Checking {'iyr': '2015', 'eyr': '2020', 'byr': '1941', 'hgt': '179cm', 'pid': '148063033', 'hcl': '#6b5442', 'ecl': 'brn'}
|
Checking {'hcl': '#6b5442', 'iyr': '2015', 'hgt': '179cm', 'byr': '1941', 'pid': '148063033', 'eyr': '2020', 'ecl': 'brn'}
|
||||||
Checking {'iyr': '1935', 'pid': '14292032', 'byr': '2020', 'eyr': '1956', 'ecl': '#c9bc33', 'hgt': '59cm', 'hcl': '#cfa07d'}
|
Checking {'hcl': '#cfa07d', 'iyr': '1935', 'hgt': '59cm', 'pid': '14292032', 'byr': '2020', 'eyr': '1956', 'ecl': '#c9bc33'}
|
||||||
Bad birth year
|
Bad birth year
|
||||||
Checking {'hgt': '165cm', 'iyr': '2010', 'eyr': '2023', 'hcl': '#733820', 'pid': '312465756', 'cid': '112', 'byr': '1993', 'ecl': 'amb'}
|
Checking {'ecl': 'amb', 'hcl': '#733820', 'iyr': '2010', 'hgt': '165cm', 'pid': '312465756', 'byr': '1993', 'eyr': '2023', 'cid': '112'}
|
||||||
Checking {'eyr': '2032', 'ecl': 'grt', 'iyr': '1963', 'byr': '1964', 'pid': '#f5628c', 'hcl': 'z', 'hgt': '111'}
|
Checking {'hcl': 'z', 'iyr': '1963', 'hgt': '111', 'byr': '1964', 'pid': '#f5628c', 'eyr': '2032', 'ecl': 'grt'}
|
||||||
Bad issue year
|
Bad issue year
|
||||||
Checking {'hgt': '169cm', 'iyr': '2012', 'hcl': '#623a2f', 'cid': '291', 'ecl': 'oth', 'pid': '809080900', 'byr': '1979', 'eyr': '2023'}
|
Checking {'eyr': '2023', 'hcl': '#623a2f', 'iyr': '2012', 'hgt': '169cm', 'pid': '809080900', 'byr': '1979', 'cid': '291', 'ecl': 'oth'}
|
||||||
Checking {'iyr': '2021', 'eyr': '2033', 'ecl': 'gmt', 'hgt': '59cm', 'byr': '1967', 'pid': '2498700612'}
|
Checking {'iyr': '2021', 'byr': '1967', 'pid': '2498700612', 'hgt': '59cm', 'eyr': '2033', 'ecl': 'gmt'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'byr': '1953', 'ecl': 'oth', 'iyr': '2013', 'hcl': '#b6652a', 'pid': '442586860'}
|
Checking {'hcl': '#b6652a', 'iyr': '2013', 'byr': '1953', 'pid': '442586860', 'ecl': 'oth'}
|
||||||
Missing expected value
|
Missing expected value
|
||||||
Checking {'pid': '095687847', 'eyr': '2022', 'hgt': '151cm', 'byr': '1967', 'iyr': '2017', 'ecl': 'oth', 'hcl': '#866857'}
|
Checking {'hcl': '#866857', 'iyr': '2017', 'hgt': '151cm', 'pid': '095687847', 'byr': '1967', 'eyr': '2022', 'ecl': 'oth'}
|
||||||
Checking {'hcl': '#866857', 'pid': '983640144', 'hgt': '61cm', 'ecl': 'hzl', 'byr': '1991', 'iyr': '1930', 'eyr': '2024'}
|
Checking {'hcl': '#866857', 'iyr': '1930', 'hgt': '61cm', 'pid': '983640144', 'byr': '1991', 'ecl': 'hzl', 'eyr': '2024'}
|
||||||
Bad issue year
|
Bad issue year
|
||||||
Checking {'ecl': 'oth', 'hcl': '#602927', 'eyr': '2025', 'iyr': '2013', 'hgt': '151cm', 'byr': '1992', 'pid': '812583062'}
|
Checking {'hcl': '#602927', 'iyr': '2013', 'hgt': '151cm', 'byr': '1992', 'pid': '812583062', 'ecl': 'oth', 'eyr': '2025'}
|
||||||
count = 194
|
count = 194
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
I am a function.
|
I am a function.
|
||||||
[42]
|
[42]
|
||||||
{'a': <class 'int'>, 'b': <class 'float'>, 'return': <class 'list'>}
|
{'return': <class 'list'>, 'a': <class 'int'>, 'b': <class 'float'>}
|
||||||
{'self': None, 'anint': <class 'int'>, 'adict': 'dict[str,object]', 'return': None}
|
{'return': None, 'anint': <class 'int'>, 'self': None, 'adict': 'dict[str,object]'}
|
||||||
I am a method taking a dict.
|
I am a method taking a dict.
|
||||||
None
|
None
|
||||||
{'abool': <class 'bool'>, 'astr': <class 'str'>}
|
{'astr': <class 'str'>, 'abool': <class 'bool'>}
|
||||||
I return a Foo? Amazing!
|
I return a Foo? Amazing!
|
||||||
True
|
True
|
||||||
|
@ -26,5 +26,5 @@ last(1,2,7,3)
|
|||||||
last(1,2,test="thing")
|
last(1,2,test="thing")
|
||||||
try:
|
try:
|
||||||
last(1,2,'c','d',7,8,extra='foo')
|
last(1,2,'c','d',7,8,extra='foo')
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Positionals: [1, 2, 3, 'a', 'b']
|
Positionals: [1, 2, 3, 'a', 'b']
|
||||||
Keywords: {'foo': 'bar', 'biz': 42}
|
Keywords: {'biz': 42, 'foo': 'bar'}
|
||||||
hi
|
hi
|
||||||
Positionals: []
|
Positionals: []
|
||||||
Keywords: {}
|
Keywords: {}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[31mCall starts here.[0m
|
[31mCall starts here.[0m
|
||||||
a = 1 b = 1
|
a = 1 b = 1
|
||||||
args = [2, 3] kwargs = {'foo': 'bar', 'stuff': 'things'}
|
args = [2, 3] kwargs = {'stuff': 'things', 'foo': 'bar'}
|
||||||
|
@ -8,7 +8,7 @@ class Foo():
|
|||||||
# This doesn't work because amethod is unbound and needs an instance.
|
# This doesn't work because amethod is unbound and needs an instance.
|
||||||
try:
|
try:
|
||||||
Foo.amethod()
|
Foo.amethod()
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
# This works
|
# This works
|
||||||
Foo.amethod(Foo())
|
Foo.amethod(Foo())
|
||||||
|
@ -9,5 +9,5 @@ print("{a} {b} {c}".format(**{'a': 1, 'b': "apples", 'c': object}))
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
print("{a} {b} {c}".format(a=True,**{'a': 1, 'b': "apples", 'c': object}))
|
print("{a} {b} {c}".format(a=True,**{'a': 1, 'b': "apples", 'c': object}))
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
def decorate(func):
|
|
||||||
def _inner(a):
|
|
||||||
print('in')
|
|
||||||
a = func(a)
|
|
||||||
print('out')
|
|
||||||
return a
|
|
||||||
return _inner
|
|
||||||
|
|
||||||
def foo():
|
|
||||||
@decorate
|
|
||||||
def bar(a):
|
|
||||||
if a <= 0:
|
|
||||||
return 0
|
|
||||||
return bar(a-1)
|
|
||||||
|
|
||||||
bar(2)
|
|
||||||
|
|
||||||
foo()
|
|
@ -1,6 +0,0 @@
|
|||||||
in
|
|
||||||
in
|
|
||||||
in
|
|
||||||
out
|
|
||||||
out
|
|
||||||
out
|
|
@ -120,7 +120,7 @@ def aMethod(with_,args=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
aMethod()
|
aMethod()
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
|
||||||
aMethod("just the first")
|
aMethod("just the first")
|
||||||
@ -128,11 +128,11 @@ aMethod("the first","and the second")
|
|||||||
aMethod(args="hello",with_="world")
|
aMethod(args="hello",with_="world")
|
||||||
try:
|
try:
|
||||||
aMethod(foo="hello",with_="bar")
|
aMethod(foo="hello",with_="bar")
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg) # unrecognized keyword arg, from inner method
|
print(exception.arg) # unrecognized keyword arg, from inner method
|
||||||
|
|
||||||
aMethod(*[1,2])
|
aMethod(*[1,2])
|
||||||
try:
|
try:
|
||||||
aMethod(*[1,2,3])
|
aMethod(*[1,2,3])
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg) # too many arguments
|
print(exception.arg) # too many arguments
|
||||||
|
@ -20,7 +20,7 @@ del l[3]
|
|||||||
print(l) # [1, 3, 4]
|
print(l) # [1, 3, 4]
|
||||||
try:
|
try:
|
||||||
del l[3]
|
del l[3]
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg) # List index out of range
|
print(exception.arg) # List index out of range
|
||||||
|
|
||||||
let o = object()
|
let o = object()
|
||||||
@ -36,13 +36,13 @@ print(dir(o.foo.bar))
|
|||||||
print(o.foo.bar.qux)
|
print(o.foo.bar.qux)
|
||||||
try:
|
try:
|
||||||
print(o.foo.bar.baz)
|
print(o.foo.bar.baz)
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg) # AttributeError
|
print(exception.arg) # AttributeError
|
||||||
del o.foo.bar
|
del o.foo.bar
|
||||||
print(dir(o.foo))
|
print(dir(o.foo))
|
||||||
try:
|
try:
|
||||||
print(o.foo.bar)
|
print(o.foo.bar)
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg) # AttributeError
|
print(exception.arg) # AttributeError
|
||||||
del o.foo
|
del o.foo
|
||||||
del o
|
del o
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
try:
|
|
||||||
try:
|
|
||||||
raise ValueError()
|
|
||||||
else:
|
|
||||||
print("oh no (else should not run)")
|
|
||||||
finally:
|
|
||||||
print('finally')
|
|
||||||
except ValueError:
|
|
||||||
print("value error caught in outer")
|
|
@ -1,2 +0,0 @@
|
|||||||
finally
|
|
||||||
value error caught in outer
|
|
@ -1,13 +0,0 @@
|
|||||||
for i in range(3):
|
|
||||||
try:
|
|
||||||
print('hello world')
|
|
||||||
if i == 1:
|
|
||||||
break
|
|
||||||
except ValueError:
|
|
||||||
print('value error')
|
|
||||||
else:
|
|
||||||
print('no error')
|
|
||||||
finally:
|
|
||||||
print('and done')
|
|
||||||
|
|
||||||
print('finished')
|
|
@ -1,6 +0,0 @@
|
|||||||
hello world
|
|
||||||
no error
|
|
||||||
and done
|
|
||||||
hello world
|
|
||||||
and done
|
|
||||||
finished
|
|
@ -1,14 +0,0 @@
|
|||||||
try:
|
|
||||||
try:
|
|
||||||
print('hello world')
|
|
||||||
raise ValueError()
|
|
||||||
except 1/0:
|
|
||||||
print("oh no")
|
|
||||||
except ValueError:
|
|
||||||
print("value error")
|
|
||||||
finally:
|
|
||||||
print('this is the finally')
|
|
||||||
except ZeroDivisionError:
|
|
||||||
print('caught the zero div error')
|
|
||||||
|
|
||||||
print('done')
|
|
@ -1,4 +0,0 @@
|
|||||||
hello world
|
|
||||||
this is the finally
|
|
||||||
caught the zero div error
|
|
||||||
done
|
|
@ -2,7 +2,7 @@ def doTheThing(excp):
|
|||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
raise excp
|
raise excp
|
||||||
except (TypeError, ValueError) as exception:
|
except (TypeError, ValueError):
|
||||||
print("Caught a", repr(exception))
|
print("Caught a", repr(exception))
|
||||||
for i in exception.traceback:
|
for i in exception.traceback:
|
||||||
let func, instr = i
|
let func, instr = i
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
import dis
|
|
||||||
import fileio
|
|
||||||
|
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
def test_a():
|
|
||||||
def bar():
|
|
||||||
return "string" + 42
|
|
||||||
def foo():
|
|
||||||
return bar() * 4
|
|
||||||
foo()
|
|
||||||
|
|
||||||
def test_b():
|
|
||||||
let foo = {
|
|
||||||
'bar': {
|
|
||||||
'baz': None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print(foo['bar']['baz']['qux']['etc'])
|
|
||||||
|
|
||||||
def test_c():
|
|
||||||
import os
|
|
||||||
os.system(42)
|
|
||||||
|
|
||||||
def test_d():
|
|
||||||
let a = object()
|
|
||||||
a.name = '42'
|
|
||||||
let b = None
|
|
||||||
let c = object()
|
|
||||||
c.name = '42'
|
|
||||||
print(a.name, b.name, c.name)
|
|
||||||
|
|
||||||
def test_e():
|
|
||||||
let a = 1
|
|
||||||
let b = 2
|
|
||||||
class Confabulator:
|
|
||||||
def __add__(self,other):
|
|
||||||
return NotImplemented
|
|
||||||
let c = Confabulator()
|
|
||||||
let d = 4
|
|
||||||
|
|
||||||
let x = (a + b) @ (c + d)
|
|
||||||
|
|
||||||
def test_f():
|
|
||||||
class Thing:
|
|
||||||
def __init__(self, required):
|
|
||||||
pass
|
|
||||||
def __eq__(self, other):
|
|
||||||
raise ValueError("oh no")
|
|
||||||
|
|
||||||
Thing('a') == Thing()
|
|
||||||
|
|
||||||
def disrec(code, seen):
|
|
||||||
let next = deque()
|
|
||||||
next.append(code)
|
|
||||||
while next:
|
|
||||||
let co = next.popleft()
|
|
||||||
seen.add(co)
|
|
||||||
let offset = 0
|
|
||||||
for inst,size,operand in dis.examine(co):
|
|
||||||
let expr = dis.ip_to_expression(co, offset + size - 1)
|
|
||||||
if expr is not None:
|
|
||||||
let lineNo, start, midStart, midEnd, end = expr
|
|
||||||
if co.__file__:
|
|
||||||
let j = 1
|
|
||||||
with fileio.open(co.__file__,'r') as f:
|
|
||||||
let line = f.readlines()[lineNo-1].rstrip()
|
|
||||||
let i = 0
|
|
||||||
while i < len(line):
|
|
||||||
if line[i] not in ' \t': break
|
|
||||||
j++
|
|
||||||
i++
|
|
||||||
while i < len(line):
|
|
||||||
print(line[i],end='')
|
|
||||||
i++
|
|
||||||
print()
|
|
||||||
while j < start:
|
|
||||||
print(' ',end='')
|
|
||||||
j++
|
|
||||||
while j < midStart:
|
|
||||||
print('~',end='')
|
|
||||||
j++
|
|
||||||
while j < midEnd:
|
|
||||||
print('^',end='')
|
|
||||||
j++
|
|
||||||
while j < end:
|
|
||||||
print('~',end='')
|
|
||||||
j++
|
|
||||||
print()
|
|
||||||
if isinstance(operand,codeobject) and operand not in seen:
|
|
||||||
next.append(operand)
|
|
||||||
offset += size
|
|
||||||
|
|
||||||
for func_name in dir():
|
|
||||||
if func_name.startswith('test_'):
|
|
||||||
disrec(globals()[func_name].__code__,set())
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
|||||||
foo()
|
|
||||||
~~~^~
|
|
||||||
return "string" + 42
|
|
||||||
~~~~~~~~~^~~~
|
|
||||||
return bar() * 4
|
|
||||||
~~~^~
|
|
||||||
return bar() * 4
|
|
||||||
~~~~~~^~~
|
|
||||||
print(foo['bar']['baz']['qux']['etc'])
|
|
||||||
~~~^~~~~~~
|
|
||||||
print(foo['bar']['baz']['qux']['etc'])
|
|
||||||
~~~~~~~~~~^~~~~~~
|
|
||||||
print(foo['bar']['baz']['qux']['etc'])
|
|
||||||
~~~~~~~~~~~~~~~~~^~~~~~~
|
|
||||||
print(foo['bar']['baz']['qux']['etc'])
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
|
|
||||||
print(foo['bar']['baz']['qux']['etc'])
|
|
||||||
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
os.system(42)
|
|
||||||
~~^~~~~~~
|
|
||||||
os.system(42)
|
|
||||||
~~~~~~~~~^~~~
|
|
||||||
let a = object()
|
|
||||||
~~~~~~^~
|
|
||||||
a.name = '42'
|
|
||||||
~~^^^^~~~~~~~
|
|
||||||
let c = object()
|
|
||||||
~~~~~~^~
|
|
||||||
c.name = '42'
|
|
||||||
~~^^^^~~~~~~~
|
|
||||||
print(a.name, b.name, c.name)
|
|
||||||
~^~~~~
|
|
||||||
print(a.name, b.name, c.name)
|
|
||||||
~^~~~~
|
|
||||||
print(a.name, b.name, c.name)
|
|
||||||
~^~~~~
|
|
||||||
print(a.name, b.name, c.name)
|
|
||||||
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
let c = Confabulator()
|
|
||||||
~~~~~~~~~~~~^~
|
|
||||||
let x = (a + b) @ (c + d)
|
|
||||||
~~^~~
|
|
||||||
let x = (a + b) @ (c + d)
|
|
||||||
~~^~~
|
|
||||||
let x = (a + b) @ (c + d)
|
|
||||||
~~~~~~~~^~~~~~~~~
|
|
||||||
Thing('a') == Thing()
|
|
||||||
~~~~~^~~~~
|
|
||||||
Thing('a') == Thing()
|
|
||||||
~~~~~^~
|
|
||||||
Thing('a') == Thing()
|
|
||||||
~~~~~~~~~~~^^~~~~~~~~
|
|
||||||
raise ValueError("oh no")
|
|
||||||
~~~~~~~~~~^~~~~~~~~
|
|
@ -1,38 +0,0 @@
|
|||||||
import math
|
|
||||||
|
|
||||||
def print_values(values):
|
|
||||||
for v in values:
|
|
||||||
# Avoiding doing just 'v' as we want this test to work in Python and Kuroko with identical results
|
|
||||||
# and Python will try to reduce the number of digits to the minimum required for disambiguity, which
|
|
||||||
# we don't currently support.
|
|
||||||
print(v.__format__('.16g'),v.__format__('f'),v.__format__('e'),v.__format__('#.16g'),v.__format__('+.4g'))
|
|
||||||
|
|
||||||
print_values([
|
|
||||||
float('75.5834073120020679681'),
|
|
||||||
float('75.5834073120020679681e-7'),
|
|
||||||
float('75.5834073120020679681e-24'),
|
|
||||||
float('75.5834073120020679681e80'),
|
|
||||||
float('75.5834073120020679681e300'),
|
|
||||||
float('75.0'),
|
|
||||||
float('0.005'),
|
|
||||||
float('0.0005'),
|
|
||||||
float('0.5'),
|
|
||||||
float('0.01'),
|
|
||||||
float('0.25'),
|
|
||||||
float('0.25')/2,
|
|
||||||
float('0.25')/4,
|
|
||||||
float('0.25')/8,
|
|
||||||
float('0.25')/16,
|
|
||||||
float('0.25')/2048,
|
|
||||||
float('0.25')/4096,
|
|
||||||
float('10000000253263163212.0'),
|
|
||||||
(10**60)/1,
|
|
||||||
float('20000000000000000'),
|
|
||||||
0.0,
|
|
||||||
-0.0,
|
|
||||||
math.inf,
|
|
||||||
-math.inf,
|
|
||||||
math.nan,
|
|
||||||
-math.nan,
|
|
||||||
float('123.45'),
|
|
||||||
])
|
|
@ -1,27 +0,0 @@
|
|||||||
75.58340731200207 75.583407 7.558341e+01 75.58340731200207 +75.58
|
|
||||||
7.558340731200207e-06 0.000008 7.558341e-06 7.558340731200207e-06 +7.558e-06
|
|
||||||
7.558340731200207e-23 0.000000 7.558341e-23 7.558340731200207e-23 +7.558e-23
|
|
||||||
7.558340731200207e+81 7558340731200206731762734930317738502291660148265284220384036778702184341124939776.000000 7.558341e+81 7.558340731200207e+81 +7.558e+81
|
|
||||||
7.558340731200207e+301 75583407312002066768461022261315172287297487251874586478724757817960934981634094788769653163852924763443651501309652569997165035925379465760402739890567710612092775360309017524288996057690996811122123668937191051711752654751620072621666259734026943606049685516409713620071805443138455356168154210369536.000000 7.558341e+301 7.558340731200207e+301 +7.558e+301
|
|
||||||
75 75.000000 7.500000e+01 75.00000000000000 +75
|
|
||||||
0.005 0.005000 5.000000e-03 0.005000000000000000 +0.005
|
|
||||||
0.0005 0.000500 5.000000e-04 0.0005000000000000000 +0.0005
|
|
||||||
0.5 0.500000 5.000000e-01 0.5000000000000000 +0.5
|
|
||||||
0.01 0.010000 1.000000e-02 0.01000000000000000 +0.01
|
|
||||||
0.25 0.250000 2.500000e-01 0.2500000000000000 +0.25
|
|
||||||
0.125 0.125000 1.250000e-01 0.1250000000000000 +0.125
|
|
||||||
0.0625 0.062500 6.250000e-02 0.06250000000000000 +0.0625
|
|
||||||
0.03125 0.031250 3.125000e-02 0.03125000000000000 +0.03125
|
|
||||||
0.015625 0.015625 1.562500e-02 0.01562500000000000 +0.01562
|
|
||||||
0.0001220703125 0.000122 1.220703e-04 0.0001220703125000000 +0.0001221
|
|
||||||
6.103515625e-05 0.000061 6.103516e-05 6.103515625000000e-05 +6.104e-05
|
|
||||||
1.000000025326316e+19 10000000253263163392.000000 1.000000e+19 1.000000025326316e+19 +1e+19
|
|
||||||
9.999999999999999e+59 999999999999999949387135297074018866963645011013410073083904.000000 1.000000e+60 9.999999999999999e+59 +1e+60
|
|
||||||
2e+16 20000000000000000.000000 2.000000e+16 2.000000000000000e+16 +2e+16
|
|
||||||
0 0.000000 0.000000e+00 0.000000000000000 +0
|
|
||||||
-0 -0.000000 -0.000000e+00 -0.000000000000000 -0
|
|
||||||
inf inf inf inf +inf
|
|
||||||
-inf -inf -inf -inf -inf
|
|
||||||
nan nan nan nan +nan
|
|
||||||
nan nan nan nan +nan
|
|
||||||
123.45 123.450000 1.234500e+02 123.4500000000000 +123.5
|
|
@ -1,8 +1,8 @@
|
|||||||
hello
|
|
||||||
foo
|
|
||||||
1
|
1
|
||||||
|
hello
|
||||||
3
|
3
|
||||||
hello: world
|
foo
|
||||||
foo: bar
|
|
||||||
1: 2
|
1: 2
|
||||||
|
hello: world
|
||||||
3: 4
|
3: 4
|
||||||
|
foo: bar
|
||||||
|
@ -4,7 +4,7 @@ class Bar(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
let b = Bar()
|
let b = Bar()
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
|
||||||
class Foo():
|
class Foo():
|
||||||
@ -13,6 +13,6 @@ class Foo():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
let f = Foo()
|
let f = Foo()
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ if True:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
let a, b = range(3)
|
let a, b = range(3)
|
||||||
except as exception:
|
except:
|
||||||
print(repr(exception))
|
print(repr(exception))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
let a, b, c, d = range(3)
|
let a, b, c, d = range(3)
|
||||||
except as exception:
|
except:
|
||||||
print(repr(exception))
|
print(repr(exception))
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
{'glossary': {'title': 'example glossary', 'GlossDiv': {'title': 'S', 'GlossList': {'GlossEntry': {'ID': 'SGML', 'SortAs': 'SGML', 'GlossTerm': 'Standard Generalized Markup Language', 'Acronym': 'SGML', 'Abbrev': 'ISO 8879:1986', 'GlossDef': {'para': 'A meta-markup language, used to create markup languages such as DocBook.', 'GlossSeeAlso': ['GML', 'XML']}, 'GlossSee': 'markup'}}}}}
|
{'glossary': {'title': 'example glossary', 'GlossDiv': {'title': 'S', 'GlossList': {'GlossEntry': {'SortAs': 'SGML', 'Abbrev': 'ISO 8879:1986', 'Acronym': 'SGML', 'GlossTerm': 'Standard Generalized Markup Language', 'GlossSee': 'markup', 'ID': 'SGML', 'GlossDef': {'para': 'A meta-markup language, used to create markup languages such as DocBook.', 'GlossSeeAlso': ['GML', 'XML']}}}}}}
|
||||||
{'web-app': {'servlet': [{'servlet-name': 'cofaxCDS', 'servlet-class': 'org.cofax.cds.CDSServlet', 'init-param': {'configGlossary:installationAt': 'Philadelphia, PA', 'configGlossary:adminEmail': 'ksm@pobox.com', 'configGlossary:poweredBy': 'Cofax', 'configGlossary:poweredByIcon': '/images/cofax.gif', 'configGlossary:staticPath': '/content/static', 'templateProcessorClass': 'org.cofax.WysiwygTemplate', 'templateLoaderClass': 'org.cofax.FilesTemplateLoader', 'templatePath': 'templates', 'templateOverridePath': '', 'defaultListTemplate': 'listTemplate.htm', 'defaultFileTemplate': 'articleTemplate.htm', 'useJSP': False, 'jspListTemplate': 'listTemplate.jsp', 'jspFileTemplate': 'articleTemplate.jsp', 'cachePackageTagsTrack': 200, 'cachePackageTagsStore': 200, 'cachePackageTagsRefresh': 60, 'cacheTemplatesTrack': 100, 'cacheTemplatesStore': 50, 'cacheTemplatesRefresh': 15, 'cachePagesTrack': 200, 'cachePagesStore': 100, 'cachePagesRefresh': 10, 'cachePagesDirtyRead': 10, 'searchEngineListTemplate': 'forSearchEnginesList.htm', 'searchEngineFileTemplate': 'forSearchEngines.htm', 'searchEngineRobotsDb': 'WEB-INF/robots.db', 'useDataStore': True, 'dataStoreClass': 'org.cofax.SqlDataStore', 'redirectionClass': 'org.cofax.SqlRedirection', 'dataStoreName': 'cofax', 'dataStoreDriver': 'com.microsoft.jdbc.sqlserver.SQLServerDriver', 'dataStoreUrl': 'jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon', 'dataStoreUser': 'sa', 'dataStorePassword': 'dataStoreTestQuery', 'dataStoreTestQuery': "SET NOCOUNT ON;select test='test';", 'dataStoreLogFile': '/usr/local/tomcat/logs/datastore.log', 'dataStoreInitConns': 10, 'dataStoreMaxConns': 100, 'dataStoreConnUsageLimit': 100, 'dataStoreLogLevel': 'debug', 'maxUrlLength': 500}}, {'servlet-name': 'cofaxEmail', 'servlet-class': 'org.cofax.cds.EmailServlet', 'init-param': {'mailHost': 'mail1', 'mailHostOverride': 'mail2'}}, {'servlet-name': 'cofaxAdmin', 'servlet-class': 'org.cofax.cds.AdminServlet'}, {'servlet-name': 'fileServlet', 'servlet-class': 'org.cofax.cds.FileServlet'}, {'servlet-name': 'cofaxTools', 'servlet-class': 'org.cofax.cms.CofaxToolsServlet', 'init-param': {'templatePath': 'toolstemplates/', 'log': 1, 'logLocation': '/usr/local/tomcat/logs/CofaxTools.log', 'logMaxSize': '', 'dataLog': 1, 'dataLogLocation': '/usr/local/tomcat/logs/dataLog.log', 'dataLogMaxSize': '', 'removePageCache': '/content/admin/remove?cache=pages&id=', 'removeTemplateCache': '/content/admin/remove?cache=templates&id=', 'fileTransferFolder': '/usr/local/tomcat/webapps/content/fileTransferFolder', 'lookInContext': 1, 'adminGroupID': 4, 'betaServer': True}}], 'servlet-mapping': {'cofaxCDS': '/', 'cofaxEmail': '/cofaxutil/aemail/*', 'cofaxAdmin': '/admin/*', 'fileServlet': '/static/*', 'cofaxTools': '/tools/*'}, 'taglib': {'taglib-uri': 'cofax.tld', 'taglib-location': '/WEB-INF/tlds/cofax.tld'}}}
|
{'web-app': {'taglib': {'taglib-uri': 'cofax.tld', 'taglib-location': '/WEB-INF/tlds/cofax.tld'}, 'servlet': [{'servlet-class': 'org.cofax.cds.CDSServlet', 'init-param': {'templateProcessorClass': 'org.cofax.WysiwygTemplate', 'cachePagesStore': 100, 'searchEngineListTemplate': 'forSearchEnginesList.htm', 'searchEngineFileTemplate': 'forSearchEngines.htm', 'cachePackageTagsStore': 200, 'dataStoreUser': 'sa', 'dataStoreInitConns': 10, 'configGlossary:installationAt': 'Philadelphia, PA', 'cachePagesTrack': 200, 'dataStoreLogFile': '/usr/local/tomcat/logs/datastore.log', 'templateOverridePath': '', 'redirectionClass': 'org.cofax.SqlRedirection', 'cachePackageTagsTrack': 200, 'useDataStore': True, 'maxUrlLength': 500, 'dataStorePassword': 'dataStoreTestQuery', 'defaultFileTemplate': 'articleTemplate.htm', 'defaultListTemplate': 'listTemplate.htm', 'dataStoreConnUsageLimit': 100, 'templatePath': 'templates', 'useJSP': False, 'configGlossary:poweredBy': 'Cofax', 'dataStoreClass': 'org.cofax.SqlDataStore', 'dataStoreName': 'cofax', 'cacheTemplatesRefresh': 15, 'dataStoreDriver': 'com.microsoft.jdbc.sqlserver.SQLServerDriver', 'cachePagesDirtyRead': 10, 'configGlossary:adminEmail': 'ksm@pobox.com', 'dataStoreTestQuery': "SET NOCOUNT ON;select test='test';", 'cacheTemplatesStore': 50, 'templateLoaderClass': 'org.cofax.FilesTemplateLoader', 'configGlossary:staticPath': '/content/static', 'searchEngineRobotsDb': 'WEB-INF/robots.db', 'cacheTemplatesTrack': 100, 'dataStoreLogLevel': 'debug', 'dataStoreUrl': 'jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon', 'cachePagesRefresh': 10, 'configGlossary:poweredByIcon': '/images/cofax.gif', 'dataStoreMaxConns': 100, 'jspFileTemplate': 'articleTemplate.jsp', 'cachePackageTagsRefresh': 60, 'jspListTemplate': 'listTemplate.jsp'}, 'servlet-name': 'cofaxCDS'}, {'servlet-class': 'org.cofax.cds.EmailServlet', 'init-param': {'mailHostOverride': 'mail2', 'mailHost': 'mail1'}, 'servlet-name': 'cofaxEmail'}, {'servlet-class': 'org.cofax.cds.AdminServlet', 'servlet-name': 'cofaxAdmin'}, {'servlet-class': 'org.cofax.cds.FileServlet', 'servlet-name': 'fileServlet'}, {'servlet-class': 'org.cofax.cms.CofaxToolsServlet', 'init-param': {'logMaxSize': '', 'log': 1, 'removeTemplateCache': '/content/admin/remove?cache=templates&id=', 'dataLogMaxSize': '', 'lookInContext': 1, 'adminGroupID': 4, 'removePageCache': '/content/admin/remove?cache=pages&id=', 'dataLogLocation': '/usr/local/tomcat/logs/dataLog.log', 'betaServer': True, 'fileTransferFolder': '/usr/local/tomcat/webapps/content/fileTransferFolder', 'logLocation': '/usr/local/tomcat/logs/CofaxTools.log', 'dataLog': 1, 'templatePath': 'toolstemplates/'}, 'servlet-name': 'cofaxTools'}], 'servlet-mapping': {'cofaxCDS': '/', 'fileServlet': '/static/*', 'cofaxEmail': '/cofaxutil/aemail/*', 'cofaxTools': '/tools/*', 'cofaxAdmin': '/admin/*'}}}
|
||||||
|
@ -9,22 +9,22 @@ function(1,2,keyword2=5)
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
function(1,keyword2=5)
|
function(1,keyword2=5)
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
function(1,2,positional1=4)
|
function(1,2,positional1=4)
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
function(1,2,keyword2=None,keyword2=5)
|
function(1,2,keyword2=None,keyword2=5)
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
|
||||||
function(1,keyword2=4,positional2="abc")
|
function(1,keyword2=4,positional2="abc")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
function(1,keyword2=4,keyword1=5,positional1="nope")
|
function(1,keyword2=4,keyword1=5,positional1="nope")
|
||||||
except as exception:
|
except:
|
||||||
print(exception.arg)
|
print(exception.arg)
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
try:
|
|
||||||
try:
|
|
||||||
print('try')
|
|
||||||
raise ValueError()
|
|
||||||
except 1/0:
|
|
||||||
print('bad: except body')
|
|
||||||
finally:
|
|
||||||
print(inner_error)
|
|
||||||
print('bad: after inner error')
|
|
||||||
except BaseException as e:
|
|
||||||
while e:
|
|
||||||
print(repr(e))
|
|
||||||
e = e.__context__
|
|
@ -1,4 +0,0 @@
|
|||||||
try
|
|
||||||
NameError("Undefined variable 'inner_error'.")
|
|
||||||
ZeroDivisionError('integer division by zero')
|
|
||||||
ValueError()
|
|
@ -8,7 +8,7 @@ def testTop():
|
|||||||
printPackage(foo)
|
printPackage(foo)
|
||||||
try:
|
try:
|
||||||
print(foo.bar, "Fail")
|
print(foo.bar, "Fail")
|
||||||
except as exception:
|
except:
|
||||||
print(repr(exception)) # AttributeError
|
print(repr(exception)) # AttributeError
|
||||||
|
|
||||||
def testCaching():
|
def testCaching():
|
||||||
@ -33,7 +33,7 @@ def testFromImport():
|
|||||||
print(baz.qux)
|
print(baz.qux)
|
||||||
try:
|
try:
|
||||||
print(foo, "Fail")
|
print(foo, "Fail")
|
||||||
except as exception:
|
except:
|
||||||
print(repr(exception))
|
print(repr(exception))
|
||||||
|
|
||||||
def testRenames():
|
def testRenames():
|
||||||
@ -42,17 +42,17 @@ def testRenames():
|
|||||||
print(blah.qux)
|
print(blah.qux)
|
||||||
try:
|
try:
|
||||||
print(foo, "Fail")
|
print(foo, "Fail")
|
||||||
except as exception:
|
except:
|
||||||
print(repr(exception))
|
print(repr(exception))
|
||||||
from foo.bar.baz import qux as thing
|
from foo.bar.baz import qux as thing
|
||||||
print(thing)
|
print(thing)
|
||||||
try:
|
try:
|
||||||
print(qux, "Fail")
|
print(qux, "Fail")
|
||||||
except as exception:
|
except:
|
||||||
print(repr(exception))
|
print(repr(exception))
|
||||||
try:
|
try:
|
||||||
print(foo.bar, "Fail")
|
print(foo.bar, "Fail")
|
||||||
except as exception:
|
except:
|
||||||
print(repr(exception))
|
print(repr(exception))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
print(float('123431236.632163e-3'))
|
|
||||||
print(float('123431236.632163e3'))
|
|
||||||
print(float('123431236.632163e30'))
|
|
||||||
print(float('123431236.632163e-30'))
|
|
||||||
print(float('-123431236.632163e-3'))
|
|
||||||
print(float('-123431236.632163e3'))
|
|
||||||
print(float('-123431236.632163e30'))
|
|
||||||
print(float('-123431236.632163e-30'))
|
|
||||||
print(float('2.1234567'))
|
|
||||||
print(float('0.1234567'))
|
|
||||||
print(float('0.001234567'))
|
|
||||||
print(float('0.00001234567'))
|
|
||||||
|
|
||||||
|
|
||||||
print(float('0.000976562500000000325260651745651330202235840260982513427734375'))
|
|
||||||
print(float('2.47032822920623272088284396434110686182e-324').__format__('.10000g'))
|
|
||||||
print(float('2.47032822920623272088284396434110686183e-324').__format__('.10000g'))
|
|
||||||
print(float('4.99e-324').__format__('.10000g'))
|
|
@ -1,16 +0,0 @@
|
|||||||
123431.236632163
|
|
||||||
123431236632.163
|
|
||||||
1.23431236632163e+38
|
|
||||||
1.23431236632163e-22
|
|
||||||
-123431.236632163
|
|
||||||
-123431236632.163
|
|
||||||
-1.23431236632163e+38
|
|
||||||
-1.23431236632163e-22
|
|
||||||
2.1234567
|
|
||||||
0.1234567
|
|
||||||
0.001234567
|
|
||||||
1.234567e-05
|
|
||||||
0.0009765625000000004
|
|
||||||
0
|
|
||||||
4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625e-324
|
|
||||||
4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625e-324
|
|
@ -2,7 +2,7 @@ import time
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
time.sleep()
|
time.sleep()
|
||||||
except as exception:
|
except:
|
||||||
print("oh no! " + exception.arg)
|
print("oh no! " + exception.arg)
|
||||||
|
|
||||||
print("Back from try/except")
|
print("Back from try/except")
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import random
|
|
||||||
def __main__(a,n=50000):
|
|
||||||
for j in range(n):
|
|
||||||
a.append(random.random())
|
|
||||||
a.append(42) # Forces heterogynous sorting in CPython
|
|
||||||
a.sort()
|
|
||||||
for j in range(n):
|
|
||||||
assert(a[j] <= a[j+1])
|
|
||||||
a.sort(reverse=True)
|
|
||||||
for j in range(n):
|
|
||||||
assert(a[j] >= a[j+1])
|
|
||||||
|
|
||||||
__main__([])
|
|
@ -1,21 +0,0 @@
|
|||||||
class R:
|
|
||||||
def __init__(self, name, department, age):
|
|
||||||
self.name = name
|
|
||||||
self.department = department
|
|
||||||
self.age = age
|
|
||||||
def __repr__(self):
|
|
||||||
return f'Record({self.name},{self.department},{self.age})'
|
|
||||||
|
|
||||||
def __main__(RS,ds,ages,names):
|
|
||||||
for i in range(50):
|
|
||||||
RS.append(R(names[i%5],ds[i%3],ages[i%17]))
|
|
||||||
|
|
||||||
print(RS)
|
|
||||||
RS.sort(key=lambda l: l.age)
|
|
||||||
print(RS)
|
|
||||||
RS.sort(key=lambda l: l.department)
|
|
||||||
print(RS)
|
|
||||||
RS.sort(key=lambda l: l.age, reverse=True)
|
|
||||||
print(RS)
|
|
||||||
|
|
||||||
__main__([], ['Engineering', 'Sales', 'Management'], [n for n in range(30,47)], ['Bob','Dan','Eric','Sue','Tina'])
|
|
@ -1,4 +0,0 @@
|
|||||||
[Record(Bob,Engineering,30), Record(Dan,Sales,31), Record(Eric,Management,32), Record(Sue,Engineering,33), Record(Tina,Sales,34), Record(Bob,Management,35), Record(Dan,Engineering,36), Record(Eric,Sales,37), Record(Sue,Management,38), Record(Tina,Engineering,39), Record(Bob,Sales,40), Record(Dan,Management,41), Record(Eric,Engineering,42), Record(Sue,Sales,43), Record(Tina,Management,44), Record(Bob,Engineering,45), Record(Dan,Sales,46), Record(Eric,Management,30), Record(Sue,Engineering,31), Record(Tina,Sales,32), Record(Bob,Management,33), Record(Dan,Engineering,34), Record(Eric,Sales,35), Record(Sue,Management,36), Record(Tina,Engineering,37), Record(Bob,Sales,38), Record(Dan,Management,39), Record(Eric,Engineering,40), Record(Sue,Sales,41), Record(Tina,Management,42), Record(Bob,Engineering,43), Record(Dan,Sales,44), Record(Eric,Management,45), Record(Sue,Engineering,46), Record(Tina,Sales,30), Record(Bob,Management,31), Record(Dan,Engineering,32), Record(Eric,Sales,33), Record(Sue,Management,34), Record(Tina,Engineering,35), Record(Bob,Sales,36), Record(Dan,Management,37), Record(Eric,Engineering,38), Record(Sue,Sales,39), Record(Tina,Management,40), Record(Bob,Engineering,41), Record(Dan,Sales,42), Record(Eric,Management,43), Record(Sue,Engineering,44), Record(Tina,Sales,45)]
|
|
||||||
[Record(Bob,Engineering,30), Record(Eric,Management,30), Record(Tina,Sales,30), Record(Dan,Sales,31), Record(Sue,Engineering,31), Record(Bob,Management,31), Record(Eric,Management,32), Record(Tina,Sales,32), Record(Dan,Engineering,32), Record(Sue,Engineering,33), Record(Bob,Management,33), Record(Eric,Sales,33), Record(Tina,Sales,34), Record(Dan,Engineering,34), Record(Sue,Management,34), Record(Bob,Management,35), Record(Eric,Sales,35), Record(Tina,Engineering,35), Record(Dan,Engineering,36), Record(Sue,Management,36), Record(Bob,Sales,36), Record(Eric,Sales,37), Record(Tina,Engineering,37), Record(Dan,Management,37), Record(Sue,Management,38), Record(Bob,Sales,38), Record(Eric,Engineering,38), Record(Tina,Engineering,39), Record(Dan,Management,39), Record(Sue,Sales,39), Record(Bob,Sales,40), Record(Eric,Engineering,40), Record(Tina,Management,40), Record(Dan,Management,41), Record(Sue,Sales,41), Record(Bob,Engineering,41), Record(Eric,Engineering,42), Record(Tina,Management,42), Record(Dan,Sales,42), Record(Sue,Sales,43), Record(Bob,Engineering,43), Record(Eric,Management,43), Record(Tina,Management,44), Record(Dan,Sales,44), Record(Sue,Engineering,44), Record(Bob,Engineering,45), Record(Eric,Management,45), Record(Tina,Sales,45), Record(Dan,Sales,46), Record(Sue,Engineering,46)]
|
|
||||||
[Record(Bob,Engineering,30), Record(Sue,Engineering,31), Record(Dan,Engineering,32), Record(Sue,Engineering,33), Record(Dan,Engineering,34), Record(Tina,Engineering,35), Record(Dan,Engineering,36), Record(Tina,Engineering,37), Record(Eric,Engineering,38), Record(Tina,Engineering,39), Record(Eric,Engineering,40), Record(Bob,Engineering,41), Record(Eric,Engineering,42), Record(Bob,Engineering,43), Record(Sue,Engineering,44), Record(Bob,Engineering,45), Record(Sue,Engineering,46), Record(Eric,Management,30), Record(Bob,Management,31), Record(Eric,Management,32), Record(Bob,Management,33), Record(Sue,Management,34), Record(Bob,Management,35), Record(Sue,Management,36), Record(Dan,Management,37), Record(Sue,Management,38), Record(Dan,Management,39), Record(Tina,Management,40), Record(Dan,Management,41), Record(Tina,Management,42), Record(Eric,Management,43), Record(Tina,Management,44), Record(Eric,Management,45), Record(Tina,Sales,30), Record(Dan,Sales,31), Record(Tina,Sales,32), Record(Eric,Sales,33), Record(Tina,Sales,34), Record(Eric,Sales,35), Record(Bob,Sales,36), Record(Eric,Sales,37), Record(Bob,Sales,38), Record(Sue,Sales,39), Record(Bob,Sales,40), Record(Sue,Sales,41), Record(Dan,Sales,42), Record(Sue,Sales,43), Record(Dan,Sales,44), Record(Tina,Sales,45), Record(Dan,Sales,46)]
|
|
||||||
[Record(Sue,Engineering,46), Record(Dan,Sales,46), Record(Bob,Engineering,45), Record(Eric,Management,45), Record(Tina,Sales,45), Record(Sue,Engineering,44), Record(Tina,Management,44), Record(Dan,Sales,44), Record(Bob,Engineering,43), Record(Eric,Management,43), Record(Sue,Sales,43), Record(Eric,Engineering,42), Record(Tina,Management,42), Record(Dan,Sales,42), Record(Bob,Engineering,41), Record(Dan,Management,41), Record(Sue,Sales,41), Record(Eric,Engineering,40), Record(Tina,Management,40), Record(Bob,Sales,40), Record(Tina,Engineering,39), Record(Dan,Management,39), Record(Sue,Sales,39), Record(Eric,Engineering,38), Record(Sue,Management,38), Record(Bob,Sales,38), Record(Tina,Engineering,37), Record(Dan,Management,37), Record(Eric,Sales,37), Record(Dan,Engineering,36), Record(Sue,Management,36), Record(Bob,Sales,36), Record(Tina,Engineering,35), Record(Bob,Management,35), Record(Eric,Sales,35), Record(Dan,Engineering,34), Record(Sue,Management,34), Record(Tina,Sales,34), Record(Sue,Engineering,33), Record(Bob,Management,33), Record(Eric,Sales,33), Record(Dan,Engineering,32), Record(Eric,Management,32), Record(Tina,Sales,32), Record(Sue,Engineering,31), Record(Bob,Management,31), Record(Dan,Sales,31), Record(Bob,Engineering,30), Record(Eric,Management,30), Record(Tina,Sales,30)]
|
|
@ -21,9 +21,9 @@ try:
|
|||||||
try:
|
try:
|
||||||
print("This is the inner level")
|
print("This is the inner level")
|
||||||
raise "This is the exception"
|
raise "This is the exception"
|
||||||
except as exception:
|
except:
|
||||||
print("This is the inner handler.")
|
print("This is the inner handler.")
|
||||||
raise exception
|
raise exception
|
||||||
except as exception:
|
except:
|
||||||
print("This is the outer handler.")
|
print("This is the outer handler.")
|
||||||
print(exception)
|
print(exception)
|
||||||
|
@ -7,7 +7,7 @@ for k,v in [(1,2),(3,4),(5,6)]:
|
|||||||
try:
|
try:
|
||||||
for k,v,z in [(1,2,7),(3,4,8),(5,6)]:
|
for k,v,z in [(1,2,7),(3,4,8),(5,6)]:
|
||||||
print(k,v,z)
|
print(k,v,z)
|
||||||
except as exception:
|
except:
|
||||||
print(exception.__class__.__name__)
|
print(exception.__class__.__name__)
|
||||||
# 1 2 7
|
# 1 2 7
|
||||||
# 3 4 8
|
# 3 4 8
|
||||||
@ -16,7 +16,7 @@ except as exception:
|
|||||||
try:
|
try:
|
||||||
for k,v in [1,2,3]:
|
for k,v in [1,2,3]:
|
||||||
print("type error")
|
print("type error")
|
||||||
except as exception:
|
except:
|
||||||
print(exception.__class__.__name__)
|
print(exception.__class__.__name__)
|
||||||
# TypeError
|
# TypeError
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user