Bladeren bron

update `amalgamate.py`

blueloveTH 1 jaar geleden
bovenliggende
commit
71dca71ead

+ 11 - 11
.github/workflows/main.yml

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

+ 117 - 68
amalgamate.py

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

+ 2 - 2
include/pocketpy/common/utils.h

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

+ 0 - 1
include/pocketpy/interpreter/frame.h

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

+ 0 - 1
include/pocketpy/interpreter/heap.h

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

+ 3 - 5
include/pocketpy/objects/base.h

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

+ 1 - 1
src/common/memorypool.c

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

+ 0 - 1
src/common/sstream.c

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

+ 0 - 1
src/compiler/compiler.c

@@ -5,7 +5,6 @@
 #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 <assert.h>
 #include <ctype.h>

+ 16 - 19
src/compiler/lexer.c

@@ -1,12 +1,9 @@
 #include "pocketpy/common/smallmap.h"
-#include "pocketpy/common/config.h"
 #include "pocketpy/common/sstream.h"
 #include "pocketpy/common/vector.h"
 #include "pocketpy/compiler/lexer.h"
 #include "pocketpy/objects/sourcedata.h"
 #include <ctype.h>
-#include <stdarg.h>
-#include <stdbool.h>
 
 #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));
     err->src = self->src;
     PK_INCREF(self->src);
@@ -219,7 +216,7 @@ static Error* eat_name(Lexer* self) {
     while(true) {
         unsigned char c = *self->curr_char;
         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(isalnum(c) || c == '_') {
                 self->curr_char++;
@@ -251,7 +248,7 @@ static Error* eat_name(Lexer* self) {
     }
 
     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};
 
     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
             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(!quote3)
-                return SyntaxError(self, "EOL while scanning string literal");
+                return LexerError(self, "EOL while scanning string literal");
             else {
                 c11_sbuf__write_char(buff, c);
                 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'};
                     int code;
                     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);
                 } break;
-                default: return SyntaxError(self, "invalid escape char");
+                default: return LexerError(self, "invalid escape char");
             }
         } else {
             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) {
                             // 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 == '}') {
@@ -351,7 +348,7 @@ static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringTy
                         // '}}' -> '}'
                         c11_sbuf__write_char(buff, '}');
                     } else {
-                        return SyntaxError(self, "f-string: single '}' is not allowed");
+                        return LexerError(self, "f-string: single '}' is not allowed");
                     }
                 } else {
                     c11_sbuf__write_char(buff, c);
@@ -412,7 +409,7 @@ static Error* eat_number(Lexer* self) {
         TokenValue value = {.index = TokenValue_I64};
         switch(c11__parse_uint(text, &value._i64, -1)) {
             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
         }
     }
@@ -434,14 +431,14 @@ static Error* eat_number(Lexer* self) {
         return NULL;
     }
 
-    return SyntaxError(self, "invalid number literal");
+    return LexerError(self, "invalid number literal");
 }
 
 static Error* eat_fstring_spec(Lexer* self, bool* eof) {
     while(true) {
         char c = eatchar_include_newline(self);
         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 == '}') {
             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
                 char c = eatchar_include_newline(self);
                 if(c != '\n') {
-                    return SyntaxError(self, "expected newline after line continuation character");
+                    return LexerError(self, "expected newline after line continuation character");
                 }
                 eat_spaces(self);
                 return NULL;
@@ -555,7 +552,7 @@ static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
                     add_token(self, TK_NE);
                     return NULL;
                 } else {
-                    return SyntaxError(self, "expected '=' after '!'");
+                    return LexerError(self, "expected '=' after '!'");
                 }
             case '*':
                 if(matchchar(self, '*')) {
@@ -576,7 +573,7 @@ static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
             case '\n': {
                 add_token(self, TK_EOL);
                 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;
             }
@@ -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;
     while(self->indents.length > 1) {

+ 1 - 1
src/interpreter/ceval.c

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

+ 1 - 1
src/modules/easing.c

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

+ 0 - 2
src/modules/os.c

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

+ 1 - 1
src/modules/time.c

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