This commit is contained in:
blueloveTH 2024-07-07 00:56:02 +08:00
parent 18fe69d579
commit 79012a6b08
5 changed files with 197 additions and 121 deletions

View File

@ -38,13 +38,7 @@ enum BindType {
BindType_CLASSMETHOD,
};
enum CompileMode {
EXEC_MODE,
EVAL_MODE,
REPL_MODE,
JSON_MODE,
CELL_MODE
};
enum CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, JSON_MODE, CELL_MODE };
/************* Global VMs *************/
void py_initialize();
@ -294,7 +288,9 @@ bool py_callmethod(py_Ref self, py_Name, int argc, py_Ref argv);
bool py_callmagic(py_Name name, int argc, py_Ref argv);
bool py_str(py_Ref val);
bool py_repr(py_Ref val);
#define py_repr(val) py_callmagic(__repr__, 1, val)
#define py_len(val) py_callmagic(__len__, 1, val)
/// The return value of the most recent call.
py_GlobalRef py_retval();

View File

@ -4,6 +4,7 @@
#include "pocketpy/objects/sourcedata.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/common/strname.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/common/config.h"
#include "pocketpy/common/memorypool.h"
#include <ctype.h>
@ -2348,6 +2349,124 @@ static Error* compile_function(Compiler* self, int decorators) {
return NULL;
}
static Error* compile_decorated(Compiler* self) {
Error* err;
int count = 0;
do {
check(EXPR(self));
count += 1;
if(!match_newlines()) return SyntaxError("expected a newline after '@'");
} while(match(TK_DECORATOR));
if(match(TK_CLASS)) {
// check(compile_class(count));
} else {
consume(TK_DEF);
check(compile_function(self, count));
}
return NULL;
}
// import a [as b]
// import a [as b], c [as d]
static Error* compile_normal_import(Compiler* self) {
do {
consume(TK_ID);
c11_sv name = Token__sv(prev());
int index = Ctx__add_const_string(ctx(), name);
Ctx__emit_(ctx(), OP_IMPORT_PATH, index, prev()->line);
if(match(TK_AS)) {
consume(TK_ID);
name = Token__sv(prev());
}
Ctx__emit_store_name(ctx(), name_scope(self), py_name2(name), prev()->line);
} while(match(TK_COMMA));
consume_end_stmt();
return NULL;
}
// from a import b [as c], d [as e]
// from a.b import c [as d]
// from . import a [as b]
// from .a import b [as c]
// from ..a import b [as c]
// from .a.b import c [as d]
// from xxx import *
static Error* compile_from_import(c11_sbuf* buf, Compiler* self) {
int dots = 0;
while(true) {
switch(curr()->type) {
case TK_DOT: dots += 1; break;
case TK_DOTDOT: dots += 2; break;
case TK_DOTDOTDOT: dots += 3; break;
default: goto __EAT_DOTS_END;
}
advance();
}
__EAT_DOTS_END:
for(int i = 0; i < dots; i++) {
c11_sbuf__write_char(buf, '.');
}
if(dots > 0) {
// @id is optional if dots > 0
if(match(TK_ID)) {
c11_sbuf__write_sv(buf, Token__sv(prev()));
while(match(TK_DOT)) {
consume(TK_ID);
c11_sbuf__write_char(buf, '.');
c11_sbuf__write_sv(buf, Token__sv(prev()));
}
}
} else {
// @id is required if dots == 0
consume(TK_ID);
c11_sbuf__write_sv(buf, Token__sv(prev()));
while(match(TK_DOT)) {
consume(TK_ID);
c11_sbuf__write_char(buf, '.');
c11_sbuf__write_sv(buf, Token__sv(prev()));
}
}
c11_string* path = c11_sbuf__submit(buf);
Ctx__emit_(ctx(),
OP_IMPORT_PATH,
Ctx__add_const_string(ctx(), c11_string__sv(path)),
prev()->line);
consume(TK_IMPORT);
if(match(TK_MUL)) {
if(name_scope(self) != NAME_GLOBAL)
return SyntaxError("from <module> import * can only be used in global scope");
// pop the module and import __all__
Ctx__emit_(ctx(), OP_POP_IMPORT_STAR, BC_NOARG, prev()->line);
consume_end_stmt();
return NULL;
}
do {
Ctx__emit_(ctx(), OP_DUP_TOP, BC_NOARG, BC_KEEPLINE);
consume(TK_ID);
c11_sv name = Token__sv(prev());
Ctx__emit_(ctx(), OP_LOAD_ATTR, py_name2(name), prev()->line);
if(match(TK_AS)) {
consume(TK_ID);
name = Token__sv(prev());
}
Ctx__emit_store_name(ctx(), name_scope(self), py_name2(name), prev()->line);
} while(match(TK_COMMA));
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
consume_end_stmt();
return NULL;
}
static Error* compile_try_except(Compiler* self) {
assert(false);
return NULL;
}
static Error* compile_stmt(Compiler* self) {
Error* err;
if(match(TK_CLASS)) {
@ -2402,11 +2521,18 @@ static Error* compile_stmt(Compiler* self) {
case TK_IF: check(compile_if_stmt(self)); break;
case TK_WHILE: check(compile_while_loop(self)); break;
case TK_FOR: check(compile_for_loop(self)); break;
// case TK_IMPORT: check(compile_normal_import()); break;
// case TK_FROM: check(compile_from_import()); break;
case TK_IMPORT: check(compile_normal_import(self)); break;
case TK_FROM: {
c11_sbuf buf;
c11_sbuf__ctor(&buf);
err = compile_from_import(&buf, self);
c11_sbuf__dtor(&buf);
if(err) return err;
break;
}
case TK_DEF: check(compile_function(self, 0)); break;
// case TK_DECORATOR: check(compile_decorated()); break;
// case TK_TRY: check(compile_try_except()); break;
case TK_DECORATOR: check(compile_decorated(self)); break;
case TK_TRY: check(compile_try_except(self)); break;
case TK_PASS: consume_end_stmt(); break;
/*************************************************/
case TK_ASSERT: {

View File

@ -70,10 +70,16 @@ static bool _py_builtins__exit(int argc, py_Ref argv) {
return false;
}
static bool _py_builtins__len(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
return py_len(argv);
}
py_TValue pk_builtins__register() {
py_Ref builtins = py_newmodule("builtins", NULL);
py_bindnativefunc(builtins, "repr", _py_builtins__repr);
py_bindnativefunc(builtins, "exit", _py_builtins__exit);
py_bindnativefunc(builtins, "len", _py_builtins__len);
return *builtins;
}

View File

@ -145,8 +145,15 @@ static bool _py_str__str__(int argc, py_Ref argv) {
static bool _py_str__repr__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
assert(false);
return false;
c11_sbuf buf;
c11_sbuf__ctor(&buf);
int size;
const char* data = py_tostrn(&argv[0], &size);
c11_sbuf__write_quoted(&buf, (c11_sv){data, size}, '\'');
c11_string* res = c11_sbuf__submit(&buf);
py_newstrn(py_retval(), res->data, res->size);
c11_string__delete(res);
return true;
}
static bool _py_str__iter__(int argc, py_Ref argv) {
@ -294,5 +301,3 @@ bool py_str(py_Ref val) {
if(!tmp) return py_repr(val);
return py_call(tmp, 1, val);
}
bool py_repr(py_Ref val) { return py_callmagic(__repr__, 1, val); }

View File

@ -44,7 +44,6 @@ assert s.replace("foo","ball") == "balltball"
assert s.startswith('f') == True;assert s.endswith('o') == False
assert t.startswith('this') == True;
assert t.split('w') == ['this is string example....', 'o', '!!!']
assert "a,b,c".split(',') == ['a', 'b', 'c']
assert 'a,'.split(',') == ['a']
@ -110,29 +109,9 @@ num = 6
assert str(num) == '6'
# test Lo group names
测试 = "test"
assert 测试 == "test"
assert "Hello, {}!".format("World") == "Hello, World!"
assert "{} {} {}".format("I", "love", "Python") == "I love Python"
assert "{0} {1} {2}".format("I", "love", "Python") == "I love Python"
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
assert "{k}={v}".format(k="key", v="value") == "key=value"
assert "{k}={k}".format(k="key") == "key=key"
assert "{0}={1}".format('{0}', '{1}') == "{0}={1}"
assert "{{{0}}}".format(1) == "{1}"
assert "{0}{1}{1}".format(1, 2, 3) == "122"
try:
"{0}={1}}".format(1, 2)
exit(1)
except ValueError:
pass
assert "{{{}xxx{}x}}".format(1, 2) == "{1xxx2x}"
assert "{{abc}}".format() == "{abc}"
# 3rd slice
a = "Hello, World!"
assert a[::-1] == "!dlroW ,olleH"
@ -152,20 +131,35 @@ assert b[5:2:-2] == [',', 'l']
a = '123'
assert a.rjust(5) == ' 123'
assert a.rjust(5, '0') == '00123'
try:
a.rjust(5, '00')
exit(1)
except TypeError:
pass
assert a.ljust(5) == '123 '
assert a.ljust(5, '0') == '12300'
try:
a.ljust(5, '00')
exit(1)
except TypeError:
pass
assert '\x30\x31\x32' == '012'
assert '\b\b\b' == '\x08\x08\x08'
assert repr('\x1f\x1e\x1f') == '\'\\x1f\\x1e\\x1f\''
assert hex(-42) == '-0x2a'
assert hex(42) == '0x2a'
assert hex(0) == '0x0'
assert hex(1) == '0x1'
assert hex(15) == '0xf'
assert hex(16) == '0x10'
assert hex(255) == '0xff'
assert hex(256) == '0x100'
assert hex(257) == '0x101'
assert hex(17) == '0x11'
a = '123'
assert a.index('2') == 1
assert a.index('1') == 0
assert a.index('3') == 2
assert a.index('2', 1) == 1
assert a.index('1', 0) == 0
assert a.find('1') == 0
assert a.find('1', 1) == -1
a = 'abcd'
assert list(a) == ['a', 'b', 'c', 'd']
@ -184,78 +178,27 @@ assert list(a) == ['b']
a = ''
assert list(a) == ['']
assert '\b\b\b' == '\x08\x08\x08'
# test format()
assert "Hello, {}!".format("World") == "Hello, World!"
assert "{} {} {}".format("I", "love", "Python") == "I love Python"
assert "{0} {1} {2}".format("I", "love", "Python") == "I love Python"
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
assert "{k}={v}".format(k="key", v="value") == "key=value"
assert "{k}={k}".format(k="key") == "key=key"
assert "{0}={1}".format('{0}', '{1}') == "{0}={1}"
assert "{{{0}}}".format(1) == "{1}"
assert "{0}{1}{1}".format(1, 2, 3) == "122"
# try:
# "{0}={1}}".format(1, 2)
# exit(1)
# except ValueError:
# pass
assert "{{{}xxx{}x}}".format(1, 2) == "{1xxx2x}"
assert "{{abc}}".format() == "{abc}"
# test f-string
stack=[1,2,3,4]; assert f"{stack[2:]}" == '[3, 4]'
assert repr('\x1f\x1e\x1f') == '\'\\x1f\\x1e\\x1f\''
assert hex(-42) == '-0x2a'
assert hex(42) == '0x2a'
assert hex(0) == '0x0'
assert hex(1) == '0x1'
assert hex(15) == '0xf'
assert hex(16) == '0x10'
assert hex(255) == '0xff'
assert hex(256) == '0x100'
assert hex(257) == '0x101'
assert hex(17) == '0x11'
import c
assert repr(c.NULL) == '<void* at 0x0>'
assert repr(c.void_p(1)) == '<void* at 0x1>'
assert repr(c.void_p(15)) == '<void* at 0xf>'
assert repr(c.void_p(16)) == '<void* at 0x10>'
assert repr(c.void_p(255)) == '<void* at 0xff>'
assert repr(c.void_p(256)) == '<void* at 0x100>'
assert repr(c.void_p(257)) == '<void* at 0x101>'
assert repr(c.void_p(17)) == '<void* at 0x11>'
# random hex test
import random
def test(__min, __max):
for _ in range(100):
num = random.randint(__min, __max)
hex_num = hex(num)
assert eval(hex_num) == num
if num >= 0:
assert repr(c.void_p(num)) == f'<void* at 0x{hex_num[2:]}>'
test(0, 100)
test(0, 100000)
test(-100, 100)
test(-100000, 100000)
test(-2**30, 2**30)
a = '123'
assert a.index('2') == 1
assert a.index('1') == 0
assert a.index('3') == 2
assert a.index('2', 1) == 1
assert a.index('1', 0) == 0
try:
a.index('1', 1)
exit(1)
except ValueError:
pass
try:
a.index('1', -1)
exit(1)
except ValueError:
pass
assert a.find('1') == 0
assert a.find('1', 1) == -1
try:
a.find('1', -1)
exit(1)
except ValueError:
pass