Compare commits
110 Commits
prerelease
...
prerelease
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d3e79b731 | ||
|
|
b3ee64d294 | ||
|
|
e6ec0fa38a | ||
|
|
c8394228b3 | ||
| 70ba81bebc | |||
|
|
50ff9fbefc | ||
|
|
042c278b8d | ||
|
|
81efaaac07 | ||
| 3b0ec79955 | |||
|
|
4be8e8e32f | ||
|
|
5846adf025 | ||
|
|
daa8056b7a | ||
|
|
1a5abd9543 | ||
|
|
774890de1d | ||
|
|
fc7cfc7cfb | ||
|
|
d46a6dc209 | ||
|
|
23c4a7ebd1 | ||
|
|
57728af0b6 | ||
|
|
df040adf45 | ||
|
|
c2e0cdc6d6 | ||
|
|
571efe46b7 | ||
|
|
67569bffc2 | ||
|
|
f5ee0f6fc8 | ||
|
|
fd5b237dfe | ||
|
|
322716af0c | ||
|
|
c67b37d8b2 | ||
|
|
b9c0503d54 | ||
|
|
19268f3070 | ||
|
|
4f91bf48f3 | ||
|
|
4fc28d3b76 | ||
| c01dee80b0 | |||
| 0f0a3f5d31 | |||
|
|
f598c215e7 | ||
|
|
fff4f6bcb5 | ||
|
|
c322d5680f | ||
|
|
db650d8ccf | ||
|
|
6ad0b2c02e | ||
|
|
624a54c90c | ||
|
|
c856e7d654 | ||
|
|
a96023ced1 | ||
|
|
1908d9bbbb | ||
|
|
47db2ca27d | ||
|
|
2e7b3b4baa | ||
|
|
24163e3389 | ||
|
|
0c0832d131 | ||
|
|
1742a0c52d | ||
|
|
224039ba43 | ||
|
|
5e7ce495e4 | ||
|
|
35a0f35cf8 | ||
|
|
f9490ceac0 | ||
|
|
4cda311008 | ||
|
|
b3aa653076 | ||
|
|
fc3321bcf0 | ||
|
|
868b3bfc3d | ||
|
|
f5e241aba0 | ||
|
|
8c3ee3fe05 | ||
|
|
eb285b6e8f | ||
|
|
78a1edd572 | ||
|
|
b905026010 | ||
|
|
677afd9433 | ||
|
|
757da3f973 | ||
|
|
5a86510c3b | ||
|
|
fb8b6a89ae | ||
|
|
6ddf9953e7 | ||
|
|
1609227a42 | ||
|
|
51f6a88ce8 | ||
|
|
f420273471 | ||
|
|
4b2a747338 | ||
|
|
5277814af0 | ||
|
|
8928ab2d99 | ||
|
|
d08b307c6e | ||
|
|
6474329afc | ||
|
|
c49e67c839 | ||
|
|
25cb96e473 | ||
|
|
f11890a8b3 | ||
|
|
59b1d222c2 | ||
|
|
2fba132016 | ||
|
|
bddc2cdc79 | ||
|
|
e1b80b42d9 | ||
|
|
dab86925b4 | ||
|
|
0f45052dce | ||
|
|
68f4207216 | ||
|
|
f9f8ca08c6 | ||
|
|
0666b02c13 | ||
|
|
1654507835 | ||
|
|
d054ece8e2 | ||
|
|
4937942d6e | ||
|
|
82ea92183f | ||
|
|
5fb15b476f | ||
|
|
d1a455dbbe | ||
|
|
a81640747d | ||
|
|
4a1ed23f96 | ||
|
|
eb36d02fcb | ||
|
|
7b3a1e1835 | ||
|
|
8e53579682 | ||
|
|
0d8f262185 | ||
|
|
fc6f41d89b | ||
|
|
6a7ce72eb7 | ||
|
|
f84e5429a5 | ||
|
|
f4c7294267 | ||
|
|
be4b04a286 | ||
|
|
9d11122e93 | ||
|
|
241e447da0 | ||
|
|
dd750ab164 | ||
|
|
922b129250 | ||
|
|
c2df9c0e83 | ||
|
|
923503493e | ||
|
|
686cb08f11 | ||
|
|
1cfb4acda6 | ||
|
|
436f30330b |
304
.github/workflows/release.yml
vendored
304
.github/workflows/release.yml
vendored
@@ -1,133 +1,231 @@
|
||||
# SPDX-FileCopyrightText: 2025 William Bell
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*' # Trigger on any tag push
|
||||
- '*' # Any tag
|
||||
|
||||
jobs:
|
||||
build_and_release:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
build-linux-x86_64:
|
||||
runs-on: ubuntu-latest # build both architectures
|
||||
steps:
|
||||
# Linux
|
||||
- name: Install build tools (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install -y flex bison
|
||||
|
||||
# macOS
|
||||
- name: Install build tools (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install flex bison
|
||||
|
||||
# Windows
|
||||
- name: Install build tools (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: choco install winflexbison -y
|
||||
shell: pwsh
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Python (needed for Conan)
|
||||
- name: Install build tools
|
||||
run: sudo apt-get update && sudo apt-get install -y flex bison
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Conan (Linux/macOS)
|
||||
if: runner.os != 'Windows'
|
||||
- name: Install Conan
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install conan
|
||||
shell: bash
|
||||
conan profile detect
|
||||
|
||||
- name: Install Conan (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install conan
|
||||
shell: pwsh
|
||||
|
||||
- name: Configure Conan (Linux/macOS)
|
||||
if: runner.os != 'Windows'
|
||||
run: conan profile detect
|
||||
shell: bash
|
||||
|
||||
- name: Configure Conan (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: conan profile detect
|
||||
shell: pwsh
|
||||
|
||||
- name: Install dependencies and build (Linux/macOS)
|
||||
if: runner.os != 'Windows'
|
||||
- name: Build
|
||||
run: |
|
||||
conan install . --build=missing
|
||||
conan build .
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies and build (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
conan install . --build=missing
|
||||
conan build .
|
||||
shell: pwsh
|
||||
|
||||
- name: Determine if prerelease (Linux/macOS)
|
||||
if: runner.os != 'Windows'
|
||||
id: prerelease_check_unix
|
||||
run: |
|
||||
TAG=${GITHUB_REF##refs/tags/}
|
||||
if [[ "$TAG" == prerelease-* ]]; then
|
||||
echo "true" > prerelease.txt
|
||||
else
|
||||
echo "false" > prerelease.txt
|
||||
fi
|
||||
echo "prerelease=$(cat prerelease.txt)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Determine if prerelease (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
id: prerelease_check_win
|
||||
run: |
|
||||
$TAG = $env:GITHUB_REF -replace 'refs/tags/', ''
|
||||
if ($TAG -like 'prerelease-*') {
|
||||
echo "true" > prerelease.txt
|
||||
} else {
|
||||
echo "false" > prerelease.txt
|
||||
}
|
||||
echo "prerelease=$(Get-Content prerelease.txt)" >> $env:GITHUB_OUTPUT
|
||||
shell: pwsh
|
||||
|
||||
- name: Archive build folder (Linux/macOS)
|
||||
if: runner.os != 'Windows'
|
||||
- name: Package
|
||||
run: |
|
||||
TAG=${GITHUB_REF##refs/tags/}
|
||||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH=$(uname -m)
|
||||
FOLDER_NAME="chloride-$TAG-$OS-$ARCH"
|
||||
TAR_NAME="$FOLDER_NAME.tar.gz"
|
||||
mv build/bin "$FOLDER_NAME"
|
||||
cp LICENSE "$FOLDER_NAME"
|
||||
tar -czf "$TAR_NAME" "$FOLDER_NAME"
|
||||
echo "TAR_NAME=$TAR_NAME" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
FOLDER="chloride-$TAG-$OS-$ARCH"
|
||||
TAR="$FOLDER.tar.gz"
|
||||
mv build/bin "$FOLDER"
|
||||
cp LICENSE.txt "$FOLDER"
|
||||
tar -czf "$TAR" "$FOLDER"
|
||||
echo "TAR_NAME=$TAR" >> $GITHUB_ENV
|
||||
|
||||
- name: Archive build folder (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-x86_64-artifact
|
||||
path: ${{ env.TAR_NAME }}
|
||||
build-linux-arm64:
|
||||
runs-on: ubuntu-22.04-arm # build both architectures
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install build tools
|
||||
run: sudo apt-get update && sudo apt-get install -y flex bison
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Conan
|
||||
run: |
|
||||
$TAG = $env:GITHUB_REF -replace 'refs/tags/', ''
|
||||
$ARCH = if ([Environment]::Is64BitOperatingSystem) { 'x64' } else { 'x86' }
|
||||
$FOLDER_NAME = "chloride-$TAG-windows-$ARCH"
|
||||
$TAR_NAME = "$FOLDER_NAME.zip"
|
||||
Rename-Item build\bin $FOLDER_NAME
|
||||
Copy-Item LICENSE $FOLDER_NAME
|
||||
Compress-Archive -Path $FOLDER_NAME -DestinationPath $TAR_NAME
|
||||
echo "TAR_NAME=$TAR_NAME" >> $env:GITHUB_ENV
|
||||
shell: pwsh
|
||||
python -m pip install --upgrade pip
|
||||
pip install conan
|
||||
conan profile detect
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
conan install . --build=missing
|
||||
conan build .
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
TAG=${GITHUB_REF##refs/tags/}
|
||||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH=$(uname -m)
|
||||
FOLDER="chloride-$TAG-$OS-$ARCH"
|
||||
TAR="$FOLDER.tar.gz"
|
||||
mv build/bin "$FOLDER"
|
||||
cp LICENSE.txt "$FOLDER"
|
||||
tar -czf "$TAR" "$FOLDER"
|
||||
echo "TAR_NAME=$TAR" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-arm64-artifact
|
||||
path: ${{ env.TAR_NAME }}
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64] # build both architectures
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install build tools
|
||||
run: brew install flex bison
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Conan
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install conan
|
||||
if [ "${{ matrix.arch }}" = "x86_64" ] && [ "$(uname -m)" = "arm64" ]; then
|
||||
arch -x86_64 conan profile detect
|
||||
else
|
||||
conan profile detect
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
BUILD_DIR="build_${{ matrix.arch }}"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
# Use Rosetta for x86_64 builds on Apple Silicon
|
||||
if [ "${{ matrix.arch }}" = "x86_64" ] && [ "$(uname -m)" = "arm64" ]; then
|
||||
arch -x86_64 bash -c "
|
||||
export CMAKE_OSX_ARCHITECTURES=x86_64
|
||||
conan install .. --build=missing
|
||||
conan build ..
|
||||
"
|
||||
else
|
||||
export CMAKE_OSX_ARCHITECTURES="${{ matrix.arch }}"
|
||||
conan install .. --build=missing
|
||||
conan build ..
|
||||
fi
|
||||
|
||||
cd ..
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
TAG=${GITHUB_REF##refs/tags/}
|
||||
OS="macos"
|
||||
ARCH="${{ matrix.arch }}"
|
||||
FOLDER="chloride-$TAG-$OS-$ARCH"
|
||||
TAR="$FOLDER.tar.gz"
|
||||
mv build/bin "$FOLDER"
|
||||
cp LICENSE.txt "$FOLDER"
|
||||
tar -czf "$TAR" "$FOLDER"
|
||||
echo "TAR_NAME=$TAR" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-${{ matrix.arch }}-artifact
|
||||
path: ${{ env.TAR_NAME }}
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
update: true
|
||||
install: >
|
||||
base-devel
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-make
|
||||
mingw-w64-x86_64-cmake
|
||||
mingw-w64-x86_64-python
|
||||
mingw-w64-x86_64-python-pip
|
||||
msys/flex
|
||||
|
||||
- name: Install Conan
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install conan
|
||||
conan profile detect
|
||||
|
||||
- name: Build Project
|
||||
run: |
|
||||
conan install . --profile windows-profile.txt --build=missing
|
||||
conan build . --profile windows-profile.txt
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
TAG=${GITHUB_REF##refs/tags/}
|
||||
OS="windows"
|
||||
ARCH=$(uname -m)
|
||||
FOLDER="chloride-$TAG-$OS-$ARCH"
|
||||
TAR="$FOLDER.tar.gz"
|
||||
mv build/bin "$FOLDER"
|
||||
cp LICENSE.txt "$FOLDER"
|
||||
tar -czf "$TAR" "$FOLDER"
|
||||
echo "TAR_NAME=$TAR" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-artifact
|
||||
path: ${{ env.TAR_NAME }}
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-linux-x86_64, build-linux-arm64, build-macos, build-windows]
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./artifacts
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: ncipollo/release-action@v1
|
||||
@@ -136,5 +234,5 @@ jobs:
|
||||
name: Release ${{ github.ref_name }}
|
||||
body: Automated release based on tag ${{ github.ref_name }}
|
||||
draft: false
|
||||
prerelease: ${{ runner.os == 'Windows' && steps.prerelease_check_win.outputs.prerelease || steps.prerelease_check_unix.outputs.prerelease }}
|
||||
artifacts: ${{ env.TAR_NAME }}
|
||||
prerelease: ${{ startsWith(github.ref_name, 'prerelease-') }}
|
||||
artifacts: "./artifacts/*"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -64,5 +64,6 @@ build
|
||||
*.yy.c
|
||||
*.yy.h
|
||||
|
||||
__isotope__
|
||||
__arcache__
|
||||
argon_modules
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -11,3 +11,6 @@
|
||||
[submodule "external/libdye"]
|
||||
path = external/libdye
|
||||
url = https://github.com/Ugric/libdye.git
|
||||
[submodule "external/linenoise"]
|
||||
path = external/linenoise
|
||||
url = https://github.com/antirez/linenoise
|
||||
|
||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -9,7 +9,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bin/argon",
|
||||
"args": ["testing.ar"],
|
||||
"args": [],
|
||||
"stopAtEntry": true,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
|
||||
@@ -33,8 +33,16 @@ add_custom_command(
|
||||
# Step 2: Custom target for lexer
|
||||
add_custom_target(GenerateLexer DEPENDS ${LEXER_C} ${LEXER_H})
|
||||
|
||||
set(SOURCES
|
||||
external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c ${CFILES} ${LEXER_C}
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
list(APPEND SOURCES external/linenoise/linenoise.c)
|
||||
endif()
|
||||
|
||||
# Step 3: Add executable
|
||||
add_executable(argon external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c ${CFILES} ${LEXER_C})
|
||||
add_executable(argon ${SOURCES})
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
target_include_directories(argon PRIVATE ${CMAKE_SOURCE_DIR}/external/cwalk/include)
|
||||
target_include_directories(argon PRIVATE ${CMAKE_SOURCE_DIR}/external/libdye/include)
|
||||
@@ -52,12 +60,12 @@ find_package(BDWgc REQUIRED)
|
||||
find_package(gmp REQUIRED)
|
||||
|
||||
target_compile_options(argon PRIVATE -O3 -Wall -Wextra -Wno-unused-function -s)
|
||||
target_link_options(argon PRIVATE -static)
|
||||
|
||||
target_link_libraries(argon PRIVATE
|
||||
BDWgc::BDWgc
|
||||
gmp::gmp
|
||||
m
|
||||
$<$<PLATFORM_ID:Windows>:bcrypt>
|
||||
)
|
||||
|
||||
target_include_directories(argon PRIVATE
|
||||
|
||||
23
Makefile
23
Makefile
@@ -2,26 +2,31 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Default FLEX tool
|
||||
BINARY = bin/argon
|
||||
FLEX_TOOL = flex
|
||||
|
||||
CFILES = external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c external/linenoise/linenoise.c $(shell find src -name '*.c')
|
||||
|
||||
LEXER_SRC = src/lexer/lex.l
|
||||
LEXER_C = src/lexer/lex.yy.c
|
||||
LEXER_H = src/lexer/lex.yy.h
|
||||
|
||||
CFILES = external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c $(shell find src -name '*.c')
|
||||
CFLAGS = $(ARCHFLAGS) -lm -lgc -lgmp -Wall -Wextra -Wno-unused-function -Werror=unused-result -Iexternal/cwalk/include -Iexternal/libdye/include
|
||||
BINARY = bin/argon
|
||||
LDFLAGS = -lgc -lgmp -lm
|
||||
|
||||
all: $(BINARY)
|
||||
|
||||
|
||||
$(LEXER_C) $(LEXER_H): $(LEXER_SRC)
|
||||
flex --header-file=$(LEXER_H) -o $(LEXER_C) $(LEXER_SRC)
|
||||
$(FLEX_TOOL) --header-file=$(LEXER_H) -o $(LEXER_C) $(LEXER_SRC)
|
||||
|
||||
$(BINARY): $(CFILES) $(LEXER_C) $(LEXER_H)
|
||||
mkdir -p bin
|
||||
gcc -O3 -o $(BINARY) $(CFILES) $(CFLAGS) -s
|
||||
gcc -O3 -o $(BINARY) $(CFILES) $(CFLAGS) ${LDFLAGS} -s
|
||||
|
||||
native: $(CFILES) $(LEXER_C) $(LEXER_H)
|
||||
mkdir -p bin
|
||||
gcc -O3 -march=native -o $(BINARY) $(CFILES) $(CFLAGS)
|
||||
gcc -O3 -march=native -o $(BINARY) $(CFILES) $(CFLAGS) ${LDFLAGS}
|
||||
|
||||
debug: $(CFILES) $(LEXER_C) $(LEXER_H)
|
||||
mkdir -p bin
|
||||
@@ -29,13 +34,13 @@ debug: $(CFILES) $(LEXER_C) $(LEXER_H)
|
||||
|
||||
full-debug: $(CFILES) $(LEXER_C) $(LEXER_H)
|
||||
mkdir -p bin
|
||||
gcc -g -O0 -fsanitize=address -fno-omit-frame-pointer -o $(BINARY) $(CFILES) $(CFLAGS)
|
||||
gcc -g -O3 -fsanitize=address -fno-omit-frame-pointer -o $(BINARY) $(CFILES) $(CFLAGS) ${LDFLAGS}
|
||||
|
||||
optimised: $(CFILES) $(LEXER_C) $(LEXER_H)
|
||||
mkdir -p bin
|
||||
gcc -O3 -fprofile-generate -o $(BINARY) $(CFILES) $(CFLAGS)
|
||||
gcc -O3 -fprofile-generate -o $(BINARY) $(CFILES) $(CFLAGS) ${LDFLAGS}
|
||||
${BINARY} rand_test.ar
|
||||
gcc -O3 -fprofile-use -o $(BINARY) $(CFILES) $(CFLAGS)
|
||||
gcc -O3 -fprofile-use -o $(BINARY) $(CFILES) $(CFLAGS) ${LDFLAGS}
|
||||
|
||||
|
||||
clean:
|
||||
|
||||
14
conanfile.py
14
conanfile.py
@@ -30,18 +30,14 @@ class ArgonConan(ConanFile):
|
||||
self.folders.generators = "build"
|
||||
|
||||
def generate(self):
|
||||
os.environ["CONAN_NON_INTERACTIVE"] = "1"
|
||||
tc = CMakeToolchain(self)
|
||||
|
||||
if os.name == "nt": # Windows
|
||||
flex_path = which("win_flex") or which("win_flex.exe")
|
||||
if not flex_path:
|
||||
raise Exception("win_flex not found in PATH. Install winflexbison via choco.")
|
||||
else:
|
||||
flex_path = which("flex")
|
||||
if not flex_path:
|
||||
raise Exception("Flex not found in system PATH. Please install flex on Linux/macOS.")
|
||||
flex_path = which("flex")
|
||||
if not flex_path:
|
||||
raise Exception("Flex not found in system PATH. Please install flex on Linux/macOS.")
|
||||
|
||||
tc.variables["FLEX_EXECUTABLE"] = flex_path
|
||||
tc.variables["FLEX_EXECUTABLE"] = flex_path.replace("\\", "\\\\")
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
|
||||
130
debug_arbin.py
130
debug_arbin.py
@@ -1,130 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2025 William Bell
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import struct
|
||||
from enum import Enum, EnumMeta, auto
|
||||
|
||||
class AutoEnumMeta(EnumMeta):
|
||||
@classmethod
|
||||
def __prepare__(metacls, clsname, bases, **kwargs):
|
||||
d = super().__prepare__(clsname, bases, **kwargs)
|
||||
d['_next_value'] = 254
|
||||
return d
|
||||
|
||||
def _generate_next_value_(cls, name, start, count, last_values):
|
||||
value = cls._next_value
|
||||
cls._next_value += 1
|
||||
return value
|
||||
|
||||
class OperationType(Enum, metaclass=AutoEnumMeta):
|
||||
OP_LOAD_CONST = auto()
|
||||
OP_DECLARE = auto()
|
||||
OP_LOAD_NULL = auto()
|
||||
OP_JUMP = auto()
|
||||
|
||||
class Types(Enum, metaclass=AutoEnumMeta):
|
||||
TYPE_OP_STRING = auto()
|
||||
TYPE_OP_NUMBER = auto()
|
||||
|
||||
def read_arbin(filename):
|
||||
with open(filename, "rb") as f:
|
||||
# Read and verify file identifier (4 bytes)
|
||||
file_id = f.read(4)
|
||||
if file_id != b"ARBI":
|
||||
raise ValueError("Invalid file identifier")
|
||||
|
||||
# Read version number (uint64_t, little-endian)
|
||||
version_number, = struct.unpack("<Q", f.read(8))
|
||||
|
||||
# Read regCount, constantsSize, bytecodeSize (all uint64_t, little-endian)
|
||||
reg_count, = struct.unpack("<Q", f.read(8))
|
||||
constants_size, = struct.unpack("<Q", f.read(8))
|
||||
bytecode_size, = struct.unpack("<Q", f.read(8))
|
||||
|
||||
# Read constants buffer (raw bytes)
|
||||
constants = f.read(constants_size)
|
||||
|
||||
# Read bytecode array (uint64_t, little-endian)
|
||||
bytecode = []
|
||||
for _ in range(bytecode_size):
|
||||
instr, = struct.unpack("<Q", f.read(8))
|
||||
bytecode.append(instr)
|
||||
|
||||
return {
|
||||
"version": version_number,
|
||||
"register_count": reg_count,
|
||||
"constants_size": constants_size,
|
||||
"bytecode_size": bytecode_size,
|
||||
"constants": constants,
|
||||
"bytecode": bytecode,
|
||||
}
|
||||
|
||||
class print_opcode:
|
||||
def start(registers,data, i):
|
||||
print()
|
||||
match data['bytecode'][i]:
|
||||
case OperationType.OP_LOAD_CONST.value:
|
||||
return print_opcode.OP_LOAD_CONST(registers,data, i)
|
||||
case OperationType.OP_DECLARE.value:
|
||||
return print_opcode.OP_DECLARE(registers,data, i)
|
||||
case OperationType.OP_LOAD_NULL.value:
|
||||
return print_opcode.OP_LOAD_NULL(registers,data, i)
|
||||
|
||||
def OP_LOAD_CONST(registers,data, i) -> int:
|
||||
print("OP_LOAD_CONST ", end="")
|
||||
i+=1
|
||||
register = data["bytecode"][i]
|
||||
print("To Register",register,"", end="")
|
||||
i+=1
|
||||
match data["bytecode"][i]:
|
||||
case Types.TYPE_OP_STRING.value:
|
||||
print("TYPE_OP_STRING ", end="")
|
||||
case Types.TYPE_OP_NUMBER.value:
|
||||
print("TYPE_OP_NUMBER ", end="")
|
||||
i+=1
|
||||
length = data["bytecode"][i]
|
||||
i+=1
|
||||
offset = data["bytecode"][i]
|
||||
i+=1
|
||||
print("Length",length,"", end="")
|
||||
print("Offset",offset,"")
|
||||
registers[register] = data["constants"][offset:offset+length].decode()
|
||||
print("const value:", registers[register])
|
||||
return i
|
||||
|
||||
def OP_DECLARE(registers,data, i) -> int:
|
||||
print("OP_DECLARE ", end="")
|
||||
i+=1
|
||||
length = data["bytecode"][i]
|
||||
i+=1
|
||||
offset = data["bytecode"][i]
|
||||
i+=1
|
||||
from_register = data["bytecode"][i]
|
||||
i+=1
|
||||
print("Name Length",length,"", end="")
|
||||
print("Name Offset",offset,"", end="")
|
||||
print("From Register",from_register,"")
|
||||
print("output: let", data['constants'][offset:offset+length].decode(),'=',registers[from_register])
|
||||
return i
|
||||
def OP_LOAD_NULL(registers,data, i) -> int:
|
||||
print("OP_LOAD_NULL ", end="")
|
||||
i+=1
|
||||
to_register = data["bytecode"][i]
|
||||
i+=1
|
||||
print("To Register",to_register,"")
|
||||
registers[to_register] = "null"
|
||||
return i
|
||||
if __name__ == "__main__":
|
||||
filename = "out.arbin"
|
||||
data = read_arbin(filename)
|
||||
print(f"Version: {data['version']}")
|
||||
print(f"Register Count: {data['register_count']}")
|
||||
print(f"Constants Size: {data['constants_size']} bytes")
|
||||
print(f"Bytecode Length: {data['bytecode_size']} elements")
|
||||
|
||||
registers = ["null"]*data['register_count']
|
||||
|
||||
i=0
|
||||
while i<len(data["bytecode"]):
|
||||
i=print_opcode.start(registers,data,i)
|
||||
1
external/linenoise
vendored
Submodule
1
external/linenoise
vendored
Submodule
Submodule external/linenoise added at d895173d67
@@ -1,56 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2025 William Bell
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
let __makeFile(name, type, data) = do
|
||||
let File = {name: name, type: type, data: data}
|
||||
let save(path) = do
|
||||
let file = file.write(path)
|
||||
file.buffer(data)
|
||||
file.close()
|
||||
File.save = save
|
||||
return File
|
||||
|
||||
let __multipart(req, res) = do
|
||||
let boundary = buffer().from(req.headers["content-type"].splitN("boundary=", 2)[1])
|
||||
let newLineSplit = buffer().from("\r\n\r\n")
|
||||
let parts = req.buffer.body.split(boundary)
|
||||
for (i from 0 to parts.length) do
|
||||
let str = parts[i].to("string")
|
||||
if (str == "" || str=="--" || str=="--\r\n") continue
|
||||
str = null
|
||||
let headers = {}
|
||||
let lines = parts[i].splitN(newLineSplit, 2)
|
||||
let headerLines = lines[0].to("string").split("\r\n")
|
||||
for (j from 0 to headerLines.length) do
|
||||
let header = headerLines[j].splitN(": ", 2)
|
||||
if (header.length != 2) continue
|
||||
headers[header[0].lower()] = header[1]
|
||||
if (lines.length != 2) continue
|
||||
let body = lines[1]
|
||||
if (i != parts.length-1) do
|
||||
body = body.slice(0, body.length-4)
|
||||
if ("content-disposition" in headers) do
|
||||
let disposition = headers["content-disposition"].split("; ")
|
||||
if (disposition[0] == "form-data") do
|
||||
let name = json.parse(disposition[1].splitN("=", 2)[1])
|
||||
if (disposition.length >= 3) do
|
||||
let filename = json.parse(disposition[2].splitN("=", 2)[1])
|
||||
req.files[name] = __makeFile(filename, headers["content-type"], body)
|
||||
else do
|
||||
req.formdata[name] = body.to("string")
|
||||
res.next()
|
||||
|
||||
|
||||
let formdata(req, res) = do
|
||||
req.formdata = {}
|
||||
req.files = {}
|
||||
|
||||
if (req.method != "POST") return res.next()
|
||||
if ("content-type" not in req.headers) return res.next()
|
||||
let loweredContentType = req.headers["content-type"].lower()
|
||||
if (loweredContentType.startswith("multipart/form-data")) return __multipart(req, res)
|
||||
else if (loweredContentType.startswith("application/x-www-form-urlencoded")) req.formdata = url.decodeURLQuery(req.buffer.body.to("string"))
|
||||
else if (loweredContentType.startswith("application/json")) req.formdata = json.parse(req.buffer.body.to("string"))
|
||||
else req.files.file = __makeFile("file", req.headers["content-type"], req.buffer.body)
|
||||
res.next()
|
||||
10
gentest.py
10
gentest.py
@@ -1,10 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2025 William Bell
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import random
|
||||
import sys
|
||||
|
||||
for i in range(10000000):
|
||||
sys.stdout.write("\"hello world\"\n")
|
||||
|
||||
2893
src/LICENSE_c.h
Normal file
2893
src/LICENSE_c.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,36 @@
|
||||
#include "runtime/internals/hashmap/hashmap.h"
|
||||
#include <gmp.h>
|
||||
|
||||
typedef enum {
|
||||
__base__,
|
||||
__class__,
|
||||
__name__,
|
||||
__binding__,
|
||||
__function__,
|
||||
|
||||
BUILT_IN_ARRAY_COUNT,
|
||||
|
||||
__add__,
|
||||
__string__,
|
||||
__subtract__,
|
||||
__multiply__,
|
||||
__divide__,
|
||||
__new__,
|
||||
__init__,
|
||||
__boolean__,
|
||||
__get_attr__,
|
||||
field__address,
|
||||
__call__,
|
||||
__number__,
|
||||
field_length,
|
||||
__getattribute__,
|
||||
__set_attr__,
|
||||
__hash__,
|
||||
__repr__,
|
||||
|
||||
BUILT_IN_FIELDS_COUNT,
|
||||
} built_in_fields;
|
||||
|
||||
typedef struct ArErr ArErr;
|
||||
typedef struct RuntimeState RuntimeState;
|
||||
|
||||
@@ -28,6 +58,7 @@ typedef enum ArgonType {
|
||||
TYPE_FUNCTION,
|
||||
TYPE_NATIVE_FUNCTION,
|
||||
TYPE_METHOD,
|
||||
TYPE_DICTIONARY,
|
||||
TYPE_OBJECT,
|
||||
} ArgonType;
|
||||
|
||||
@@ -40,6 +71,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
uint8_t registerCount;
|
||||
uint8_t registerAssignment;
|
||||
DArray *return_jumps;
|
||||
DArray bytecode;
|
||||
ConstantArena constants;
|
||||
@@ -47,11 +79,15 @@ typedef struct {
|
||||
} Translated;
|
||||
|
||||
struct string_struct {
|
||||
uint64_t prehash;
|
||||
uint64_t hash;
|
||||
char *data;
|
||||
size_t length;
|
||||
bool hash_computed;
|
||||
};
|
||||
|
||||
typedef struct Stack {
|
||||
uint64_t fake_new_scopes;
|
||||
struct hashmap_GC *scope;
|
||||
struct Stack *prev;
|
||||
} Stack;
|
||||
@@ -67,17 +103,33 @@ struct argon_function_struct {
|
||||
uint64_t column;
|
||||
};
|
||||
|
||||
struct built_in_slot {
|
||||
built_in_fields field;
|
||||
ArgonObject *value;
|
||||
};
|
||||
|
||||
struct as_number {
|
||||
union {
|
||||
mpq_t *mpq;
|
||||
int64_t i64;
|
||||
} n;
|
||||
bool is_int64;
|
||||
};
|
||||
|
||||
// full definition of ArgonObject (no typedef again!)
|
||||
struct ArgonObject {
|
||||
ArgonType type;
|
||||
ArgonType child_type;
|
||||
struct hashmap_GC *dict;
|
||||
size_t built_in_slot_length;
|
||||
struct built_in_slot built_in_slot[BUILT_IN_ARRAY_COUNT];
|
||||
union {
|
||||
mpq_t *as_number;
|
||||
struct string_struct as_str;
|
||||
struct as_number *as_number;
|
||||
struct hashmap_GC *as_hashmap;
|
||||
struct string_struct *as_str;
|
||||
native_fn native_fn;
|
||||
struct argon_function_struct argon_fn;
|
||||
struct argon_function_struct *argon_fn;
|
||||
} value;
|
||||
ArgonType type;
|
||||
bool as_bool;
|
||||
};
|
||||
|
||||
#endif // AROBJECT_H
|
||||
21
src/err.c
21
src/err.c
@@ -7,6 +7,7 @@
|
||||
#include "err.h"
|
||||
#include "../external/libdye/include/dye.h"
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
@@ -14,7 +15,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
|
||||
@@ -61,20 +61,23 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
|
||||
}
|
||||
#endif
|
||||
|
||||
const ArErr no_err = (ArErr){false};
|
||||
const ArErr no_err = (ArErr){"", "", "", 0, 0, 0, false};
|
||||
|
||||
ArErr create_err(int64_t line, int64_t column, int length, char *path,
|
||||
const char *type, const char *fmt, ...) {
|
||||
ArErr err;
|
||||
err.exists = true;
|
||||
err.path = path;
|
||||
if (path)
|
||||
strcpy(err.path, path);
|
||||
else {
|
||||
err.path[0] = '\0';
|
||||
}
|
||||
err.line = line;
|
||||
err.column = column;
|
||||
err.length = length;
|
||||
|
||||
// Copy error type safely
|
||||
strncpy(err.type, type, sizeof(err.type) - 1);
|
||||
err.type[sizeof(err.type) - 1] = '\0';
|
||||
snprintf(err.type, sizeof(err.type), "%s", (char *)type);
|
||||
|
||||
// Format error message
|
||||
va_list args;
|
||||
@@ -104,7 +107,7 @@ void output_err(ArErr err) {
|
||||
dyefg(stderr, DYE_RESET);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
if (err.path && err.line) {
|
||||
if (strlen(err.path) && err.line) {
|
||||
dyefg(stderr, DYE_GRAY);
|
||||
fprintf(stderr, " --> ");
|
||||
dyefg(stderr, DYE_CYAN);
|
||||
@@ -112,7 +115,7 @@ void output_err(ArErr err) {
|
||||
dyefg(stderr, DYE_GRAY);
|
||||
fprintf(stderr, ":");
|
||||
dyefg(stderr, DYE_YELLOW);
|
||||
fprintf(stderr, "%" PRIu64 , err.line);
|
||||
fprintf(stderr, "%" PRIu64, err.line);
|
||||
dyefg(stderr, DYE_GRAY);
|
||||
fprintf(stderr, ":");
|
||||
dyefg(stderr, DYE_YELLOW);
|
||||
@@ -141,7 +144,7 @@ void output_err(ArErr err) {
|
||||
fprintf(stderr, " ");
|
||||
}
|
||||
fprintf(stderr, "|\n");
|
||||
for (ssize_t i = 0;i<len;i++) {
|
||||
for (ssize_t i = 0; i < len; i++) {
|
||||
if (buffer[i] == '\n') {
|
||||
buffer[i] = '\0';
|
||||
break;
|
||||
@@ -165,7 +168,7 @@ void output_err(ArErr err) {
|
||||
dyefg(stderr, DYE_RESET);
|
||||
fprintf(stderr, "%.*s",
|
||||
(int)len - (int)skipped_chars - (int)err.column -
|
||||
(int)err.length+1,
|
||||
(int)err.length + 1,
|
||||
line_starts + (int)err.column + err.length - 1);
|
||||
for (int64_t i = 0; i < err.column - 1; i++) {
|
||||
fprintf(stderr, " ");
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef RETURN_TYPE_H
|
||||
#define RETURN_TYPE_H
|
||||
#include "returnTypes.h"
|
||||
|
||||
extern const ArErr no_err;
|
||||
|
||||
ArErr create_err(int64_t line, int64_t column, int length, char *path, const char *type,
|
||||
const char *fmt, ...);
|
||||
void output_err(ArErr err);
|
||||
void output_err(ArErr err);
|
||||
#endif // RETURN_TYPE_H
|
||||
@@ -39,10 +39,6 @@ uint64_t siphash64_bytes(const void *data, size_t len,const uint8_t hash_key[16]
|
||||
uint8_t out[8];
|
||||
if (siphash(data, len, hash_key, out, sizeof(out)) != 0)
|
||||
return 0;
|
||||
|
||||
uint64_t hash = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
hash |= ((uint64_t)out[i]) << (8 * i);
|
||||
|
||||
return hash;
|
||||
|
||||
return *(uint64_t *)out;
|
||||
}
|
||||
469
src/import.c
Normal file
469
src/import.c
Normal file
@@ -0,0 +1,469 @@
|
||||
#include "../external/cwalk/include/cwalk.h"
|
||||
#include "../external/xxhash/xxhash.h"
|
||||
#include "arobject.h"
|
||||
#include "err.h"
|
||||
#include "hash_data/hash_data.h"
|
||||
#include "hashmap/hashmap.h"
|
||||
#include "lexer/lexer.h"
|
||||
#include "lexer/token.h"
|
||||
#include "runtime/internals/hashmap/hashmap.h"
|
||||
#include "runtime/runtime.h"
|
||||
#include "translator/translator.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#ifdef _WIN32
|
||||
#include <direct.h> // for _mkdir
|
||||
#include <sys/stat.h> // for _stat
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "err.h"
|
||||
#include <pthread.h>
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
// Windows / MinGW usually uses little-endian, so these can be no-ops
|
||||
// But define them explicitly to avoid implicit declaration warnings
|
||||
|
||||
static inline uint32_t le32toh(uint32_t x) { return x; }
|
||||
static inline uint64_t le64toh(uint64_t x) { return x; }
|
||||
static inline uint32_t htole32(uint32_t x) { return x; }
|
||||
static inline uint64_t htole64(uint64_t x) { return x; }
|
||||
|
||||
#elif defined(__linux__)
|
||||
#include <endian.h>
|
||||
#include <malloc.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
#define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
#define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
#define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
// Add others as needed
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#include <stdlib.h>
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
|
||||
int ensure_dir_exists(const char *path) {
|
||||
#ifdef _WIN32
|
||||
struct _stat st;
|
||||
if (_stat(path, &st) != 0) {
|
||||
// Directory does not exist, create it
|
||||
if (_mkdir(path) != 0) {
|
||||
perror("_mkdir failed");
|
||||
return -1;
|
||||
}
|
||||
} else if (!(st.st_mode & _S_IFDIR)) {
|
||||
fprintf(stderr, "Path exists but is not a directory\n");
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) {
|
||||
// Directory does not exist, create it
|
||||
if (mkdir(path, 0755) != 0) {
|
||||
perror("mkdir failed");
|
||||
return -1;
|
||||
}
|
||||
} else if (!S_ISDIR(st.st_mode)) {
|
||||
fprintf(stderr, "Path exists but is not a directory\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *CWD;
|
||||
|
||||
const char CACHE_FOLDER[] = "__arcache__";
|
||||
const char FILE_IDENTIFIER[5] = "ARBI";
|
||||
const char BYTECODE_EXTENTION[] = "arbin";
|
||||
const uint32_t version_number = 0;
|
||||
const char version_string[] = "4.0.0";
|
||||
|
||||
bool file_exists(const char *path) {
|
||||
struct stat st;
|
||||
if (stat(path, &st) == 0) {
|
||||
return S_ISREG(st.st_mode); // true only if it's a regular file
|
||||
}
|
||||
return false; // doesn't exist, or stat failed
|
||||
}
|
||||
|
||||
static inline void write_and_hash(FILE *file, XXH64_state_t *state,
|
||||
const void *ptr, size_t size, size_t count) {
|
||||
fwrite(ptr, size, count, file);
|
||||
XXH64_update(state, ptr, size * count);
|
||||
}
|
||||
|
||||
static inline void update_hash_from_file(FILE *file, XXH64_state_t *state,
|
||||
size_t size) {
|
||||
char buffer[4096];
|
||||
size_t bytes_read;
|
||||
size_t remaining = size;
|
||||
|
||||
while (remaining > 0 &&
|
||||
(bytes_read =
|
||||
fread(buffer, 1,
|
||||
remaining > sizeof(buffer) ? sizeof(buffer) : remaining,
|
||||
file)) > 0) {
|
||||
XXH64_update(state, buffer, bytes_read);
|
||||
remaining -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
int load_cache(Translated *translated_dest, char *joined_paths, uint64_t hash,
|
||||
char *source_path) {
|
||||
FILE *bytecode_file = fopen(joined_paths, "rb");
|
||||
if (!bytecode_file) {
|
||||
fprintf(stderr, "cache doesnt exist... compiling from source.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Find file size
|
||||
fseek(bytecode_file, 0, SEEK_END);
|
||||
long file_size = ftell(bytecode_file);
|
||||
if (file_size < (long)sizeof(uint64_t)) {
|
||||
goto FAILED;
|
||||
}
|
||||
fseek(bytecode_file, 0, SEEK_SET);
|
||||
|
||||
// Footer is the last 8 bytes
|
||||
long data_size = file_size - sizeof(uint64_t);
|
||||
|
||||
// Set up hash state
|
||||
XXH64_state_t *state = XXH64_createState();
|
||||
XXH64_reset(state, 0);
|
||||
|
||||
// Hash everything except last 8 bytes
|
||||
update_hash_from_file(bytecode_file, state, data_size);
|
||||
|
||||
// Read stored footer hash
|
||||
uint64_t stored_hash_le;
|
||||
if (fread(&stored_hash_le, 1, sizeof(stored_hash_le), bytecode_file) !=
|
||||
sizeof(stored_hash_le)) {
|
||||
XXH64_freeState(state);
|
||||
goto FAILED;
|
||||
}
|
||||
uint64_t stored_hash = le64toh(stored_hash_le);
|
||||
|
||||
// Compare
|
||||
uint64_t calc_hash = XXH64_digest(state);
|
||||
XXH64_freeState(state);
|
||||
|
||||
if (calc_hash != stored_hash) {
|
||||
fprintf(stderr, "cache hash mismatch (corrupted?)\n");
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
// Now actually parse the file contents
|
||||
fseek(bytecode_file, 0, SEEK_SET); // rewind to start
|
||||
|
||||
char file_identifier_from_cache[sizeof(FILE_IDENTIFIER)] = {0};
|
||||
if (fread(&file_identifier_from_cache, 1,
|
||||
sizeof(file_identifier_from_cache) - 1,
|
||||
bytecode_file) != sizeof(file_identifier_from_cache) - 1 ||
|
||||
memcmp(file_identifier_from_cache, FILE_IDENTIFIER,
|
||||
sizeof(file_identifier_from_cache)) != 0) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint32_t read_version;
|
||||
if (fread(&read_version, 1, sizeof(read_version), bytecode_file) !=
|
||||
sizeof(read_version)) {
|
||||
goto FAILED;
|
||||
}
|
||||
read_version = le32toh(read_version);
|
||||
|
||||
if (read_version != version_number) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint64_t read_hash;
|
||||
if (fread(&read_hash, 1, sizeof(read_hash), bytecode_file) !=
|
||||
sizeof(read_hash)) {
|
||||
goto FAILED;
|
||||
}
|
||||
read_hash = le64toh(read_hash);
|
||||
|
||||
if (read_hash != hash) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint8_t register_count;
|
||||
if (fread(®ister_count, 1, sizeof(register_count), bytecode_file) !=
|
||||
sizeof(register_count)) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint64_t constantsSize;
|
||||
if (fread(&constantsSize, 1, sizeof(constantsSize), bytecode_file) !=
|
||||
sizeof(constantsSize)) {
|
||||
goto FAILED;
|
||||
}
|
||||
constantsSize = le64toh(constantsSize);
|
||||
|
||||
uint64_t bytecodeSize;
|
||||
if (fread(&bytecodeSize, 1, sizeof(bytecodeSize), bytecode_file) !=
|
||||
sizeof(bytecodeSize)) {
|
||||
goto FAILED;
|
||||
}
|
||||
bytecodeSize = le64toh(bytecodeSize);
|
||||
|
||||
*translated_dest = init_translator(source_path);
|
||||
|
||||
translated_dest->registerCount = register_count;
|
||||
|
||||
arena_resize(&translated_dest->constants, constantsSize);
|
||||
|
||||
if (fread(translated_dest->constants.data, 1, constantsSize, bytecode_file) !=
|
||||
constantsSize) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
translated_dest->constants.size = constantsSize;
|
||||
|
||||
darray_resize(&translated_dest->bytecode, bytecodeSize);
|
||||
|
||||
if (fread(translated_dest->bytecode.data, 1, bytecodeSize, bytecode_file) !=
|
||||
bytecodeSize) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
translated_dest->bytecode.size = bytecodeSize;
|
||||
|
||||
fprintf(stderr, "cache exists and is valid, so will be used.\n");
|
||||
fclose(bytecode_file);
|
||||
return 0;
|
||||
FAILED:
|
||||
fprintf(stderr, "cache is invalid... compiling from source.\n");
|
||||
fclose(bytecode_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Translated load_argon_file(char *path, ArErr *err) {
|
||||
clock_t start, end;
|
||||
clock_t beginning = clock();
|
||||
double time_spent, total_time_spent = 0;
|
||||
|
||||
const char *basename_ptr;
|
||||
size_t basename_length;
|
||||
cwk_path_get_basename(path, &basename_ptr, &basename_length);
|
||||
|
||||
if (!basename_ptr) {
|
||||
*err = create_err(0, 0, 0, NULL, "Path Error", "path has no basename '%s'",
|
||||
path);
|
||||
return (Translated){};
|
||||
}
|
||||
|
||||
char basename[FILENAME_MAX];
|
||||
memcpy(basename, basename_ptr, basename_length);
|
||||
|
||||
size_t parent_directory_length;
|
||||
cwk_path_get_dirname(path, &parent_directory_length);
|
||||
|
||||
char parent_directory[FILENAME_MAX];
|
||||
memcpy(parent_directory, path, parent_directory_length);
|
||||
parent_directory[parent_directory_length] = '\0';
|
||||
|
||||
char cache_folder_path[FILENAME_MAX];
|
||||
cwk_path_join(parent_directory, CACHE_FOLDER, cache_folder_path,
|
||||
sizeof(cache_folder_path));
|
||||
|
||||
char cache_file_path[FILENAME_MAX];
|
||||
cwk_path_join(cache_folder_path, basename, cache_file_path,
|
||||
sizeof(cache_file_path));
|
||||
cwk_path_change_extension(cache_file_path, BYTECODE_EXTENTION,
|
||||
cache_file_path, sizeof(cache_file_path));
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file) {
|
||||
*err = create_err(0, 0, 0, NULL, "File Error", "Unable to open file '%s'",
|
||||
path);
|
||||
return (Translated){};
|
||||
}
|
||||
|
||||
XXH3_state_t *hash_state = XXH3_createState();
|
||||
XXH3_64bits_reset(hash_state);
|
||||
|
||||
char buffer[8192];
|
||||
size_t bytes;
|
||||
while ((bytes = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||||
XXH3_64bits_update(hash_state, buffer, bytes);
|
||||
}
|
||||
rewind(file);
|
||||
uint64_t hash = XXH3_64bits_digest(hash_state);
|
||||
XXH3_freeState(hash_state);
|
||||
|
||||
Translated translated;
|
||||
|
||||
if (load_cache(&translated, cache_file_path, hash, path) != 0) {
|
||||
|
||||
DArray tokens;
|
||||
darray_init(&tokens, sizeof(Token));
|
||||
|
||||
LexerState state = {path, file, 0, 0, &tokens};
|
||||
start = clock();
|
||||
*err = lexer(state);
|
||||
if (err->exists) {
|
||||
darray_free(&tokens, free_token);
|
||||
return (Translated){};
|
||||
}
|
||||
end = clock();
|
||||
time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
fprintf(stderr, "Lexer time taken: %f seconds\n", time_spent);
|
||||
fclose(state.file);
|
||||
|
||||
DArray ast;
|
||||
|
||||
darray_init(&ast, sizeof(ParsedValue));
|
||||
|
||||
start = clock();
|
||||
*err = parser(path, &ast, &tokens, false);
|
||||
darray_free(&tokens, free_token);
|
||||
if (err->exists) {
|
||||
darray_free(&ast, free_parsed);
|
||||
return (Translated){};
|
||||
}
|
||||
end = clock();
|
||||
time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
fprintf(stderr, "Parser time taken: %f seconds\n", time_spent);
|
||||
|
||||
start = clock();
|
||||
|
||||
translated = init_translator(path);
|
||||
*err = translate(&translated, &ast);
|
||||
darray_free(&ast, free_parsed);
|
||||
if (err->exists) {
|
||||
darray_free(&translated.bytecode, NULL);
|
||||
free(translated.constants.data);
|
||||
hashmap_free(translated.constants.hashmap, NULL);
|
||||
return (Translated){};
|
||||
}
|
||||
end = clock();
|
||||
time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
fprintf(stderr, "Translation time taken: %f seconds\n", time_spent);
|
||||
#if defined(__linux__)
|
||||
malloc_trim(0);
|
||||
#endif
|
||||
|
||||
ensure_dir_exists(cache_folder_path);
|
||||
|
||||
file = fopen(cache_file_path, "wb");
|
||||
|
||||
uint64_t constantsSize = translated.constants.size;
|
||||
uint64_t bytecodeSize = translated.bytecode.size;
|
||||
|
||||
uint32_t version_number_htole32ed = htole32(version_number);
|
||||
uint64_t net_hash = htole64(hash);
|
||||
constantsSize = htole64(constantsSize);
|
||||
bytecodeSize = htole64(bytecodeSize);
|
||||
|
||||
XXH64_state_t *hash_state = XXH64_createState();
|
||||
XXH64_reset(hash_state, 0);
|
||||
|
||||
write_and_hash(file, hash_state, &FILE_IDENTIFIER, sizeof(char),
|
||||
strlen(FILE_IDENTIFIER));
|
||||
write_and_hash(file, hash_state, &version_number_htole32ed,
|
||||
sizeof(uint32_t), 1);
|
||||
write_and_hash(file, hash_state, &net_hash, sizeof(net_hash), 1);
|
||||
write_and_hash(file, hash_state, &translated.registerCount, sizeof(uint8_t),
|
||||
1);
|
||||
write_and_hash(file, hash_state, &constantsSize, sizeof(uint64_t), 1);
|
||||
write_and_hash(file, hash_state, &bytecodeSize, sizeof(uint64_t), 1);
|
||||
write_and_hash(file, hash_state, translated.constants.data, 1,
|
||||
translated.constants.size);
|
||||
write_and_hash(file, hash_state, translated.bytecode.data,
|
||||
translated.bytecode.element_size, translated.bytecode.size);
|
||||
|
||||
// Finalize the hash
|
||||
uint64_t file_hash = XXH64_digest(hash_state);
|
||||
XXH64_freeState(hash_state);
|
||||
|
||||
// Convert to little-endian before writing if needed
|
||||
uint64_t file_hash_le = htole64(file_hash);
|
||||
fwrite(&file_hash_le, sizeof(file_hash_le), 1, file);
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
hashmap_free(translated.constants.hashmap, NULL);
|
||||
Translated gc_translated = {
|
||||
translated.registerCount, translated.registerAssignment, NULL, {}, {},
|
||||
translated.path};
|
||||
gc_translated.bytecode.data = ar_alloc_atomic(translated.bytecode.capacity);
|
||||
memcpy(gc_translated.bytecode.data, translated.bytecode.data,
|
||||
translated.bytecode.capacity);
|
||||
gc_translated.bytecode.element_size = translated.bytecode.element_size;
|
||||
gc_translated.bytecode.size = translated.bytecode.size;
|
||||
gc_translated.bytecode.resizable = false;
|
||||
gc_translated.bytecode.capacity =
|
||||
translated.bytecode.size * translated.bytecode.element_size;
|
||||
gc_translated.constants.data = ar_alloc_atomic(translated.constants.capacity);
|
||||
memcpy(gc_translated.constants.data, translated.constants.data,
|
||||
translated.constants.capacity);
|
||||
gc_translated.constants.size = translated.constants.size;
|
||||
gc_translated.constants.capacity = translated.constants.capacity;
|
||||
free(translated.bytecode.data);
|
||||
free(translated.constants.data);
|
||||
total_time_spent = (double)(clock() - beginning) / CLOCKS_PER_SEC;
|
||||
fprintf(stderr, "total time taken loading file (%s): %f seconds\n", path,
|
||||
total_time_spent);
|
||||
return gc_translated;
|
||||
}
|
||||
|
||||
const char *PRE_PATHS_TO_TEST[] = {"", "", "argon_modules", "argon_modules"};
|
||||
const char *POST_PATHS_TO_TEST[sizeof(PRE_PATHS_TO_TEST)/sizeof(char *)] = {"", "init.ar", "",
|
||||
"init.ar"};
|
||||
|
||||
struct hashmap *importing_hash_table = NULL;
|
||||
struct hashmap_GC *imported_hash_table = NULL;
|
||||
|
||||
Stack *ar_import(char *current_directory, char *path_relative, ArErr *err) {
|
||||
char path[FILENAME_MAX];
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < sizeof(PRE_PATHS_TO_TEST)/sizeof(char *); i++) {
|
||||
cwk_path_get_absolute(current_directory, PRE_PATHS_TO_TEST[i], path, sizeof(path));
|
||||
cwk_path_get_absolute(path, path_relative, path, sizeof(path));
|
||||
cwk_path_get_absolute(path, POST_PATHS_TO_TEST[i], path, sizeof(path));
|
||||
if (file_exists(path)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
*err = create_err(0, 0, 0, NULL, "File Error", "Unable to find file '%s'",
|
||||
path_relative);
|
||||
return NULL;
|
||||
}
|
||||
if (!importing_hash_table) importing_hash_table = createHashmap();
|
||||
uint64_t hash = siphash64_bytes(path, strlen(path), siphash_key);
|
||||
hashmap_insert(importing_hash_table, hash, path, (void*)true, 0);
|
||||
Translated translated = load_argon_file(path, err);
|
||||
if (err->exists) {
|
||||
return NULL;
|
||||
}
|
||||
clock_t start = clock(), end;
|
||||
RuntimeState state = init_runtime_state(translated, path);
|
||||
Stack *main_scope = create_scope(Global_Scope, true);
|
||||
runtime(translated, state, main_scope, err);
|
||||
if (err->exists) {
|
||||
return NULL;
|
||||
}
|
||||
end = clock();
|
||||
double time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
fprintf(stderr, "Execution time taken: %f seconds\n", time_spent);
|
||||
hashmap_insert(importing_hash_table, hash, path, (void*)false, 0);
|
||||
if (!imported_hash_table) imported_hash_table = createHashmap_GC();
|
||||
hashmap_insert_GC(imported_hash_table, hash, path, main_scope, 0);
|
||||
return main_scope;
|
||||
}
|
||||
17
src/import.h
Normal file
17
src/import.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef IMPORT_H
|
||||
#define IMPORT_H
|
||||
#include "err.h"
|
||||
|
||||
extern char*CWD;
|
||||
|
||||
extern const char version_string[];
|
||||
|
||||
Stack *ar_import(char *current_directory, char *path_relative, ArErr *err);
|
||||
|
||||
#endif // IMPORT_H
|
||||
@@ -32,13 +32,6 @@ int yywrap(void * unused_param) {
|
||||
":" { return TOKEN_COLON; }
|
||||
|
||||
"=" { return TOKEN_ASSIGN; }
|
||||
"+=" { return TOKEN_ASSIGN_PLUS; }
|
||||
"-=" { return TOKEN_ASSIGN_MINUS; }
|
||||
"//=" { return TOKEN_ASSIGN_FLOORDIV; }
|
||||
"/=" { return TOKEN_ASSIGN_SLASH; }
|
||||
"%=" { return TOKEN_ASSIGN_MODULO; }
|
||||
"*=" { return TOKEN_ASSIGN_STAR; }
|
||||
"^=" { return TOKEN_ASSIGN_CARET; }
|
||||
|
||||
"not"[ \t]+"in" { return TOKEN_NOT_IN; }
|
||||
"in" { return TOKEN_IN; }
|
||||
|
||||
@@ -17,13 +17,6 @@ typedef enum {
|
||||
TOKEN_INDENT,
|
||||
|
||||
TOKEN_ASSIGN,
|
||||
TOKEN_ASSIGN_PLUS,
|
||||
TOKEN_ASSIGN_MINUS,
|
||||
TOKEN_ASSIGN_FLOORDIV,
|
||||
TOKEN_ASSIGN_SLASH,
|
||||
TOKEN_ASSIGN_MODULO,
|
||||
TOKEN_ASSIGN_STAR,
|
||||
TOKEN_ASSIGN_CARET,
|
||||
|
||||
// Operators
|
||||
TOKEN_CARET, // ^ (Exponentiation)
|
||||
@@ -31,8 +24,8 @@ typedef enum {
|
||||
TOKEN_SLASH, // / (Division)
|
||||
TOKEN_FLOORDIV, // // (Floor Division)
|
||||
TOKEN_MODULO, // % (Modulo)
|
||||
TOKEN_PLUS, // + (Addition)
|
||||
TOKEN_MINUS, // - (Subtraction)
|
||||
TOKEN_PLUS, // + (Addition)
|
||||
TOKEN_LT, // <
|
||||
TOKEN_GT, // >
|
||||
TOKEN_LE, // <=
|
||||
|
||||
435
src/main.c
435
src/main.c
@@ -4,70 +4,23 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "arobject.h"
|
||||
#include "dynamic_array/darray.h"
|
||||
#include "err.h"
|
||||
#include "hashmap/hashmap.h"
|
||||
#include "lexer/lexer.h"
|
||||
#include "lexer/token.h"
|
||||
#include "import.h"
|
||||
#include "memory.h"
|
||||
#include "parser/parser.h"
|
||||
#include "runtime/objects/object.h"
|
||||
#include "runtime/runtime.h"
|
||||
#include "translator/translator.h"
|
||||
#include "shell.h"
|
||||
|
||||
#include "../external/xxhash/xxhash.h"
|
||||
#include "hash_data/hash_data.h"
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _WIN32
|
||||
#include <direct.h> // for _mkdir
|
||||
#include <sys/stat.h> // for _stat
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "../external/cwalk/include/cwalk.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "err.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
// Windows / MinGW usually uses little-endian, so these can be no-ops
|
||||
// But define them explicitly to avoid implicit declaration warnings
|
||||
|
||||
static inline uint32_t le32toh(uint32_t x) { return x; }
|
||||
static inline uint64_t le64toh(uint64_t x) { return x; }
|
||||
static inline uint32_t htole32(uint32_t x) { return x; }
|
||||
static inline uint64_t htole64(uint64_t x) { return x; }
|
||||
|
||||
#elif defined(__linux__)
|
||||
#include <endian.h>
|
||||
#include <malloc.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
#define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
#define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
#define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
// Add others as needed
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
char *get_current_directory() {
|
||||
@@ -98,385 +51,29 @@ char *get_current_directory() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int ensure_dir_exists(const char *path) {
|
||||
#ifdef _WIN32
|
||||
struct _stat st;
|
||||
if (_stat(path, &st) != 0) {
|
||||
// Directory does not exist, create it
|
||||
if (_mkdir(path) != 0) {
|
||||
perror("_mkdir failed");
|
||||
return -1;
|
||||
}
|
||||
} else if (!(st.st_mode & _S_IFDIR)) {
|
||||
fprintf(stderr, "Path exists but is not a directory\n");
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) {
|
||||
// Directory does not exist, create it
|
||||
if (mkdir(path, 0755) != 0) {
|
||||
perror("mkdir failed");
|
||||
return -1;
|
||||
}
|
||||
} else if (!S_ISDIR(st.st_mode)) {
|
||||
fprintf(stderr, "Path exists but is not a directory\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void write_and_hash(FILE *file, XXH64_state_t *state,
|
||||
const void *ptr, size_t size, size_t count) {
|
||||
fwrite(ptr, size, count, file);
|
||||
XXH64_update(state, ptr, size * count);
|
||||
}
|
||||
|
||||
static inline void update_hash_from_file(FILE *file, XXH64_state_t *state,
|
||||
size_t size) {
|
||||
char buffer[4096];
|
||||
size_t bytes_read;
|
||||
size_t remaining = size;
|
||||
|
||||
while (remaining > 0 &&
|
||||
(bytes_read =
|
||||
fread(buffer, 1,
|
||||
remaining > sizeof(buffer) ? sizeof(buffer) : remaining,
|
||||
file)) > 0) {
|
||||
XXH64_update(state, buffer, bytes_read);
|
||||
remaining -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
const char CACHE_FOLDER[] = "__arcache__";
|
||||
const char FILE_IDENTIFIER[5] = "ARBI";
|
||||
const char BYTECODE_EXTENTION[] = "arbin";
|
||||
const uint32_t version_number = 0;
|
||||
|
||||
int load_cache(Translated *translated_dest, char *joined_paths, uint64_t hash,
|
||||
char *source_path) {
|
||||
FILE *bytecode_file = fopen(joined_paths, "rb");
|
||||
if (!bytecode_file) {
|
||||
fprintf(stderr,"cache doesnt exist... compiling from source.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Find file size
|
||||
fseek(bytecode_file, 0, SEEK_END);
|
||||
long file_size = ftell(bytecode_file);
|
||||
if (file_size < (long)sizeof(uint64_t)) {
|
||||
goto FAILED;
|
||||
}
|
||||
fseek(bytecode_file, 0, SEEK_SET);
|
||||
|
||||
// Footer is the last 8 bytes
|
||||
long data_size = file_size - sizeof(uint64_t);
|
||||
|
||||
// Set up hash state
|
||||
XXH64_state_t *state = XXH64_createState();
|
||||
XXH64_reset(state, 0);
|
||||
|
||||
// Hash everything except last 8 bytes
|
||||
update_hash_from_file(bytecode_file, state, data_size);
|
||||
|
||||
// Read stored footer hash
|
||||
uint64_t stored_hash_le;
|
||||
if (fread(&stored_hash_le, 1, sizeof(stored_hash_le), bytecode_file) !=
|
||||
sizeof(stored_hash_le)) {
|
||||
XXH64_freeState(state);
|
||||
goto FAILED;
|
||||
}
|
||||
uint64_t stored_hash = le64toh(stored_hash_le);
|
||||
|
||||
// Compare
|
||||
uint64_t calc_hash = XXH64_digest(state);
|
||||
XXH64_freeState(state);
|
||||
|
||||
if (calc_hash != stored_hash) {
|
||||
fprintf(stderr,"cache hash mismatch (corrupted?)\n");
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
// Now actually parse the file contents
|
||||
fseek(bytecode_file, 0, SEEK_SET); // rewind to start
|
||||
|
||||
char file_identifier_from_cache[sizeof(FILE_IDENTIFIER)] = {0};
|
||||
if (fread(&file_identifier_from_cache, 1,
|
||||
sizeof(file_identifier_from_cache) - 1,
|
||||
bytecode_file) != sizeof(file_identifier_from_cache) - 1 ||
|
||||
memcmp(file_identifier_from_cache, FILE_IDENTIFIER,
|
||||
sizeof(file_identifier_from_cache)) != 0) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint32_t read_version;
|
||||
if (fread(&read_version, 1, sizeof(read_version), bytecode_file) !=
|
||||
sizeof(read_version)) {
|
||||
goto FAILED;
|
||||
}
|
||||
read_version = le32toh(read_version);
|
||||
|
||||
if (read_version != version_number) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint64_t read_hash;
|
||||
if (fread(&read_hash, 1, sizeof(read_hash), bytecode_file) !=
|
||||
sizeof(read_hash)) {
|
||||
goto FAILED;
|
||||
}
|
||||
read_hash = le64toh(read_hash);
|
||||
|
||||
if (read_hash != hash) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint8_t register_count;
|
||||
if (fread(®ister_count, 1, sizeof(register_count), bytecode_file) !=
|
||||
sizeof(register_count)) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
uint64_t constantsSize;
|
||||
if (fread(&constantsSize, 1, sizeof(constantsSize), bytecode_file) !=
|
||||
sizeof(constantsSize)) {
|
||||
goto FAILED;
|
||||
}
|
||||
constantsSize = le64toh(constantsSize);
|
||||
|
||||
uint64_t bytecodeSize;
|
||||
if (fread(&bytecodeSize, 1, sizeof(bytecodeSize), bytecode_file) !=
|
||||
sizeof(bytecodeSize)) {
|
||||
goto FAILED;
|
||||
}
|
||||
bytecodeSize = le64toh(bytecodeSize);
|
||||
|
||||
*translated_dest = init_translator(source_path);
|
||||
|
||||
arena_resize(&translated_dest->constants, constantsSize);
|
||||
|
||||
if (fread(translated_dest->constants.data, 1, constantsSize, bytecode_file) !=
|
||||
constantsSize) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
darray_resize(&translated_dest->bytecode, bytecodeSize);
|
||||
|
||||
if (fread(translated_dest->bytecode.data, 1, bytecodeSize, bytecode_file) !=
|
||||
bytecodeSize) {
|
||||
goto FAILED;
|
||||
}
|
||||
|
||||
fprintf(stderr,"cache exists and is valid, so will be used.\n");
|
||||
fclose(bytecode_file);
|
||||
return 0;
|
||||
FAILED:
|
||||
fprintf(stderr,"cache is invalid... compiling from source.\n");
|
||||
fclose(bytecode_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Execution execute(char *path, Stack *stack) {
|
||||
clock_t start, end;
|
||||
double time_spent, total_time_spent = 0;
|
||||
|
||||
const char *basename_ptr;
|
||||
size_t basename_length;
|
||||
cwk_path_get_basename(path, &basename_ptr, &basename_length);
|
||||
|
||||
if (!basename_ptr)
|
||||
return (Execution){create_err(0, 0, 0, NULL, "Path Error",
|
||||
"path has no basename '%s'", path),
|
||||
(Stack){NULL, NULL}};
|
||||
|
||||
char basename[FILENAME_MAX];
|
||||
memcpy(basename, basename_ptr, basename_length);
|
||||
|
||||
size_t parent_directory_length;
|
||||
cwk_path_get_dirname(path, &parent_directory_length);
|
||||
|
||||
char parent_directory[FILENAME_MAX];
|
||||
memcpy(parent_directory, path, parent_directory_length);
|
||||
parent_directory[parent_directory_length] = '\0';
|
||||
|
||||
char cache_folder_path[FILENAME_MAX];
|
||||
cwk_path_join(parent_directory, CACHE_FOLDER, cache_folder_path,
|
||||
sizeof(cache_folder_path));
|
||||
|
||||
char cache_file_path[FILENAME_MAX];
|
||||
cwk_path_join(cache_folder_path, basename, cache_file_path,
|
||||
sizeof(cache_file_path));
|
||||
cwk_path_change_extension(cache_file_path, BYTECODE_EXTENTION,
|
||||
cache_file_path, sizeof(cache_file_path));
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file) {
|
||||
return (Execution){create_err(0, 0, 0, NULL, "File Error",
|
||||
"Unable to open file '%s'", path),
|
||||
(Stack){NULL, NULL}};
|
||||
}
|
||||
|
||||
XXH3_state_t *hash_state = XXH3_createState();
|
||||
XXH3_64bits_reset(hash_state);
|
||||
|
||||
char buffer[8192];
|
||||
size_t bytes;
|
||||
while ((bytes = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||||
XXH3_64bits_update(hash_state, buffer, bytes);
|
||||
}
|
||||
rewind(file);
|
||||
uint64_t hash = XXH3_64bits_digest(hash_state);
|
||||
XXH3_freeState(hash_state);
|
||||
|
||||
Translated translated;
|
||||
|
||||
if (load_cache(&translated, cache_file_path, hash, path) != 0) {
|
||||
|
||||
DArray tokens;
|
||||
darray_init(&tokens, sizeof(Token));
|
||||
|
||||
LexerState state = {path, file, 0, 0, &tokens};
|
||||
start = clock();
|
||||
ArErr err = lexer(state);
|
||||
if (err.exists) {
|
||||
darray_free(&tokens, free_token);
|
||||
return (Execution){err, (Stack){NULL, NULL}};
|
||||
}
|
||||
end = clock();
|
||||
time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
total_time_spent += time_spent;
|
||||
fprintf(stderr,"Lexer time taken: %f seconds\n", time_spent);
|
||||
fclose(state.file);
|
||||
|
||||
DArray ast;
|
||||
|
||||
darray_init(&ast, sizeof(ParsedValue));
|
||||
|
||||
start = clock();
|
||||
err = parser(path, &ast, &tokens, false);
|
||||
if (err.exists) {
|
||||
darray_free(&tokens, free_token);
|
||||
darray_free(&ast, free_parsed);
|
||||
return (Execution){err, (Stack){NULL, NULL}};
|
||||
}
|
||||
end = clock();
|
||||
time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
total_time_spent += time_spent;
|
||||
fprintf(stderr,"Parser time taken: %f seconds\n", time_spent);
|
||||
darray_free(&tokens, free_token);
|
||||
|
||||
start = clock();
|
||||
|
||||
translated = init_translator(path);
|
||||
err = translate(&translated, &ast);
|
||||
if (err.exists) {
|
||||
darray_free(&translated.bytecode, NULL);
|
||||
free(translated.constants.data);
|
||||
hashmap_free(translated.constants.hashmap, NULL);
|
||||
darray_free(&ast, free_parsed);
|
||||
return (Execution){err, (Stack){NULL, NULL}};
|
||||
}
|
||||
end = clock();
|
||||
time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
total_time_spent += time_spent;
|
||||
fprintf(stderr,"Translation time taken: %f seconds\n", time_spent);
|
||||
|
||||
darray_free(&ast, free_parsed);
|
||||
#if defined(__linux__)
|
||||
malloc_trim(0);
|
||||
#endif
|
||||
|
||||
ensure_dir_exists(cache_folder_path);
|
||||
|
||||
file = fopen(cache_file_path, "wb");
|
||||
|
||||
uint64_t constantsSize = translated.constants.size;
|
||||
uint64_t bytecodeSize = translated.bytecode.size;
|
||||
|
||||
uint32_t version_number_htole32ed = htole32(version_number);
|
||||
uint64_t net_hash = htole64(hash);
|
||||
constantsSize = htole64(constantsSize);
|
||||
bytecodeSize = htole64(bytecodeSize);
|
||||
|
||||
XXH64_state_t *hash_state = XXH64_createState();
|
||||
XXH64_reset(hash_state, 0);
|
||||
|
||||
write_and_hash(file, hash_state, &FILE_IDENTIFIER, sizeof(char),
|
||||
strlen(FILE_IDENTIFIER));
|
||||
write_and_hash(file, hash_state, &version_number_htole32ed,
|
||||
sizeof(uint32_t), 1);
|
||||
write_and_hash(file, hash_state, &net_hash, sizeof(net_hash), 1);
|
||||
write_and_hash(file, hash_state, &translated.registerCount, sizeof(uint8_t),
|
||||
1);
|
||||
write_and_hash(file, hash_state, &constantsSize, sizeof(uint64_t), 1);
|
||||
write_and_hash(file, hash_state, &bytecodeSize, sizeof(uint64_t), 1);
|
||||
write_and_hash(file, hash_state, translated.constants.data, 1,
|
||||
translated.constants.size);
|
||||
write_and_hash(file, hash_state, translated.bytecode.data,
|
||||
translated.bytecode.element_size, translated.bytecode.size);
|
||||
|
||||
// Finalize the hash
|
||||
uint64_t file_hash = XXH64_digest(hash_state);
|
||||
XXH64_freeState(hash_state);
|
||||
|
||||
// Convert to little-endian before writing if needed
|
||||
uint64_t file_hash_le = htole64(file_hash);
|
||||
fwrite(&file_hash_le, sizeof(file_hash_le), 1, file);
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
hashmap_free(translated.constants.hashmap, NULL);
|
||||
Translated gc_translated = {
|
||||
translated.registerCount, NULL, {}, {}, translated.path};
|
||||
gc_translated.bytecode.data = ar_alloc(translated.bytecode.capacity);
|
||||
memcpy(gc_translated.bytecode.data, translated.bytecode.data,
|
||||
translated.bytecode.capacity);
|
||||
gc_translated.bytecode.element_size = translated.bytecode.element_size;
|
||||
gc_translated.bytecode.size = translated.bytecode.size;
|
||||
gc_translated.bytecode.resizable = false;
|
||||
gc_translated.bytecode.capacity =
|
||||
translated.bytecode.size * translated.bytecode.element_size;
|
||||
gc_translated.constants.data = ar_alloc(translated.constants.capacity);
|
||||
memcpy(gc_translated.constants.data, translated.constants.data,
|
||||
translated.constants.capacity);
|
||||
gc_translated.constants.size = translated.constants.size;
|
||||
gc_translated.constants.capacity = translated.constants.capacity;
|
||||
darray_free(&translated.bytecode, NULL);
|
||||
free(translated.constants.data);
|
||||
|
||||
start = clock();
|
||||
RuntimeState state = init_runtime_state(gc_translated, path);
|
||||
Stack *main_scope = create_scope(stack);
|
||||
ArErr err = runtime(gc_translated, state, main_scope);
|
||||
end = clock();
|
||||
time_spent = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
total_time_spent += time_spent;
|
||||
fprintf(stderr,"Execution time taken: %f seconds\n", time_spent);
|
||||
fprintf(stderr,"total time taken: %f seconds\n", total_time_spent);
|
||||
|
||||
return (Execution){err, *main_scope};
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
setlocale(LC_ALL, "");
|
||||
ar_memory_init();
|
||||
|
||||
generate_siphash_key(siphash_key);
|
||||
init_built_in_field_hashes();
|
||||
bootstrap_types();
|
||||
bootstrap_globals();
|
||||
char *CWD = get_current_directory();
|
||||
if (argc <= 1)
|
||||
return -1;
|
||||
return shell();
|
||||
CWD = get_current_directory();
|
||||
char *path_non_absolute = argv[1];
|
||||
char path[FILENAME_MAX];
|
||||
cwk_path_get_absolute(CWD, path_non_absolute, path, sizeof(path));
|
||||
ArErr err = no_err;
|
||||
ar_import(CWD, path_non_absolute, &err);
|
||||
if (err.exists) {
|
||||
output_err(err);
|
||||
return 1;
|
||||
}
|
||||
free(CWD);
|
||||
Execution resp = execute(path, Global_Scope);
|
||||
ar_memory_shutdown();
|
||||
if (runtime_hash_table)
|
||||
hashmap_free(runtime_hash_table, NULL);
|
||||
if (resp.err.exists) {
|
||||
output_err(resp.err);
|
||||
if (err.exists) {
|
||||
output_err(err);
|
||||
return 1;
|
||||
}
|
||||
// Your main thread code
|
||||
|
||||
28
src/memory.c
28
src/memory.c
@@ -6,10 +6,12 @@
|
||||
|
||||
#include "memory.h"
|
||||
#include <gc.h>
|
||||
#include <gmp.h>
|
||||
#include <gc/gc.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // for malloc/free (temp arena fallback)
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
void *checked_malloc(size_t size) {
|
||||
void *ptr = malloc(size);
|
||||
@@ -20,19 +22,23 @@ void *checked_malloc(size_t size) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *gmp_gc_realloc(void *ptr, size_t old_size, size_t new_size) {
|
||||
(void)old_size; // Ignore old_size, Boehm doesn't need it
|
||||
return GC_realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
void gmp_gc_free(void *ptr, size_t size) {
|
||||
(void)size; // Boehm GC manages this itself
|
||||
// No-op — memory will be collected automatically
|
||||
GC_FREE(ptr);
|
||||
}
|
||||
struct allocation*memory_allocations = NULL;
|
||||
size_t memory_allocations_size = 0;
|
||||
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void ar_memory_init() {
|
||||
GC_INIT();
|
||||
// memory_allocations_size = 8;
|
||||
// memory_allocations = malloc(memory_allocations_size*sizeof(struct allocation));
|
||||
}
|
||||
|
||||
void ar_memory_shutdown() {
|
||||
// for (size_t i = 0; i<memory_allocations_size;i++) {
|
||||
// if (memory_allocations[i].status != allocation_fully_freed) {
|
||||
// free(memory_allocations[i].ptr);
|
||||
// }
|
||||
// }
|
||||
// free(memory_allocations);
|
||||
}
|
||||
|
||||
void *ar_alloc(size_t size) { return GC_MALLOC(size); }
|
||||
|
||||
14
src/memory.h
14
src/memory.h
@@ -8,10 +8,23 @@
|
||||
#define ARGON_MEMORY_H
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdbool.h>
|
||||
#include <gc/gc.h>
|
||||
|
||||
// GC-managed allocations
|
||||
|
||||
typedef enum allocation_status {
|
||||
allocation_used,
|
||||
allocation_soft_free, // avaiable for use, since it hasnt been freed but isnt in use.
|
||||
allocation_fully_freed,
|
||||
} allocation_status;
|
||||
|
||||
struct allocation {
|
||||
void*ptr;
|
||||
size_t size;
|
||||
allocation_status status;
|
||||
};
|
||||
|
||||
void ar_finalizer(void *obj, GC_finalization_proc fn, void *client_data,
|
||||
GC_finalization_proc *old_fn, void **old_client_data);
|
||||
void *ar_alloc(size_t size);
|
||||
@@ -21,6 +34,7 @@ char *ar_strdup(const char *str);
|
||||
|
||||
// Memory init/shutdown
|
||||
void ar_memory_init();
|
||||
void ar_memory_shutdown();
|
||||
|
||||
void *checked_malloc(size_t size);
|
||||
|
||||
|
||||
@@ -18,13 +18,12 @@ ParsedValueReturn parse_access(char *file, DArray *tokens, size_t *index,
|
||||
Token *first_token = darray_get(tokens, *index);
|
||||
(*index)++;
|
||||
ParsedValue *parsedValue = checked_malloc(sizeof(ParsedValue));
|
||||
ParsedAccess *parsedAccess = checked_malloc(sizeof(ParsedAccess));
|
||||
parsedAccess->to_access = *to_access;
|
||||
parsedValue->type = AST_ACCESS;
|
||||
parsedValue->data = parsedAccess;
|
||||
free(to_access);
|
||||
darray_init(&parsedAccess->access, sizeof(ParsedValue));
|
||||
if (first_token->type == TOKEN_DOT) {
|
||||
ParsedAccess *parsedAccess = checked_malloc(sizeof(ParsedAccess));
|
||||
parsedAccess->to_access = to_access;
|
||||
parsedAccess->access = NULL;
|
||||
parsedValue->type = AST_ACCESS;
|
||||
parsedValue->data = parsedAccess;
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
free_parsed(parsedValue);
|
||||
@@ -32,6 +31,14 @@ ParsedValueReturn parse_access(char *file, DArray *tokens, size_t *index,
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
Token *token = darray_get(tokens, *index);
|
||||
if (token->type != TOKEN_IDENTIFIER) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"expected identifier after dot"),
|
||||
NULL};
|
||||
}
|
||||
parsedAccess->line = token->line;
|
||||
parsedAccess->column = token->column;
|
||||
parsedAccess->length = token->length;
|
||||
@@ -41,59 +48,7 @@ ParsedValueReturn parse_access(char *file, DArray *tokens, size_t *index,
|
||||
free(parsedValue);
|
||||
return parsedString;
|
||||
}
|
||||
darray_push(&parsedAccess->access, parsedString.value);
|
||||
free(parsedString.value);
|
||||
parsedAccess->access_fields = true;
|
||||
} else {
|
||||
parsedAccess->line = first_token->line;
|
||||
parsedAccess->column = first_token->column;
|
||||
parsedAccess->length = first_token->length;
|
||||
parsedAccess->access_fields = false;
|
||||
Token *token = first_token;
|
||||
while (true) {
|
||||
skip_newlines_and_indents(tokens, index);
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
ParsedValueReturn parsedAccessValue =
|
||||
parse_token(file, tokens, index, true);
|
||||
if (parsedAccessValue.err.exists) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return parsedAccessValue;
|
||||
} else if (!parsedAccessValue.value) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file,
|
||||
"Syntax Error", "expected value"),
|
||||
NULL};
|
||||
}
|
||||
darray_push(&parsedAccess->access, parsedAccessValue.value);
|
||||
free(parsedAccessValue.value);
|
||||
skip_newlines_and_indents(tokens, index);
|
||||
err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
token = darray_get(tokens, *index);
|
||||
if (token->type == TOKEN_RBRACKET) {
|
||||
break;
|
||||
} else if (token->type != TOKEN_COLON) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file,
|
||||
"Syntax Error", "expected colon"),
|
||||
NULL};
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
parsedAccess->access = parsedString.value;
|
||||
}
|
||||
(*index)++;
|
||||
return (ParsedValueReturn){no_err, parsedValue};
|
||||
@@ -102,7 +57,11 @@ ParsedValueReturn parse_access(char *file, DArray *tokens, size_t *index,
|
||||
void free_parse_access(void *ptr) {
|
||||
ParsedValue *parsedValue = ptr;
|
||||
ParsedAccess *parsedAccess = parsedValue->data;
|
||||
free_parsed(&parsedAccess->to_access);
|
||||
darray_free(&parsedAccess->access, free_parsed);
|
||||
free_parsed(parsedAccess->to_access);
|
||||
free(parsedAccess->to_access);
|
||||
if (parsedAccess->access) {
|
||||
free_parsed(parsedAccess->access);
|
||||
free(parsedAccess->access);
|
||||
}
|
||||
free(parsedAccess);
|
||||
}
|
||||
@@ -10,9 +10,8 @@
|
||||
#include "../../../lexer/token.h" // for Token
|
||||
|
||||
typedef struct {
|
||||
ParsedValue to_access;
|
||||
bool access_fields;
|
||||
DArray access;
|
||||
ParsedValue *to_access;
|
||||
ParsedValue *access;
|
||||
size_t line;
|
||||
size_t column;
|
||||
size_t length;
|
||||
|
||||
@@ -7,25 +7,38 @@
|
||||
#include "assign.h"
|
||||
#include "../../../lexer/token.h"
|
||||
#include "../../../memory.h"
|
||||
#include "../../function/function.h"
|
||||
#include "../../parser.h"
|
||||
#include "../../string/string.h"
|
||||
#include "../access/access.h"
|
||||
#include "../call/call.h"
|
||||
#include "../identifier/identifier.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
ParsedValueReturn parse_assign(char *file, DArray *tokens,
|
||||
ParsedValue *assign_to, size_t *index) {
|
||||
Token *token = darray_get(tokens, *index);
|
||||
bool is_function = false;
|
||||
char *function_name;
|
||||
bool to_free_function_name = false;
|
||||
DArray function_args;
|
||||
ParsedValue *function_assign_to;
|
||||
switch (assign_to->type) {
|
||||
case AST_IDENTIFIER:
|
||||
case AST_ACCESS:
|
||||
break;
|
||||
case AST_CALL:;
|
||||
ParsedCall *call = assign_to->data;
|
||||
darray_init(&function_args, sizeof(char *));
|
||||
for (size_t i = 0; i < call->args.size; i++) {
|
||||
if (((ParsedValue *)darray_get(&call->args, i))->type != AST_IDENTIFIER) {
|
||||
ParsedValue *arg = darray_get(&call->args, i);
|
||||
if (arg->type != AST_IDENTIFIER) {
|
||||
free_parsed(assign_to);
|
||||
free(assign_to);
|
||||
darray_free(&function_args, free_parameter);
|
||||
return (ParsedValueReturn){
|
||||
create_err(
|
||||
token->line, token->column, token->length, file, "Syntax Error",
|
||||
@@ -34,6 +47,32 @@ ParsedValueReturn parse_assign(char *file, DArray *tokens,
|
||||
"only use letters, digits, or _, and can't be keywords."),
|
||||
NULL};
|
||||
}
|
||||
|
||||
char *param = strdup(((ParsedIdentifier *)arg->data)->name);
|
||||
darray_push(&function_args, ¶m);
|
||||
}
|
||||
darray_free(&call->args, free_parsed);
|
||||
is_function = true;
|
||||
function_assign_to = call->to_call;
|
||||
switch (function_assign_to->type) {
|
||||
case AST_IDENTIFIER:
|
||||
function_name = ((ParsedIdentifier *)function_assign_to->data)->name;
|
||||
break;
|
||||
case AST_ACCESS:
|
||||
if (((ParsedAccess *)function_assign_to->data)->access->type ==
|
||||
AST_STRING) {
|
||||
ParsedString *name =
|
||||
((ParsedAccess *)function_assign_to->data)->access->data;
|
||||
function_name = checked_malloc(name->length + 1);
|
||||
function_name[name->length] = 0;
|
||||
memcpy(function_name, name->string, name->length);
|
||||
to_free_function_name = true;
|
||||
break;
|
||||
}
|
||||
// FALL THROUGH
|
||||
default:
|
||||
function_name = "anonymous";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
@@ -44,35 +83,40 @@ ParsedValueReturn parse_assign(char *file, DArray *tokens,
|
||||
free(assign_to);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
(*index)++;
|
||||
token = darray_get(tokens, *index);
|
||||
ParsedValueReturn from = parse_token(file, tokens, index, true);
|
||||
if (from.err.exists) {
|
||||
return from;
|
||||
}
|
||||
if (!from.value) {
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"expected body"),
|
||||
NULL};
|
||||
}
|
||||
if (is_function) {
|
||||
from.value =
|
||||
create_parsed_function(function_name, function_args, from.value);
|
||||
if (to_free_function_name) free(function_name);
|
||||
free(assign_to->data);
|
||||
free(assign_to);
|
||||
assign_to = function_assign_to;
|
||||
}
|
||||
ParsedAssign *assign = checked_malloc(sizeof(ParsedAssign));
|
||||
assign->to = assign_to;
|
||||
assign->type = token->type;
|
||||
assign->type = 0;
|
||||
assign->from = NULL;
|
||||
ParsedValue *parsedValue = checked_malloc(sizeof(ParsedValue));
|
||||
parsedValue->type = AST_ASSIGN;
|
||||
parsedValue->data = assign;
|
||||
(*index)++;
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
token = darray_get(tokens, *index);
|
||||
ParsedValueReturn from = parse_token(file, tokens, index, true);
|
||||
if (from.err.exists) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return from;
|
||||
}
|
||||
assign->from = from.value;
|
||||
if (!assign->from) {
|
||||
free_parsed(parsedValue);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"expected body"),
|
||||
NULL};
|
||||
}
|
||||
return (ParsedValueReturn){no_err, parsedValue};
|
||||
}
|
||||
|
||||
|
||||
50
src/parser/not/not.c
Normal file
50
src/parser/not/not.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include "not.h"
|
||||
#include "../../lexer/token.h"
|
||||
#include "../../memory.h"
|
||||
#include "../parser.h"
|
||||
#include <stdio.h>
|
||||
|
||||
ParsedValueReturn parse_not(char *file, DArray *tokens, size_t *index) {
|
||||
bool invert = true;
|
||||
(*index)++;
|
||||
while (tokens->size > *index) {
|
||||
Token *token = darray_get(tokens, *index);
|
||||
if (token->type != TOKEN_EXCLAMATION) {
|
||||
ParsedValueReturn value =
|
||||
parse_token_full(file, tokens, index, true, false);
|
||||
if (value.err.exists) {
|
||||
return value;
|
||||
} else if (!value.value) {
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file,
|
||||
"Syntax Error", "expected value"),
|
||||
NULL};
|
||||
}
|
||||
|
||||
ParsedValue *parsedValue = checked_malloc(sizeof(ParsedValue));
|
||||
ParsedToBool *parsedToBool = checked_malloc(sizeof(ParsedToBool));
|
||||
parsedToBool->value = value.value;
|
||||
parsedToBool->invert = invert;
|
||||
parsedValue->data = parsedToBool;
|
||||
parsedValue->type = AST_TO_BOOL;
|
||||
return (ParsedValueReturn){no_err, parsedValue};
|
||||
}
|
||||
invert = !invert;
|
||||
(*index)++;
|
||||
}
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
|
||||
void free_not(void *ptr) {
|
||||
ParsedValue *parsedValue = ptr;
|
||||
ParsedToBool *parsedToBool = parsedValue->data;
|
||||
free_parsed(parsedToBool->value);
|
||||
free(parsedToBool->value);
|
||||
free(parsedToBool);
|
||||
}
|
||||
20
src/parser/not/not.h
Normal file
20
src/parser/not/not.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef NOT_H
|
||||
#define NOT_H
|
||||
#include "../parser.h"
|
||||
|
||||
typedef struct {
|
||||
bool invert;
|
||||
ParsedValue*value;
|
||||
} ParsedToBool;
|
||||
|
||||
ParsedValueReturn parse_not(char *file, DArray *tokens, size_t *index);
|
||||
|
||||
void free_not(void *ptr);
|
||||
|
||||
#endif // NOT_H
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "../../memory.h"
|
||||
#include <ctype.h>
|
||||
#include <gmp.h>
|
||||
#include <gmp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -180,7 +179,8 @@ ParsedValueReturn parse_number(Token *token, char *path) {
|
||||
mpq_init(*r_ptr);
|
||||
int err = mpq_set_decimal_str_exp(*r_ptr, token->value, token->length);
|
||||
if (err) {
|
||||
free_parsed(parsedValue);
|
||||
mpq_clear(*r_ptr);
|
||||
free(r_ptr);
|
||||
free(parsedValue);
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, path, "Parsing Error",
|
||||
@@ -189,4 +189,5 @@ ParsedValueReturn parse_number(Token *token, char *path) {
|
||||
}
|
||||
parsedValue->data = r_ptr;
|
||||
return (ParsedValueReturn){no_err, parsedValue};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct operation {};
|
||||
|
||||
ParsedValue convert_to_operation(DArray *to_operate_on, DArray *operations) {
|
||||
if (to_operate_on->size == 1) {
|
||||
return *((ParsedValue *)darray_get(to_operate_on, 0));
|
||||
@@ -31,7 +29,9 @@ ParsedValue convert_to_operation(DArray *to_operate_on, DArray *operations) {
|
||||
operation = *current_operation;
|
||||
darray_init(&positions, sizeof(size_t));
|
||||
}
|
||||
darray_push(&positions, &i);
|
||||
if (operation_type == current_operation->type) {
|
||||
darray_push(&positions, &i);
|
||||
}
|
||||
}
|
||||
ParsedValue parsedValue;
|
||||
parsedValue.type = AST_OPERATION;
|
||||
@@ -42,24 +42,22 @@ ParsedValue convert_to_operation(DArray *to_operate_on, DArray *operations) {
|
||||
operationStruct->column = operation.column;
|
||||
operationStruct->length = operation.length;
|
||||
darray_init(&operationStruct->to_operate_on, sizeof(ParsedValue));
|
||||
size_t last_position = 0;
|
||||
size_t to_operate_on_last_position = 0;
|
||||
for (size_t i = 0; i < positions.size; i++) {
|
||||
size_t *position = darray_get(&positions, i);
|
||||
DArray to_operate_on_slice = darray_slice(
|
||||
to_operate_on, to_operate_on_last_position, (*position) + 1);
|
||||
DArray operations_slice =
|
||||
darray_slice(operations, last_position, *position);
|
||||
darray_slice(operations, to_operate_on_last_position, (*position));
|
||||
ParsedValue result =
|
||||
convert_to_operation(&to_operate_on_slice, &operations_slice);
|
||||
darray_push(&operationStruct->to_operate_on, &result);
|
||||
last_position = (*position);
|
||||
to_operate_on_last_position = (*position) + 1;
|
||||
}
|
||||
DArray to_operate_on_slice = darray_slice(
|
||||
to_operate_on, to_operate_on_last_position, to_operate_on->size);
|
||||
DArray operations_slice =
|
||||
darray_slice(operations, last_position, operations->size);
|
||||
darray_slice(operations, to_operate_on_last_position, operations->size);
|
||||
ParsedValue result =
|
||||
convert_to_operation(&to_operate_on_slice, &operations_slice);
|
||||
darray_push(&operationStruct->to_operate_on, &result);
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "parentheses-and-anonymous-function.h"
|
||||
#include "../../memory.h"
|
||||
#include "../assignable/identifier/identifier.h"
|
||||
#include "../function/function.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
ParsedValueReturn parse_parentheses(char *file, DArray *tokens, size_t *index) {
|
||||
(*index)++;
|
||||
skip_newlines_and_indents(tokens, index);
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
// darray_free(&list, free_parsed);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
DArray list;
|
||||
darray_init(&list, sizeof(ParsedValue));
|
||||
Token *token = darray_get(tokens, *index);
|
||||
if (token->type != TOKEN_RPAREN) {
|
||||
while (*index < tokens->size) {
|
||||
ParsedValueReturn parsedItem = parse_token(file, tokens, index, true);
|
||||
if (parsedItem.err.exists) {
|
||||
darray_free(&list, free_parsed);
|
||||
return parsedItem;
|
||||
}
|
||||
darray_push(&list, parsedItem.value);
|
||||
free(parsedItem.value);
|
||||
skip_newlines_and_indents(tokens, index);
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
darray_free(&list, free_parsed);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
token = darray_get(tokens, *index);
|
||||
if (token->type == TOKEN_RPAREN) {
|
||||
break;
|
||||
} else if (token->type != TOKEN_COMMA) {
|
||||
darray_free(&list, free_parsed);
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file,
|
||||
"Syntax Error", "expected comma"),
|
||||
NULL};
|
||||
}
|
||||
skip_newlines_and_indents(tokens, index);
|
||||
err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
darray_free(&list, free_parsed);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
}
|
||||
}
|
||||
(*index)++;
|
||||
if (*index < tokens->size) {
|
||||
token = darray_get(tokens, *index);
|
||||
if (token->type == TOKEN_ASSIGN) {
|
||||
(*index)++;
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
darray_free(&list, free_parsed);
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
DArray parameters;
|
||||
darray_init(¶meters, sizeof(char *));
|
||||
for (size_t i = 0; i < list.size; i++) {
|
||||
ParsedValue *item = darray_get(&list, i);
|
||||
if (item->type != AST_IDENTIFIER) {
|
||||
darray_free(&list, free_parsed);
|
||||
darray_free(¶meters, free_parameter);
|
||||
return (ParsedValueReturn){
|
||||
create_err(token->line, token->column, token->length, file,
|
||||
"Syntax Error", "expected identifier"),
|
||||
NULL};
|
||||
}
|
||||
char *param = strdup(((ParsedIdentifier *)item->data)->name);
|
||||
darray_push(¶meters, ¶m);
|
||||
}
|
||||
darray_free(&list, free_parsed);
|
||||
ParsedValueReturn parsedBody = parse_token(file, tokens, index, true);
|
||||
if (parsedBody.err.exists) {
|
||||
darray_free(¶meters, free_parameter);
|
||||
return parsedBody;
|
||||
}
|
||||
return (ParsedValueReturn){
|
||||
no_err,
|
||||
create_parsed_function("anonymous", parameters, parsedBody.value)};
|
||||
}
|
||||
}
|
||||
if (list.size != 1) {
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"expected 1 body"),
|
||||
NULL};
|
||||
}
|
||||
ParsedValue *parsedValue = checked_malloc(sizeof(ParsedValue));
|
||||
memcpy(parsedValue, darray_get(&list, 0), sizeof(ParsedValue));
|
||||
darray_free(&list, NULL);
|
||||
return (ParsedValueReturn){no_err, parsedValue};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef parentheses_and_anonymous_function_H
|
||||
#define parentheses_and_anonymous_function_H
|
||||
#include "../parser.h"
|
||||
#include "../../lexer/token.h"
|
||||
|
||||
ParsedValueReturn parse_parentheses(char *file, DArray *tokens, size_t *index);
|
||||
|
||||
#endif // parentheses_and_anonymous_function_H
|
||||
@@ -20,8 +20,11 @@
|
||||
#include "literals/literals.h"
|
||||
#include "number/number.h"
|
||||
#include "operations/operations.h"
|
||||
#include "parentheses-and-anonymous-function/parentheses-and-anonymous-function.h"
|
||||
#include "return/return.h"
|
||||
#include "string/string.h"
|
||||
#include "not/not.h"
|
||||
#include "while/while.h"
|
||||
#include <gmp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
@@ -30,10 +33,10 @@
|
||||
#include <string.h>
|
||||
|
||||
const char *ValueTypeNames[] = {
|
||||
"string", "assign", "identifier", "number",
|
||||
"if statement", "access", "call", "declaration",
|
||||
"null", "boolean", "do wrap", "operations",
|
||||
"list", "dictionary", "function", "return"};
|
||||
"string", "assign", "identifier", "number", "if statement",
|
||||
"access", "call", "declaration", "null", "boolean",
|
||||
"do wrap", "operations", "list", "dictionary", "function",
|
||||
"return", "while loop", "not"};
|
||||
|
||||
ArErr error_if_finished(char *file, DArray *tokens, size_t *index) {
|
||||
if ((*index) >= tokens->size) {
|
||||
@@ -72,6 +75,8 @@ ParsedValueReturn parse_token_full(char *file, DArray *tokens, size_t *index,
|
||||
switch (token->type) {
|
||||
case TOKEN_IF:
|
||||
return parse_if(file, tokens, index);
|
||||
case TOKEN_WHILE:
|
||||
return parse_while(file, tokens, index);
|
||||
case TOKEN_RETURN:
|
||||
return parse_return(file, tokens, index);
|
||||
case TOKEN_LET:
|
||||
@@ -128,9 +133,15 @@ ParsedValueReturn parse_token_full(char *file, DArray *tokens, size_t *index,
|
||||
case TOKEN_LBRACKET:
|
||||
output = parse_list(file, tokens, index);
|
||||
break;
|
||||
case TOKEN_LPAREN:
|
||||
output = parse_parentheses(file, tokens, index);
|
||||
break;
|
||||
case TOKEN_LBRACE:
|
||||
output = parse_dictionary(file, tokens, index);
|
||||
break;
|
||||
case TOKEN_EXCLAMATION:
|
||||
output = parse_not(file, tokens, index);
|
||||
break;
|
||||
default:
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
@@ -147,13 +158,6 @@ ParsedValueReturn parse_token_full(char *file, DArray *tokens, size_t *index,
|
||||
token = darray_get(tokens, *index);
|
||||
switch (token->type) {
|
||||
case TOKEN_ASSIGN:
|
||||
case TOKEN_ASSIGN_CARET:
|
||||
case TOKEN_ASSIGN_FLOORDIV:
|
||||
case TOKEN_ASSIGN_MINUS:
|
||||
case TOKEN_ASSIGN_MODULO:
|
||||
case TOKEN_ASSIGN_PLUS:
|
||||
case TOKEN_ASSIGN_SLASH:
|
||||
case TOKEN_ASSIGN_STAR:
|
||||
output = parse_assign(file, tokens, output.value, index);
|
||||
break;
|
||||
case TOKEN_LPAREN:
|
||||
@@ -243,6 +247,9 @@ void free_parsed(void *ptr) {
|
||||
case AST_IF:
|
||||
free_parsed_if(parsed);
|
||||
break;
|
||||
case AST_WHILE:
|
||||
free_parsed_while(parsed);
|
||||
break;
|
||||
case AST_OPERATION:
|
||||
free_operation(parsed);
|
||||
break;
|
||||
@@ -261,5 +268,8 @@ void free_parsed(void *ptr) {
|
||||
case AST_RETURN:
|
||||
free_parsed_return(parsed);
|
||||
break;
|
||||
case AST_TO_BOOL:
|
||||
free_not(parsed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,9 @@ typedef enum {
|
||||
AST_LIST,
|
||||
AST_DICTIONARY,
|
||||
AST_FUNCTION,
|
||||
AST_RETURN
|
||||
AST_RETURN,
|
||||
AST_WHILE,
|
||||
AST_TO_BOOL
|
||||
} ValueType;
|
||||
|
||||
extern const char *ValueTypeNames[];
|
||||
|
||||
101
src/parser/while/while.c
Normal file
101
src/parser/while/while.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include "while.h"
|
||||
#include "../../lexer/token.h"
|
||||
#include "../../memory.h"
|
||||
#include "../parser.h"
|
||||
#include <stddef.h>
|
||||
|
||||
ParsedValueReturn parse_while(char *file, DArray *tokens, size_t *index) {
|
||||
Token *token = darray_get(tokens, *index);
|
||||
(*index)++;
|
||||
// Parse ( condition )
|
||||
token = darray_get(tokens, *index);
|
||||
if (token->type != TOKEN_LPAREN) {
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"expected '(' after while"),
|
||||
NULL};
|
||||
}
|
||||
|
||||
(*index)++;
|
||||
ArErr err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
skip_newlines_and_indents(tokens, index);
|
||||
ParsedValueReturn condition = parse_token(file, tokens, index, true);
|
||||
if (condition.err.exists) {
|
||||
return condition;
|
||||
} else if (!condition.value) {
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"expected condition"),
|
||||
NULL};
|
||||
}
|
||||
skip_newlines_and_indents(tokens, index);
|
||||
|
||||
token = darray_get(tokens, *index);
|
||||
if (token->type != TOKEN_RPAREN) {
|
||||
if (condition.value) {
|
||||
free_parsed(condition.value);
|
||||
free(condition.value);
|
||||
}
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"missing closing ')' in condition"),
|
||||
NULL};
|
||||
}
|
||||
|
||||
(*index)++;
|
||||
err = error_if_finished(file, tokens, index);
|
||||
if (err.exists) {
|
||||
if (condition.value) {
|
||||
free_parsed(condition.value);
|
||||
free(condition.value);
|
||||
}
|
||||
return (ParsedValueReturn){err, NULL};
|
||||
}
|
||||
// Parse the body
|
||||
ParsedValueReturn parsed_content = parse_token(file, tokens, index, false);
|
||||
|
||||
if (parsed_content.err.exists) {
|
||||
if (condition.value) {
|
||||
free_parsed(condition.value);
|
||||
free(condition.value);
|
||||
}
|
||||
return parsed_content;
|
||||
}
|
||||
|
||||
if (!parsed_content.value) {
|
||||
if (condition.value) {
|
||||
free_parsed(condition.value);
|
||||
free(condition.value);
|
||||
}
|
||||
return (ParsedValueReturn){create_err(token->line, token->column,
|
||||
token->length, file, "Syntax Error",
|
||||
"expected body"),
|
||||
NULL};
|
||||
}
|
||||
|
||||
ParsedValue *Parsedvalue = checked_malloc(sizeof(ParsedValue));
|
||||
Parsedvalue->type = AST_WHILE;
|
||||
ParsedWhile *Parsed_while = checked_malloc(sizeof(ParsedWhile));
|
||||
Parsedvalue->data = Parsed_while;
|
||||
Parsed_while->condition = condition.value;
|
||||
Parsed_while->content = parsed_content.value;
|
||||
return (ParsedValueReturn){no_err, Parsedvalue};
|
||||
}
|
||||
|
||||
void free_parsed_while(void *ptr) {
|
||||
ParsedValue *parsedValue = ptr;
|
||||
ParsedWhile *parsed_while = parsedValue->data;
|
||||
free_parsed(parsed_while->condition);
|
||||
free(parsed_while->condition);
|
||||
free_parsed(parsed_while->content);
|
||||
free(parsed_while->content);
|
||||
free(parsed_while);
|
||||
}
|
||||
20
src/parser/while/while.h
Normal file
20
src/parser/while/while.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PARSE_WHILE_H
|
||||
#define PARSE_WHILE_H
|
||||
#include "../parser.h"
|
||||
|
||||
typedef struct {
|
||||
ParsedValue * condition;
|
||||
ParsedValue *content;
|
||||
} ParsedWhile;
|
||||
|
||||
ParsedValueReturn parse_while(char *file, DArray *tokens, size_t *index);
|
||||
|
||||
void free_parsed_while(void *ptr);
|
||||
|
||||
#endif // PARSE_WHILE_H
|
||||
@@ -8,21 +8,16 @@
|
||||
#define RETURN_TYPES_H
|
||||
#include <stdint.h>
|
||||
#include "arobject.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define ERR_MSG_MAX_LEN 256
|
||||
|
||||
typedef struct ArErr {
|
||||
bool exists;
|
||||
char *path;
|
||||
char path[FILENAME_MAX];
|
||||
char message[128];
|
||||
char type[64];
|
||||
int64_t line;
|
||||
int64_t column;
|
||||
int length;
|
||||
char type[32];
|
||||
char message[ERR_MSG_MAX_LEN];
|
||||
bool exists;
|
||||
} ArErr;
|
||||
|
||||
typedef struct Execution {
|
||||
ArErr err;
|
||||
Stack stack;
|
||||
} Execution;
|
||||
#endif // RETURN_TYPES_
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include "access.h"
|
||||
#include <stdio.h>
|
||||
|
||||
ArgonObject *ARGON_TYPE_TYPE___get_attr__(size_t argc, ArgonObject **argv,
|
||||
ArErr *err, RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 3) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__get_attr__ expects 3 arguments, got %" PRIu64);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
ArgonObject *to_access = argv[0];
|
||||
ArgonObject *access = argv[1];
|
||||
bool check_field = argv[2] == ARGON_TRUE;
|
||||
if (check_field) {
|
||||
ArgonObject *value = get_field_l(to_access, access->value.as_str.data,
|
||||
access->value.as_str.length, true, false);
|
||||
if (value)
|
||||
return value;
|
||||
}
|
||||
ArgonObject *name = get_field_for_class(
|
||||
get_field(to_access, "__class__", false, false), "__name__", to_access);
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"'%.*s' object has no attribute '%.*s'",
|
||||
(int)name->value.as_str.length, name->value.as_str.data,
|
||||
(int)access->value.as_str.length, access->value.as_str.data);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef runtime_access_H
|
||||
#define runtime_access_H
|
||||
#include "../objects/literals/literals.h"
|
||||
#include "../objects/object.h"
|
||||
#include "../runtime.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
ArgonObject *ARGON_TYPE_TYPE___get_attr__(size_t argc, ArgonObject **argv,
|
||||
ArErr *err, RuntimeState *state);
|
||||
|
||||
#endif // runtime_access_H
|
||||
29
src/runtime/assignment/assignment.c
Normal file
29
src/runtime/assignment/assignment.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "assignment.h"
|
||||
|
||||
void runtime_assignment(Translated *translated, RuntimeState *state,
|
||||
struct Stack *stack) {
|
||||
int64_t length = pop_bytecode(translated, state);
|
||||
int64_t offset = pop_bytecode(translated, state);
|
||||
int64_t prehash = pop_bytecode(translated, state);
|
||||
int64_t from_register = pop_byte(translated, state);
|
||||
void *data = arena_get(&translated->constants, offset);
|
||||
uint64_t hash = runtime_hash(data, length, prehash);
|
||||
ArgonObject *key = new_string_object(data, length, prehash, hash);
|
||||
for (Stack *current_stack = stack; current_stack;
|
||||
current_stack = current_stack->prev) {
|
||||
ArgonObject *exists = hashmap_lookup_GC(current_stack->scope, hash);
|
||||
if (exists) {
|
||||
hashmap_insert_GC(current_stack->scope, hash, key,
|
||||
state->registers[from_register], 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
hashmap_insert_GC(stack->scope, hash, key, state->registers[from_register],
|
||||
0);
|
||||
}
|
||||
15
src/runtime/assignment/assignment.h
Normal file
15
src/runtime/assignment/assignment.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef runtime_assignment_H
|
||||
#define runtime_assignment_H
|
||||
#include "../runtime.h"
|
||||
#include "../objects/string/string.h"
|
||||
|
||||
void runtime_assignment(Translated *translated, RuntimeState *state,
|
||||
struct Stack *stack);
|
||||
|
||||
#endif // runtime_assignment_H
|
||||
@@ -16,6 +16,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0602
|
||||
#endif
|
||||
#include <psapi.h>
|
||||
#include <windows.h>
|
||||
|
||||
@@ -72,27 +75,26 @@ double get_memory_usage_mb() {
|
||||
|
||||
ArgonObject *argon_call(ArgonObject *original_object, size_t argc,
|
||||
ArgonObject **argv, ArErr *err, RuntimeState *state) {
|
||||
*err = run_call(original_object, argc, argv, state, true);
|
||||
run_call(original_object, argc, argv, state, true, err);
|
||||
return state->registers[0];
|
||||
}
|
||||
|
||||
ArErr run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
|
||||
RuntimeState *state, bool CStackFrame) {
|
||||
void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
|
||||
RuntimeState *state, bool CStackFrame, ArErr *err) {
|
||||
ArgonObject *object = original_object;
|
||||
if (object->type != TYPE_FUNCTION && object->type != TYPE_NATIVE_FUNCTION &&
|
||||
object->type != TYPE_METHOD) {
|
||||
ArgonObject *call_method =
|
||||
get_field_for_class(get_field(object, "__class__", false, false),
|
||||
"__call__", original_object);
|
||||
ArgonObject *call_method = get_builtin_field_for_class(
|
||||
get_builtin_field(object, __class__), __call__, original_object);
|
||||
if (call_method) {
|
||||
object = call_method;
|
||||
}
|
||||
}
|
||||
if (object->type == TYPE_METHOD) {
|
||||
ArgonObject *binding_object =
|
||||
get_field(object, "__binding__", false, false);
|
||||
ArgonObject *binding_object = get_builtin_field(object, __binding__);
|
||||
if (binding_object) {
|
||||
ArgonObject **new_call_args = ar_alloc(sizeof(ArgonObject *) * (argc + 1));
|
||||
ArgonObject **new_call_args =
|
||||
ar_alloc(sizeof(ArgonObject *) * (argc + 1));
|
||||
new_call_args[0] = binding_object;
|
||||
if (argc > 0) {
|
||||
memcpy(new_call_args + 1, argv, argc * sizeof(ArgonObject *));
|
||||
@@ -100,46 +102,48 @@ ArErr run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
|
||||
argv = new_call_args;
|
||||
argc++;
|
||||
}
|
||||
ArgonObject *function_object =
|
||||
get_field(object, "__function__", false, false);
|
||||
ArgonObject *function_object = get_builtin_field(object, __function__);
|
||||
if (function_object)
|
||||
object = function_object;
|
||||
}
|
||||
if (object->type == TYPE_FUNCTION) {
|
||||
if (argc != object->value.argon_fn.number_of_parameters) {
|
||||
ArgonObject *type_object_name =
|
||||
get_field_for_class(get_field(object, "__class__", false, false),
|
||||
"__name__", original_object);
|
||||
if (argc != object->value.argon_fn->number_of_parameters) {
|
||||
ArgonObject *type_object_name = get_builtin_field_for_class(
|
||||
get_builtin_field(object, __class__), __name__, original_object);
|
||||
ArgonObject *object_name =
|
||||
get_field_for_class(object, "__name__", original_object);
|
||||
return create_err(
|
||||
get_builtin_field_for_class(object, __name__, original_object);
|
||||
*err = create_err(
|
||||
state->source_location.line, state->source_location.column,
|
||||
state->source_location.length, state->path, "Type Error",
|
||||
"%.*s %.*s takes %" PRIu64 " argument(s) but %" PRIu64 " was given",
|
||||
(int)type_object_name->value.as_str.length,
|
||||
type_object_name->value.as_str.data,
|
||||
(int)object_name->value.as_str.length, object_name->value.as_str.data,
|
||||
object->value.argon_fn.number_of_parameters, argc);
|
||||
(int)type_object_name->value.as_str->length,
|
||||
type_object_name->value.as_str->data,
|
||||
(int)object_name->value.as_str->length,
|
||||
object_name->value.as_str->data,
|
||||
object->value.argon_fn->number_of_parameters, argc);
|
||||
}
|
||||
Stack *scope = create_scope(object->value.argon_fn.stack);
|
||||
Stack *scope = create_scope(object->value.argon_fn->stack, true);
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
struct string_struct key = object->value.argon_fn.parameters[i];
|
||||
struct string_struct key = object->value.argon_fn->parameters[i];
|
||||
ArgonObject *value = argv[i];
|
||||
uint64_t hash = siphash64_bytes(key.data, key.length, siphash_key);
|
||||
hashmap_insert_GC(scope->scope, hash,
|
||||
new_string_object(key.data, key.length), value, 0);
|
||||
new_string_object(key.data, key.length, 0, hash), value,
|
||||
0);
|
||||
}
|
||||
StackFrame new_stackFrame = {
|
||||
{object->value.argon_fn.translated.registerCount,
|
||||
{object->value.argon_fn->translated.registerCount,
|
||||
object->value.argon_fn->translated.registerAssignment,
|
||||
NULL,
|
||||
{object->value.argon_fn.bytecode, sizeof(uint8_t),
|
||||
object->value.argon_fn.bytecode_length,
|
||||
object->value.argon_fn.bytecode_length, false},
|
||||
object->value.argon_fn.translated.constants,
|
||||
object->value.argon_fn.translated.path},
|
||||
{state->registers,
|
||||
{object->value.argon_fn->bytecode, sizeof(uint8_t),
|
||||
object->value.argon_fn->bytecode_length,
|
||||
object->value.argon_fn->bytecode_length, false},
|
||||
object->value.argon_fn->translated.constants,
|
||||
object->value.argon_fn->translated.path},
|
||||
{ar_alloc(object->value.argon_fn->translated.registerCount *
|
||||
sizeof(ArgonObject *)),
|
||||
0,
|
||||
object->value.argon_fn.translated.path,
|
||||
object->value.argon_fn->translated.path,
|
||||
NULL,
|
||||
state->currentStackFramePointer,
|
||||
{},
|
||||
@@ -147,17 +151,17 @@ ArErr run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
|
||||
scope,
|
||||
*state->currentStackFramePointer,
|
||||
(*state->currentStackFramePointer)->depth + 1};
|
||||
for (size_t i = 0; i < new_stackFrame.translated.registerCount; i++) {
|
||||
new_stackFrame.state.registers[i] = NULL;
|
||||
}
|
||||
if (CStackFrame) {
|
||||
return runtime(new_stackFrame.translated, new_stackFrame.state, new_stackFrame.stack);
|
||||
runtime(new_stackFrame.translated, new_stackFrame.state,
|
||||
new_stackFrame.stack, err);
|
||||
state->registers[0] = new_stackFrame.state.registers[0];
|
||||
} else {
|
||||
if (((*state->currentStackFramePointer)->depth + 1) % STACKFRAME_CHUNKS ==
|
||||
0) {
|
||||
*state->currentStackFramePointer =
|
||||
ar_alloc(sizeof(StackFrame) * STACKFRAME_CHUNKS);
|
||||
} else {
|
||||
*state->currentStackFramePointer = *state->currentStackFramePointer + 1;
|
||||
}
|
||||
**state->currentStackFramePointer = new_stackFrame;
|
||||
StackFrame *currentStackFrame = ar_alloc(sizeof(StackFrame));
|
||||
*currentStackFrame = new_stackFrame;
|
||||
*state->currentStackFramePointer = currentStackFrame;
|
||||
if ((*state->currentStackFramePointer)->depth >= 10000) {
|
||||
double logval =
|
||||
log10((double)(*state->currentStackFramePointer)->depth);
|
||||
@@ -176,25 +180,23 @@ ArErr run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
|
||||
}
|
||||
}
|
||||
};
|
||||
return no_err;
|
||||
return;
|
||||
}
|
||||
} else if (object->type == TYPE_NATIVE_FUNCTION) {
|
||||
ArErr err = no_err;
|
||||
state->registers[0] = object->value.native_fn(argc, argv, &err, state);
|
||||
if (err.exists && strlen(err.path) == 0) {
|
||||
err.line = state->source_location.line;
|
||||
err.column = state->source_location.column;
|
||||
err.length = state->source_location.length;
|
||||
err.path = state->path;
|
||||
state->registers[0] = object->value.native_fn(argc, argv, err, state);
|
||||
if (err->exists && strlen(err->path) == 0) {
|
||||
err->line = state->source_location.line;
|
||||
err->column = state->source_location.column;
|
||||
err->length = state->source_location.length;
|
||||
strcpy(err->path, state->path);
|
||||
}
|
||||
return err;
|
||||
return;
|
||||
}
|
||||
ArgonObject *type_object_name =
|
||||
get_field_for_class(get_field(original_object, "__class__", false, false),
|
||||
"__name__", original_object);
|
||||
return create_err(state->source_location.line, state->source_location.column,
|
||||
ArgonObject *type_object_name = get_builtin_field_for_class(
|
||||
get_builtin_field(original_object, __class__), __name__, original_object);
|
||||
*err = create_err(state->source_location.line, state->source_location.column,
|
||||
state->source_location.length, state->path, "Type Error",
|
||||
"'%.*s' object is not callable",
|
||||
(int)type_object_name->value.as_str.length,
|
||||
type_object_name->value.as_str.data);
|
||||
(int)type_object_name->value.as_str->length,
|
||||
type_object_name->value.as_str->data);
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
ArgonObject *argon_call(ArgonObject *original_object, size_t argc,
|
||||
ArgonObject **argv, ArErr *err, RuntimeState *state);
|
||||
|
||||
ArErr run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
|
||||
RuntimeState *state, bool CStackFrame);
|
||||
void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
|
||||
RuntimeState *state, bool CStackFrame, ArErr *err);
|
||||
|
||||
#endif // runtime_call_H
|
||||
@@ -6,18 +6,23 @@
|
||||
|
||||
#include "declaration.h"
|
||||
|
||||
ArErr runtime_declaration(Translated *translated, RuntimeState *state,
|
||||
struct Stack *stack) {
|
||||
void runtime_declaration(Translated *translated, RuntimeState *state,
|
||||
struct Stack *stack, ArErr *err) {
|
||||
int64_t length = pop_bytecode(translated, state);
|
||||
int64_t offset = pop_bytecode(translated, state);
|
||||
int64_t prehash = pop_bytecode(translated, state);
|
||||
int64_t from_register = pop_byte(translated, state);
|
||||
uint64_t hash = runtime_hash(arena_get(&translated->constants, offset), length, prehash);
|
||||
ArgonObject * exists = hashmap_lookup_GC(stack->scope, hash);
|
||||
void *data = arena_get(&translated->constants, offset);
|
||||
uint64_t hash = runtime_hash(data, length, prehash);
|
||||
ArgonObject *exists = hashmap_lookup_GC(stack->scope, hash);
|
||||
if (exists) {
|
||||
return create_err(state->source_location.line, state->source_location.column, state->source_location.length, state->path, "Runtime Error", "Identifier '%.*s' has already been declared in the current scope", length, arena_get(&translated->constants, offset));
|
||||
*err = create_err(
|
||||
state->source_location.line, state->source_location.column,
|
||||
state->source_location.length, state->path, "Runtime Error",
|
||||
"Identifier '%.*s' has already been declared in the current scope",
|
||||
length, arena_get(&translated->constants, offset));
|
||||
}
|
||||
ArgonObject * key = new_string_object(arena_get(&translated->constants, offset), length);
|
||||
hashmap_insert_GC(stack->scope, hash, key, state->registers[from_register], 0);
|
||||
return no_err;
|
||||
ArgonObject *key = new_string_object(data, length, prehash, hash);
|
||||
hashmap_insert_GC(stack->scope, hash, key, state->registers[from_register],
|
||||
0);
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "../runtime.h"
|
||||
#include "../objects/string/string.h"
|
||||
|
||||
ArErr runtime_declaration(Translated *translated, RuntimeState *state,
|
||||
struct Stack *stack);
|
||||
void runtime_declaration(Translated *translated, RuntimeState *state,
|
||||
struct Stack *stack, ArErr *err);
|
||||
|
||||
#endif // runtime_declaration_H
|
||||
@@ -10,23 +10,58 @@
|
||||
#include <gc/gc.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct hashmap_GC *createHashmap_GC() {
|
||||
size_t size = 8;
|
||||
struct hashmap_GC *t =
|
||||
(struct hashmap_GC *)ar_alloc(sizeof(struct hashmap_GC));
|
||||
t->size = size;
|
||||
t->size = 0;
|
||||
t->order = 1;
|
||||
t->list = (struct node_GC **)ar_alloc(sizeof(struct node_GC *) * size);
|
||||
memset(t->list, 0, sizeof(struct node_GC *) * size);
|
||||
t->list = NULL;
|
||||
t->hashmap_count = 0;
|
||||
t->count = 0;
|
||||
t->inline_count = 0;
|
||||
return t;
|
||||
}
|
||||
void hashmap_GC_to_array(struct hashmap_GC *t, struct node_GC**array,
|
||||
size_t *array_length) {
|
||||
size_t array_size = 8;
|
||||
*array_length = 0;
|
||||
*array = ar_alloc(array_size * sizeof(struct node_GC));
|
||||
for (size_t i = 0; i < t->inline_count; i++) {
|
||||
if (*array_length >=array_size) {
|
||||
array_size*=2;
|
||||
*array=ar_realloc(*array, array_size * sizeof(struct node_GC));
|
||||
}
|
||||
(*array)[(*array_length)++] = t->inline_values[i];
|
||||
}
|
||||
for (size_t i = 0; i < t->size; i++) {
|
||||
if (!t->list[i]) continue;
|
||||
if (*array_length >=array_size) {
|
||||
array_size*=2;
|
||||
*array=ar_realloc(*array, array_size * sizeof(struct node_GC));
|
||||
}
|
||||
(*array)[(*array_length)++] = *t->list[i];
|
||||
}
|
||||
}
|
||||
|
||||
void clear_hashmap_GC(struct hashmap_GC *t) {
|
||||
if (!t->count)
|
||||
return;
|
||||
t->order = 1;
|
||||
t->count = 0;
|
||||
t->inline_count = 0;
|
||||
t->hashmap_count = 0;
|
||||
memset(t->list, 0, sizeof(struct node_GC *) * t->size);
|
||||
}
|
||||
|
||||
void resize_hashmap_GC(struct hashmap_GC *t) {
|
||||
int old_size = t->size;
|
||||
int new_size = old_size * 2;
|
||||
if (new_size == 0)
|
||||
new_size = 8;
|
||||
|
||||
struct node_GC **old_list = t->list;
|
||||
|
||||
@@ -35,8 +70,9 @@ void resize_hashmap_GC(struct hashmap_GC *t) {
|
||||
memset(t->list, 0, sizeof(struct node_GC *) * new_size);
|
||||
|
||||
t->size = new_size;
|
||||
t->count = 0;
|
||||
|
||||
if (!old_list)
|
||||
return;
|
||||
t->hashmap_count = 0;
|
||||
// Rehash old entries into new list
|
||||
for (int i = 0; i < old_size; i++) {
|
||||
struct node_GC *temp = old_list[i];
|
||||
@@ -53,6 +89,17 @@ int hashCode_GC(struct hashmap_GC *t, uint64_t hash) {
|
||||
}
|
||||
|
||||
int hashmap_remove_GC(struct hashmap_GC *t, uint64_t hash) {
|
||||
for (size_t i = 0; i < t->inline_count; i++) {
|
||||
if (t->inline_values[i].hash == hash) {
|
||||
size_t length = t->inline_count - i;
|
||||
if (length > 1) {
|
||||
memmove(&t->inline_values[i], &t->inline_values[i + 1],
|
||||
(length - 1) * sizeof(struct node_GC));
|
||||
}
|
||||
t->inline_count--;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int pos = hashCode_GC(t, hash);
|
||||
struct node_GC *list = t->list[pos];
|
||||
struct node_GC *temp = list;
|
||||
@@ -68,9 +115,6 @@ int hashmap_remove_GC(struct hashmap_GC *t, uint64_t hash) {
|
||||
prev = temp;
|
||||
temp = temp->next;
|
||||
}
|
||||
list = NULL;
|
||||
prev = NULL;
|
||||
temp = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -79,7 +123,22 @@ void hashmap_insert_GC(struct hashmap_GC *t, uint64_t hash, void *key,
|
||||
if (!order) {
|
||||
order = t->order++;
|
||||
}
|
||||
if ((t->count + 1) > t->size * 0.75) {
|
||||
for (size_t i = 0; i < t->inline_count; i++) {
|
||||
if (t->inline_values[i].hash == hash) {
|
||||
t->inline_values[i].val = val;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!t->list && t->inline_count < INLINE_HASHMAP_ARRAY_SIZE) {
|
||||
t->inline_values[t->inline_count].hash = hash;
|
||||
t->inline_values[t->inline_count].key = key;
|
||||
t->inline_values[t->inline_count].val = val;
|
||||
t->inline_values[t->inline_count].order = order;
|
||||
t->inline_count++;
|
||||
t->count++;
|
||||
return;
|
||||
}
|
||||
if ((t->hashmap_count + 1) > t->size * 0.75) {
|
||||
resize_hashmap_GC(t);
|
||||
}
|
||||
|
||||
@@ -104,10 +163,18 @@ void hashmap_insert_GC(struct hashmap_GC *t, uint64_t hash, void *key,
|
||||
newNode->order = order;
|
||||
newNode->next = list;
|
||||
t->list[pos] = newNode;
|
||||
t->hashmap_count++;
|
||||
t->count++;
|
||||
}
|
||||
|
||||
void *hashmap_lookup_GC(struct hashmap_GC *t, uint64_t hash) {
|
||||
size_t stop = t->inline_count;
|
||||
for (size_t i = 0; i < stop; i++) {
|
||||
if (t->inline_values[i].hash == hash)
|
||||
return t->inline_values[i].val;
|
||||
}
|
||||
if (!t->list)
|
||||
return NULL;
|
||||
int pos = hashCode_GC(t, hash);
|
||||
struct node_GC *list = t->list[pos];
|
||||
struct node_GC *temp = list;
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
|
||||
#ifndef HASHMAP_GC_H
|
||||
#define HASHMAP_GC_H
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INLINE_HASHMAP_ARRAY_SIZE 3
|
||||
|
||||
struct node_GC {
|
||||
uint64_t hash;
|
||||
void *key;
|
||||
@@ -18,19 +21,27 @@ struct node_GC {
|
||||
};
|
||||
struct hashmap_GC {
|
||||
size_t size;
|
||||
struct node_GC inline_values[INLINE_HASHMAP_ARRAY_SIZE];
|
||||
size_t count;
|
||||
size_t inline_count;
|
||||
size_t hashmap_count;
|
||||
size_t order;
|
||||
struct node_GC **list;
|
||||
};
|
||||
|
||||
struct hashmap_GC *createHashmap_GC();
|
||||
|
||||
void clear_hashmap_GC(struct hashmap_GC *t);
|
||||
|
||||
void hashmap_GC_to_array(struct hashmap_GC *t, struct node_GC**array,
|
||||
size_t *array_length);
|
||||
|
||||
int hashCode_GC(struct hashmap_GC *t, uint64_t hash);
|
||||
|
||||
int hashmap_remove_GC(struct hashmap_GC *t, uint64_t hash);
|
||||
|
||||
void hashmap_insert_GC(struct hashmap_GC *t, uint64_t hash, void *key,
|
||||
void *val, size_t order);
|
||||
void *val, size_t order);
|
||||
|
||||
void *hashmap_lookup_GC(struct hashmap_GC *t, uint64_t hash);
|
||||
|
||||
|
||||
195
src/runtime/objects/dictionary/dictionary.c
Normal file
195
src/runtime/objects/dictionary/dictionary.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include "dictionary.h"
|
||||
#include "../../call/call.h"
|
||||
#include "../functions/functions.h"
|
||||
#include "../literals/literals.h"
|
||||
#include "../string/string.h"
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
ArgonObject *ARGON_DICTIONARY_TYPE = NULL;
|
||||
|
||||
ArgonObject *create_ARGON_DICTIONARY_TYPE___init__(size_t argc,
|
||||
ArgonObject **argv,
|
||||
ArErr *err,
|
||||
RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 1) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__init__ expects 1 argument, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
ArgonObject *object = argv[0];
|
||||
object->type = TYPE_DICTIONARY;
|
||||
object->value.as_hashmap = createHashmap_GC();
|
||||
return object;
|
||||
}
|
||||
|
||||
ArgonObject *create_ARGON_DICTIONARY_TYPE___string__(size_t argc,
|
||||
ArgonObject **argv,
|
||||
ArErr *err,
|
||||
RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 1) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__init__ expects 1 argument, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
ArgonObject *object = argv[0];
|
||||
size_t string_length = 0;
|
||||
char *string = NULL;
|
||||
struct node_GC *keys;
|
||||
size_t keys_length;
|
||||
hashmap_GC_to_array(object->value.as_hashmap, &keys, &keys_length);
|
||||
char *string_obj = "{";
|
||||
size_t length = strlen(string_obj);
|
||||
string = realloc(string, string_length + length);
|
||||
memcpy(string + string_length, string_obj, length);
|
||||
string_length += length;
|
||||
for (size_t i = 0; i < keys_length; i++) {
|
||||
struct node_GC node = keys[i];
|
||||
ArgonObject *key = node.key;
|
||||
ArgonObject *value = node.val;
|
||||
|
||||
ArgonObject *string_convert_method = get_builtin_field_for_class(
|
||||
get_builtin_field(key, __class__), __repr__, key);
|
||||
|
||||
if (string_convert_method) {
|
||||
ArgonObject *string_object =
|
||||
argon_call(string_convert_method, 0, NULL, err, state);
|
||||
string =
|
||||
realloc(string, string_length + string_object->value.as_str->length);
|
||||
memcpy(string + string_length, string_object->value.as_str->data,
|
||||
string_object->value.as_str->length);
|
||||
string_length += string_object->value.as_str->length;
|
||||
} else {
|
||||
char *string_obj = "<object>";
|
||||
size_t length = strlen(string_obj);
|
||||
string = realloc(string, string_length + length);
|
||||
memcpy(string + string_length, string_obj, length);
|
||||
string_length += length;
|
||||
}
|
||||
|
||||
char *string_obj = ": ";
|
||||
size_t length = strlen(string_obj);
|
||||
string = realloc(string, string_length + length);
|
||||
memcpy(string + string_length, string_obj, length);
|
||||
string_length += length;
|
||||
|
||||
string_convert_method = get_builtin_field_for_class(
|
||||
get_builtin_field(value, __class__), __repr__, value);
|
||||
|
||||
if (string_convert_method && value != object) {
|
||||
ArgonObject *string_object =
|
||||
argon_call(string_convert_method, 0, NULL, err, state);
|
||||
string =
|
||||
realloc(string, string_length + string_object->value.as_str->length);
|
||||
memcpy(string + string_length, string_object->value.as_str->data,
|
||||
string_object->value.as_str->length);
|
||||
string_length += string_object->value.as_str->length;
|
||||
} else {
|
||||
char *string_obj = "{...}";
|
||||
size_t length = strlen(string_obj);
|
||||
string = realloc(string, string_length + length);
|
||||
memcpy(string + string_length, string_obj, length);
|
||||
string_length += length;
|
||||
}
|
||||
|
||||
if (i != keys_length - 1) {
|
||||
char *string_obj = ", ";
|
||||
size_t length = strlen(string_obj);
|
||||
string = realloc(string, string_length + length);
|
||||
memcpy(string + string_length, string_obj, length);
|
||||
string_length += length;
|
||||
}
|
||||
}
|
||||
string_obj = "}";
|
||||
length = strlen(string_obj);
|
||||
string = realloc(string, string_length + length);
|
||||
memcpy(string + string_length, string_obj, length);
|
||||
string_length += length;
|
||||
ArgonObject* result = new_string_object(string, string_length, 0, 0);
|
||||
free(string);
|
||||
return result;
|
||||
}
|
||||
|
||||
ArgonObject *create_ARGON_DICTIONARY_TYPE___get_attr__(size_t argc,
|
||||
ArgonObject **argv,
|
||||
ArErr *err,
|
||||
RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 2) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__get_attr__ expects 2 argument, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
ArgonObject *object = argv[0];
|
||||
ArgonObject *key = argv[1];
|
||||
int64_t hash = hash_object(key, err, state);
|
||||
if (err->exists) {
|
||||
return ARGON_NULL;
|
||||
}
|
||||
ArgonObject *result = hashmap_lookup_GC(object->value.as_hashmap, hash);
|
||||
if (!result) {
|
||||
*err = create_err(0, 0, 0, NULL, "Attribute Error",
|
||||
"Dictionary has no attribute '%.*s'", key->value.as_str->length, key->value.as_str->data);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ArgonObject *create_ARGON_DICTIONARY_TYPE___set_attr__(size_t argc,
|
||||
ArgonObject **argv,
|
||||
ArErr *err,
|
||||
RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 3) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__set_attr__ expects 2 argument, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
ArgonObject *object = argv[0];
|
||||
ArgonObject *key = argv[1];
|
||||
ArgonObject *value = argv[2];
|
||||
int64_t hash = hash_object(key, err, state);
|
||||
if (err->exists) {
|
||||
return ARGON_NULL;
|
||||
}
|
||||
hashmap_insert_GC(object->value.as_hashmap, hash, key, value, 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
void create_ARGON_DICTIONARY_TYPE() {
|
||||
ARGON_DICTIONARY_TYPE = new_class();
|
||||
add_builtin_field(ARGON_DICTIONARY_TYPE, __name__,
|
||||
new_string_object_null_terminated("dictionary"));
|
||||
add_builtin_field(ARGON_DICTIONARY_TYPE, __init__,
|
||||
create_argon_native_function(
|
||||
"__init__", create_ARGON_DICTIONARY_TYPE___init__));
|
||||
add_builtin_field(
|
||||
ARGON_DICTIONARY_TYPE, __get_attr__,
|
||||
create_argon_native_function("__get_attr__",
|
||||
create_ARGON_DICTIONARY_TYPE___get_attr__));
|
||||
add_builtin_field(
|
||||
ARGON_DICTIONARY_TYPE, __set_attr__,
|
||||
create_argon_native_function("__set_attr__",
|
||||
create_ARGON_DICTIONARY_TYPE___set_attr__));
|
||||
add_builtin_field(ARGON_DICTIONARY_TYPE, __string__,
|
||||
create_argon_native_function(
|
||||
"__string__", create_ARGON_DICTIONARY_TYPE___string__));
|
||||
}
|
||||
|
||||
ArgonObject *create_dictionary(struct hashmap_GC *hashmap) {
|
||||
ArgonObject *object = new_instance(ARGON_DICTIONARY_TYPE);
|
||||
object->type = TYPE_DICTIONARY;
|
||||
object->value.as_hashmap = hashmap;
|
||||
return object;
|
||||
}
|
||||
17
src/runtime/objects/dictionary/dictionary.h
Normal file
17
src/runtime/objects/dictionary/dictionary.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DICTIONARY_H
|
||||
#define DICTIONARY_H
|
||||
#include "../object.h"
|
||||
|
||||
extern ArgonObject *ARGON_DICTIONARY_TYPE;
|
||||
|
||||
void create_ARGON_DICTIONARY_TYPE();
|
||||
|
||||
ArgonObject* create_dictionary(struct hashmap_GC*hashmap);
|
||||
|
||||
#endif // DICTIONARY_H
|
||||
@@ -7,44 +7,48 @@
|
||||
#include "../../runtime.h"
|
||||
#include "../object.h"
|
||||
#include "../string/string.h"
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
ArgonObject *ARGON_FUNCTION_TYPE = NULL;
|
||||
|
||||
ArgonObject *create_argon_native_function(char*name, native_fn native_fn) {
|
||||
ArgonObject *object = new_object();
|
||||
add_field(object, "__class__", ARGON_FUNCTION_TYPE);
|
||||
ArgonObject *create_argon_native_function(char *name, native_fn native_fn) {
|
||||
ArgonObject *object = new_instance(ARGON_FUNCTION_TYPE);
|
||||
object->type = TYPE_NATIVE_FUNCTION;
|
||||
add_field(object, "__name__", new_string_object(name, strlen(name)));
|
||||
add_builtin_field(object, __name__,
|
||||
new_string_object(name, strlen(name), 0, 0));
|
||||
object->value.native_fn = native_fn;
|
||||
return object;
|
||||
}
|
||||
|
||||
void load_argon_function(Translated *translated, RuntimeState *state,
|
||||
struct Stack *stack) {
|
||||
ArgonObject *object = new_object();
|
||||
add_field(object, "__class__", ARGON_FUNCTION_TYPE);
|
||||
struct Stack *stack) {
|
||||
ArgonObject *object = new_instance(ARGON_FUNCTION_TYPE);
|
||||
object->type = TYPE_FUNCTION;
|
||||
uint64_t offset = pop_bytecode(translated, state);
|
||||
uint64_t length = pop_bytecode(translated, state);
|
||||
add_field(object, "__name__", new_string_object(arena_get(&translated->constants, offset), length));
|
||||
object->value.argon_fn.translated = *translated;
|
||||
object->value.argon_fn.number_of_parameters = pop_bytecode(translated, state);
|
||||
object->value.argon_fn.parameters =
|
||||
ar_alloc(object->value.argon_fn.number_of_parameters * sizeof(struct string_struct));
|
||||
for (size_t i = 0; i < object->value.argon_fn.number_of_parameters; i++) {
|
||||
add_builtin_field(object, __name__,
|
||||
new_string_object(arena_get(&translated->constants, offset),
|
||||
length, 0, 0));
|
||||
object->value.argon_fn = ar_alloc(sizeof(struct argon_function_struct));
|
||||
object->value.argon_fn->translated = *translated;
|
||||
object->value.argon_fn->number_of_parameters = pop_bytecode(translated, state);
|
||||
object->value.argon_fn->parameters =
|
||||
ar_alloc(object->value.argon_fn->number_of_parameters *
|
||||
sizeof(struct string_struct));
|
||||
for (size_t i = 0; i < object->value.argon_fn->number_of_parameters; i++) {
|
||||
offset = pop_bytecode(translated, state);
|
||||
length = pop_bytecode(translated, state);
|
||||
object->value.argon_fn.parameters[i].data = arena_get(&translated->constants, offset);
|
||||
object->value.argon_fn.parameters[i].length = length;
|
||||
object->value.argon_fn->parameters[i].data =
|
||||
arena_get(&translated->constants, offset);
|
||||
object->value.argon_fn->parameters[i].length = length;
|
||||
}
|
||||
offset = pop_bytecode(translated, state);
|
||||
length = pop_bytecode(translated, state);
|
||||
object->value.argon_fn.bytecode = arena_get(&translated->constants, offset);
|
||||
object->value.argon_fn.bytecode_length = length;
|
||||
object->value.argon_fn.stack = stack;
|
||||
state->registers[0]=object;
|
||||
object->value.argon_fn->bytecode = arena_get(&translated->constants, offset);
|
||||
object->value.argon_fn->bytecode_length = length;
|
||||
object->value.argon_fn->stack = stack;
|
||||
state->registers[0] = object;
|
||||
}
|
||||
@@ -36,8 +36,8 @@ ArgonObject *ARGON_NUMBER_TYPE___new__(size_t argc, ArgonObject **argv,
|
||||
ArgonObject *object = argv[1];
|
||||
|
||||
self->type = TYPE_STRING;
|
||||
ArgonObject *boolean_convert_method = get_field_for_class(
|
||||
get_field(object, "__class__", false, false), "__number__", object);
|
||||
ArgonObject *boolean_convert_method = get_builtin_field_for_class(
|
||||
get_builtin_field(object, __class__), __number__, object);
|
||||
if (boolean_convert_method) {
|
||||
ArgonObject *boolean_object =
|
||||
argon_call(boolean_convert_method, 0, NULL, err, state);
|
||||
@@ -45,11 +45,11 @@ ArgonObject *ARGON_NUMBER_TYPE___new__(size_t argc, ArgonObject **argv,
|
||||
return ARGON_NULL;
|
||||
return boolean_object;
|
||||
}
|
||||
ArgonObject *type_name = get_field_for_class(
|
||||
get_field(object, "__class__", false, false), "__name__", object);
|
||||
ArgonObject *type_name = get_builtin_field_for_class(
|
||||
get_builtin_field(object, __class__), __name__, object);
|
||||
*err = create_err(
|
||||
0, 0, 0, "", "Runtime Error", "cannot convert type '%.*s' to number",
|
||||
type_name->value.as_str.length, type_name->value.as_str.data);
|
||||
type_name->value.as_str->length, type_name->value.as_str->data);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
|
||||
@@ -72,8 +72,7 @@ ArgonObject *ARGON_NUMBER_TYPE___boolean__(size_t argc, ArgonObject **argv,
|
||||
"__boolean__ expects 1 arguments, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
return mpq_cmp_si(*argv[0]->value.as_number, 0, 1) == 0 ? ARGON_FALSE
|
||||
: ARGON_TRUE;
|
||||
return argv[0]->as_bool ? ARGON_FALSE : ARGON_TRUE;
|
||||
}
|
||||
|
||||
ArgonObject *ARGON_NUMBER_TYPE___add__(size_t argc, ArgonObject **argv,
|
||||
@@ -85,45 +84,251 @@ ArgonObject *ARGON_NUMBER_TYPE___add__(size_t argc, ArgonObject **argv,
|
||||
return ARGON_NULL;
|
||||
}
|
||||
if (argv[1]->type != TYPE_NUMBER) {
|
||||
ArgonObject *type_name = get_field_for_class(
|
||||
get_field(argv[1], "__class__", false, false), "__name__", argv[1]);
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__add__ cannot perform addition between a number and %.*s",
|
||||
type_name->value.as_str.length,
|
||||
type_name->value.as_str.data);
|
||||
ArgonObject *type_name = get_builtin_field_for_class(
|
||||
get_builtin_field(argv[1], __class__), __name__, argv[1]);
|
||||
*err = create_err(
|
||||
0, 0, 0, "", "Runtime Error",
|
||||
"__add__ cannot perform addition between a number and %.*s",
|
||||
type_name->value.as_str->length, type_name->value.as_str->data);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_add(r, *argv[0]->value.as_number, *argv[1]->value.as_number);
|
||||
ArgonObject *result = new_number_object(r);
|
||||
mpq_clear(r);
|
||||
return result;
|
||||
if (argv[0]->value.as_number->is_int64 && argv[1]->value.as_number->is_int64) {
|
||||
int64_t a = argv[0]->value.as_number->n.i64;
|
||||
int64_t b = argv[1]->value.as_number->n.i64;
|
||||
bool gonna_overflow = (a > 0 && b > 0 && a > INT64_MAX - b) ||
|
||||
(a < 0 && b < 0 && a < INT64_MIN - b);
|
||||
if (!gonna_overflow) {
|
||||
return new_number_object_from_int64(a + b);
|
||||
}
|
||||
mpq_t a_GMP, b_GMP;
|
||||
mpq_init(a_GMP);
|
||||
mpq_init(b_GMP);
|
||||
mpq_set_si(a_GMP, a, 1);
|
||||
mpq_set_si(b_GMP, b, 1);
|
||||
mpq_add(a_GMP, a_GMP, b_GMP);
|
||||
ArgonObject *result = new_number_object(a_GMP);
|
||||
mpq_clear(a_GMP);
|
||||
mpq_clear(b_GMP);
|
||||
return result;
|
||||
} else if (!argv[0]->value.as_number->is_int64 &&
|
||||
!argv[1]->value.as_number->is_int64) {
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_add(r, *argv[0]->value.as_number->n.mpq,
|
||||
*argv[1]->value.as_number->n.mpq);
|
||||
ArgonObject *result = new_number_object(r);
|
||||
mpq_clear(r);
|
||||
return result;
|
||||
} else {
|
||||
mpq_t a_GMP, b_GMP;
|
||||
mpq_init(a_GMP);
|
||||
mpq_init(b_GMP);
|
||||
if (argv[0]->value.as_number->is_int64) {
|
||||
mpq_set_si(a_GMP, argv[0]->value.as_number->n.i64, 1);
|
||||
mpq_set(b_GMP, *argv[1]->value.as_number->n.mpq);
|
||||
} else {
|
||||
mpq_set(a_GMP, *argv[0]->value.as_number->n.mpq);
|
||||
mpq_set_si(b_GMP, argv[1]->value.as_number->n.i64, 1);
|
||||
}
|
||||
mpq_add(a_GMP, a_GMP, b_GMP);
|
||||
ArgonObject *result = new_number_object(a_GMP);
|
||||
mpq_clear(a_GMP);
|
||||
mpq_clear(b_GMP);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
ArgonObject *ARGON_NUMBER_TYPE___subtract__(size_t argc, ArgonObject **argv,
|
||||
ArErr *err, RuntimeState *state) {
|
||||
ArErr *err, RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 2) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__subtract__ expects 2 arguments, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
if (argv[1]->type != TYPE_NUMBER) {
|
||||
ArgonObject *type_name = get_field_for_class(
|
||||
get_field(argv[1], "__class__", false, false), "__name__", argv[1]);
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__subtract__ cannot perform subtraction between number and %.*s",
|
||||
type_name->value.as_str.length,
|
||||
type_name->value.as_str.data);
|
||||
ArgonObject *type_name = get_builtin_field_for_class(
|
||||
get_builtin_field(argv[1], __class__), __name__, argv[1]);
|
||||
*err = create_err(
|
||||
0, 0, 0, "", "Runtime Error",
|
||||
"__subtract__ cannot perform subtraction between number and %.*s",
|
||||
type_name->value.as_str->length, type_name->value.as_str->data);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
mpq_sub(r, *argv[0]->value.as_number, *argv[1]->value.as_number);
|
||||
ArgonObject *result = new_number_object(r);
|
||||
mpq_clear(r);
|
||||
return result;
|
||||
|
||||
if (argv[0]->value.as_number->is_int64 && argv[1]->value.as_number->is_int64) {
|
||||
int64_t a = argv[0]->value.as_number->n.i64;
|
||||
int64_t b = argv[1]->value.as_number->n.i64;
|
||||
int64_t neg_a = -a;
|
||||
bool gonna_overflow = (neg_a > 0 && b > 0 && b > INT64_MAX - neg_a) ||
|
||||
(neg_a < 0 && b < 0 && b < INT64_MIN - neg_a);
|
||||
if (!gonna_overflow) {
|
||||
return new_number_object_from_int64(a - b);
|
||||
}
|
||||
mpq_t a_GMP, b_GMP;
|
||||
mpq_init(a_GMP);
|
||||
mpq_init(b_GMP);
|
||||
mpq_set_si(a_GMP, a, 1);
|
||||
mpq_set_si(b_GMP, b, 1);
|
||||
mpq_sub(a_GMP, a_GMP, b_GMP);
|
||||
ArgonObject *result = new_number_object(a_GMP);
|
||||
mpq_clear(a_GMP);
|
||||
mpq_clear(b_GMP);
|
||||
return result;
|
||||
} else if (!argv[0]->value.as_number->is_int64 &&
|
||||
!argv[1]->value.as_number->is_int64) {
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_sub(r, *argv[0]->value.as_number->n.mpq,
|
||||
*argv[1]->value.as_number->n.mpq);
|
||||
ArgonObject *result = new_number_object(r);
|
||||
mpq_clear(r);
|
||||
return result;
|
||||
} else {
|
||||
mpq_t a_GMP, b_GMP;
|
||||
mpq_init(a_GMP);
|
||||
mpq_init(b_GMP);
|
||||
if (argv[0]->value.as_number->is_int64) {
|
||||
mpq_set_si(a_GMP, argv[0]->value.as_number->n.i64, 1);
|
||||
mpq_set(b_GMP, *argv[1]->value.as_number->n.mpq);
|
||||
} else {
|
||||
mpq_set(a_GMP, *argv[0]->value.as_number->n.mpq);
|
||||
mpq_set_si(b_GMP, argv[1]->value.as_number->n.i64, 1);
|
||||
}
|
||||
mpq_sub(a_GMP, a_GMP, b_GMP);
|
||||
ArgonObject *result = new_number_object(a_GMP);
|
||||
mpq_clear(a_GMP);
|
||||
mpq_clear(b_GMP);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
ArgonObject *ARGON_NUMBER_TYPE___multiply__(size_t argc, ArgonObject **argv,
|
||||
ArErr *err, RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 2) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__multiply__ expects 2 arguments, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
if (argv[1]->type != TYPE_NUMBER) {
|
||||
ArgonObject *type_name = get_builtin_field_for_class(
|
||||
get_builtin_field(argv[1], __class__), __name__, argv[1]);
|
||||
*err = create_err(
|
||||
0, 0, 0, "", "Runtime Error",
|
||||
"__multiply__ cannot perform multiplication between number and %.*s",
|
||||
type_name->value.as_str->length, type_name->value.as_str->data);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
|
||||
if (argv[0]->value.as_number->is_int64 && argv[1]->value.as_number->is_int64) {
|
||||
int64_t a = argv[0]->value.as_number->n.i64;
|
||||
int64_t b = argv[1]->value.as_number->n.i64;
|
||||
bool gonna_overflow =
|
||||
a > 0 ? (b > 0 ? a > INT64_MAX / b : b < INT64_MIN / a)
|
||||
: (b > 0 ? a < INT64_MIN / b : a != 0 && b < INT64_MAX / a);
|
||||
if (!gonna_overflow) {
|
||||
return new_number_object_from_int64(a * b);
|
||||
}
|
||||
mpq_t a_GMP, b_GMP;
|
||||
mpq_init(a_GMP);
|
||||
mpq_init(b_GMP);
|
||||
mpq_set_si(a_GMP, a, 1);
|
||||
mpq_set_si(b_GMP, b, 1);
|
||||
mpq_mul(a_GMP, a_GMP, b_GMP);
|
||||
ArgonObject *result = new_number_object(a_GMP);
|
||||
mpq_clear(a_GMP);
|
||||
mpq_clear(b_GMP);
|
||||
return result;
|
||||
} else if (!argv[0]->value.as_number->is_int64 &&
|
||||
!argv[1]->value.as_number->is_int64) {
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_mul(r, *argv[0]->value.as_number->n.mpq,
|
||||
*argv[1]->value.as_number->n.mpq);
|
||||
ArgonObject *result = new_number_object(r);
|
||||
mpq_clear(r);
|
||||
return result;
|
||||
} else {
|
||||
mpq_t a_GMP, b_GMP;
|
||||
mpq_init(a_GMP);
|
||||
mpq_init(b_GMP);
|
||||
if (argv[0]->value.as_number->is_int64) {
|
||||
mpq_set_si(a_GMP, argv[0]->value.as_number->n.i64, 1);
|
||||
mpq_set(b_GMP, *argv[1]->value.as_number->n.mpq);
|
||||
} else {
|
||||
mpq_set(a_GMP, *argv[0]->value.as_number->n.mpq);
|
||||
mpq_set_si(b_GMP, argv[1]->value.as_number->n.i64, 1);
|
||||
}
|
||||
mpq_mul(a_GMP, a_GMP, b_GMP);
|
||||
ArgonObject *result = new_number_object(a_GMP);
|
||||
mpq_clear(a_GMP);
|
||||
mpq_clear(b_GMP);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
ArgonObject *ARGON_NUMBER_TYPE___division__(size_t argc, ArgonObject **argv,
|
||||
ArErr *err, RuntimeState *state) {
|
||||
(void)state;
|
||||
if (argc != 2) {
|
||||
*err = create_err(0, 0, 0, "", "Runtime Error",
|
||||
"__division__ expects 2 arguments, got %" PRIu64, argc);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
if (argv[1]->type != TYPE_NUMBER) {
|
||||
ArgonObject *type_name = get_builtin_field_for_class(
|
||||
get_builtin_field(argv[1], __class__), __name__, argv[1]);
|
||||
*err = create_err(
|
||||
0, 0, 0, "", "Runtime Error",
|
||||
"__division__ cannot perform division between number and %.*s",
|
||||
type_name->value.as_str->length, type_name->value.as_str->data);
|
||||
return ARGON_NULL;
|
||||
}
|
||||
if (argv[0]->value.as_number->is_int64 && argv[1]->value.as_number->is_int64) {
|
||||
int64_t a = argv[0]->value.as_number->n.i64;
|
||||
int64_t b = argv[1]->value.as_number->n.i64;
|
||||
if (!b) {
|
||||
*err =
|
||||
create_err(state->source_location.line, state->source_location.column,
|
||||
state->source_location.length, state->path,
|
||||
"Zero Division Error", "division by zero");
|
||||
return NULL;
|
||||
}
|
||||
return new_number_object_from_num_and_den(a, b);
|
||||
} else if (!argv[0]->value.as_number->is_int64 &&
|
||||
!argv[1]->value.as_number->is_int64) {
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_div(r, *argv[0]->value.as_number->n.mpq,
|
||||
*argv[1]->value.as_number->n.mpq);
|
||||
ArgonObject *result = new_number_object(r);
|
||||
mpq_clear(r);
|
||||
return result;
|
||||
} else {
|
||||
mpq_t a_GMP, b_GMP;
|
||||
mpq_init(a_GMP);
|
||||
mpq_init(b_GMP);
|
||||
if (argv[0]->value.as_number->is_int64) {
|
||||
mpq_set_si(a_GMP, argv[0]->value.as_number->n.i64, 1);
|
||||
mpq_set(b_GMP, *argv[1]->value.as_number->n.mpq);
|
||||
} else {
|
||||
mpq_set(a_GMP, *argv[0]->value.as_number->n.mpq);
|
||||
if (!argv[1]->value.as_number->n.i64) {
|
||||
*err = create_err(state->source_location.line,
|
||||
state->source_location.column,
|
||||
state->source_location.length, state->path,
|
||||
"Zero Division Error", "division by zero");
|
||||
return NULL;
|
||||
}
|
||||
mpq_set_si(b_GMP, argv[1]->value.as_number->n.i64, 1);
|
||||
}
|
||||
mpq_div(a_GMP, a_GMP, b_GMP);
|
||||
ArgonObject *result = new_number_object(a_GMP);
|
||||
mpq_clear(a_GMP);
|
||||
mpq_clear(b_GMP);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
ArgonObject *ARGON_NUMBER_TYPE___string__(size_t argc, ArgonObject **argv,
|
||||
@@ -135,7 +340,13 @@ ArgonObject *ARGON_NUMBER_TYPE___string__(size_t argc, ArgonObject **argv,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mpq_t *num = argv[0]->value.as_number;
|
||||
if (argv[0]->value.as_number->is_int64) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%" PRId64, argv[0]->value.as_number->n.i64);
|
||||
return new_string_object_null_terminated(buf);
|
||||
}
|
||||
|
||||
mpq_t *num = argv[0]->value.as_number->n.mpq;
|
||||
|
||||
/* If denominator == 1, print numerator as full integer */
|
||||
if (mpz_cmp_ui(mpq_denref(*num), 1) == 0) {
|
||||
@@ -277,33 +488,60 @@ ArgonObject *ARGON_NUMBER_TYPE___string__(size_t argc, ArgonObject **argv,
|
||||
return result;
|
||||
}
|
||||
|
||||
#define small_ints_min -256
|
||||
#define small_ints_max 256
|
||||
ArgonObject small_ints[small_ints_max - small_ints_min + 1];
|
||||
struct as_number small_ints_as_number[small_ints_max - small_ints_min + 1];
|
||||
|
||||
void init_small_ints() {
|
||||
for (int64_t i = 0; i <= small_ints_max - small_ints_min; i++) {
|
||||
int64_t n = i + small_ints_min;
|
||||
small_ints[i].type = TYPE_NUMBER;
|
||||
small_ints[i].built_in_slot_length = 0;
|
||||
small_ints[i].dict = NULL;
|
||||
small_ints[i].value.as_number = &small_ints_as_number[i];
|
||||
add_builtin_field(&small_ints[i], __class__, ARGON_NUMBER_TYPE);
|
||||
small_ints[i].value.as_number->is_int64 = true;
|
||||
small_ints[i].value.as_number->n.i64 = n;
|
||||
small_ints[i].as_bool = n;
|
||||
}
|
||||
}
|
||||
|
||||
void create_ARGON_NUMBER_TYPE() {
|
||||
ARGON_NUMBER_TYPE = new_object();
|
||||
add_field(ARGON_NUMBER_TYPE, "__name__",
|
||||
new_string_object_null_terminated("number"));
|
||||
add_field(
|
||||
ARGON_NUMBER_TYPE, "__string__",
|
||||
ARGON_NUMBER_TYPE = new_class();
|
||||
add_builtin_field(ARGON_NUMBER_TYPE, __name__,
|
||||
new_string_object_null_terminated("number"));
|
||||
add_builtin_field(
|
||||
ARGON_NUMBER_TYPE, __string__,
|
||||
create_argon_native_function("__string__", ARGON_NUMBER_TYPE___string__));
|
||||
add_field(ARGON_NUMBER_TYPE, "__new__",
|
||||
create_argon_native_function("__new__", ARGON_NUMBER_TYPE___new__));
|
||||
add_field(
|
||||
ARGON_NUMBER_TYPE, "__number__",
|
||||
add_builtin_field(
|
||||
ARGON_NUMBER_TYPE, __new__,
|
||||
create_argon_native_function("__new__", ARGON_NUMBER_TYPE___new__));
|
||||
add_builtin_field(
|
||||
ARGON_NUMBER_TYPE, __number__,
|
||||
create_argon_native_function("__number__", ARGON_NUMBER_TYPE___number__));
|
||||
add_field(ARGON_NUMBER_TYPE, "__boolean__",
|
||||
create_argon_native_function("__boolean__",
|
||||
ARGON_NUMBER_TYPE___boolean__));
|
||||
add_field(ARGON_NUMBER_TYPE, "__add__",
|
||||
create_argon_native_function("__add__",
|
||||
ARGON_NUMBER_TYPE___add__));
|
||||
add_field(ARGON_NUMBER_TYPE, "__subtract__",
|
||||
create_argon_native_function("__subtract__",
|
||||
ARGON_NUMBER_TYPE___subtract__));
|
||||
add_builtin_field(ARGON_NUMBER_TYPE, __boolean__,
|
||||
create_argon_native_function(
|
||||
"__boolean__", ARGON_NUMBER_TYPE___boolean__));
|
||||
add_builtin_field(
|
||||
ARGON_NUMBER_TYPE, __add__,
|
||||
create_argon_native_function("__add__", ARGON_NUMBER_TYPE___add__));
|
||||
add_builtin_field(ARGON_NUMBER_TYPE, __subtract__,
|
||||
create_argon_native_function(
|
||||
"__subtract__", ARGON_NUMBER_TYPE___subtract__));
|
||||
add_builtin_field(ARGON_NUMBER_TYPE, __multiply__,
|
||||
create_argon_native_function(
|
||||
"__multiply__", ARGON_NUMBER_TYPE___multiply__));
|
||||
add_builtin_field(ARGON_NUMBER_TYPE, __divide__,
|
||||
create_argon_native_function(
|
||||
"__division__", ARGON_NUMBER_TYPE___division__));
|
||||
init_small_ints();
|
||||
}
|
||||
|
||||
void mpz_init_gc_managed(mpz_t z, size_t limbs_count) {
|
||||
z->_mp_alloc = limbs_count;
|
||||
z->_mp_size = 0;
|
||||
z->_mp_d = ar_alloc(limbs_count * sizeof(mp_limb_t));
|
||||
z->_mp_d = ar_alloc_atomic(limbs_count * sizeof(mp_limb_t));
|
||||
}
|
||||
|
||||
void mpq_init_gc_managed(mpq_t q, size_t num_limbs, size_t den_limbs) {
|
||||
@@ -337,40 +575,129 @@ mpq_t *mpq_new_gc_from(const mpq_t src) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool mpq_to_int64(mpq_t q, int64_t *out) {
|
||||
// Check denominator == 1
|
||||
if (mpz_cmp_ui(mpq_denref(q), 1) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get numerator
|
||||
mpz_t num;
|
||||
mpz_init(num);
|
||||
mpz_set(num, mpq_numref(q));
|
||||
|
||||
// Check bounds
|
||||
if (mpz_cmp_si(num, INT64_MIN) < 0 || mpz_cmp_si(num, INT64_MAX) > 0) {
|
||||
mpz_clear(num);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = mpz_get_si(num); // safe because we checked range
|
||||
mpz_clear(num);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool double_to_int64(double x, int64_t *out) {
|
||||
if (x < (double)INT64_MIN || x > (double)INT64_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t i = (int64_t)x;
|
||||
if ((double)i == x) { // no fractional part
|
||||
*out = i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ArgonObject *new_number_object(mpq_t number) {
|
||||
ArgonObject *object = new_object();
|
||||
add_field(object, "__class__", ARGON_NUMBER_TYPE);
|
||||
int64_t i64 = 0;
|
||||
bool is_int64 = mpq_to_int64(number, &i64);
|
||||
if (is_int64 && i64 >= small_ints_min && i64 <= small_ints_max) {
|
||||
return &small_ints[i64 - small_ints_min];
|
||||
}
|
||||
ArgonObject *object = new_instance(ARGON_NUMBER_TYPE);
|
||||
object->value.as_number = ar_alloc(sizeof(struct as_number));
|
||||
object->type = TYPE_NUMBER;
|
||||
object->value.as_number = mpq_new_gc_from(number);
|
||||
object->value.as_number->n.i64 = i64;
|
||||
object->value.as_number->is_int64 = is_int64;
|
||||
if (object->value.as_number->is_int64) {
|
||||
object->as_bool = object->value.as_number->n.i64;
|
||||
} else {
|
||||
object->value.as_number->n.mpq = mpq_new_gc_from(number);
|
||||
object->as_bool = mpq_cmp_si(number, 0, 1) != 0;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
ArgonObject *new_number_object_from_long(long n, unsigned long d) {
|
||||
ArgonObject *object = new_object();
|
||||
add_field(object, "__class__", ARGON_NUMBER_TYPE);
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_set_si(r, n, d);
|
||||
ArgonObject *new_number_object_from_num_and_den(int64_t n, uint64_t d) {
|
||||
if (d == 1 && n >= small_ints_min && n <= small_ints_max) {
|
||||
return &small_ints[n - small_ints_min];
|
||||
}
|
||||
ArgonObject *object = new_instance(ARGON_NUMBER_TYPE);
|
||||
object->value.as_number = ar_alloc(sizeof(struct as_number));
|
||||
object->type = TYPE_NUMBER;
|
||||
object->value.as_number = mpq_new_gc_from(r);
|
||||
mpq_clear(r);
|
||||
if (d == 1) {
|
||||
object->value.as_number->is_int64 = true;
|
||||
object->value.as_number->n.i64 = n;
|
||||
object->as_bool = n;
|
||||
} else {
|
||||
object->value.as_number->is_int64 = false;
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_set_si(r, n, d);
|
||||
object->value.as_number->n.mpq = mpq_new_gc_from(r);
|
||||
object->as_bool = n != 0;
|
||||
mpq_clear(r);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
ArgonObject *new_number_object_from_int64(int64_t i64) {
|
||||
if (i64 >= small_ints_min && i64 <= small_ints_max) {
|
||||
return &small_ints[i64 - small_ints_min];
|
||||
}
|
||||
ArgonObject *object = new_instance(ARGON_NUMBER_TYPE);
|
||||
object->value.as_number = ar_alloc(sizeof(struct as_number));
|
||||
object->type = TYPE_NUMBER;
|
||||
object->value.as_number->is_int64 = true;
|
||||
object->value.as_number->n.i64 = i64;
|
||||
object->as_bool = i64;
|
||||
return object;
|
||||
}
|
||||
|
||||
ArgonObject *new_number_object_from_double(double d) {
|
||||
ArgonObject *object = new_object();
|
||||
add_field(object, "__class__", ARGON_NUMBER_TYPE);
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_set_d(r, d);
|
||||
int64_t i64 = 0;
|
||||
bool is_int64 = double_to_int64(d, &i64);
|
||||
if (is_int64 && i64 >= small_ints_min && i64 <= small_ints_max) {
|
||||
return &small_ints[i64 - small_ints_min];
|
||||
}
|
||||
ArgonObject *object = new_instance(ARGON_NUMBER_TYPE);
|
||||
object->value.as_number = ar_alloc(sizeof(struct as_number));
|
||||
object->type = TYPE_NUMBER;
|
||||
object->value.as_number = mpq_new_gc_from(r);
|
||||
mpq_clear(r);
|
||||
object->value.as_number->n.i64 = i64;
|
||||
object->value.as_number->is_int64 = is_int64;
|
||||
if (object->value.as_number->is_int64) {
|
||||
object->as_bool = object->value.as_number->n.i64;
|
||||
} else {
|
||||
mpq_t r;
|
||||
mpq_init(r);
|
||||
mpq_set_d(r, d);
|
||||
object->value.as_number->n.mpq = mpq_new_gc_from(r);
|
||||
object->as_bool = d != 0;
|
||||
mpq_clear(r);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
void load_number(Translated *translated, RuntimeState *state) {
|
||||
uint8_t to_register = pop_byte(translated, state);
|
||||
uint8_t is_int64 = pop_byte(translated, state);
|
||||
if (is_int64) {
|
||||
state->registers[to_register] =
|
||||
new_number_object_from_int64(pop_bytecode(translated, state));
|
||||
return;
|
||||
}
|
||||
size_t num_size = pop_bytecode(translated, state);
|
||||
size_t num_pos = pop_bytecode(translated, state);
|
||||
mpq_t r;
|
||||
|
||||
@@ -14,10 +14,14 @@ void create_ARGON_NUMBER_TYPE();
|
||||
|
||||
ArgonObject *new_number_object(mpq_t number);
|
||||
|
||||
bool mpq_to_int64(mpq_t q, int64_t *out);
|
||||
|
||||
void load_number(Translated *translated, RuntimeState *state);
|
||||
|
||||
ArgonObject *new_number_object_from_double(double d);
|
||||
|
||||
ArgonObject *new_number_object_from_long(long n, unsigned long d);
|
||||
ArgonObject *new_number_object_from_num_and_den(int64_t n, uint64_t d);
|
||||
|
||||
ArgonObject *new_number_object_from_int64(int64_t i64);
|
||||
|
||||
#endif // RUNTIME_NUMBER_H
|
||||
@@ -7,48 +7,159 @@
|
||||
#include "object.h"
|
||||
#include "../../hash_data/hash_data.h"
|
||||
#include "../../memory.h"
|
||||
#include "../call/call.h"
|
||||
#include "type/type.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int strcmp_len(const char *s1, size_t len, const char *s2) {
|
||||
size_t s2len = strlen(s2); // length of null-terminated string
|
||||
size_t n = (len < s2len) ? len : s2len;
|
||||
|
||||
int cmp = memcmp(s1, s2, n);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
|
||||
// If prefixes match, decide based on length
|
||||
if (len == s2len)
|
||||
return 0;
|
||||
return (len < s2len) ? -1 : 1;
|
||||
}
|
||||
|
||||
ArgonObject *BASE_CLASS = NULL;
|
||||
|
||||
const char *built_in_field_names[BUILT_IN_FIELDS_COUNT] = {
|
||||
"__base__",
|
||||
"__class__",
|
||||
"__name__",
|
||||
"", // above is anything that gets stored in built in slots
|
||||
"__add__",
|
||||
"__string__",
|
||||
"__subtract__",
|
||||
"__multiply__",
|
||||
"__divide__",
|
||||
"__new__",
|
||||
"__init__",
|
||||
"__boolean__",
|
||||
"__get_attr__",
|
||||
"__binding__",
|
||||
"__function__",
|
||||
"address",
|
||||
"__call__",
|
||||
"__number__",
|
||||
"length",
|
||||
"__getattribute__",
|
||||
"__set_attr__",
|
||||
"__hash__",
|
||||
"__repr__"};
|
||||
|
||||
uint64_t built_in_field_hashes[BUILT_IN_FIELDS_COUNT];
|
||||
|
||||
ArgonObject *new_object() {
|
||||
ArgonObject *object = ar_alloc(sizeof(ArgonObject));
|
||||
object->built_in_slot_length = 0;
|
||||
object->type = TYPE_OBJECT;
|
||||
object->dict = createHashmap_GC();
|
||||
add_field(object, "__class__", ARGON_TYPE_TYPE);
|
||||
add_field(object, "__base__", BASE_CLASS);
|
||||
object->dict = NULL;
|
||||
object->as_bool = true;
|
||||
return object;
|
||||
}
|
||||
|
||||
void add_field(ArgonObject *target, char *name, ArgonObject *object) {
|
||||
hashmap_insert_GC(target->dict,
|
||||
siphash64_bytes(name, strlen(name), siphash_key), name,
|
||||
object, 0);
|
||||
void init_built_in_field_hashes() {
|
||||
for (int i = 0; i < BUILT_IN_FIELDS_COUNT; i++) {
|
||||
built_in_field_hashes[i] = siphash64_bytes(
|
||||
built_in_field_names[i], strlen(built_in_field_names[i]), siphash_key);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t hash_object(ArgonObject *object, ArErr *err, RuntimeState *state) {
|
||||
ArgonObject *hash_function = get_builtin_field_for_class(
|
||||
get_builtin_field(object, __class__), __hash__, object);
|
||||
if (!hash_function) {
|
||||
*err = create_err(err->line, err->column, err->length, err->path,
|
||||
"Hash Error", "objects class has no __hash__ method");
|
||||
return 0;
|
||||
}
|
||||
ArgonObject *hash_result = argon_call(hash_function, 0, NULL, err, state);
|
||||
if (hash_result->type != TYPE_NUMBER ||
|
||||
!hash_result->value.as_number->is_int64) {
|
||||
*err =
|
||||
create_err(err->line, err->column, err->length, err->path, "Hash Error",
|
||||
"hash result needs to be a 64 bit integer.");
|
||||
return 0;
|
||||
}
|
||||
return hash_result->value.as_number->n.i64;
|
||||
}
|
||||
|
||||
ArgonObject *new_class() {
|
||||
ArgonObject *object = new_object();
|
||||
add_builtin_field(object, __class__, ARGON_TYPE_TYPE);
|
||||
add_builtin_field(object, __base__, BASE_CLASS);
|
||||
return object;
|
||||
}
|
||||
|
||||
ArgonObject *new_instance(ArgonObject *of) {
|
||||
ArgonObject *object = new_object();
|
||||
add_builtin_field(object, __class__, of);
|
||||
return object;
|
||||
}
|
||||
|
||||
inline void add_builtin_field(ArgonObject *target, built_in_fields field,
|
||||
ArgonObject *object) {
|
||||
for (size_t i = 0; i < target->built_in_slot_length; i++) {
|
||||
if (target->built_in_slot[i].field == field) {
|
||||
target->built_in_slot[i].value = object;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (field > BUILT_IN_ARRAY_COUNT) {
|
||||
if (!target->dict)
|
||||
target->dict = createHashmap_GC();
|
||||
hashmap_insert_GC(target->dict, built_in_field_hashes[field],
|
||||
(char *)built_in_field_names[field], object, 0);
|
||||
return;
|
||||
}
|
||||
target->built_in_slot[target->built_in_slot_length++] =
|
||||
(struct built_in_slot){field, object};
|
||||
// hashmap_insert_GC(target->dict, built_in_field_hashes[field],
|
||||
// (char *)built_in_field_names[field], object, 0);
|
||||
}
|
||||
|
||||
void add_field_l(ArgonObject *target, char *name, uint64_t hash, size_t length,
|
||||
ArgonObject *object) {
|
||||
for (size_t i = 0; i < BUILT_IN_ARRAY_COUNT; i++) {
|
||||
if (strcmp_len(name, length, built_in_field_names[i]) == 0) {
|
||||
add_builtin_field(target, i, object);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!target->dict)
|
||||
target->dict = createHashmap_GC();
|
||||
char *name_copy = ar_alloc(length);
|
||||
memcpy(name_copy, name, length);
|
||||
name_copy[length] = '\0';
|
||||
hashmap_insert_GC(target->dict, hash, name_copy, object, 0);
|
||||
}
|
||||
|
||||
ArgonObject *bind_object_to_function(ArgonObject *object,
|
||||
ArgonObject *function) {
|
||||
ArgonObject *bound_method_wrapper = new_object();
|
||||
bound_method_wrapper->type = TYPE_METHOD;
|
||||
add_field(bound_method_wrapper, "__class__", ARGON_METHOD_TYPE);
|
||||
add_field(bound_method_wrapper, "__binding__", object);
|
||||
add_field(bound_method_wrapper, "__function__", function);
|
||||
ArgonObject *function_name = get_field(function, "__name__", false, false);
|
||||
add_builtin_field(bound_method_wrapper, __class__, ARGON_METHOD_TYPE);
|
||||
add_builtin_field(bound_method_wrapper, __binding__, object);
|
||||
add_builtin_field(bound_method_wrapper, __function__, function);
|
||||
ArgonObject *function_name = get_builtin_field(function, __name__);
|
||||
if (function_name)
|
||||
add_field(bound_method_wrapper, "__name__", function_name);
|
||||
add_builtin_field(bound_method_wrapper, __name__, function_name);
|
||||
return bound_method_wrapper;
|
||||
}
|
||||
|
||||
ArgonObject *get_field_for_class_l(ArgonObject *target, char *name,
|
||||
size_t length, ArgonObject *binding_object) {
|
||||
char *field = "__base__";
|
||||
size_t field_size = strlen(field);
|
||||
uint64_t hash, size_t length,
|
||||
ArgonObject *binding_object) {
|
||||
while (target) {
|
||||
ArgonObject *object = get_field_l(target, name, length, false, false);
|
||||
ArgonObject *object = get_field_l(target, name, hash, length, false, false);
|
||||
if (object) {
|
||||
if ((object->type == TYPE_FUNCTION ||
|
||||
object->type == TYPE_NATIVE_FUNCTION) &&
|
||||
@@ -57,35 +168,74 @@ ArgonObject *get_field_for_class_l(ArgonObject *target, char *name,
|
||||
}
|
||||
return object;
|
||||
}
|
||||
target = get_field_l(target, field, field_size, false, false);
|
||||
target = get_builtin_field(target, __base__);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ArgonObject *get_field_l(ArgonObject *target, char *name, size_t length,
|
||||
bool recursive, bool disable_method_wrapper) {
|
||||
if(!target|| !target->dict) return NULL;
|
||||
char *field = "__class__";
|
||||
size_t field_size = strlen(field);
|
||||
ArgonObject *object = hashmap_lookup_GC(
|
||||
target->dict, siphash64_bytes(name, length, siphash_key));
|
||||
ArgonObject *get_field_l(ArgonObject *target, char *name, uint64_t hash,
|
||||
size_t length, bool recursive,
|
||||
bool disable_method_wrapper) {
|
||||
for (size_t i = 0; i < target->built_in_slot_length; i++) {
|
||||
if (strcmp_len(name, length, built_in_field_names[i]) == 0) {
|
||||
return target->built_in_slot[i].value;
|
||||
}
|
||||
}
|
||||
if (!target->dict)
|
||||
return NULL;
|
||||
ArgonObject *object = hashmap_lookup_GC(target->dict, hash);
|
||||
if (!recursive || object)
|
||||
return object;
|
||||
ArgonObject *binding = target;
|
||||
if (disable_method_wrapper)
|
||||
binding = NULL;
|
||||
return get_field_for_class_l(
|
||||
hashmap_lookup_GC(target->dict,
|
||||
siphash64_bytes(field, field_size, siphash_key)),
|
||||
name, length, binding);
|
||||
return get_field_for_class_l(get_builtin_field(target, __class__), name, hash,
|
||||
length, binding);
|
||||
}
|
||||
|
||||
ArgonObject *get_field(ArgonObject *target, char *name, bool recursive,
|
||||
bool disable_method_wrapper) {
|
||||
return get_field_l(target, name, strlen(name), recursive,
|
||||
disable_method_wrapper);
|
||||
ArgonObject *get_builtin_field_for_class(ArgonObject *target,
|
||||
built_in_fields field,
|
||||
ArgonObject *binding_object) {
|
||||
while (target) {
|
||||
ArgonObject *object = get_builtin_field(target, field);
|
||||
if (object) {
|
||||
if ((object->type == TYPE_FUNCTION ||
|
||||
object->type == TYPE_NATIVE_FUNCTION) &&
|
||||
binding_object) {
|
||||
object = bind_object_to_function(binding_object, object);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
target = get_builtin_field(target, __base__);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
ArgonObject *get_field_for_class(ArgonObject *target, char *name,
|
||||
ArgonObject *binding_object) {
|
||||
return get_field_for_class_l(target, name, strlen(name), binding_object);
|
||||
inline ArgonObject *get_builtin_field(ArgonObject *target,
|
||||
built_in_fields field) {
|
||||
return get_builtin_field_with_recursion_support(target, field, false, false);
|
||||
}
|
||||
|
||||
ArgonObject *
|
||||
get_builtin_field_with_recursion_support(ArgonObject *target,
|
||||
built_in_fields field, bool recursive,
|
||||
bool disable_method_wrapper) {
|
||||
if (!target)
|
||||
return NULL;
|
||||
for (size_t i = 0; i < target->built_in_slot_length; i++) {
|
||||
if (target->built_in_slot[i].field == field) {
|
||||
return target->built_in_slot[i].value;
|
||||
}
|
||||
}
|
||||
if (!target->dict)
|
||||
return NULL;
|
||||
ArgonObject *object =
|
||||
hashmap_lookup_GC(target->dict, built_in_field_hashes[field]);
|
||||
if (!recursive || object)
|
||||
return object;
|
||||
ArgonObject *binding = target;
|
||||
if (disable_method_wrapper)
|
||||
binding = NULL;
|
||||
return get_builtin_field_for_class(
|
||||
hashmap_lookup_GC(target->dict, built_in_field_hashes[__class__]), field,
|
||||
binding);
|
||||
}
|
||||
@@ -9,27 +9,42 @@
|
||||
#include "../internals/hashmap/hashmap.h"
|
||||
#include "../runtime.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
extern ArgonObject *BASE_CLASS;
|
||||
|
||||
typedef struct ArgonObject ArgonObject;
|
||||
ArgonObject *new_object();
|
||||
|
||||
void add_field(ArgonObject *target, char *name, ArgonObject *object);
|
||||
ArgonObject *new_class();
|
||||
ArgonObject *new_instance(ArgonObject * of);
|
||||
|
||||
void init_built_in_field_hashes();
|
||||
|
||||
int64_t hash_object(ArgonObject *object, ArErr *err, RuntimeState *state);
|
||||
|
||||
void add_builtin_field(ArgonObject *target, built_in_fields field,
|
||||
ArgonObject *object);
|
||||
|
||||
void add_field_l(ArgonObject *target, char *name, uint64_t hash, size_t length,
|
||||
ArgonObject *object);
|
||||
|
||||
ArgonObject *bind_object_to_function(ArgonObject *object,
|
||||
ArgonObject *function);
|
||||
|
||||
ArgonObject *get_field_for_class_l(ArgonObject *target, char *name,
|
||||
size_t length, ArgonObject *binding_object);
|
||||
uint64_t hash, size_t length,
|
||||
ArgonObject *binding_object);
|
||||
|
||||
ArgonObject *get_field_l(ArgonObject *target, char *name, size_t length,
|
||||
bool recursive, bool disable_method_wrapper);
|
||||
ArgonObject *get_field_l(ArgonObject *target, char *name, uint64_t hash,
|
||||
size_t length, bool recursive,
|
||||
bool disable_method_wrapper);
|
||||
|
||||
ArgonObject *get_field_for_class(ArgonObject *target, char *name,
|
||||
ArgonObject *binding_object);
|
||||
ArgonObject *get_builtin_field_for_class(ArgonObject *target,
|
||||
built_in_fields field,
|
||||
ArgonObject *binding_object);
|
||||
|
||||
ArgonObject *get_field(ArgonObject *target, char *name, bool recursive,
|
||||
bool disable_method_wrapper);
|
||||
ArgonObject *get_builtin_field(ArgonObject *target, built_in_fields field);
|
||||
|
||||
ArgonObject *get_builtin_field_with_recursion_support(ArgonObject *target, built_in_fields field, bool recursive, bool disable_method_wrapper);
|
||||
|
||||
#endif // OBJECT_H
|
||||
@@ -4,27 +4,85 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "../object.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "string.h"
|
||||
#include "../number/number.h"
|
||||
#include "../object.h"
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
ArgonObject *ARGON_STRING_TYPE = NULL;
|
||||
|
||||
ArgonObject *new_string_object(char*data, size_t length) {
|
||||
ArgonObject * object = new_object();
|
||||
add_field(object, "__class__", ARGON_STRING_TYPE);
|
||||
add_field(object, "length", new_number_object_from_long(length, 1));
|
||||
|
||||
|
||||
|
||||
char *c_quote_string(const char *input, size_t len) {
|
||||
// Worst case: every byte becomes "\uXXXX" (6 chars) + quotes + NUL
|
||||
size_t max_out = 2 + (len * 6) + 1;
|
||||
char *out = malloc(max_out);
|
||||
if (!out) return NULL;
|
||||
|
||||
size_t j = 0;
|
||||
out[j++] = '"';
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
unsigned char c = (unsigned char)input[i];
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
out[j++] = '\\'; out[j++] = 'n'; break;
|
||||
case '\t':
|
||||
out[j++] = '\\'; out[j++] = 't'; break;
|
||||
case '\r':
|
||||
out[j++] = '\\'; out[j++] = 'r'; break;
|
||||
case '\\':
|
||||
out[j++] = '\\'; out[j++] = '\\'; break;
|
||||
case '\"':
|
||||
out[j++] = '\\'; out[j++] = '\"'; break;
|
||||
default:
|
||||
if (isprint(c)) {
|
||||
out[j++] = c;
|
||||
} else {
|
||||
// write \uXXXX
|
||||
j += sprintf(&out[j], "\\u%04X", c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out[j++] = '"';
|
||||
out[j] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
void init_string(ArgonObject*object,char *data, size_t length, uint64_t prehash,
|
||||
uint64_t hash) {
|
||||
add_builtin_field(object, field_length,
|
||||
new_number_object_from_int64(length));
|
||||
object->type = TYPE_STRING;
|
||||
object->value.as_str.data = ar_alloc_atomic(length);
|
||||
memcpy(object->value.as_str.data, data, length);
|
||||
object->value.as_str.length = length;
|
||||
object->value.as_str = ar_alloc(sizeof(struct string_struct));
|
||||
object->value.as_str->data = data;
|
||||
object->value.as_str->prehash = prehash;
|
||||
object->value.as_str->hash_computed = hash;
|
||||
object->value.as_str->hash = hash;
|
||||
object->value.as_str->length = length;
|
||||
object->as_bool = length;
|
||||
}
|
||||
|
||||
ArgonObject *new_string_object_without_memcpy(char *data, size_t length, uint64_t prehash,
|
||||
uint64_t hash) {
|
||||
ArgonObject *object = new_instance(ARGON_STRING_TYPE);
|
||||
init_string(object,data,length,prehash,hash);
|
||||
return object;
|
||||
}
|
||||
|
||||
ArgonObject *new_string_object_null_terminated(char*data) {
|
||||
return new_string_object(data, strlen(data));
|
||||
ArgonObject *new_string_object(char *data, size_t length, uint64_t prehash,
|
||||
uint64_t hash) {
|
||||
char*data_copy = ar_alloc_atomic(length);
|
||||
memcpy(data_copy, data, length);
|
||||
return new_string_object_without_memcpy(data_copy,length, prehash, hash);
|
||||
}
|
||||
|
||||
ArgonObject *new_string_object_null_terminated(char *data) {
|
||||
return new_string_object(data, strlen(data), 0, 0);
|
||||
}
|
||||
@@ -10,7 +10,15 @@
|
||||
|
||||
extern ArgonObject *ARGON_STRING_TYPE;
|
||||
|
||||
ArgonObject *new_string_object(char*data, size_t length);
|
||||
char *c_quote_string(const char *input, size_t len);
|
||||
|
||||
void init_string(ArgonObject*object,char *data, size_t length, uint64_t prehash,
|
||||
uint64_t hash);
|
||||
|
||||
ArgonObject *new_string_object_without_memcpy(char *data, size_t length, uint64_t prehash,
|
||||
uint64_t hash);
|
||||
|
||||
ArgonObject *new_string_object(char *data, size_t length, uint64_t prehash, uint64_t hash);
|
||||
|
||||
ArgonObject *new_string_object_null_terminated(char*data);
|
||||
#endif // STRING_OBJ_H
|
||||
@@ -13,14 +13,14 @@ ArgonObject *term_log(size_t argc, ArgonObject **argv, ArErr *err,
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
if (i != 0)
|
||||
printf(" ");
|
||||
ArgonObject *string_convert_method = get_field_for_class(
|
||||
get_field(argv[i], "__class__", false, false), "__string__", argv[i]);
|
||||
ArgonObject *string_convert_method = get_builtin_field_for_class(
|
||||
get_builtin_field(argv[i], __class__), __string__, argv[i]);
|
||||
|
||||
if (string_convert_method) {
|
||||
ArgonObject *string_object =
|
||||
argon_call(string_convert_method, 0, NULL, err, state);
|
||||
fwrite(string_object->value.as_str.data, sizeof(char),
|
||||
string_object->value.as_str.length, stdout);
|
||||
fwrite(string_object->value.as_str->data, sizeof(char),
|
||||
string_object->value.as_str->length, stdout);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,10 @@
|
||||
#include "../translator/translator.h"
|
||||
#include "internals/dynamic_array_armem/darray_armem.h"
|
||||
#include "internals/hashmap/hashmap.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
extern ArgonObject *ARGON_METHOD_TYPE;
|
||||
extern Stack *Global_Scope;
|
||||
@@ -54,8 +58,6 @@ typedef struct StackFrame {
|
||||
uint64_t depth;
|
||||
} StackFrame;
|
||||
|
||||
#define STACKFRAME_CHUNKS 64
|
||||
|
||||
void bootstrap_types();
|
||||
|
||||
extern struct hashmap *runtime_hash_table;
|
||||
@@ -64,17 +66,34 @@ uint64_t runtime_hash(const void *data, size_t len, uint64_t prehash);
|
||||
|
||||
void bootstrap_globals();
|
||||
|
||||
uint8_t pop_byte(Translated *translated, RuntimeState *state);
|
||||
static inline void *arena_get(ConstantArena *arena, size_t offset) {
|
||||
return (char *)arena->data + offset;
|
||||
}
|
||||
|
||||
uint64_t pop_bytecode(Translated *translated, RuntimeState *state);
|
||||
static inline uint8_t pop_byte(Translated *translated, RuntimeState *state) {
|
||||
return *(((uint8_t *)(translated->bytecode.data)) + state->head++);
|
||||
}
|
||||
|
||||
ArErr run_instruction(Translated *translated, RuntimeState *state,
|
||||
struct Stack **stack);
|
||||
static inline uint64_t pop_bytecode(Translated *translated,
|
||||
RuntimeState *state) {
|
||||
uint64_t *ptr =
|
||||
(uint64_t *)((uint8_t *)translated->bytecode.data + state->head);
|
||||
state->head += 8;
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
static inline void run_instruction(Translated *translated, RuntimeState *state,
|
||||
struct Stack **stack, ArErr *err);
|
||||
|
||||
RuntimeState init_runtime_state(Translated translated, char *path);
|
||||
|
||||
Stack *create_scope(Stack *prev);
|
||||
Stack *create_scope(Stack *prev, bool force);
|
||||
|
||||
ArErr runtime(Translated translated, RuntimeState state, Stack *stack);
|
||||
void add_to_scope(Stack *stack, char *name, ArgonObject *value);
|
||||
|
||||
void add_to_hashmap(struct hashmap_GC *hashmap, char *name, ArgonObject *value);
|
||||
|
||||
void runtime(Translated translated, RuntimeState state, Stack *stack,
|
||||
ArErr *err);
|
||||
|
||||
#endif // RUNTIME_H
|
||||
321
src/shell.c
Normal file
321
src/shell.c
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include "./lexer/lexer.h"
|
||||
#include "./runtime/call/call.h"
|
||||
#include "./runtime/objects/functions/functions.h"
|
||||
#include "./runtime/objects/term/term.h"
|
||||
#include "./runtime/runtime.h"
|
||||
#include "./translator/translator.h"
|
||||
#include "LICENSE_c.h"
|
||||
#include "import.h"
|
||||
#include "runtime/objects/literals/literals.h"
|
||||
#include "runtime/objects/string/string.h"
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__linux__)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
FILE *fmemopen(void *buf, size_t size, const char *mode) {
|
||||
if (strchr(mode, 'r') == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE *tmp = tmpfile();
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
|
||||
if (fwrite(buf, 1, size, tmp) != size) {
|
||||
fclose(tmp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rewind(tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
typedef long ssize_t;
|
||||
|
||||
ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
|
||||
if (!lineptr || !n || !stream) return -1;
|
||||
|
||||
size_t pos = 0;
|
||||
int c;
|
||||
|
||||
if (*lineptr == NULL || *n == 0) {
|
||||
*n = 128;
|
||||
*lineptr = malloc(*n);
|
||||
if (!*lineptr) return -1;
|
||||
}
|
||||
|
||||
while ((c = fgetc(stream)) != EOF) {
|
||||
if (pos + 1 >= *n) {
|
||||
*n *= 2;
|
||||
char *tmp = realloc(*lineptr, *n);
|
||||
if (!tmp) return -1;
|
||||
*lineptr = tmp;
|
||||
}
|
||||
(*lineptr)[pos++] = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos == 0 && c == EOF)
|
||||
return -1;
|
||||
|
||||
(*lineptr)[pos] = '\0';
|
||||
return (ssize_t)pos;
|
||||
}
|
||||
#else
|
||||
#include "../external/linenoise/linenoise.h"
|
||||
#endif
|
||||
|
||||
// Ctrl+C handler
|
||||
void handle_sigint(int sig) {
|
||||
(void)sig;
|
||||
printf("\nBye :)\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int execute_code(FILE *stream, char *path, Stack *scope,
|
||||
RuntimeState *runtime_state) {
|
||||
if (!stream) {
|
||||
perror("fmemopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ArErr err = no_err;
|
||||
|
||||
DArray tokens;
|
||||
darray_init(&tokens, sizeof(Token));
|
||||
LexerState state = {path, stream, 0, 0, &tokens};
|
||||
err = lexer(state);
|
||||
if (err.exists) {
|
||||
darray_free(&tokens, free_token);
|
||||
output_err(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DArray ast;
|
||||
|
||||
darray_init(&ast, sizeof(ParsedValue));
|
||||
|
||||
err = parser(path, &ast, &tokens, false);
|
||||
darray_free(&tokens, free_token);
|
||||
if (err.exists) {
|
||||
darray_free(&ast, free_parsed);
|
||||
output_err(err);
|
||||
return 1;
|
||||
}
|
||||
Translated __translated = init_translator(path);
|
||||
err = translate(&__translated, &ast);
|
||||
darray_free(&ast, free_parsed);
|
||||
if (err.exists) {
|
||||
darray_free(&__translated.bytecode, NULL);
|
||||
free(__translated.constants.data);
|
||||
hashmap_free(__translated.constants.hashmap, NULL);
|
||||
output_err(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
hashmap_free(__translated.constants.hashmap, NULL);
|
||||
Translated translated = {
|
||||
__translated.registerCount, __translated.registerAssignment, NULL, {}, {},
|
||||
__translated.path};
|
||||
translated.bytecode.data = ar_alloc(__translated.bytecode.capacity);
|
||||
memcpy(translated.bytecode.data, __translated.bytecode.data,
|
||||
__translated.bytecode.capacity);
|
||||
translated.bytecode.element_size = __translated.bytecode.element_size;
|
||||
translated.bytecode.size = __translated.bytecode.size;
|
||||
translated.bytecode.resizable = false;
|
||||
translated.bytecode.capacity =
|
||||
__translated.bytecode.size * __translated.bytecode.element_size;
|
||||
translated.constants.data = ar_alloc(__translated.constants.capacity);
|
||||
memcpy(translated.constants.data, __translated.constants.data,
|
||||
__translated.constants.capacity);
|
||||
translated.constants.size = __translated.constants.size;
|
||||
translated.constants.capacity = __translated.constants.capacity;
|
||||
darray_free(&__translated.bytecode, NULL);
|
||||
free(__translated.constants.data);
|
||||
*runtime_state = init_runtime_state(translated, path);
|
||||
runtime(translated, *runtime_state, scope, &err);
|
||||
if (err.exists) {
|
||||
output_err(err);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Simple input function
|
||||
char *input(const char *prompt) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
printf("%s", prompt);
|
||||
fflush(stdout);
|
||||
|
||||
char *buffer = NULL;
|
||||
size_t size = 0;
|
||||
ssize_t len = getline(&buffer, &size, stdin);
|
||||
|
||||
if (len == -1) {
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len > 0 && buffer[len - 1] == '\n') {
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
#else
|
||||
char *buffer = linenoise(prompt);
|
||||
if (buffer && buffer[0] != '\0') {
|
||||
linenoiseHistoryAdd(buffer);
|
||||
}
|
||||
#endif
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *read_all_stdin(size_t *out_len) {
|
||||
size_t size = 1024;
|
||||
size_t len = 0;
|
||||
char *buffer = malloc(size);
|
||||
if (!buffer)
|
||||
exit(1);
|
||||
|
||||
int c;
|
||||
while ((c = fgetc(stdin)) != EOF) {
|
||||
if (len + 1 >= size) {
|
||||
size *= 2;
|
||||
buffer = realloc(buffer, size);
|
||||
if (!buffer)
|
||||
exit(1);
|
||||
}
|
||||
buffer[len++] = (char)c;
|
||||
}
|
||||
|
||||
*out_len = len;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int shell() {
|
||||
|
||||
Stack *main_scope = create_scope(Global_Scope, true);
|
||||
|
||||
if (!isatty(STDIN_FILENO)) {
|
||||
RuntimeState runtime_state;
|
||||
size_t len;
|
||||
char *data = read_all_stdin(&len);
|
||||
FILE *file = fmemopen(data, len, "r");
|
||||
int resp = execute_code(file, "<stdin>", main_scope, &runtime_state);
|
||||
fclose(file);
|
||||
free(data);
|
||||
return resp;
|
||||
}
|
||||
add_to_scope(main_scope, "license",
|
||||
new_string_object_without_memcpy((char *)LICENSE_txt,
|
||||
LICENSE_txt_len, 0, 0));
|
||||
|
||||
signal(SIGINT, handle_sigint);
|
||||
|
||||
printf("Argon (Chloride %s)\nType \"license\" for more information.\n",
|
||||
version_string);
|
||||
|
||||
ArgonObject *output_object = create_argon_native_function("log", term_log);
|
||||
char *totranslate = NULL;
|
||||
size_t totranslatelength = 0;
|
||||
|
||||
while (true) {
|
||||
#if defined(__linux__)
|
||||
malloc_trim(0);
|
||||
#endif
|
||||
if (totranslate) {
|
||||
free(totranslate);
|
||||
totranslate = NULL;
|
||||
totranslatelength = 0;
|
||||
};
|
||||
int indent = 0;
|
||||
int last_indent = 0;
|
||||
char textBefore[] = ">>> ";
|
||||
|
||||
// Dynamic array of lines
|
||||
|
||||
do {
|
||||
last_indent = indent;
|
||||
// indent string
|
||||
size_t isz = (size_t)indent * 4;
|
||||
char *indentStr = (char *)malloc(isz + 1);
|
||||
if (!indentStr)
|
||||
exit(1);
|
||||
memset(indentStr, ' ', isz);
|
||||
indentStr[isz] = '\0';
|
||||
|
||||
// prompt
|
||||
size_t p_len = strlen(textBefore) + isz;
|
||||
char *prompt = (char *)malloc(p_len + 1);
|
||||
if (!prompt)
|
||||
exit(1);
|
||||
memcpy(prompt, textBefore, strlen(textBefore));
|
||||
memcpy(prompt + strlen(textBefore), indentStr, isz + 1);
|
||||
|
||||
char *inp = input(prompt);
|
||||
free(prompt);
|
||||
|
||||
if (!inp) {
|
||||
printf("\nBye :)\n");
|
||||
// Free previously collected lines
|
||||
free(inp);
|
||||
free(totranslate);
|
||||
free(indentStr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Append line to totranslate
|
||||
size_t length = strlen(inp);
|
||||
totranslate = realloc(totranslate, totranslatelength + isz + length + 1);
|
||||
memcpy(totranslate + totranslatelength, indentStr, isz);
|
||||
memcpy(totranslate + totranslatelength + isz, inp, length);
|
||||
totranslatelength += isz + length + 1;
|
||||
totranslate[totranslatelength - 1] = '\n';
|
||||
|
||||
char *trimmed = inp;
|
||||
while (*trimmed == ' ' || *trimmed == '\t')
|
||||
trimmed++;
|
||||
|
||||
size_t len = strlen(trimmed);
|
||||
if (len >= 2 && strcmp(trimmed + len - 2, "do") == 0) {
|
||||
indent++;
|
||||
} else if (len == 0 && indent > 0) {
|
||||
indent--;
|
||||
}
|
||||
free(inp);
|
||||
strcpy(textBefore, "... ");
|
||||
free(indentStr);
|
||||
|
||||
} while (indent > 0 || last_indent != 0);
|
||||
totranslate = realloc(totranslate, totranslatelength + 1);
|
||||
totranslate[totranslatelength] = '\0';
|
||||
RuntimeState runtime_state;
|
||||
FILE *file = fmemopen((void *)totranslate, totranslatelength, "r");
|
||||
int resp = execute_code(file, "<shell>", main_scope, &runtime_state);
|
||||
fclose(file);
|
||||
if (resp) {
|
||||
continue;
|
||||
}
|
||||
if (runtime_state.registers[0]&&runtime_state.registers[0] != ARGON_NULL) {
|
||||
ArErr err = no_err;
|
||||
argon_call(output_object, 1,
|
||||
(ArgonObject *[]){runtime_state.registers[0]}, &err,
|
||||
&runtime_state);
|
||||
}
|
||||
totranslatelength = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
src/shell.h
Normal file
12
src/shell.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef ARGON_SHELL_H
|
||||
#define ARGON_SHELL_H
|
||||
|
||||
int shell();
|
||||
|
||||
#endif // ARGON_SHELL_H
|
||||
@@ -8,21 +8,19 @@
|
||||
size_t translate_access(Translated *translated, ParsedAccess *access,
|
||||
ArErr *err) {
|
||||
set_registers(translated, 1);
|
||||
uint64_t first = push_instruction_byte(translated, OP_LOAD_ACCESS_FUNCTION);
|
||||
uint64_t first = translate_parsed(translated, access->to_access, err);
|
||||
if (err->exists)
|
||||
return 0;
|
||||
push_instruction_byte(translated, OP_LOAD_GETATTRIBUTE_METHOD);
|
||||
push_instruction_byte(translated, OP_INIT_CALL);
|
||||
push_instruction_code(translated, 3);
|
||||
|
||||
translate_parsed(translated, &access->to_access, err);
|
||||
push_instruction_byte(translated, OP_INSERT_ARG);
|
||||
push_instruction_code(translated, 0);
|
||||
translate_parsed(translated, darray_get(&access->access, 0), err);
|
||||
push_instruction_byte(translated, OP_INSERT_ARG);
|
||||
push_instruction_code(translated, 1);
|
||||
|
||||
push_instruction_byte(translated, OP_LOAD_BOOL);
|
||||
push_instruction_byte(translated, access->access_fields);
|
||||
translate_parsed(translated, access->access, err);
|
||||
if (err->exists)
|
||||
return 0;
|
||||
push_instruction_byte(translated, OP_INSERT_ARG);
|
||||
push_instruction_code(translated, 2);
|
||||
push_instruction_code(translated, 0);
|
||||
|
||||
push_instruction_byte(translated, OP_SOURCE_LOCATION);
|
||||
push_instruction_code(translated, access->line);
|
||||
push_instruction_code(translated, access->column);
|
||||
|
||||
80
src/translator/assignment/assignment.c
Normal file
80
src/translator/assignment/assignment.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "assignment.h"
|
||||
#include "../../hash_data/hash_data.h"
|
||||
#include "../../parser/assignable/access/access.h"
|
||||
#include "../../parser/assignable/identifier/identifier.h"
|
||||
#include "../translator.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
size_t translate_parsed_assignment(Translated *translated,
|
||||
ParsedAssign *assignment, ArErr *err) {
|
||||
set_registers(translated, 1);
|
||||
DArray *old_return_jumps = translated->return_jumps;
|
||||
translated->return_jumps = NULL;
|
||||
size_t first = translate_parsed(translated, assignment->from, err);
|
||||
if (err->exists)
|
||||
return 0;
|
||||
switch (assignment->to->type) {
|
||||
case AST_IDENTIFIER:;
|
||||
ParsedIdentifier *identifier = assignment->to->data;
|
||||
size_t length = strlen(identifier->name);
|
||||
size_t offset =
|
||||
arena_push(&translated->constants, identifier->name, length);
|
||||
|
||||
push_instruction_byte(translated, OP_SOURCE_LOCATION);
|
||||
push_instruction_code(translated, identifier->line);
|
||||
push_instruction_code(translated, identifier->column);
|
||||
push_instruction_code(translated, length);
|
||||
|
||||
push_instruction_byte(translated, OP_ASSIGN);
|
||||
push_instruction_code(translated, length);
|
||||
push_instruction_code(translated, offset);
|
||||
push_instruction_code(translated,
|
||||
siphash64_bytes(identifier->name, length,
|
||||
siphash_key_fixed_for_translator));
|
||||
push_instruction_byte(translated, 0);
|
||||
break;
|
||||
case AST_ACCESS:;
|
||||
ParsedAccess *access = assignment->to->data;
|
||||
uint8_t registerA = translated->registerAssignment++;
|
||||
set_registers(translated, registerA);
|
||||
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
|
||||
push_instruction_byte(translated, 0);
|
||||
push_instruction_byte(translated, registerA);
|
||||
translate_parsed(translated, access->to_access, err);
|
||||
if (err->exists)
|
||||
return 0;
|
||||
push_instruction_byte(translated, OP_LOAD_SETATTR_METHOD);
|
||||
push_instruction_byte(translated, OP_INIT_CALL);
|
||||
push_instruction_code(translated, 2);
|
||||
translate_parsed(translated, access->access, err);
|
||||
if (err->exists)
|
||||
return 0;
|
||||
push_instruction_byte(translated, OP_INSERT_ARG);
|
||||
push_instruction_code(translated, 0);
|
||||
|
||||
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
|
||||
push_instruction_byte(translated, registerA);
|
||||
push_instruction_byte(translated, 0);
|
||||
push_instruction_byte(translated, OP_INSERT_ARG);
|
||||
push_instruction_code(translated, 1);
|
||||
|
||||
push_instruction_byte(translated, OP_CALL);
|
||||
translated->registerAssignment--;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "panic: unsupported assignment\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
translated->return_jumps = old_return_jumps;
|
||||
return first;
|
||||
}
|
||||
15
src/translator/assignment/assignment.h
Normal file
15
src/translator/assignment/assignment.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BYTECODE_ASSIGNMENT_H
|
||||
#define BYTECODE_ASSIGNMENT_H
|
||||
#include "../translator.h"
|
||||
#include "../../parser/assignable/assign/assign.h"
|
||||
|
||||
size_t translate_parsed_assignment(Translated *translated,
|
||||
ParsedAssign *assignment, ArErr *err);
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,17 @@ this operation takes 3 operands.
|
||||
1. the fixed hash of the variable name.
|
||||
1. the register of the given value (*)
|
||||
|
||||
## OP_ASSIGN
|
||||
|
||||
assigns to a variable in the stack. if the variable doesnt exist on the stack, it is automatically declared on the current scope.
|
||||
|
||||
this operation takes 3 operands.
|
||||
|
||||
1. the length of the variable name.
|
||||
1. the offset in the constant buffer of the variable name.
|
||||
1. the fixed hash of the variable name.
|
||||
1. the register of the given value (*)
|
||||
|
||||
## OP_LOAD_NULL
|
||||
|
||||
sets a given register to null.
|
||||
@@ -60,9 +71,7 @@ initilises a function to a given register.
|
||||
|
||||
## OP_BOOL
|
||||
|
||||
converts a value in a given register into true or false depending on the result from \_\_bool\_\_
|
||||
|
||||
1. the register to read and write to. (*)
|
||||
converts a value in register 0 into true or false depending on the result from \_\_bool\_\_ (using asBool if the object is a primitive)
|
||||
|
||||
## OP_JUMP_IF_FALSE
|
||||
|
||||
@@ -71,17 +80,20 @@ jumps when a the value in the given register is false.
|
||||
1. the register to read. (*)
|
||||
1. the index to jump to.
|
||||
|
||||
## OP_JUMP_IF_FALSE
|
||||
## OP_JUMP
|
||||
|
||||
jumps unconditionally to an index.
|
||||
|
||||
1. the index to jump to.
|
||||
|
||||
|
||||
## OP_NEW_SCOPE
|
||||
|
||||
creates a new stack
|
||||
|
||||
## OP_EMPTY_SCOPE
|
||||
|
||||
empties the current scope so the same memory can be reused.
|
||||
|
||||
## OP_POP_SCOPE
|
||||
|
||||
pops the top scope off the current
|
||||
@@ -108,9 +120,9 @@ sets the source location onto the runtime
|
||||
1. the column
|
||||
1. the length
|
||||
|
||||
## OP_LOAD_ACCESS_FUNCTION
|
||||
## OP_LOAD_GETATTRIBUTE_METHOD
|
||||
|
||||
loads the access function into register 1
|
||||
loads the \_\_getattribute\_\_ method from the objects class in register 1 and put it into register 1
|
||||
|
||||
## OP_LOAD_BOOL
|
||||
|
||||
@@ -120,19 +132,59 @@ loads a boolean into register 1
|
||||
|
||||
## OP_LOAD_NUMBER
|
||||
|
||||
loads a mpq_t number into memory
|
||||
loads a mpq_t / int64 number into memory
|
||||
|
||||
1. the register to write to. (*)
|
||||
1. is int64 (*)
|
||||
1. the size of the numerator in the constant buffer.
|
||||
1. the offset in the constant buffer of the numerator.
|
||||
1. is integer. (*)
|
||||
1. the size of the denominator in the constant buffer.
|
||||
1. the offset in the constant buffer of the denominator.
|
||||
|
||||
## OP_LOAD_ADDITION_FUNCTION
|
||||
## OP_COPY_TO_REGISTER
|
||||
|
||||
loads the addition function into register 1
|
||||
copies the value from one register to another
|
||||
|
||||
## OP_LOAD_SUBTRACTION_FUNCTION
|
||||
1. the register to copy from (*)
|
||||
2. the register to write to (*)
|
||||
|
||||
loads the subtraction function into register 1
|
||||
## OP_ADDITION
|
||||
|
||||
performs an addition between register A and register B, storing the result in register C
|
||||
|
||||
1. the register A (*)
|
||||
2. the register B (*)
|
||||
2. the register C (*)
|
||||
|
||||
## OP_SUBTRACTION
|
||||
|
||||
performs an subtraction between register A and register B, storing the result in register C
|
||||
|
||||
1. the register A (*)
|
||||
2. the register B (*)
|
||||
2. the register C (*)
|
||||
|
||||
## OP_MULTIPLICATION
|
||||
|
||||
performs an multiplication between register A and register B, storing the result in register C
|
||||
|
||||
1. the register A (*)
|
||||
2. the register B (*)
|
||||
2. the register C (*)
|
||||
|
||||
## OP_DIVISION
|
||||
|
||||
performs an division between register A and register B, storing the result in register C
|
||||
|
||||
1. the register A (*)
|
||||
2. the register B (*)
|
||||
2. the register C (*)
|
||||
|
||||
## OP_NOT
|
||||
|
||||
inverts the boolean value in register 0.
|
||||
|
||||
## OP_LOAD_SETATTR_METHOD
|
||||
|
||||
loads the \_\_setattr\_\_ method from the objects class in register 1 and put it into register 1
|
||||
@@ -14,17 +14,18 @@
|
||||
size_t translate_parsed_function(Translated *translated,
|
||||
ParsedFunction *parsedFunction, ArErr *err) {
|
||||
DArray main_bytecode = translated->bytecode;
|
||||
DArray _temp_bytecode;
|
||||
darray_init(&_temp_bytecode, sizeof(uint8_t));
|
||||
uint8_t old_assignment = translated->registerAssignment;
|
||||
translated->registerAssignment = 1;
|
||||
darray_init(&translated->bytecode, sizeof(uint8_t));
|
||||
set_registers(translated, 1);
|
||||
translated->bytecode = _temp_bytecode;
|
||||
translate_parsed(translated, parsedFunction->body, err);
|
||||
size_t function_bytecode_offset =
|
||||
arena_push(&translated->constants, translated->bytecode.data,
|
||||
translated->bytecode.size*translated->bytecode.element_size);
|
||||
size_t function_bytecode_length = translated->bytecode.size;
|
||||
darray_free(&translated->bytecode, NULL);
|
||||
translated->bytecode = main_bytecode;
|
||||
darray_free(&_temp_bytecode, NULL);
|
||||
translated->registerAssignment = old_assignment;
|
||||
size_t start = push_instruction_byte(translated, OP_LOAD_FUNCTION);
|
||||
size_t offset = arena_push(&translated->constants, parsedFunction->name,
|
||||
strlen(parsedFunction->name));
|
||||
|
||||
@@ -40,7 +40,6 @@ size_t translate_parsed_if(Translated *translated, DArray *parsedIf,
|
||||
return 0;
|
||||
}
|
||||
push_instruction_byte(translated, OP_BOOL);
|
||||
push_instruction_byte(translated, 0);
|
||||
push_instruction_byte(translated, OP_JUMP_IF_FALSE);
|
||||
push_instruction_byte(translated, 0);
|
||||
uint64_t last_jump_index = push_instruction_code(translated, 0);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "number.h"
|
||||
#include "../../runtime/objects/number/number.h"
|
||||
#include "../translator.h"
|
||||
#include <gmp.h>
|
||||
#include <stddef.h>
|
||||
@@ -16,6 +17,13 @@ size_t translate_parsed_number(Translated *translated, mpq_t *number,
|
||||
set_registers(translated, to_register + 1);
|
||||
size_t start = push_instruction_byte(translated, OP_LOAD_NUMBER);
|
||||
push_instruction_byte(translated, to_register);
|
||||
int64_t i64;
|
||||
uint8_t is_int64 = mpq_to_int64(*number, &i64);
|
||||
push_instruction_byte(translated, is_int64);
|
||||
if (is_int64) {
|
||||
push_instruction_code(translated, i64);
|
||||
return start;
|
||||
}
|
||||
size_t num_size;
|
||||
void *num_data = mpz_export(NULL, &num_size, 1, 1, 0, 0, mpq_numref(*number));
|
||||
size_t numerator_pos = arena_push(&translated->constants, num_data, num_size);
|
||||
|
||||
@@ -6,36 +6,94 @@
|
||||
|
||||
#include "operation.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
size_t translate_operation(Translated *translated, ParsedOperation *operation,
|
||||
ArErr *err) {
|
||||
set_registers(translated, 1);
|
||||
uint64_t first;
|
||||
switch (operation->operation) {
|
||||
case TOKEN_PLUS:;
|
||||
first = push_instruction_byte(translated, OP_LOAD_ADDITION_FUNCTION);
|
||||
break;
|
||||
case TOKEN_MINUS:
|
||||
first = push_instruction_byte(translated, OP_LOAD_SUBTRACTION_FUNCTION);
|
||||
break;
|
||||
default:
|
||||
*err = create_err(operation->line, operation->column, operation->length,
|
||||
translated->path, "Syntax Error", "unknown operation");
|
||||
return 0;
|
||||
}
|
||||
push_instruction_byte(translated, OP_INIT_CALL);
|
||||
push_instruction_code(translated, operation->to_operate_on.size);
|
||||
if (operation->operation == TOKEN_AND || operation->operation == TOKEN_OR) {
|
||||
size_t *jump_to_if_false =
|
||||
checked_malloc(operation->to_operate_on.size * sizeof(size_t));
|
||||
uint8_t registerA = translated->registerAssignment++;
|
||||
set_registers(translated, translated->registerAssignment);
|
||||
uint64_t first = 0;
|
||||
for (size_t i = 0; i < operation->to_operate_on.size; i++) {
|
||||
uint64_t position = translate_parsed(
|
||||
translated, darray_get(&operation->to_operate_on, i), err);
|
||||
if (i == 0)
|
||||
first = position;
|
||||
if (err->exists) {
|
||||
free(jump_to_if_false);
|
||||
return first;
|
||||
}
|
||||
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
|
||||
push_instruction_byte(translated, 0);
|
||||
push_instruction_byte(translated, registerA);
|
||||
|
||||
for (size_t i = 0; i < operation->to_operate_on.size; i++) {
|
||||
push_instruction_byte(translated, OP_BOOL);
|
||||
if (operation->operation == TOKEN_OR) push_instruction_byte(translated, OP_NOT);
|
||||
|
||||
push_instruction_byte(translated, OP_JUMP_IF_FALSE);
|
||||
push_instruction_byte(translated, 0);
|
||||
jump_to_if_false[i] = push_instruction_code(translated, 0);
|
||||
}
|
||||
for (size_t i = 0; i < operation->to_operate_on.size; i++) {
|
||||
set_instruction_code(translated, jump_to_if_false[i],
|
||||
translated->bytecode.size);
|
||||
}
|
||||
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
|
||||
push_instruction_byte(translated, registerA);
|
||||
push_instruction_byte(translated, 0);
|
||||
push_instruction_byte(translated, OP_LOAD_NULL);
|
||||
push_instruction_byte(translated, registerA);
|
||||
|
||||
free(jump_to_if_false);
|
||||
return first;
|
||||
}
|
||||
uint8_t registerA = translated->registerAssignment++;
|
||||
uint8_t registerB = translated->registerAssignment++;
|
||||
set_registers(translated, translated->registerAssignment);
|
||||
uint64_t first = translate_parsed(
|
||||
translated, darray_get(&operation->to_operate_on, 0), err);
|
||||
if (err->exists)
|
||||
return first;
|
||||
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
|
||||
push_instruction_byte(translated, 0);
|
||||
push_instruction_byte(translated, registerA);
|
||||
for (size_t i = 1; i < operation->to_operate_on.size; i++) {
|
||||
translate_parsed(translated, darray_get(&operation->to_operate_on, i), err);
|
||||
push_instruction_byte(translated, OP_INSERT_ARG);
|
||||
push_instruction_code(translated, i);
|
||||
if (err->exists)
|
||||
return first;
|
||||
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
|
||||
push_instruction_byte(translated, 0);
|
||||
push_instruction_byte(translated, registerB);
|
||||
switch (operation->operation) {
|
||||
case TOKEN_PLUS:;
|
||||
push_instruction_byte(translated, OP_ADDITION);
|
||||
break;
|
||||
case TOKEN_MINUS:;
|
||||
push_instruction_byte(translated, OP_SUBTRACTION);
|
||||
break;
|
||||
case TOKEN_STAR:;
|
||||
push_instruction_byte(translated, OP_MULTIPLICATION);
|
||||
break;
|
||||
case TOKEN_SLASH:;
|
||||
push_instruction_byte(translated, OP_DIVISION);
|
||||
break;
|
||||
default:
|
||||
*err = create_err(operation->line, operation->column, operation->length,
|
||||
translated->path, "Syntax Error", "unknown operation");
|
||||
return 0;
|
||||
}
|
||||
push_instruction_byte(translated, registerA);
|
||||
push_instruction_byte(translated, registerB);
|
||||
push_instruction_byte(
|
||||
translated, operation->to_operate_on.size - 1 == i ? 0 : registerA);
|
||||
}
|
||||
|
||||
push_instruction_byte(translated, OP_SOURCE_LOCATION);
|
||||
push_instruction_code(translated, operation->line);
|
||||
push_instruction_code(translated, operation->column);
|
||||
push_instruction_code(translated, operation->length);
|
||||
push_instruction_byte(translated, OP_CALL);
|
||||
push_instruction_byte(translated, OP_LOAD_NULL);
|
||||
push_instruction_byte(translated, registerA);
|
||||
push_instruction_byte(translated, OP_LOAD_NULL);
|
||||
push_instruction_byte(translated, registerB);
|
||||
translated->registerAssignment -= 2;
|
||||
return first;
|
||||
}
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
#include "translator.h"
|
||||
#include "../hash_data/hash_data.h"
|
||||
#include "../parser/not/not.h"
|
||||
#include "access/access.h"
|
||||
#include "assignment/assignment.h"
|
||||
#include "call/call.h"
|
||||
#include "declaration/declaration.h"
|
||||
#include "dowrap/dowrap.h"
|
||||
@@ -17,6 +19,7 @@
|
||||
#include "operation/operation.h"
|
||||
#include "return/return.h"
|
||||
#include "string/string.h"
|
||||
#include "while/while.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -49,10 +52,6 @@ void arena_resize(ConstantArena *arena, size_t new_size) {
|
||||
arena->capacity = new_capacity;
|
||||
}
|
||||
|
||||
void *arena_get(ConstantArena *arena, size_t offset) {
|
||||
return arena->data + offset;
|
||||
}
|
||||
|
||||
size_t arena_push(ConstantArena *arena, const void *data, size_t length) {
|
||||
uint64_t hash = siphash64_bytes(data, length, siphash_key);
|
||||
|
||||
@@ -84,6 +83,7 @@ Translated init_translator(char *path) {
|
||||
Translated translated;
|
||||
translated.path = path;
|
||||
translated.registerCount = 1;
|
||||
translated.registerAssignment = 1;
|
||||
translated.return_jumps = NULL;
|
||||
darray_init(&translated.bytecode, sizeof(uint8_t));
|
||||
arena_init(&translated.constants);
|
||||
@@ -148,6 +148,9 @@ size_t translate_parsed(Translated *translated, ParsedValue *parsedValue,
|
||||
(ParsedIdentifier *)parsedValue->data);
|
||||
case AST_IF:
|
||||
return translate_parsed_if(translated, (DArray *)parsedValue->data, err);
|
||||
case AST_WHILE:
|
||||
return translate_parsed_while(translated, (ParsedWhile *)parsedValue->data,
|
||||
err);
|
||||
case AST_DOWRAP:
|
||||
return translate_parsed_dowrap(translated, (DArray *)parsedValue->data,
|
||||
err);
|
||||
@@ -162,6 +165,17 @@ size_t translate_parsed(Translated *translated, ParsedValue *parsedValue,
|
||||
case AST_OPERATION:
|
||||
return translate_operation(translated, (ParsedOperation *)parsedValue->data,
|
||||
err);
|
||||
case AST_ASSIGN:
|
||||
return translate_parsed_assignment(translated,
|
||||
(ParsedAssign *)parsedValue->data, err);
|
||||
case AST_TO_BOOL: {
|
||||
size_t first = translate_parsed(
|
||||
translated, ((ParsedToBool *)parsedValue->data)->value, err);
|
||||
push_instruction_byte(translated, OP_BOOL);
|
||||
if (((ParsedToBool *)parsedValue->data)->invert)
|
||||
push_instruction_byte(translated, OP_NOT);
|
||||
return first;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -24,22 +24,27 @@ typedef enum {
|
||||
OP_JUMP_IF_FALSE,
|
||||
OP_JUMP,
|
||||
OP_NEW_SCOPE,
|
||||
OP_EMPTY_SCOPE,
|
||||
OP_POP_SCOPE,
|
||||
OP_INIT_CALL,
|
||||
OP_INSERT_ARG,
|
||||
OP_CALL,
|
||||
OP_SOURCE_LOCATION,
|
||||
OP_LOAD_ACCESS_FUNCTION,
|
||||
OP_LOAD_BOOL,
|
||||
OP_LOAD_NUMBER,
|
||||
OP_LOAD_ADDITION_FUNCTION,
|
||||
OP_LOAD_SUBTRACTION_FUNCTION
|
||||
OP_ASSIGN,
|
||||
OP_COPY_TO_REGISTER,
|
||||
OP_ADDITION,
|
||||
OP_SUBTRACTION,
|
||||
OP_LOAD_GETATTRIBUTE_METHOD,
|
||||
OP_MULTIPLICATION,
|
||||
OP_DIVISION,
|
||||
OP_NOT,
|
||||
OP_LOAD_SETATTR_METHOD
|
||||
} OperationType;
|
||||
|
||||
void arena_resize(ConstantArena *arena, size_t new_size);
|
||||
|
||||
void *arena_get(ConstantArena *arena, size_t offset);
|
||||
|
||||
size_t arena_push(ConstantArena *arena, const void *data, size_t length);
|
||||
|
||||
size_t push_instruction_byte(Translated *translator, uint8_t byte);
|
||||
|
||||
55
src/translator/while/while.c
Normal file
55
src/translator/while/while.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "while.h"
|
||||
#include <stddef.h>
|
||||
|
||||
size_t translate_parsed_while(Translated *translated, ParsedWhile *parsedWhile,
|
||||
ArErr *err) {
|
||||
set_registers(translated, 1);
|
||||
DArray return_jumps;
|
||||
DArray *old_return_jumps = NULL;
|
||||
if (translated->return_jumps) {
|
||||
darray_init(&return_jumps, sizeof(size_t));
|
||||
old_return_jumps = translated->return_jumps;
|
||||
translated->return_jumps = &return_jumps;
|
||||
}
|
||||
size_t first = push_instruction_byte(translated, OP_NEW_SCOPE);
|
||||
size_t start_of_loop =
|
||||
translate_parsed(translated, parsedWhile->condition, err);
|
||||
if (err->exists) {
|
||||
return 0;
|
||||
}
|
||||
push_instruction_byte(translated, OP_BOOL);
|
||||
push_instruction_byte(translated, OP_JUMP_IF_FALSE);
|
||||
push_instruction_byte(translated, 0);
|
||||
uint64_t jump_index = push_instruction_code(translated, 0);
|
||||
translate_parsed(translated, parsedWhile->content, err);
|
||||
push_instruction_byte(translated, OP_EMPTY_SCOPE);
|
||||
push_instruction_byte(translated, OP_JUMP);
|
||||
push_instruction_code(translated, start_of_loop);
|
||||
set_instruction_code(translated, jump_index, translated->bytecode.size);
|
||||
push_instruction_byte(translated, OP_POP_SCOPE);
|
||||
push_instruction_byte(translated, OP_LOAD_NULL);
|
||||
push_instruction_byte(translated, 0);
|
||||
if (translated->return_jumps) {
|
||||
push_instruction_byte(translated, OP_JUMP);
|
||||
size_t skip_return = push_instruction_code(translated, 0);
|
||||
|
||||
size_t return_jump_to = push_instruction_byte(translated, OP_POP_SCOPE);
|
||||
push_instruction_byte(translated, OP_JUMP);
|
||||
size_t return_up = push_instruction_code(translated, 0);
|
||||
darray_push(old_return_jumps, &return_up);
|
||||
for (size_t i = 0; i < return_jumps.size; i++) {
|
||||
size_t *index = darray_get(&return_jumps, i);
|
||||
set_instruction_code(translated, *index, return_jump_to);
|
||||
}
|
||||
set_instruction_code(translated, skip_return, translated->bytecode.size);
|
||||
darray_free(&return_jumps, NULL);
|
||||
translated->return_jumps = old_return_jumps;
|
||||
}
|
||||
return first;
|
||||
}
|
||||
15
src/translator/while/while.h
Normal file
15
src/translator/while/while.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 William Bell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef TRANSLATE_WHILE_H
|
||||
#define TRANSLATE_WHILE_H
|
||||
#include "../translator.h"
|
||||
#include "../../parser/while/while.h"
|
||||
|
||||
size_t translate_parsed_while(Translated *translated, ParsedWhile *parsedWhile,
|
||||
ArErr *err);
|
||||
|
||||
#endif // TRANSLATE_WHILE_H
|
||||
94
test.ar
94
test.ar
@@ -1,94 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2025 William Bell
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"h"
|
||||
"e"
|
||||
"ll"
|
||||
"o"
|
||||
" "
|
||||
"wo"
|
||||
"rl"
|
||||
"d"
|
||||
"world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello world"
|
||||
"hello\u0000world"
|
||||
"🇬🇧"
|
||||
"\u0000"
|
||||
"hello"
|
||||
|
||||
let hello = "helllo\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nbruhhhhh"
|
||||
|
||||
1.24323234e2312324
|
||||
|
||||
let a,
|
||||
b = "hello",
|
||||
c,
|
||||
d = 42,
|
||||
temp_result,
|
||||
compute_area(radius) = 3.1415,
|
||||
identity(x) = x,
|
||||
f(x)=do
|
||||
term.log("hello world")
|
||||
do
|
||||
term.log('hello world')
|
||||
term.log("hello world")
|
||||
,
|
||||
g(y, z),
|
||||
result,
|
||||
z = 0,
|
||||
extremely_long_variable_name_to_test_limits,
|
||||
cache_value = compute_area(5),
|
||||
placeholder_fn_with_no_body(arg1, arg2, arg3),
|
||||
total = identity(100),
|
||||
deeply_nested_args_function(arg1, arg2, arg3, arg4, arg5),
|
||||
sum = a,
|
||||
another,
|
||||
another_function(),
|
||||
just_null_here,
|
||||
x
|
||||
|
||||
|
||||
|
||||
|
||||
if (x) do
|
||||
term.log("hello world")
|
||||
term.log("hello world")
|
||||
else term.log("bruh")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mm=x/2/4/2/4354/534/534//534//3422*404203420234+3432423324&&430234230||4320423040230423^384239423043024923%4432042304920.3432423423
|
||||
|
||||
let X = [
|
||||
'hello world',
|
||||
'wow',
|
||||
10
|
||||
]
|
||||
|
||||
term.log(x[0:1:1])
|
||||
|
||||
let y = {
|
||||
'hello':test,
|
||||
world:'nice'
|
||||
}
|
||||
|
||||
term.log(y['hello'],y.world)
|
||||
38
test.py
38
test.py
@@ -1,38 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2025 William Bell
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import string
|
||||
from itertools import product
|
||||
import sys
|
||||
|
||||
def generate_names(max_width, skip_keywords=None):
|
||||
if skip_keywords is None:
|
||||
skip_keywords = {"if", "else", "while", "forever", "for", "break", "continue",
|
||||
"return", "let", "import", "from", "do", "true", "false", "null",
|
||||
"delete", "not", "try", "catch", "in", "or", "and", "elif"}
|
||||
else:
|
||||
skip_keywords = set(skip_keywords)
|
||||
|
||||
chars = string.ascii_lowercase
|
||||
first = True
|
||||
write = sys.stdout.write
|
||||
|
||||
for length in range(1, max_width + 1):
|
||||
print(length, file=sys.stderr)
|
||||
i = 0
|
||||
for p in product(chars, repeat=length):
|
||||
name = ''.join(p)
|
||||
if name in skip_keywords:
|
||||
continue
|
||||
write('let ')
|
||||
write(name)
|
||||
write(' = null\n')
|
||||
first = False
|
||||
if i>10000000:
|
||||
break
|
||||
i+=1
|
||||
|
||||
# Example usage:
|
||||
max_width = 5
|
||||
generate_names(max_width)
|
||||
10
testing.ar
10
testing.ar
@@ -1,10 +0,0 @@
|
||||
let say_hi(name) = do
|
||||
let z(y) = do
|
||||
return y
|
||||
let u = z(
|
||||
do
|
||||
return name
|
||||
)
|
||||
return "hello "+u+", how are you?"
|
||||
|
||||
term.log(say_hi("william"))
|
||||
1
tests/anonymous_function.ar
Normal file
1
tests/anonymous_function.ar
Normal file
@@ -0,0 +1 @@
|
||||
term.log(()=10)
|
||||
1
tests/env.ar
Normal file
1
tests/env.ar
Normal file
@@ -0,0 +1 @@
|
||||
term.log("hello,", env.USER+'.', 'how is', env.XDG_SESSION_TYPE+'?')
|
||||
10
tests/intergral.ar
Normal file
10
tests/intergral.ar
Normal file
@@ -0,0 +1,10 @@
|
||||
let f(x) = sin(x)
|
||||
|
||||
let intergral_aprox(n, a, b) = do
|
||||
let h = (b-a)/n
|
||||
let output = 0
|
||||
for (i from 1 to n+1) do
|
||||
output += h*f(a+i*h)
|
||||
return output
|
||||
|
||||
term.log(intergral_aprox(1000, 0,1))
|
||||
4
tests/iteration-test.ar
Normal file
4
tests/iteration-test.ar
Normal file
@@ -0,0 +1,4 @@
|
||||
term.log(global)
|
||||
let i = 1e8
|
||||
while (i) do
|
||||
i=i-1
|
||||
3
tests/iteration-test.py
Normal file
3
tests/iteration-test.py
Normal file
@@ -0,0 +1,3 @@
|
||||
i = 1000000
|
||||
while i:
|
||||
i=i-1
|
||||
6
tests/number-memory-leak.ar
Normal file
6
tests/number-memory-leak.ar
Normal file
@@ -0,0 +1,6 @@
|
||||
let i = 10
|
||||
let x = 1
|
||||
while (i) do
|
||||
x = x * 1e100000000
|
||||
term.log(i=i-1)
|
||||
term.log(x)
|
||||
12
windows-profile.txt
Normal file
12
windows-profile.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
[settings]
|
||||
os=Windows
|
||||
compiler=gcc
|
||||
compiler.version=12
|
||||
compiler.libcxx=libstdc++11
|
||||
compiler.threads=posix
|
||||
compiler.exception=seh
|
||||
arch=x86_64
|
||||
build_type=Release
|
||||
|
||||
[tool_requires]
|
||||
mingw-builds/12.2.0
|
||||
Reference in New Issue
Block a user