This commit is contained in:
blueloveTH 2023-10-11 13:05:07 +08:00
parent 23f1c6e941
commit 57beebdfd9
5 changed files with 102 additions and 27 deletions

View File

@ -21,6 +21,11 @@ echo "> Running prebuild.py... "
python3 prebuild.py python3 prebuild.py
if [ $? -ne 0 ]; then
echo "prebuild.py failed."
exit 1
fi
SRC=$(find src/ -name "*.cpp") SRC=$(find src/ -name "*.cpp")
echo "> Compiling and linking source files... " echo "> Compiling and linking source files... "

View File

@ -48,7 +48,6 @@ const std::map<std::string_view, TokenIndex> kTokenKwMap = [](){
return map; return map;
}(); }();
struct Token{ struct Token{
TokenIndex type; TokenIndex type;
const char* start; const char* start;
@ -139,4 +138,6 @@ struct Lexer {
std::vector<Token> run(); std::vector<Token> run();
}; };
bool parse_int(std::string_view text, i64* out, int base=10);
} // namespace pkpy } // namespace pkpy

View File

@ -4,7 +4,8 @@ from datetime import datetime
def generate_python_sources(): def generate_python_sources():
sources = {} sources = {}
for file in os.listdir("python"): for file in os.listdir("python"):
assert file.endswith(".py") if not file.endswith(".py"):
continue
key = file.split(".")[0] key = file.split(".")[0]
with open("python/" + file) as f: with open("python/" + file) as f:
value = f.read() value = f.read()

View File

@ -281,28 +281,35 @@ static bool is_unicode_Lo_char(uint32_t c) {
SyntaxError("binary/hex/octal literal should not contain a dot"); SyntaxError("binary/hex/octal literal should not contain a dot");
} }
try{ int base = 10;
int base = 10; if (m[1].matched) {
char tag = m[1].first.base()[1];
switch (tag) {
case 'x': base = 16; break;
case 'o': base = 8; break;
case 'b': base = 2; break;
default: FATAL_ERROR();
}
}
if (m[2].matched) {
// float point number
f64 out;
size_t size; size_t size;
if (m[1].matched) { try{
if (m[1].str() == "0b") base = 2; out = Number::stof(m[0], &size);
else if (m[1].str() == "0o") base = 8; PK_ASSERT((int)size == (int)m[0].length());
else base = 16; }catch(...){
SyntaxError("invalid number literal");
} }
if (m[2].matched) { add_token(TK("@num"), out);
PK_ASSERT(base == 10); } else {
add_token(TK("@num"), Number::stof(m[0], &size)); std::string_view text(m[0].first.base(), m[0].length());
} else { i64 out;
// If we're base 8/2, chop off the "o" bool ok = parse_int(text, &out, base);
std::string match = m[0].str(); if(!ok){
if (base == 8 || base == 2) match.erase(1, 1); SyntaxError("invalid number literal for base " + std::to_string(base));
add_token(TK("@num"), (i64)std::stoll(match, &size, base));
} }
// HACK: We need to check length-1 for octal since python octals are "0o..." and c/c++ octals are "0..." add_token(TK("@num"), out);
if (base == 8 || base == 2) {PK_ASSERT((int)size == (int)m.length()-1);}
else {PK_ASSERT((int)size == (int)m.length());}
}catch(...){
SyntaxError("invalid number literal");
} }
} }
@ -475,4 +482,68 @@ static bool is_unicode_Lo_char(uint32_t c) {
return std::move(nexts); return std::move(nexts);
} }
bool parse_int(std::string_view text, i64* out, int base){
// TODO: detect overflow
*out = 0;
const auto f_startswith_2 = [](std::string_view t, const char* prefix) -> bool{
if(t.length() < 2) return false;
return t[0] == prefix[0] && t[1] == prefix[1];
};
if(base == 10){
// 10-base 12334
if(text.length() == 0) return false;
for(char c : text){
if(c >= '0' && c <= '9'){
*out = (*out * 10) + (c - '0');
}else{
return false;
}
}
return true;
}else if(base == 2){
// 2-base 0b101010
if(f_startswith_2(text, "0b")) text.remove_prefix(2);
if(text.length() == 0) return false;
for(char c : text){
if(c == '0' || c == '1'){
*out = (*out << 1) | (c - '0');
}else{
return false;
}
}
return true;
}else if(base == 8){
// 8-base 0o123
if(f_startswith_2(text, "0o")) text.remove_prefix(2);
if(text.length() == 0) return false;
for(char c : text){
if(c >= '0' && c <= '7'){
*out = (*out << 3) | (c - '0');
}else{
return false;
}
}
return true;
}else if(base == 16){
// 16-base 0x123
if(f_startswith_2(text, "0x")) text.remove_prefix(2);
if(text.length() == 0) return false;
for(char c : text){
if(c >= '0' && c <= '9'){
*out = (*out << 4) | (c - '0');
}else if(c >= 'a' && c <= 'f'){
*out = (*out << 4) | (c - 'a' + 10);
}else if(c >= 'A' && c <= 'F'){
*out = (*out << 4) | (c - 'A' + 10);
}else{
return false;
}
}
return true;
}
return false;
}
} // namespace pkpy } // namespace pkpy

View File

@ -434,14 +434,11 @@ void init_builtins(VM* _vm) {
int base = 10; int base = 10;
if(args.size() == 1+2) base = CAST(i64, args[2]); if(args.size() == 1+2) base = CAST(i64, args[2]);
const Str& s = CAST(Str&, args[1]); const Str& s = CAST(Str&, args[1]);
try{ i64 val;
size_t parsed = 0; if(!parse_int(s.sv(), &val, base)){
i64 val = std::stoll(s.str(), &parsed, base);
PK_ASSERT(parsed == s.length());
return VAR(val);
}catch(...){
vm->ValueError("invalid literal for int(): " + s.escape()); vm->ValueError("invalid literal for int(): " + s.escape());
} }
return VAR(val);
} }
vm->TypeError("invalid arguments for int()"); vm->TypeError("invalid arguments for int()");
return vm->None; return vm->None;