update amalgamate.py

This commit is contained in:
blueloveTH 2024-08-20 13:04:55 +08:00
parent 86675d7887
commit 71dca71ead
14 changed files with 148 additions and 110 deletions

View File

@ -12,17 +12,17 @@ on:
- 'web/**' - 'web/**'
- '**.md' - '**.md'
jobs: jobs:
# build_win32_amalgamated: build_win32_amalgamated:
# runs-on: windows-latest runs-on: windows-latest
# steps: steps:
# - uses: actions/checkout@v4 - uses: actions/checkout@v4
# - uses: ilammy/msvc-dev-cmd@v1 - uses: ilammy/msvc-dev-cmd@v1
# - name: Compile - name: Compile
# shell: powershell shell: powershell
# run: | run: |
# python amalgamate.py python amalgamate.py
# cd amalgamated cd amalgamated
# cl.exe /std:c++17 /EHsc /utf-8 /Ox /I. /DPK_ENABLE_OS=1 main.cpp /link /out:pkpy.exe cl.exe /std:c11 /utf-8 /Ox /I. /DPK_ENABLE_OS=1 main.c /link /out:pkpy.exe
build_win32: build_win32:
runs-on: windows-latest runs-on: windows-latest
steps: steps:

View File

@ -7,36 +7,51 @@ from typing import List, Dict
assert os.system("python prebuild.py") == 0 assert os.system("python prebuild.py") == 0
with open("include/pocketpy/xmacros/opcodes.h", "rt", encoding='utf-8') as f: ROOT = 'include/pocketpy'
OPCODES_TEXT = '\n' + f.read() + '\n' PUBLIC_HEADERS = ['common/config.h', 'common/export.h', 'pocketpy.h']
COPYRIGHT = '''/*
* Copyright (c) 2024 blueloveTH
* Distributed Under The MIT License
* https://github.com/pocketpy/pocketpy
*/
'''
def read_file(path):
with open(path, 'rt', encoding='utf-8') as f:
return f.read()
def write_file(path, content):
with open(path, 'wt', encoding='utf-8', newline='\n') as f:
f.write(content)
if os.path.exists('amalgamated'):
shutil.rmtree('amalgamated')
time.sleep(0.5)
os.mkdir('amalgamated')
class Header: class Header:
path: str path: str
content: str # header+source (if exists) content: str # header source
dependencies: List[str] dependencies: List[str]
def __init__(self, path: str): def __init__(self, path: str):
self.path = path self.path = path
self.dependencies = [] self.dependencies = []
self.content = read_file(f'{ROOT}/{path}')
# get raw content
with open(f'include/pocketpy/{path}', 'rt', encoding='utf-8') as f:
self.content = f.read()
src_path = path.replace('.hpp', '.cpp').replace('.h', '.cpp')
if os.path.exists(f'src/{src_path}'):
with open(f'src/{src_path}', 'rt', encoding='utf-8') as f:
self.content += f'\n\n/* {src_path} */\n\n'
self.content += f.read()
# process raw content and get dependencies # process raw content and get dependencies
self.content = self.content.replace('#pragma once', '') self.content = self.content.replace('#pragma once', '')
def _replace(m): def _replace(m):
path = m.group(1) path = m.group(1)
if path == 'opcodes.h': if path.startswith('xmacros/'):
return OPCODES_TEXT return read_file(f'{ROOT}/{path}') + '\n'
if path in PUBLIC_HEADERS:
return '' # remove include
if path != self.path: if path != self.path:
self.dependencies.append(path) self.dependencies.append(path)
return '' return '' # remove include
self.content = re.sub( self.content = re.sub(
r'#include\s+"pocketpy/(.+)"\s*', r'#include\s+"pocketpy/(.+)"\s*',
@ -47,27 +62,28 @@ class Header:
def __repr__(self): def __repr__(self):
return f'Header({self.path!r}, dependencies={self.dependencies})' return f'Header({self.path!r}, dependencies={self.dependencies})'
def text(self):
return f'// {self.path}\n{self.content}\n'
headers: Dict[str, Header] = {} headers: Dict[str, Header] = {}
for path in ['pocketpy.hpp', 'pocketpy_c.h']: for entry in os.listdir(ROOT):
headers[path] = Header(path) if os.path.isdir(f'{ROOT}/{entry}'):
if entry == 'xmacros' or entry in PUBLIC_HEADERS:
directories = ['common', 'objects', 'interpreter', 'compiler', 'modules', 'tools'] continue
for directory in directories: files = os.listdir(f'{ROOT}/{entry}')
files = os.listdir(f'include/pocketpy/{directory}')
for file in sorted(files): for file in sorted(files):
assert file.endswith('.h') or file.endswith('.hpp') assert file.endswith('.h')
headers[f'{directory}/{file}'] = Header(f'{directory}/{file}') if entry in PUBLIC_HEADERS:
continue
headers[f'{entry}/{file}'] = Header(f'{entry}/{file}')
text = '''#pragma once def merge_c_files():
c_files = [COPYRIGHT, '\n', '#include "pocketpy.h"', '\n']
/*
* Copyright (c) 2024 blueloveTH
* Distributed Under The MIT License
* https://github.com/pocketpy/pocketpy
*/'''
# merge internal headers
internal_h = []
while True: while True:
for h in headers.values(): for h in headers.values():
if not h.dependencies: if not h.dependencies:
@ -75,40 +91,73 @@ while True:
else: else:
if headers: if headers:
print(headers) print(headers)
raise Exception("Circular dependencies detected") raise RuntimeError("Circular dependencies detected")
break break
print(h.path) # print(h.path)
text += h.content internal_h.append(h.text())
del headers[h.path] del headers[h.path]
for h2 in headers.values(): for h2 in headers.values():
h2.dependencies = [d for d in h2.dependencies if d != h.path] h2.dependencies = [d for d in h2.dependencies if d != h.path]
if os.path.exists("amalgamated"): c_files.extend(internal_h)
shutil.rmtree("amalgamated")
time.sleep(0.5)
os.mkdir("amalgamated")
# use LF line endings instead of CRLF def _replace(m):
with open("amalgamated/pocketpy.h", "wt", encoding='utf-8', newline='\n') as f: path = m.group(1)
f.write(text) if path.startswith('xmacros/'):
return read_file(f'{ROOT}/{path}') + '\n'
return '' # remove include
shutil.copy("src2/main.cpp", "amalgamated/main.cpp") for root, _, files in os.walk('src/'):
with open("amalgamated/main.cpp", "rt", encoding='utf-8') as f: for file in files:
text = f.read() if file.endswith('.c'):
text = text.replace('#include "pocketpy/pocketpy.h"', '#include "pocketpy.h"') path = os.path.join(root, file)
with open("amalgamated/main.cpp", "wt", encoding='utf-8', newline='\n') as f: c_files.append(f'// {path}\n')
f.write(text) content = read_file(path)
content = re.sub(
r'#include\s+"pocketpy/(.+)"\s*',
_replace,
content,
)
c_files.append(content)
c_files.append('\n')
return ''.join(c_files)
def merge_h_files():
h_files = [COPYRIGHT, '#pragma once']
def _replace(m):
path = m.group(1)
if path.startswith('xmacros/'):
return read_file(f'{ROOT}/{path}') + '\n'
return '' # remove include
for path in PUBLIC_HEADERS:
content = read_file(f'{ROOT}/{path}')
content = content.replace('#pragma once', '')
content = re.sub(
r'#include\s+"pocketpy/(.+)"\s*',
_replace,
content,
)
h_files.append(content)
return '\n'.join(h_files)
write_file('amalgamated/pocketpy.c', merge_c_files())
write_file('amalgamated/pocketpy.h', merge_h_files())
shutil.copy("src2/main.c", "amalgamated/main.c")
if sys.platform in ['linux', 'darwin']: if sys.platform in ['linux', 'darwin']:
ok = os.system("clang++ -o main amalgamated/main.cpp -O1 --std=c++17 -frtti -stdlib=libc++") ok = os.system("clang -o main amalgamated/pocketpy.c amalgamated/main.c -O1 --std=c11 -lm")
if ok == 0: if ok == 0:
print("Test build success!") print("Test build success!")
print("amalgamated/pocketpy.h") print("amalgamated/pocketpy.h")
def sync(path): # def sync(path):
shutil.copy("amalgamated/pocketpy.h", os.path.join(path, "pocketpy.h")) # shutil.copy("amalgamated/pocketpy.h", os.path.join(path, "pocketpy.h"))
with open(os.path.join(path, "pocketpy.cpp"), "wt", encoding='utf-8', newline='\n') as f: # with open(os.path.join(path, "pocketpy.cpp"), "wt", encoding='utf-8', newline='\n') as f:
f.write("#include \"pocketpy.h\"\n") # f.write("#include \"pocketpy.h\"\n")
sync("plugins/macos/pocketpy") # sync("plugins/macos/pocketpy")

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "stdio.h" #include <stdio.h>
#include "stdlib.h" #include <stdlib.h>
#define PK_REGION(name) 1 #define PK_REGION(name) 1

View File

@ -4,7 +4,6 @@
#include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/namedict.h" #include "pocketpy/objects/namedict.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/common/config.h"
#include "pocketpy/common/strname.h" #include "pocketpy/common/strname.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"

View File

@ -1,5 +1,4 @@
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/common/config.h"
typedef struct ManagedHeap{ typedef struct ManagedHeap{
c11_vector no_gc; c11_vector no_gc;

View File

@ -1,10 +1,8 @@
#pragma once #pragma once
#include "stdint.h" #include <stdlib.h>
#include "stdbool.h" #include <assert.h>
#include "stdlib.h" #include <string.h>
#include "assert.h"
#include "string.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"

View File

@ -1,5 +1,5 @@
#include "pocketpy/common/memorypool.h" #include "pocketpy/common/memorypool.h"
#include "pocketpy/common/config.h" #include "pocketpy/pocketpy.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>

View File

@ -1,5 +1,4 @@
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/common/config.h"
#include "pocketpy/common/str.h" #include "pocketpy/common/str.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"

View File

@ -5,7 +5,6 @@
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/common/strname.h" #include "pocketpy/common/strname.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/common/config.h"
#include "pocketpy/common/memorypool.h" #include "pocketpy/common/memorypool.h"
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>

View File

@ -1,12 +1,9 @@
#include "pocketpy/common/smallmap.h" #include "pocketpy/common/smallmap.h"
#include "pocketpy/common/config.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/common/vector.h" #include "pocketpy/common/vector.h"
#include "pocketpy/compiler/lexer.h" #include "pocketpy/compiler/lexer.h"
#include "pocketpy/objects/sourcedata.h" #include "pocketpy/objects/sourcedata.h"
#include <ctype.h> #include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#define is_raw_string_used(t) ((t) == TK_ID) #define is_raw_string_used(t) ((t) == TK_ID)
@ -201,7 +198,7 @@ static bool is_possible_number_char(char c) {
} }
/******************************/ /******************************/
static Error* SyntaxError(Lexer* self, const char* fmt, ...) { static Error* LexerError(Lexer* self, const char* fmt, ...) {
Error* err = malloc(sizeof(Error)); Error* err = malloc(sizeof(Error));
err->src = self->src; err->src = self->src;
PK_INCREF(self->src); PK_INCREF(self->src);
@ -219,7 +216,7 @@ static Error* eat_name(Lexer* self) {
while(true) { while(true) {
unsigned char c = *self->curr_char; unsigned char c = *self->curr_char;
int u8bytes = c11__u8_header(c, true); int u8bytes = c11__u8_header(c, true);
if(u8bytes == 0) return SyntaxError(self, "invalid char: %c", c); if(u8bytes == 0) return LexerError(self, "invalid char: %c", c);
if(u8bytes == 1) { if(u8bytes == 1) {
if(isalnum(c) || c == '_') { if(isalnum(c) || c == '_') {
self->curr_char++; self->curr_char++;
@ -251,7 +248,7 @@ static Error* eat_name(Lexer* self) {
} }
int length = (int)(self->curr_char - self->token_start); int length = (int)(self->curr_char - self->token_start);
if(length == 0) return SyntaxError(self, "@id contains invalid char"); if(length == 0) return LexerError(self, "@id contains invalid char");
c11_sv name = {self->token_start, length}; c11_sv name = {self->token_start, length};
const char** KW_BEGIN = TokenSymbols + TK_FALSE; const char** KW_BEGIN = TokenSymbols + TK_FALSE;
@ -289,10 +286,10 @@ static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringTy
// end of string // end of string
break; break;
} }
if(c == '\0') { return SyntaxError(self, "EOL while scanning string literal"); } if(c == '\0') { return LexerError(self, "EOL while scanning string literal"); }
if(c == '\n') { if(c == '\n') {
if(!quote3) if(!quote3)
return SyntaxError(self, "EOL while scanning string literal"); return LexerError(self, "EOL while scanning string literal");
else { else {
c11_sbuf__write_char(buff, c); c11_sbuf__write_char(buff, c);
continue; continue;
@ -311,11 +308,11 @@ static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringTy
char hex[3] = {eatchar(self), eatchar(self), '\0'}; char hex[3] = {eatchar(self), eatchar(self), '\0'};
int code; int code;
if(sscanf(hex, "%x", &code) != 1) { if(sscanf(hex, "%x", &code) != 1) {
return SyntaxError(self, "invalid hex char"); return LexerError(self, "invalid hex char");
} }
c11_sbuf__write_char(buff, (char)code); c11_sbuf__write_char(buff, (char)code);
} break; } break;
default: return SyntaxError(self, "invalid escape char"); default: return LexerError(self, "invalid escape char");
} }
} else { } else {
if(is_fstring) { if(is_fstring) {
@ -343,7 +340,7 @@ static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringTy
} }
if(self->nexts.length == token_count) { if(self->nexts.length == token_count) {
// f'{}' is not allowed // f'{}' is not allowed
return SyntaxError(self, "f-string: empty expression not allowed"); return LexerError(self, "f-string: empty expression not allowed");
} }
} }
} else if(c == '}') { } else if(c == '}') {
@ -351,7 +348,7 @@ static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringTy
// '}}' -> '}' // '}}' -> '}'
c11_sbuf__write_char(buff, '}'); c11_sbuf__write_char(buff, '}');
} else { } else {
return SyntaxError(self, "f-string: single '}' is not allowed"); return LexerError(self, "f-string: single '}' is not allowed");
} }
} else { } else {
c11_sbuf__write_char(buff, c); c11_sbuf__write_char(buff, c);
@ -412,7 +409,7 @@ static Error* eat_number(Lexer* self) {
TokenValue value = {.index = TokenValue_I64}; TokenValue value = {.index = TokenValue_I64};
switch(c11__parse_uint(text, &value._i64, -1)) { switch(c11__parse_uint(text, &value._i64, -1)) {
case IntParsing_SUCCESS: add_token_with_value(self, TK_NUM, value); return NULL; case IntParsing_SUCCESS: add_token_with_value(self, TK_NUM, value); return NULL;
case IntParsing_OVERFLOW: return SyntaxError(self, "int literal is too large"); case IntParsing_OVERFLOW: return LexerError(self, "int literal is too large");
case IntParsing_FAILURE: break; // do nothing case IntParsing_FAILURE: break; // do nothing
} }
} }
@ -434,14 +431,14 @@ static Error* eat_number(Lexer* self) {
return NULL; return NULL;
} }
return SyntaxError(self, "invalid number literal"); return LexerError(self, "invalid number literal");
} }
static Error* eat_fstring_spec(Lexer* self, bool* eof) { static Error* eat_fstring_spec(Lexer* self, bool* eof) {
while(true) { while(true) {
char c = eatchar_include_newline(self); char c = eatchar_include_newline(self);
if(c == '\n' || c == '\0') { if(c == '\n' || c == '\0') {
return SyntaxError(self, "EOL while scanning f-string format spec"); return LexerError(self, "EOL while scanning f-string format spec");
} }
if(c == '}') { if(c == '}') {
add_token(self, TK_FSTR_SPEC); add_token(self, TK_FSTR_SPEC);
@ -491,7 +488,7 @@ static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
// line continuation character // line continuation character
char c = eatchar_include_newline(self); char c = eatchar_include_newline(self);
if(c != '\n') { if(c != '\n') {
return SyntaxError(self, "expected newline after line continuation character"); return LexerError(self, "expected newline after line continuation character");
} }
eat_spaces(self); eat_spaces(self);
return NULL; return NULL;
@ -555,7 +552,7 @@ static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
add_token(self, TK_NE); add_token(self, TK_NE);
return NULL; return NULL;
} else { } else {
return SyntaxError(self, "expected '=' after '!'"); return LexerError(self, "expected '=' after '!'");
} }
case '*': case '*':
if(matchchar(self, '*')) { if(matchchar(self, '*')) {
@ -576,7 +573,7 @@ static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
case '\n': { case '\n': {
add_token(self, TK_EOL); add_token(self, TK_EOL);
if(!eat_indentation(self)) { if(!eat_indentation(self)) {
return SyntaxError(self, "unindent does not match any outer indentation level"); return LexerError(self, "unindent does not match any outer indentation level");
} }
return NULL; return NULL;
} }
@ -597,7 +594,7 @@ static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
} }
} }
if(is_fstring) return SyntaxError(self, "unterminated f-string expression"); if(is_fstring) return LexerError(self, "unterminated f-string expression");
self->token_start = self->curr_char; self->token_start = self->curr_char;
while(self->indents.length > 1) { while(self->indents.length > 1) {

View File

@ -1,4 +1,3 @@
#include "pocketpy/common/config.h"
#include "pocketpy/common/str.h" #include "pocketpy/common/str.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/frame.h"
@ -90,6 +89,7 @@ FrameResult VM__run_top_frame(VM* self) {
#if PK_DEBUG #if PK_DEBUG
pk_print_stack(self, frame, byte); pk_print_stack(self, frame, byte);
// assert(!py_checkexc(true));
#endif #endif
switch((Opcode)byte.op) { switch((Opcode)byte.op) {

View File

@ -1,7 +1,7 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "math.h" #include <math.h>
// https://easings.net/ // https://easings.net/

View File

@ -1,5 +1,3 @@
#include "pocketpy/common/config.h"
#include "pocketpy/common/export.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"

View File

@ -1,6 +1,6 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "time.h" #include <time.h>
#define NANOS_PER_SEC 1000000000 #define NANOS_PER_SEC 1000000000