Skip to content

Commit 7289206

Browse files
committed
Repo Update
- Setting up repo
1 parent 3e38ff6 commit 7289206

File tree

8 files changed

+1549
-1
lines changed

8 files changed

+1549
-1
lines changed

.github/workflows/changelog.yml

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
name: Update Changelog
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
workflow_dispatch:
9+
10+
jobs:
11+
update-changelog:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout Repository
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
token: ${{ secrets.GITHUB_TOKEN }}
20+
21+
- name: Setup Python
22+
uses: actions/setup-python@v4
23+
with:
24+
python-version: '3.x'
25+
26+
- name: Generate Changelog
27+
run: |
28+
python3 << 'EOF'
29+
import subprocess
30+
import sys
31+
from datetime import datetime
32+
from collections import defaultdict
33+
34+
def run_git_log(args):
35+
try:
36+
result = subprocess.run(['git'] + args, capture_output=True, text=True, check=True)
37+
# FIX #1: Split by a null character instead of a newline to handle multi-line bodies.
38+
return result.stdout.strip().split('\x00') if result.stdout.strip() else []
39+
except subprocess.CalledProcessError as e:
40+
print(f"Error running git: {e}")
41+
sys.exit(1)
42+
43+
def get_commits_by_tag():
44+
try:
45+
latest_tag = subprocess.run(
46+
['git', 'describe', '--tags', '--abbrev=0'],
47+
capture_output=True, text=True, check=True
48+
).stdout.strip()
49+
50+
tag_date = subprocess.run(
51+
['git', 'log', '-1', '--format=%ad', '--date=short', latest_tag],
52+
capture_output=True, text=True, check=True
53+
).stdout.strip()
54+
55+
tagged = run_git_log([
56+
'log', latest_tag,
57+
# FIX #2: Add the %x00 null character as a safe delimiter for commits.
58+
'--pretty=format:%s|||%b|||%an|||%ad|||%H%x00',
59+
'--date=short',
60+
'--invert-grep',
61+
'--grep=docs: update changelog',
62+
'--grep=changelog.yml',
63+
'--grep=\\[skip ci\\]'
64+
])
65+
66+
unreleased = run_git_log([
67+
'log', f'{latest_tag}..HEAD',
68+
# FIX #3: Add the delimiter here as well.
69+
'--pretty=format:%s|||%b|||%an|||%ad|||%H%x00',
70+
'--date=short',
71+
'--invert-grep',
72+
'--grep=docs: update changelog',
73+
'--grep=changelog.yml',
74+
'--grep=\\[skip ci\\]'
75+
])
76+
77+
return {
78+
'tagged': {latest_tag: {'commits': tagged, 'date': tag_date}},
79+
'unreleased': unreleased
80+
}
81+
82+
except subprocess.CalledProcessError:
83+
all_commits = run_git_log([
84+
'log',
85+
# FIX #4: And add the delimiter here for the fallback case.
86+
'--pretty=format:%s|||%b|||%an|||%ad|||%H%x00',
87+
'--date=short',
88+
'--invert-grep',
89+
'--grep=docs: update changelog',
90+
'--grep=changelog.yml',
91+
'--grep=\\[skip ci\\]'
92+
])
93+
return {
94+
'tagged': {},
95+
'unreleased': all_commits
96+
}
97+
98+
def categorize_commit(subject, body):
99+
text = (subject + ' ' + body).lower()
100+
if any(x in text for x in ['security', 'vulnerability', 'cve', 'exploit']):
101+
return 'security'
102+
if any(x in text for x in ['breaking change', 'breaking:', 'break:']):
103+
return 'breaking'
104+
if any(x in text for x in ['deprecat', 'obsolete', 'phase out']):
105+
return 'deprecated'
106+
if any(x in text for x in ['remove', 'delete', 'drop', 'eliminate']):
107+
return 'removed'
108+
# Correction: Check for 'added' keywords before 'fixed' keywords.
109+
if any(x in text for x in ['add', 'new', 'create', 'implement', 'feat', 'feature']):
110+
return 'added'
111+
if any(x in text for x in ['fix', 'resolve', 'correct', 'patch', 'bug', 'issue']):
112+
return 'fixed'
113+
return 'changed'
114+
115+
def format_commit_entry(commit):
116+
entry = f"- **{commit['subject']}** ({commit['date']} – {commit['author']})"
117+
body = commit['body'].replace('\\n', '\n')
118+
if body.strip():
119+
lines = [line.strip() for line in body.splitlines() if line.strip()]
120+
for line in lines:
121+
entry += f"\n {line}"
122+
return entry + "\n"
123+
124+
def parse_commits(lines):
125+
commits = []
126+
for line in lines:
127+
if not line: continue
128+
parts = line.split('|||')
129+
if len(parts) >= 5:
130+
subject, body, author, date, hash_id = map(str.strip, parts)
131+
commits.append({
132+
'subject': subject,
133+
'body': body,
134+
'author': author,
135+
'date': date,
136+
'hash': hash_id[:7]
137+
})
138+
return commits
139+
140+
def build_changelog(commits_by_version):
141+
sections = [
142+
('security', 'Security'),
143+
('breaking', 'Breaking Changes'),
144+
('deprecated', 'Deprecated'),
145+
('added', 'Added'),
146+
('changed', 'Changed'),
147+
('fixed', 'Fixed'),
148+
('removed', 'Removed')
149+
]
150+
151+
lines = [
152+
"# Changelog",
153+
"",
154+
"All notable changes to this project will be documented in this file.",
155+
"",
156+
"The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),",
157+
"and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).",
158+
""
159+
]
160+
161+
unreleased = parse_commits(commits_by_version['unreleased'])
162+
if unreleased:
163+
lines.append("## [Unreleased]")
164+
lines.append("")
165+
categorized = defaultdict(list)
166+
for commit in unreleased:
167+
cat = categorize_commit(commit['subject'], commit['body'])
168+
categorized[cat].append(commit)
169+
170+
for key, label in sections:
171+
if categorized[key]:
172+
lines.append(f"### {label}")
173+
for commit in categorized[key]:
174+
lines.append(format_commit_entry(commit))
175+
lines.append("")
176+
else:
177+
lines.append("## [Unreleased]\n")
178+
lines.append("_No unreleased changes._\n")
179+
180+
for tag, info in commits_by_version['tagged'].items():
181+
commits = parse_commits(info['commits'])
182+
lines.append(f"## [{tag}] - {info['date']}\n")
183+
categorized = defaultdict(list)
184+
for commit in commits:
185+
cat = categorize_commit(commit['subject'], commit['body'])
186+
categorized[cat].append(commit)
187+
188+
for key, label in sections:
189+
if categorized[key]:
190+
lines.append(f"### {label}")
191+
for commit in categorized[key]:
192+
lines.append(format_commit_entry(commit))
193+
lines.append("")
194+
195+
return "\n".join(lines)
196+
197+
try:
198+
commit_data = get_commits_by_tag()
199+
changelog = build_changelog(commit_data)
200+
with open("CHANGELOG.md", "w", encoding="utf-8") as f:
201+
f.write(changelog)
202+
print("Changelog generated.")
203+
except Exception as e:
204+
print(f"Error generating changelog: {e}")
205+
sys.exit(1)
206+
EOF
207+
208+
- name: Commit Updated Changelog
209+
run: |
210+
git config --local user.email "[email protected]"
211+
git config --local user.name "GitHub Action"
212+
git add CHANGELOG.md
213+
if git diff --staged --quiet; then
214+
echo "No changes to commit"
215+
else
216+
git commit -m "docs: update changelog [skip ci]"
217+
git push
218+
fi

README.md

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,116 @@
1+
![TinyPascal](media/tinypascal.png)
2+
[![Chat on Discord](https://img.shields.io/discord/754884471324672040?style=for-the-badge)](https://discord.gg/tPWjMwK)
3+
[![Follow on Bluesky](https://img.shields.io/badge/Bluesky-tinyBigGAMES-blue?style=for-the-badge&logo=bluesky)](https://bsky.app/profile/tinybiggames.com)
4+
5+
> 🚧 **This repository is currently under construction.**
6+
>
7+
> TinyPascal is actively being developed and rapidly evolving. Some features mentioned in this documentation may not yet be fully implemented, and both APIs and internal structure are subject to change as we continue to improve and expand the library.
8+
>
9+
> Your contributions, feedback, and issue reports are highly valued and will help shape TinyPascal into the ultimate 2D game framework for Delphi!
10+
111
# TinyPascal
2-
TinyPascal is a lightweight, embeddable Win64 Pascal compiler.
12+
13+
**TinyPascal** is a lightweight, embeddable Pascal compiler designed to compile Pascal source code directly into Win64 PE format in memory, with immediate execution support. It is built in Delphi, follows a modular architecture, and uses a case-sensitive, Unicode-aware variant of Pascal with UTF-8 strings and clean interop with the Windows API and C ABI.
14+
15+
TinyPascal generates x64 machine code without external dependencies, making it ideal for embedding and runtime code generation. It supports writing to memory and will also support .exe, .dll, and .lib output using the same backend.
16+
17+
Inspired by the design philosophy of TinyCC, TinyPascal is written in Delphi and designed to be small, fast, and modular. It generates x64 machine code directly, with no external dependencies, making it ideal for embedding in other applications or for use in runtime code generation scenarios.
18+
19+
## ✅ Key Features
20+
21+
- **Memory-first Compilation**
22+
Compiles Pascal source directly to memory as a valid Win64 PE executable.
23+
24+
- **Direct Execution**
25+
Supports executing compiled code in memory without writing to disk.
26+
27+
- **Delphi-Compatible Core**
28+
Uses a modern, case-sensitive Pascal syntax with strong compatibility for Delphi features.
29+
30+
- **Unicode by Design**
31+
All strings are UTF-8 internally, with runtime UTF-8 → UTF-16 conversion for Windows API compatibility.
32+
33+
- **C ABI Interop**
34+
Clean interoperation with C libraries via well-defined basic types.
35+
36+
- **Self-contained Runtime**
37+
Built-in x64 codegen and PE generation with no external toolchain required.
38+
39+
## 🔧 Type System
40+
41+
TinyPascal supports the following core types:
42+
43+
- `Int` → signed 64-bit integer
44+
- `UInt` → unsigned 64-bit integer
45+
- `Float` → IEEE 754 double
46+
- `String` → UTF-8 encoded, ref-counted string
47+
- `PString` → null-terminated `PUTF8Char` for C interop
48+
49+
## 🧱 Modular Architecture
50+
51+
TinyPascal is built using a clean modular design. Units follow a consistent naming convention:
52+
53+
- `TinyPascal.Lexer` — Lexical analyzer
54+
- `TinyPascal.Parser` — Syntax parsing and AST generation
55+
- `TinyPascal.CodeGen` — x64 code generation
56+
- `TinyPascal.PE` — Win64 PE format emitter
57+
- `TinyPascal.Runtime` — Core runtime helpers (e.g., UTF-8 to UTF-16 conversion)
58+
- `TinyPascal.Compiler` — Entry point and orchestration
59+
60+
## 📜 Example
61+
62+
```pascal
63+
program HelloWorld;
64+
begin
65+
WriteLn('Hello, World! 🚀');
66+
end.
67+
```
68+
69+
70+
## 💬 Support & Resources
71+
72+
- 🐞 **Report Issues:** [GitHub Issue Tracker](https://github.com/tinyBigGAMES/TinyPascal/issues)
73+
- 💬 **Join the Community:** [Forum](https://github.com/tinyBigGAMES/TinyPascal/discussions) | [Discord](https://discord.gg/tPWjMwK)
74+
- 📚 **Learn Delphi:** [Learn Delphi](https://learndelphi.org)
75+
76+
## 🤝 Contributing
77+
78+
We welcome contributions to **TinyPascal**! 🚀
79+
80+
### 💡 Ways to Contribute:
81+
- 🐛 **Report Bugs** – Help improve `TinyPascal` by submitting issues.
82+
-**Suggest Features** – Share ideas to enhance its functionality.
83+
- 🔧 **Submit Pull Requests** – Improve the codebase and add features.
84+
85+
### 🏆 Contributors
86+
87+
<a href="https://github.com/tinyBigGAMES/TinyPascal/graphs/contributors">
88+
<img src="https://contrib.rocks/image?repo=tinyBigGAMES/TinyPascal&max=500&columns=20&anon=1" />
89+
</a>
90+
91+
## 📜 License
92+
93+
**TinyPascal** is distributed under the **BSD-3-Clause License**, allowing redistribution and modification in both source and binary forms.
94+
See the [LICENSE](https://github.com/tinyBigGAMES/TinyPascal?tab=BSD-3-Clause-1-ov-file#BSD-3-Clause-1-ov-file) for details.
95+
96+
## 💖 Support & Sponsorship
97+
98+
Your support keeps **TinyPascal** evolving! If you find this library useful, please consider [sponsoring the project](https://github.com/sponsors/tinyBigGAMES). Every contribution helps drive future enhancements and innovations.
99+
100+
### Other ways to support:
101+
-**Star the repo** – Show your appreciation.
102+
- 📢 **Share with your network** – Spread the word.
103+
- 🐛 **Report bugs** – Help improve `TinyPascal`.
104+
- 🔧 **Submit fixes** – Contribute by fixing issues.
105+
- 💡 **Suggest features** – Help shape its future.
106+
107+
🚀 Every contribution makes a difference – thank you for being part of the journey!
108+
109+
---
110+
111+
**TinyPascal** — Pascal reborn for systems ⚙️, memory 🧠, and modern interop 🔗.
112+
113+
<p align="center">
114+
<img src="media/delphi.png" alt="Delphi">
115+
</p>
116+
<h5 align="center">Made with ❤️ in Delphi</h5>

examples/testbed/Testbed.dpr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
program Testbed;
2+
3+
{$APPTYPE CONSOLE}
4+
5+
{$R *.res}
6+
7+
uses
8+
System.SysUtils;
9+
10+
begin
11+
try
12+
{ TODO -oUser -cConsole Main : Insert code here }
13+
except
14+
on E: Exception do
15+
Writeln(E.ClassName, ': ', E.Message);
16+
end;
17+
end.

0 commit comments

Comments
 (0)