diff --git a/src/ceval.h b/src/ceval.h index 20c2fb2e..ebbe49b7 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -80,7 +80,6 @@ __NEXT_STEP:; TARGET(LOAD_FALSE) PUSH(False); DISPATCH(); TARGET(LOAD_INTEGER) PUSH(VAR(byte.arg)); DISPATCH(); TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH(); - TARGET(LOAD_BUILTIN_EVAL) PUSH(builtins->attr(m_eval)); DISPATCH(); TARGET(LOAD_FUNCTION) { FuncDecl_ decl = co->func_decls[byte.arg]; bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator; @@ -547,6 +546,11 @@ __NEXT_STEP:; TARGET(SETUP_DOCSTRING) TOP()->attr().set(__doc__, co_consts[byte.arg]); DISPATCH(); + TARGET(FORMAT_STRING) { + _0 = POPX(); + const Str& spec = CAST(Str&, co_consts[byte.arg]); + PUSH(VAR(format(spec, _0))); + } DISPATCH(); #if !PK_ENABLE_COMPUTED_GOTO #if DEBUG_EXTRA_CHECK default: throw std::runtime_error(fmt(OP_NAMES[byte.op], " is not implemented")); diff --git a/src/expr.h b/src/expr.h index fcc01f58..0e106e24 100644 --- a/src/expr.h +++ b/src/expr.h @@ -536,10 +536,19 @@ struct FStringExpr: Expr{ ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line); size++; } - ctx->emit(OP_LOAD_BUILTIN_EVAL, BC_NOARG, line); - ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); - ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(m[1].str())), line); - ctx->emit(OP_CALL, 1, line); + Str expr = m[1].str(); + int conon = expr.index(":"); + if(conon >= 0){ + ctx->emit( + OP_LOAD_NAME, + StrName(expr.substr(0, conon)).index, + line + ); + Str spec = expr.substr(conon+1); + ctx->emit(OP_FORMAT_STRING, ctx->add_const(VAR(spec)), line); + }else{ + ctx->emit(OP_LOAD_NAME, StrName(expr).index, line); + } size++; i = (int)(m.position() + m.length()); } diff --git a/src/opcodes.h b/src/opcodes.h index 8788624b..477fac86 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -14,7 +14,6 @@ OPCODE(LOAD_TRUE) OPCODE(LOAD_FALSE) OPCODE(LOAD_INTEGER) OPCODE(LOAD_ELLIPSIS) -OPCODE(LOAD_BUILTIN_EVAL) OPCODE(LOAD_FUNCTION) OPCODE(LOAD_NULL) /**************************/ @@ -111,4 +110,5 @@ OPCODE(RAISE) OPCODE(RE_RAISE) /**************************/ OPCODE(SETUP_DOCSTRING) +OPCODE(FORMAT_STRING) #endif \ No newline at end of file diff --git a/src/str.h b/src/str.h index fc2de107..bbac8fb4 100644 --- a/src/str.h +++ b/src/str.h @@ -164,6 +164,10 @@ struct Str{ return ret; } + Str substr(int start) const { + return substr(start, size - start); + } + char* c_str_dup() const { char* p = (char*)malloc(size + 1); memcpy(p, data, size); @@ -396,7 +400,6 @@ const StrName __setattr__ = StrName::get("__setattr__"); const StrName __call__ = StrName::get("__call__"); const StrName __doc__ = StrName::get("__doc__"); -const StrName m_eval = StrName::get("eval"); const StrName m_self = StrName::get("self"); const StrName m_dict = StrName::get("dict"); const StrName m_set = StrName::get("set"); diff --git a/src/vm.h b/src/vm.h index 4fc7bd51..0ed6cbc3 100644 --- a/src/vm.h +++ b/src/vm.h @@ -395,6 +395,7 @@ public: PyObject* getattr(PyObject* obj, StrName name, bool throw_err=true); PyObject* get_unbound_method(PyObject* obj, StrName name, PyObject** self, bool throw_err=true, bool fallback=false); void parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step); + Str format(Str, PyObject*); void setattr(PyObject* obj, StrName name, PyObject* value); template void bind_method(PyObject*, Str, NativeFuncC); @@ -641,6 +642,60 @@ inline PyObject* VM::asRepr(PyObject* obj){ return call_method(obj, __repr__); } +inline Str VM::format(Str spec, PyObject* obj){ + char type = spec.end()[-1]; + int dot = -1; + int width, precision; + char align; + if(spec[0] == '>'){ + align = '>'; + spec = spec.substr(1); + dot = spec.index("."); + }else if(spec[0] == '<'){ + align = '<'; + spec = spec.substr(1); + dot = spec.index("."); + }else{ + if(is_int(obj) || is_float(obj)) align = '>'; + else align = '<'; + } + + try{ + if(dot >= 0){ + width = Number::stoi(spec.substr(0, dot).str()); + precision = Number::stoi(spec.substr(dot+1).str()); + }else{ + width = Number::stoi(spec.str()); + precision = -1; + } + }catch(...){ + ValueError("invalid format specifer"); + } + + if(type != 'f' && dot >= 0) ValueError("precision not allowed in the format specifier"); + Str ret; + if(type == 'f'){ + f64 val = num_to_float(obj); + if(precision < 0) precision = 6; + std::stringstream ss; + ss << std::fixed << std::setprecision(precision) << val; + ret = ss.str(); + }else if(type == 'd'){ + ret = std::to_string(CAST(i64, obj)); + }else if(type == 's'){ + ret = CAST(Str&, obj); + }else{ + ret = CAST(Str&, asStr(obj)); + } + if(width > ret.length()){ + int pad = width - ret.length(); + std::string padding(pad, ' '); + if(align == '>') ret = padding.c_str() + ret; + else ret = ret + padding.c_str(); + } + return ret; +} + inline PyObject* VM::new_module(StrName name) { PyObject* obj = heap._new(tp_module, DummyModule()); obj->attr().set(__name__, VAR(name.sv())); diff --git a/tests/25_rawstring.py b/tests/25_rawstring.py index 98aa4e99..cb2b8dd1 100644 --- a/tests/25_rawstring.py +++ b/tests/25_rawstring.py @@ -17,12 +17,29 @@ asds1321321321测试\测试''' assert s == 'asdasd\nasds1321321321测试\\测试' -assert f'123{2*2}56789' == '123456789' +t = 4 +assert f'123{t}56789' == '123456789' +b = 123 s = f'''->->{s}<-<- -{123} +{b} ''' assert s == '->->asdasd\nasds1321321321测试\\测试<-<-\n123\n' -assert r''' ' ''' == " ' " \ No newline at end of file +assert r''' ' ''' == " ' " + +a = 10 +assert f'{a}' == '10' +assert f'{a:>10}' == ' 10' +assert f'{a:<10}' == '10 ' +assert f'{a:<10.2f}' == '10.00 ' +assert f'{a:>10.2f}' == ' 10.00' +assert f'{a:3d}' == ' 10' +assert f'{a:10d}' == ' 10' +assert f'{a:1d}' == '10' +b = '123' +assert f'{b:10}' == '123 ' +assert f'{b:>10}' == ' 123' +assert f'{b:1}' == '123' +assert f'{b:10s}' == '123 '