implement repl

This commit is contained in:
blueloveTH 2024-07-06 00:36:51 +08:00
parent d74ca31f68
commit f93aefdbe5
13 changed files with 143 additions and 178 deletions

View File

@ -1,39 +1,48 @@
#pragma once
// clang-format off
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
//define something for Windows (32-bit and 64-bit, this part is common)
#define PK_EXPORT __declspec(dllexport)
#define PK_SYS_PLATFORM 0
#define PY_SYS_PLATFORM 0
#define PY_SYS_PLATFORM_STRING "win32"
#elif __EMSCRIPTEN__
#define PK_EXPORT
#define PK_SYS_PLATFORM 1
#define PY_SYS_PLATFORM 1
#define PY_SYS_PLATFORM_STRING "emscripten"
#elif __APPLE__
#include <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR
// iOS, tvOS, or watchOS Simulator
#define PK_SYS_PLATFORM 2
#define PY_SYS_PLATFORM 2
#define PY_SYS_PLATFORM_STRING "ios"
#elif TARGET_OS_IPHONE
// iOS, tvOS, or watchOS device
#define PK_SYS_PLATFORM 2
#define PY_SYS_PLATFORM 2
#define PY_SYS_PLATFORM_STRING "ios"
#elif TARGET_OS_MAC
#define PK_SYS_PLATFORM 3
#define PY_SYS_PLATFORM 3
#define PY_SYS_PLATFORM_STRING "darwin"
#else
# error "Unknown Apple platform"
#endif
#define PK_EXPORT __attribute__((visibility("default")))
#elif __ANDROID__
#define PK_EXPORT __attribute__((visibility("default")))
#define PK_SYS_PLATFORM 4
#define PY_SYS_PLATFORM 4
#define PY_SYS_PLATFORM_STRING "android"
#elif __linux__
#define PK_EXPORT __attribute__((visibility("default")))
#define PK_SYS_PLATFORM 5
#define PY_SYS_PLATFORM 5
#define PY_SYS_PLATFORM_STRING "linux"
#else
#define PK_EXPORT
#define PK_SYS_PLATFORM 6
#define PY_SYS_PLATFORM 6
#define PY_SYS_PLATFORM_STRING "unknown"
#endif
#if PK_SYS_PLATFORM == 0 || PK_SYS_PLATFORM == 3 || PK_SYS_PLATFORM == 5
#if PY_SYS_PLATFORM == 0 || PY_SYS_PLATFORM == 3 || PY_SYS_PLATFORM == 5
#define PK_IS_DESKTOP_PLATFORM 1
#else
#define PK_IS_DESKTOP_PLATFORM 0

View File

@ -21,8 +21,6 @@ extern "C" {
// global constants
#define PK_HEX_TABLE "0123456789abcdef"
extern const char* kPlatformStrings[];
#ifdef _MSC_VER
#define c11__unreachedable() __assume(0)
#else

View File

@ -1,6 +1,7 @@
#pragma once
#include <stdbool.h>
#include "pocketpy/pocketpy.h"
#include "pocketpy/common/str.h"
#include "pocketpy/common/vector.h"
@ -8,7 +9,6 @@
extern "C" {
#endif
enum CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, JSON_MODE, CELL_MODE };
struct pk_SourceData {
RefCounted rc;

View File

@ -4,6 +4,9 @@
#include <stdbool.h>
#include <stdarg.h>
#include "pocketpy/common/config.h"
#include "pocketpy/common/export.h"
/************* Public Types *************/
typedef struct py_TValue py_TValue;
typedef uint16_t py_Name;
@ -29,11 +32,19 @@ typedef py_TValue* py_TmpRef;
/// @return true if the function is successful.
typedef bool (*py_CFunction)(int argc, py_StackRef argv);
typedef enum BindType {
enum BindType {
BindType_FUNCTION,
BindType_STATICMETHOD,
BindType_CLASSMETHOD,
} BindType;
};
enum CompileMode {
EXEC_MODE,
EVAL_MODE,
REPL_MODE,
JSON_MODE,
CELL_MODE
};
/************* Global VMs *************/
void py_initialize();
@ -44,6 +55,7 @@ bool py_exec(const char* source);
/// Eval a simple expression.
/// The result will be set to `py_retval()`.
bool py_eval(const char* source);
bool py_exec2(const char* source, const char* filename, enum CompileMode mode);
/************* Values Creation *************/
void py_newint(py_Ref, py_i64);
@ -81,7 +93,7 @@ void py_newfunction(py_Ref out, py_CFunction, const char* sig);
void py_newfunction2(py_Ref out,
py_CFunction,
const char* sig,
BindType bt,
enum BindType bt,
const char* docstring,
const py_Ref upvalue);
// old style argc-based function
@ -118,7 +130,8 @@ bool py_issubclass(py_Type derived, py_Type base);
#define PY_CHECK_ARGC(n) \
if(argc != n) return TypeError("expected %d arguments, got %d", n, argc)
#define PY_CHECK_ARG_TYPE(i, type) if(!py_checktype(py_arg(i), type)) return false
#define PY_CHECK_ARG_TYPE(i, type) \
if(!py_checktype(py_arg(i), type)) return false
#define py_offset(p, i) ((py_Ref)((char*)p + ((i) << 4)))
#define py_arg(i) py_offset(argv, i)
@ -131,12 +144,12 @@ py_TmpRef py_bind(py_Ref obj, const char* sig, py_CFunction f);
py_TmpRef py_bind2(py_Ref obj,
const char* sig,
py_CFunction f,
BindType bt,
enum BindType bt,
const char* docstring,
const py_Ref upvalue);
// old style argc-based bindings
void py_bindmethod(py_Type type, const char* name, py_CFunction f);
void py_bindmethod2(py_Type type, const char* name, py_CFunction f, BindType bt);
void py_bindmethod2(py_Type type, const char* name, py_CFunction f, enum BindType bt);
void py_bindnativefunc(py_Ref obj, const char* name, py_CFunction f);
/// Get the reference to the i-th register.
@ -321,9 +334,10 @@ py_GlobalRef py_tpobject(py_Type type);
const char* py_tpname(py_Type type);
/// Check if the object is an instance of the given type.
/// Re
bool py_checktype(const py_Ref self, py_Type type);
int py_replinput(char* buf);
/// Python favored string formatting.
/// %d: int
/// %i: py_i64 (int64_t)
@ -379,12 +393,13 @@ enum py_PredefinedTypes {
}
#endif
/*
Some notes:
## Macros
1. Function macros are partial functions. They can be used as normal expressions. Use the same naming convention as functions.
2. Snippet macros are `do {...} while(0)` blocks. They cannot be used as expressions. Use `UPPER_CASE` naming convention.
1. Function macros are partial functions. They can be used as normal expressions. Use the same
naming convention as functions.
2. Snippet macros are `do {...} while(0)` blocks. They cannot be used as expressions. Use
`UPPER_CASE` naming convention.
3. Constant macros are used for global constants. Use `UPPER_CASE` or k-prefix naming convention.
*/

View File

@ -1,18 +0,0 @@
#pragma once
#include "pocketpy/interpreter/vm.hpp"
namespace pkpy {
class REPL {
protected:
int need_more_lines = 0;
std::string buffer;
VM* vm;
public:
REPL(VM* vm);
bool input(std::string line);
};
} // namespace pkpy

View File

@ -220,3 +220,38 @@ void pk_sprintf(c11_sbuf* ss, const char* fmt, ...) {
pk_vsprintf(ss, fmt, args);
va_end(args);
}
int py_replinput(char* buf) {
int size = 0;
bool multiline = false;
printf(">>> ");
while(true) {
char c = getchar();
if(c == EOF) break;
if(c == '\n') {
char last = '\0';
if(size > 0) last = buf[size - 1];
if(multiline) {
if(last == '\n'){
break; // 2 consecutive newlines to end multiline input
}else{
printf("... ");
}
} else {
if(last == ':' || last == '(' || last == '[' || last == '{') {
printf("... ");
multiline = true;
} else {
break;
}
}
}
buf[size++] = c;
}
buf[size] = '\0';
return size;
}

View File

@ -1,9 +0,0 @@
const char* kPlatformStrings[] = {
"win32", // 0
"emscripten", // 1
"ios", // 2
"darwin", // 3
"android", // 4
"linux", // 5
"unknown" // 6
};

View File

@ -1454,7 +1454,7 @@ static void Compiler__dtor(Compiler* self) {
#define mode() self->src->mode
#define ctx() (&c11_vector__back(Ctx, &self->contexts))
#define match_newlines() match_newlines_repl(self, NULL)
#define match_newlines() match_newlines_impl(self)
#define consume(expected) \
if(!match(expected)) \
@ -1463,12 +1463,7 @@ static void Compiler__dtor(Compiler* self) {
pk_TokenSymbols[curr()->type]);
#define consume_end_stmt() \
if(!match_end_stmt(self)) return SyntaxError("expected statement end")
#define check_newlines_repl() \
do { \
bool __nml; \
match_newlines_repl(self, &__nml); \
if(__nml) return NeedMoreLines(); \
} while(0)
#define check(B) \
if((err = B)) return err
@ -1480,8 +1475,6 @@ static NameScope name_scope(Compiler* self) {
#define SyntaxError(...) NULL
static Error* NeedMoreLines() { return NULL; }
/* Matchers */
static bool is_expression(Compiler* self, bool allow_slice) {
PrattCallback prefix = rules[curr()->type].prefix;
@ -1490,14 +1483,13 @@ static bool is_expression(Compiler* self, bool allow_slice) {
#define match(expected) (curr()->type == expected ? (++self->i) : 0)
static bool match_newlines_repl(Compiler* self, bool* need_more_lines) {
static bool match_newlines_impl(Compiler* self) {
bool consumed = false;
if(curr()->type == TK_EOL) {
while(curr()->type == TK_EOL)
advance();
consumed = true;
}
if(need_more_lines) { *need_more_lines = (mode() == REPL_MODE && curr()->type == TK_EOF); }
return consumed;
}
@ -1540,11 +1532,11 @@ static Error* EXPR_TUPLE_ALLOW_SLICE(Compiler* self, bool allow_slice) {
// tuple expression // (a, )
int count = 1;
do {
if(curr()->brackets_level) check_newlines_repl();
if(curr()->brackets_level) match_newlines();
if(!is_expression(self, allow_slice)) break;
check(parse_expression(self, PREC_LOWEST + 1, allow_slice));
count += 1;
if(curr()->brackets_level) check_newlines_repl();
if(curr()->brackets_level) match_newlines();
} while(match(TK_COMMA));
// pop `count` expressions from the stack and merge them into a TupleExpr
SequenceExpr* e = TupleExpr__new(prev()->line, count);
@ -1791,9 +1783,9 @@ static Error* exprUnaryOp(Compiler* self) {
static Error* exprGroup(Compiler* self) {
Error* err;
int line = prev()->line;
check_newlines_repl();
match_newlines();
check(EXPR_TUPLE(self)); // () is just for change precedence
check_newlines_repl();
match_newlines();
consume(TK_RPAREN);
if(Ctx__s_top(ctx())->vt->is_tuple) return NULL;
GroupedExpr* g = GroupedExpr__new(line, Ctx__s_popx(ctx()));
@ -1833,7 +1825,7 @@ static Error* consume_comp(Compiler* self, Opcode op0, Opcode op1) {
check(EXPR_VARS(self)); // [expr, vars]
consume(TK_IN);
check(parse_expression(self, PREC_TERNARY + 1, false)); // [expr, vars, iter]
check_newlines_repl();
match_newlines();
if(match(TK_IF)) {
check(parse_expression(self, PREC_TERNARY + 1, false)); // [expr, vars, iter, cond]
has_cond = true;
@ -1844,7 +1836,7 @@ static Error* consume_comp(Compiler* self, Opcode op0, Opcode op1) {
ce->vars = Ctx__s_popx(ctx());
ce->expr = Ctx__s_popx(ctx());
Ctx__s_push(ctx(), (Expr*)ce);
check_newlines_repl();
match_newlines();
return NULL;
}
@ -1853,17 +1845,17 @@ static Error* exprList(Compiler* self) {
int line = prev()->line;
int count = 0;
do {
check_newlines_repl();
match_newlines();
if(curr()->type == TK_RBRACKET) break;
check(EXPR(self));
count += 1;
check_newlines_repl();
match_newlines();
if(count == 1 && match(TK_FOR)) {
check(consume_comp(self, OP_BUILD_LIST, OP_LIST_APPEND));
consume(TK_RBRACKET);
return NULL;
}
check_newlines_repl();
match_newlines();
} while(match(TK_COMMA));
consume(TK_RBRACKET);
SequenceExpr* e = ListExpr__new(line, count);
@ -1880,7 +1872,7 @@ static Error* exprMap(Compiler* self) {
bool parsing_dict = false; // {...} may be dict or set
int count = 0;
do {
check_newlines_repl();
match_newlines();
if(curr()->type == TK_RBRACE) break;
check(EXPR(self)); // [key]
if(curr()->type == TK_COLON) { parsing_dict = true; }
@ -1889,7 +1881,7 @@ static Error* exprMap(Compiler* self) {
check(EXPR(self)); // [key, value]
}
count += 1; // key-value pair count
check_newlines_repl();
match_newlines();
if(count == 1 && match(TK_FOR)) {
if(parsing_dict) {
check(consume_comp(self, OP_BUILD_DICT, OP_DICT_ADD));
@ -1899,7 +1891,7 @@ static Error* exprMap(Compiler* self) {
consume(TK_RBRACE);
return NULL;
}
check_newlines_repl();
match_newlines();
} while(match(TK_COMMA));
consume(TK_RBRACE);
@ -1922,7 +1914,7 @@ static Error* exprCall(Compiler* self) {
CallExpr* e = CallExpr__new(prev()->line, Ctx__s_popx(ctx()));
Ctx__s_push(ctx(), (Expr*)e); // push onto the stack in advance
do {
check_newlines_repl();
match_newlines();
if(curr()->type == TK_RPAREN) break;
if(curr()->type == TK_ID && next()->type == TK_ASSIGN) {
consume(TK_ID);
@ -1948,7 +1940,7 @@ static Error* exprCall(Compiler* self) {
c11_vector__push(Expr*, &e->args, Ctx__s_popx(ctx()));
}
}
check_newlines_repl();
match_newlines();
} while(match(TK_COMMA));
consume(TK_RPAREN);
return NULL;
@ -1998,9 +1990,9 @@ static Error* exprSlice1(Compiler* self) {
static Error* exprSubscr(Compiler* self) {
Error* err;
int line = prev()->line;
check_newlines_repl();
match_newlines();
check(EXPR_TUPLE_ALLOW_SLICE(self, true));
check_newlines_repl();
match_newlines();
consume(TK_RBRACKET); // [lhs, rhs]
SubscrExpr* e = SubscrExpr__new(line);
e->rhs = Ctx__s_popx(ctx()); // [lhs]
@ -2032,9 +2024,7 @@ static Error* compile_block_body(Compiler* self, PrattCallback callback) {
return NULL;
}
bool need_more_lines;
bool consumed = match_newlines_repl(self, &need_more_lines);
if(need_more_lines) return NeedMoreLines();
bool consumed = match_newlines();
if(!consumed) return SyntaxError("expected a new line after ':'");
consume(TK_INDENT);
@ -2100,7 +2090,8 @@ static Error* compile_for_loop(Compiler* self) {
Expr* vars = Ctx__s_popx(ctx());
bool ok = vtemit_store(vars, ctx());
vtdelete(vars);
if(!ok) return SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind
if(!ok)
return SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind
// TODO: ??
// ctx()->try_merge_for_iter_store(for_codei);
@ -2405,14 +2396,14 @@ Error* pk_compile(pk_SourceData_ src, CodeObject* out) {
Error* err = pk_Lexer__process(src, &tokens);
if(err) return err;
Token* data = (Token*)tokens.data;
printf("%s\n", src->filename->data);
for(int i = 0; i < tokens.count; i++) {
Token* t = data + i;
c11_string* tmp = c11_string__new2(t->start, t->length);
printf("[%d] %s: %s\n", t->line, pk_TokenSymbols[t->type], tmp->data);
c11_string__delete(tmp);
}
// Token* data = (Token*)tokens.data;
// printf("%s\n", src->filename->data);
// for(int i = 0; i < tokens.count; i++) {
// Token* t = data + i;
// c11_string* tmp = c11_string__new2(t->start, t->length);
// printf("[%d] %s: %s\n", t->line, pk_TokenSymbols[t->type], tmp->data);
// c11_string__delete(tmp);
// }
Compiler compiler;
Compiler__ctor(&compiler, src, tokens);

View File

@ -201,10 +201,6 @@ static Error* SyntaxError(const char* fmt, ...){
return NULL;
}
static Error* NeedMoreLines(){
return NULL;
}
static Error* eat_name(pk_Lexer* self){
self->curr_char--;
while(true) {
@ -288,9 +284,6 @@ static Error* eat_string_until(pk_Lexer* self, char quote, bool raw, c11_string*
break;
}
if(c == '\0') {
if(quote3 && self->src->mode == REPL_MODE){
return NeedMoreLines();
}
return SyntaxError("EOL while scanning string literal");
}
if(c == '\n') {
@ -431,7 +424,6 @@ static Error* lex_one_token(pk_Lexer* self, bool* eof){
// line continuation character
char c = eatchar_include_newline(self);
if(c != '\n') {
if(self->src->mode == REPL_MODE && c == '\0') return NeedMoreLines();
return SyntaxError("expected newline after line continuation character");
}
eat_spaces(self);

View File

@ -49,7 +49,7 @@ void py_newfunction(py_Ref out, py_CFunction f, const char* sig) {
void py_newfunction2(py_Ref out,
py_CFunction f,
const char* sig,
BindType bt,
enum BindType bt,
const char* docstring,
const py_Ref upvalue) {}
@ -63,7 +63,7 @@ void py_bindmethod(py_Type type, const char *name, py_CFunction f){
py_bindmethod2(type, name, f, BindType_FUNCTION);
}
void py_bindmethod2(py_Type type, const char *name, py_CFunction f, BindType bt){
void py_bindmethod2(py_Type type, const char *name, py_CFunction f, enum BindType bt){
py_TValue tmp;
py_newnativefunc(&tmp, f);
py_setdict(py_tpobject(type), py_name(name), &tmp);

View File

@ -168,7 +168,7 @@ static bool
return false;
}
disassemble(&co);
// disassemble(&co);
Frame* frame = Frame__new(&co, &vm->main, NULL, vm->stack.sp, vm->stack.sp, &co);
pk_VM__push_frame(vm, frame);
@ -184,6 +184,10 @@ bool py_exec(const char* source) { return pk_VM__exec(pk_current_vm, source, "<e
bool py_eval(const char* source) { return pk_VM__exec(pk_current_vm, source, "<eval>", EVAL_MODE); }
bool py_exec2(const char* source, const char* filename, enum CompileMode mode){
return pk_VM__exec(pk_current_vm, source, filename, mode);
}
bool py_call(py_Ref f, int argc, py_Ref argv) { return -1; }
bool py_callmethod(py_Ref self, py_Name name, int argc, py_Ref argv) { return -1; }

View File

@ -1,43 +0,0 @@
#include "pocketpy/tools/repl.hpp"
#include "pocketpy/common/export.h"
namespace pkpy {
REPL::REPL(VM* vm) : vm(vm) {
vm->stdout_write("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
vm->stdout_write(_S("[", sizeof(void*) * 8, " bit] on ", kPlatformStrings[PK_SYS_PLATFORM], "\n"));
vm->stdout_write("https://github.com/pocketpy/pocketpy" "\n");
vm->stdout_write("Type \"exit()\" to exit." "\n");
}
bool REPL::input(std::string line) {
CompileMode mode = REPL_MODE;
if(need_more_lines) {
buffer += line;
buffer += '\n';
int n = buffer.size();
if(n >= need_more_lines) {
for(int i = buffer.size() - need_more_lines; i < buffer.size(); i++) {
// no enough lines
if(buffer[i] != '\n') return true;
}
need_more_lines = 0;
line = buffer;
buffer.clear();
mode = CELL_MODE;
} else {
return true;
}
}
try {
vm->exec(line, "<stdin>", mode);
} catch(NeedMoreLines ne) {
buffer += line;
buffer += '\n';
need_more_lines = ne.is_compiling_class ? 3 : 2;
if(need_more_lines) return true;
}
return false;
}
} // namespace pkpy

View File

@ -19,49 +19,40 @@ char* read_file(const char* path) {
return buffer;
}
static char buf[2048];
int main(int argc, char** argv) {
#if _WIN32
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
#endif
#if 0
py_initialize();
const char* source = "1 < 2";
if(py_eval(source)) {
// handle the result
bool _L0 = py_tobool(py_retval());
printf("%d\n", _L0);
}
py_Ref r0 = py_reg(0);
py_Ref r1 = py_reg(1);
py_newint(r0, 1);
py_newfloat(r1, 2.5);
bool ok = py_binaryadd(r0, r1);
assert(ok);
double res = py_tofloat(py_retval());
printf("%f\n", res);
py_finalize();
return 0;
#endif
if(argc != 2) goto __HELP;
char* source = read_file(argv[1]);
py_initialize();
if(!py_exec(source)) py_printexc();
py_finalize();
free(source);
return 0;
__HELP:
if(argc > 2){
printf("Usage: pocketpy [filename]\n");
return 0;
}
py_initialize();
if(argc == 1){
printf("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
printf("[%d bit] on %s" "\n", (int)(sizeof(void*) * 8), PY_SYS_PLATFORM_STRING);
printf("https://github.com/pocketpy/pocketpy" "\n");
printf("Type \"exit()\" to exit." "\n");
while(true){
int size = py_replinput(buf);
assert(size < sizeof(buf));
if(size >= 0){
if(!py_exec2(buf, "<stdin>", REPL_MODE)) py_printexc();
}
}
}else{
char* source = read_file(argv[1]);
if(!py_exec(source)) py_printexc();
free(source);
}
py_finalize();
return 0;
}