Compare commits

..

30 Commits

Author SHA1 Message Date
William Bell
357c8745a4 fix dependancies 2025-11-30 05:55:40 +00:00
William Bell
4101144e26 test 2025-11-30 05:50:34 +00:00
William Bell
6a32c4721c update readme to have build instructions 2025-11-30 05:33:20 +00:00
William Bell
6de89ebb2f test 2025-11-30 05:26:10 +00:00
William Bell
4d4749c65e test 2025-11-30 05:24:24 +00:00
William Bell
916d94b32b test 2025-11-30 04:31:32 +00:00
e3108b2606 Merge branch 'main' of https://github.com/Open-Argon/Chloride 2025-11-26 03:07:22 +00:00
f3912ae49f add item access 2025-11-26 03:07:06 +00:00
William Bell
9de85f3b72 change logo colours to match C 2025-11-21 01:06:59 +00:00
William Bell
41a6fb17da Merge branch 'main' of https://github.com/Open-Argon/Carbon 2025-11-21 01:05:28 +00:00
William Bell
7a48771976 add logos 2025-11-21 01:05:04 +00:00
William Bell
89abf4e036 Update README.md 2025-11-21 01:04:51 +00:00
William Bell
df409dd3f6 Update README.md 2025-11-21 00:30:57 +00:00
William Bell
646938ea79 Update README.md 2025-11-21 00:21:36 +00:00
William Bell
9befc3760a Merge branch 'main' of https://github.com/Open-Argon/Carbon 2025-11-20 23:54:54 +00:00
William Bell
3d2ba09518 change back to stack level registers on call 2025-11-20 23:54:35 +00:00
94b86fc416 change the tests 2025-11-12 11:29:48 +00:00
William Bell
0a2aa7369f fix memory leak that occures when converting a hashmap to a string in debug mode 2025-11-11 19:57:24 +00:00
William Bell
0b594d7882 revert test 2025-11-11 18:06:54 +00:00
William Bell
c0ba18c37e fix buffer overflow in hashmap to array function. 2025-11-11 03:11:09 +00:00
William Bell
608fd86003 fix memory bug occuring in debug mode and attempting to fix another 2025-11-11 02:30:14 +00:00
William Bell
434b0ed99e fix bug where it hashmap output bug 2025-11-11 02:00:17 +00:00
William Bell
bfaf8df0d0 attempt to remove redundent memory allocations 2025-11-08 04:29:09 +00:00
William Bell
3e3df5595e fix hashmap to list missing items when they are in the same position in the list 2025-11-04 18:10:19 +00:00
William Bell
51c6bdcea9 fix seg fault in dictionary creation 2025-10-22 20:33:01 +01:00
William Bell
dd3b3b936d add dictionaries 2025-10-22 19:53:19 +01:00
William Bell
b6714b390a fix seg fault when callng a function with not enough parameters 2025-10-22 03:52:47 +01:00
William Bell
a9b1d23f79 fix windows build 2025-10-21 21:43:02 +01:00
William Bell
6d3e79b731 fix window build 2025-10-21 21:32:02 +01:00
William Bell
b3ee64d294 fix windows build 2025-10-21 21:08:27 +01:00
46 changed files with 1111 additions and 260 deletions

68
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,68 @@
pipeline {
agent any
stages {
stage('Checkout') {
steps {
checkout scm
sh 'git submodule update --init --recursive'
}
}
stage('Setup Conan') {
steps {
sh '''
python3 -m venv /tmp/venv
. /tmp/venv/bin/activate
apt update
apt upgrade
apt install -y cmake flex python3 python3-pip python3-venv
pip install --upgrade pip
pip install conan
'''
}
}
stage('Setup Conan Profile') {
steps {
sh '''
. /tmp/venv/bin/activate
if [ ! -f ~/.conan2/profiles/default ]; then
conan profile detect
fi
'''
}
}
stage('Install Dependencies') {
steps {
sh '''
. /tmp/venv/bin/activate
conan install . --build=missing
'''
}
}
stage('Build Project') {
steps {
sh '''
. /tmp/venv/bin/activate
conan build .
'''
}
}
stage('Archive Build Artifacts') {
steps {
sh '''
# Copy LICENSE.txt into build/bin
cp LICENSE.txt build/bin/
# Create tarball with the contents of build/bin at the root
tar -czf chloride.tar.gz -C build/bin .
'''
// Archive the tarball
archiveArtifacts artifacts: 'chloride.tar.gz', allowEmptyArchive: false
}
}
}
}

View File

@@ -4,6 +4,79 @@ SPDX-FileCopyrightText: 2025 William Bell
SPDX-License-Identifier: GPL-3.0-or-later
-->
# Chloride
<div align="center">
<p>
<img width="150" src="logo/logo.png">
</p>
<h1>Chloride</h1>
</div>
An Argon interpreter written in C
Chloride is the new C-based interpreter for the Argon programming language.
It is designed as a drop-in replacement for the older Go implementation (argon-v3), while introducing a more efficient runtime and a cleaner, more consistent object model.
## Build
Currently, builds are only being made for linux x86_64 at [the projects Jenklins instance](https://jenkins.wbell.dev/job/chloride/).
If this does not satify your requirements, feel free to build for your platform. the dependancies are `conan`, `flex`, `cmake` and `gcc`.
install using conan.
```
conan install . --build=missing
```
and finally build using conan.
```
conan build .
```
the final build can be found in `build/bin`.
## Overview
Chloride aims to remain as interchangeable with argon-v3 as possible.
Most existing Argon code should run with minimal or no changes, and backwards compatibility is an ongoing priority.
Where behaviour differs, the goal is for those differences to be predictable and well-defined rather than accidental quirks.
This interpreter replaces argon-v3's AST-walking runtime with a proper bytecode compiler, caching system, and virtual machine.
The result is a more consistent execution model, lower memory usage, and improved overall performance, even though speed is not the sole focus of the project.
## Key Improvements Over argon-v3
- **Bytecode + VM architecture**
Chloride compiles source code into bytecode and executes it through a dedicated virtual machine.
The previous interpreter evaluated the AST directly at runtime, which limited performance and made optimisations difficult.
- **Reduced memory usage and CPU overhead**
Chloride is written in C with an emphasis on minimal allocations, predictable lifetimes, and efficient object handling.
- **Unified object model**
In contrast to argon-v3, where some values (such as numbers) were not objects, Chloride treats every value as a first-class object.
This simplifies the runtime and ensures a more consistent behaviour across all types.
- **Proper class and inheritance system**
Classes in Chloride are real objects, supporting inheritance and introspection in a clean, well-defined manner.
The old interpreter treated classes as a special-case construct, which restricted the language's expressiveness.
- **Backwards compatibility focus**
Chloride aims to match argon-v3s behaviour closely enough that most existing Argon programs run unchanged.
Compatibility fixes and behavioural parity are treated as long-term goals.
## Project Goals
- Maintain high compatibility with argon-v3.
- Minimise memory usage and improve runtime efficiency.
- Provide a stable, maintainable interpreter core.
- Keep the implementation straightforward so that future language features can be built cleanly on top of it.
- Serve as the reference interpreter for Argon going forward.
## Project Status
Chloride is still under active development.
The object model is largely complete, but several core language features are missing or experimental. Basic control flow constructs such as for loops are not implemented yet, partly because the older syntax was confusing and may be replaced with something clearer. While backwards compatibility is a goal, perfect compatibility is unlikely, especially where new syntax or improved semantics resolve long-standing issues in argon-v3.
The interpreter currently contains known performance issues and occasional segmentation faults, and part of the development process is identifying and removing these. The intention is to stabilise the runtime, finalise the syntax, and avoid any further major redesigns. The hope is that Chloride becomes both the long-term Argon interpreter and the last large rewrite the language needs.
# Licence
GNU General Public License v3.0

View File

@@ -16,12 +16,15 @@ class ArgonConan(ConanFile):
# Remove tool_requires, no flex from Conan
requires = [
"gmp/6.3.0",
"bdwgc/8.2.8"
"bdwgc/8.2.6"
]
default_options = {
"gmp/*:shared": False,
"bdwgc/*:shared": False
"bdwgc/*:shared": False,
"bdwgc/*:parallel_mark": False,
"bdwgc/*:threads": True,
"bdwgc/*:disable_debug": True,
}
def layout(self):

BIN
logo/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

84
logo/logo.svg Normal file
View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="logo.svg"
inkscape:export-filename="logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="1.6199927"
inkscape:cx="166.66742"
inkscape:cy="208.33427"
inkscape:window-width="2560"
inkscape:window-height="1414"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<rect
x="96.463768"
y="150.58824"
width="287.20887"
height="242.68713"
id="rect1" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
sodipodi:type="star"
style="fill:#555555;fill-opacity:1"
id="path7"
inkscape:flatsided="false"
sodipodi:sides="17"
sodipodi:cx="150.15175"
sodipodi:cy="58.052856"
sodipodi:r1="46.661324"
sodipodi:r2="37.32906"
sodipodi:arg1="0.30400094"
sodipodi:arg2="0.48880051"
inkscape:rounded="0.4"
inkscape:randomized="2.7755576e-17"
d="m 194.67349,72.020461 c -1.44879,4.618019 -9.29136,-0.712277 -11.56404,3.560897 -2.27268,4.273175 6.53108,7.796197 3.5119,11.579008 -3.01918,3.782812 -8.40662,-4.020601 -12.06948,-0.856972 -3.66286,3.163629 3.27373,9.629034 -0.90807,12.065747 -4.18181,2.436719 -6.38654,-6.785922 -10.94489,-5.159103 -4.55835,1.626819 -0.42574,10.161412 -5.2054,10.922942 -4.77966,0.76152 -3.50391,-8.634768 -8.34212,-8.76447 -4.83821,-0.129703 -4.06772,9.32144 -8.79971,8.30493 -4.732,-1.01651 -0.14806,-9.317437 -4.61271,-11.186144 -4.46464,-1.868708 -7.16032,7.222558 -11.20557,4.565293 -4.04525,-2.657264 3.22778,-8.741737 -0.26032,-12.097069 -3.4881,-3.355333 -9.28589,4.148226 -12.09806,0.209088 -2.81217,-3.939138 6.1677,-6.985416 4.12723,-11.374218 -2.04047,-4.388801 -10.15735,0.513654 -11.35664,-4.175355 -1.19929,-4.689009 8.27463,-4.285677 7.95737,-9.115215 -0.31727,-4.829539 -9.65701,-3.190291 -9.08144,-7.995895 0.57556,-4.805603 9.26403,-1.007133 10.71282,-5.625152 1.44879,-4.61802 -7.85242,-6.463369 -5.57974,-10.736544 2.27268,-4.273174 9.00227,2.407431 12.02145,-1.375381 3.01917,-3.782811 -4.98733,-8.863533 -1.32447,-12.027163 3.66286,-3.163629 7.5247,5.496857 11.70651,3.060143 4.18181,-2.436714 -1.44866,-10.066629 3.10969,-11.693447 4.55835,-1.626819 5.03088,7.843901 9.81054,7.082377 4.77966,-0.761525 2.28565,-9.91017 7.12386,-9.780467 4.83821,0.129703 1.85761,9.131584 6.58961,10.148098 4.73199,1.016513 5.71126,-8.415288 10.17591,-6.546581 4.46464,1.868707 -1.56654,9.185997 2.47871,11.843261 4.04525,2.657265 8.36555,-5.783874 11.85365,-2.428541 3.4881,3.355332 -4.77911,7.999788 -1.96695,11.938927 2.81217,3.939138 9.89002,-2.371317 11.93049,2.017485 2.04047,4.388801 -7.34625,5.733164 -6.14696,10.422174 1.19929,4.689009 10.07879,1.3615 10.39605,6.191039 0.31727,4.829538 -8.92123,2.692245 -9.49679,7.497849 -0.57556,4.805603 8.90636,4.910439 7.45757,9.528459 z"
inkscape:transform-center-x="1.5847721"
inkscape:transform-center-y="-0.51694771"
transform="matrix(5.4829104,-0.55336355,0.55336355,5.4829104,-597.77534,21.197933)" />
<path
style="font-weight:bold;font-size:96px;font-family:'TeX Gyre Heros';-inkscape-font-specification:'TeX Gyre Heros, Bold';white-space:pre;inline-size:117.996;fill:#a9bacd"
d="m 231.18969,194.33982 h -14.016 c -0.864,8.928 -6.528,12.96 -15.456,12.96 -11.136,0 -17.664,-8.544 -17.664,-23.808 0,-15.456 6.816,-24.096 18.144,-24.096 7.776,0 12.864,3.552 14.976,12.576 h 13.728 c -1.248,-16.128 -13.44,-24.864 -29.184,-24.864 -19.776,0 -32.064,13.536 -32.064,36.192 0,22.464 12.192,36.096 31.68,36.096 17.376,0 28.992,-9.024 29.856,-25.056 z m 23.42401,23.904 v -69.984 h -13.44 v 69.984 z"
id="text2"
transform="matrix(4.4012947,0,0,3.7064713,-720.43857,-423.2176)"
aria-label="Cl" />
<rect
style="fill:#a9bacd;fill-opacity:1;stroke-width:0.783784"
id="rect4"
width="43.448219"
height="12.658142"
x="421.52911"
y="134.00171"
sodipodi:insensitive="true" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -16,11 +16,11 @@ typedef enum {
__base__,
__class__,
__name__,
__binding__,
__function__,
BUILT_IN_ARRAY_COUNT,
__binding__,
__function__,
__add__,
__string__,
__subtract__,
@@ -29,13 +29,15 @@ typedef enum {
__new__,
__init__,
__boolean__,
__get_attr__,
__getattr__,
field__address,
__call__,
__number__,
field_length,
__getattribute__,
__set_attr__,
__setattr__,
__getitem__,
__setitem__,
__hash__,
__repr__,

View File

@@ -43,6 +43,15 @@ static inline uint64_t htole64(uint64_t x) { return x; }
#elif defined(__linux__)
#include <endian.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int is_regular_file(const char *path) {
struct stat path_stat;
stat(path, &path_stat);
return S_ISREG(path_stat.st_mode);
}
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define htole32(x) OSSwapHostToLittleInt32(x)
@@ -285,7 +294,6 @@ Translated load_argon_file(char *path, ArErr *err) {
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'",
@@ -400,7 +408,8 @@ Translated load_argon_file(char *path, ArErr *err) {
Translated gc_translated = {
translated.registerCount, translated.registerAssignment, NULL, {}, {},
translated.path};
gc_translated.bytecode.data = ar_alloc_atomic(translated.bytecode.capacity);
gc_translated.bytecode.data = ar_alloc_atomic(translated.bytecode.capacity +
translated.constants.capacity);
memcpy(gc_translated.bytecode.data, translated.bytecode.data,
translated.bytecode.capacity);
gc_translated.bytecode.element_size = translated.bytecode.element_size;
@@ -408,7 +417,8 @@ Translated load_argon_file(char *path, ArErr *err) {
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);
gc_translated.constants.data =
gc_translated.bytecode.data + translated.bytecode.capacity;
memcpy(gc_translated.constants.data, translated.constants.data,
translated.constants.capacity);
gc_translated.constants.size = translated.constants.size;
@@ -422,8 +432,8 @@ Translated load_argon_file(char *path, ArErr *err) {
}
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"};
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;
@@ -432,7 +442,8 @@ 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(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)) {
@@ -445,7 +456,8 @@ Stack *ar_import(char *current_directory, char *path_relative, ArErr *err) {
path_relative);
return NULL;
}
if (!importing_hash_table) importing_hash_table = createHashmap();
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);
@@ -463,7 +475,8 @@ Stack *ar_import(char *current_directory, char *path_relative, ArErr *err) {
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();
if (!imported_hash_table)
imported_hash_table = createHashmap_GC();
hashmap_insert_GC(imported_hash_table, hash, path, main_scope, 0);
return main_scope;
}

View File

@@ -6,12 +6,11 @@
#include "memory.h"
#include <gc.h>
#include <gc/gc.h>
#include <pthread.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);
@@ -22,6 +21,15 @@ void *checked_malloc(size_t size) {
return ptr;
}
void *checked_realloc(void *ptr, size_t size) {
void *new_ptr = realloc(ptr, size);
if (!new_ptr) {
fprintf(stderr, "fatal error: failed to allocate %zu bytes\n", size);
exit(EXIT_FAILURE);
}
return new_ptr;
}
struct allocation *memory_allocations = NULL;
size_t memory_allocations_size = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
@@ -29,7 +37,8 @@ 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));
// memory_allocations = malloc(memory_allocations_size*sizeof(struct
// allocation));
}
void ar_memory_shutdown() {
@@ -41,9 +50,23 @@ void ar_memory_shutdown() {
// free(memory_allocations);
}
void *ar_alloc(size_t size) { return GC_MALLOC(size); }
void *ar_alloc(size_t size) {
void *ptr = GC_MALLOC(size);
if (!ptr) {
fprintf(stderr, "panic: unable to allocate memory\n");
exit(EXIT_FAILURE);
}
return ptr;
}
void *ar_realloc(void *old, size_t size) { return GC_REALLOC(old, size); }
void *ar_realloc(void *old, size_t size) {
void *ptr = GC_REALLOC(old, size);
if (!ptr) {
fprintf(stderr, "panic: unable to allocate memory\n");
exit(EXIT_FAILURE);
}
return ptr;
}
void ar_finalizer(void *obj, GC_finalization_proc fn, void *client_data,
GC_finalization_proc *old_fn, void **old_client_data) {

View File

@@ -9,7 +9,7 @@
#include <stddef.h> // for size_t
#include <stdbool.h>
#include <gc/gc.h>
#include <gc.h>
// GC-managed allocations
@@ -37,5 +37,6 @@ void ar_memory_init();
void ar_memory_shutdown();
void *checked_malloc(size_t size);
void *checked_realloc(void *ptr, size_t size);
#endif // ARGON_MEMORY_H

View File

@@ -15,15 +15,13 @@
ParsedValueReturn parse_access(char *file, DArray *tokens, size_t *index,
ParsedValue *to_access) {
Token *first_token = darray_get(tokens, *index);
(*index)++;
ParsedValue *parsedValue = checked_malloc(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;
(*index)++;
ArErr err = error_if_finished(file, tokens, index);
if (err.exists) {
free_parsed(parsedValue);
@@ -49,7 +47,6 @@ ParsedValueReturn parse_access(char *file, DArray *tokens, size_t *index,
return parsedString;
}
parsedAccess->access = parsedString.value;
}
(*index)++;
return (ParsedValueReturn){no_err, parsedValue};
}

View File

@@ -84,12 +84,22 @@ ParsedValueReturn parse_assign(char *file, DArray *tokens,
return (ParsedValueReturn){err, NULL};
}
(*index)++;
ArErr err = error_if_finished(file, tokens, index);
if (err.exists) {
free_parsed(assign_to);
free(assign_to);
return (ParsedValueReturn){err, NULL};
}
token = darray_get(tokens, *index);
ParsedValueReturn from = parse_token(file, tokens, index, true);
if (from.err.exists) {
free_parsed(assign_to);
free(assign_to);
return from;
}
if (!from.value) {
free_parsed(assign_to);
free(assign_to);
return (ParsedValueReturn){create_err(token->line, token->column,
token->length, file, "Syntax Error",
"expected body"),
@@ -110,12 +120,6 @@ ParsedValueReturn parse_assign(char *file, DArray *tokens,
ParsedValue *parsedValue = checked_malloc(sizeof(ParsedValue));
parsedValue->type = AST_ASSIGN;
parsedValue->data = assign;
ArErr err = error_if_finished(file, tokens, index);
if (err.exists) {
free_parsed(parsedValue);
free(parsedValue);
return (ParsedValueReturn){err, NULL};
}
assign->from = from.value;
return (ParsedValueReturn){no_err, parsedValue};
}

View File

@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2025 William Bell
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "item.h"
#include "../../../lexer/token.h"
#include "../../../memory.h"
#include "../../parser.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
ParsedValueReturn parse_item_access(char *file, DArray *tokens, size_t *index,
ParsedValue *to_access) {
ParsedValue *parsedValue = checked_malloc(sizeof(ParsedValue));
ParsedItemAccess *parsedItemAccess = checked_malloc(sizeof(ParsedItemAccess));
parsedItemAccess->to_access = to_access;
size_t capacity = 4;
parsedItemAccess->items = checked_malloc(capacity * sizeof(ParsedValue *));
parsedItemAccess->itemc = 0;
parsedValue->type = AST_ITEM_ACCESS;
parsedValue->data = parsedItemAccess;
Token *token = darray_get(tokens, *index);
parsedItemAccess->line = token->line;
parsedItemAccess->column = token->column;
parsedItemAccess->length = token->length;
(*index)++;
while (true) {
ArErr err = error_if_finished(file, tokens, index);
if (err.exists) {
free_parsed(parsedValue);
free(parsedValue);
return (ParsedValueReturn){err, NULL};
}
ParsedValueReturn parsedKey = parse_token(file, tokens, index, true);
if (parsedKey.err.exists) {
free_parsed(parsedValue);
free(parsedValue);
return parsedKey;
}
parsedItemAccess->items[parsedItemAccess->itemc++] = parsedKey.value;
if (parsedItemAccess->itemc > capacity) {
capacity *= 2;
parsedItemAccess->items = checked_realloc(
parsedItemAccess->items, capacity * sizeof(ParsedValue *));
}
Token *token = darray_get(tokens, *index);
(*index)++;
if (token->type == TOKEN_COMMA) {
continue;
} else if (token->type == TOKEN_RBRACKET) {
break;
} else {
free_parsed(parsedValue);
free(parsedValue);
return (ParsedValueReturn){
create_err(token->line, token->column, token->length, file,
"Syntax Error",
"expected either a comma or a closing bracket"),
NULL};
}
}
return (ParsedValueReturn){no_err, parsedValue};
}
void free_parse_item_access(void *ptr) {
ParsedValue *parsedValue = ptr;
ParsedItemAccess *parsedItemAccess = parsedValue->data;
free_parsed(parsedItemAccess->to_access);
free(parsedItemAccess->to_access);
for (size_t i = 0; i < parsedItemAccess->itemc; i++) {
free_parsed(parsedItemAccess->items[i]);
free(parsedItemAccess->items[i]);
}
free(parsedItemAccess->items);
free(parsedItemAccess);
}

View File

@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2025 William Bell
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef ITEM_ACCESS_H
#define ITEM_ACCESS_H
#include "../../parser.h"
#include "../../../lexer/token.h" // for Token
typedef struct {
ParsedValue *to_access;
ParsedValue **items;
size_t itemc;
size_t line;
size_t column;
size_t length;
} ParsedItemAccess;
ParsedValueReturn parse_item_access(char *file, DArray *tokens, size_t *index,
ParsedValue *to_access);
void free_parse_item_access(void *ptr);
#endif // ACCESS_H

View File

@@ -47,6 +47,7 @@ ParsedValueReturn parse_parentheses(char *file, DArray *tokens, size_t *index) {
"Syntax Error", "expected comma"),
NULL};
}
(*index)++;
skip_newlines_and_indents(tokens, index);
err = error_if_finished(file, tokens, index);
if (err.exists) {

View File

@@ -11,6 +11,7 @@
#include "assignable/assign/assign.h"
#include "assignable/call/call.h"
#include "assignable/identifier/identifier.h"
#include "assignable/item/item.h"
#include "declaration/declaration.h"
#include "dictionary/dictionary.h"
#include "dowrap/dowrap.h"
@@ -164,9 +165,11 @@ ParsedValueReturn parse_token_full(char *file, DArray *tokens, size_t *index,
output = parse_call(file, tokens, index, output.value);
break;
case TOKEN_DOT:
case TOKEN_LBRACKET:
output = parse_access(file, tokens, index, output.value);
break;
case TOKEN_LBRACKET:
output = parse_item_access(file, tokens, index, output.value);
break;
SWITCH_OPERATIONS
if (process_operations) {
output = parse_operations(file, tokens, index, output.value);
@@ -199,7 +202,7 @@ ArErr parser(char *file, DArray *parsed, DArray *tokens, bool inline_flag) {
if (expecting_new_line) {
Token *token = darray_get(tokens, old_index);
return create_err(token->line, token->column, token->length, file,
"Syntax Error", "expected new line");
"Syntax Error", "invalid syntax");
}
expecting_new_line = true;
darray_push(parsed, parsed_code.value);
@@ -241,6 +244,9 @@ void free_parsed(void *ptr) {
case AST_ACCESS:
free_parse_access(parsed);
break;
case AST_ITEM_ACCESS:
free_parse_item_access(parsed);
break;
case AST_NULL:
case AST_BOOLEAN:
break;

View File

@@ -40,6 +40,7 @@ typedef enum {
AST_NUMBER,
AST_IF,
AST_ACCESS,
AST_ITEM_ACCESS,
AST_CALL,
AST_DECLARATION,
AST_NULL,

View File

@@ -11,6 +11,7 @@
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -121,6 +122,7 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
(int)object_name->value.as_str->length,
object_name->value.as_str->data,
object->value.argon_fn->number_of_parameters, argc);
return;
}
Stack *scope = create_scope(object->value.argon_fn->stack, true);
for (size_t i = 0; i < argc; i++) {
@@ -131,6 +133,8 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
new_string_object(key.data, key.length, 0, hash), value,
0);
}
if (CStackFrame) {
ArgonObject * registers[MAX_REGISTERS]; // fixed on the stack for speed purposes
StackFrame new_stackFrame = {
{object->value.argon_fn->translated.registerCount,
object->value.argon_fn->translated.registerAssignment,
@@ -140,8 +144,7 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
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 *)),
{registers,
0,
object->value.argon_fn->translated.path,
NULL,
@@ -154,13 +157,34 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv,
for (size_t i = 0; i < new_stackFrame.translated.registerCount; i++) {
new_stackFrame.state.registers[i] = NULL;
}
if (CStackFrame) {
runtime(new_stackFrame.translated, new_stackFrame.state,
new_stackFrame.stack, err);
state->registers[0] = new_stackFrame.state.registers[0];
} else {
StackFrame *currentStackFrame = ar_alloc(sizeof(StackFrame));
*currentStackFrame = new_stackFrame;
StackFrame *currentStackFrame = ar_alloc(sizeof(StackFrame)+object->value.argon_fn->translated.registerCount *
sizeof(ArgonObject *));
*currentStackFrame = (StackFrame){
{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},
{(ArgonObject **)((char*)currentStackFrame+sizeof(StackFrame)),
0,
object->value.argon_fn->translated.path,
NULL,
state->currentStackFramePointer,
{},
{}},
scope,
*state->currentStackFramePointer,
(*state->currentStackFramePointer)->depth + 1};
for (size_t i = 0; i < (*currentStackFrame).translated.registerCount; i++) {
(*currentStackFrame).state.registers[i] = NULL;
}
*state->currentStackFramePointer = currentStackFrame;
if ((*state->currentStackFramePointer)->depth >= 10000) {
double logval =

View File

@@ -6,7 +6,7 @@
#include "darray_armem.h"
#include "../../../memory.h"
#include <gc/gc.h>
#include <gc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

View File

@@ -7,7 +7,8 @@
#include "hashmap.h"
#include "../../../memory.h"
#include <gc/gc.h>
#include <gc.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -25,28 +26,50 @@ struct hashmap_GC *createHashmap_GC() {
t->inline_count = 0;
return t;
}
void hashmap_GC_to_array(struct hashmap_GC *t, struct node_GC**array,
static int compare_node_asc(const void *a, const void *b) {
const struct node_GC *na = *(const struct node_GC **)a;
const struct node_GC *nb = *(const struct node_GC **)b;
// Ascending order (smallest order first)
if (na->order < nb->order)
return -1;
if (na->order > nb->order)
return 1;
return 0;
}
struct node_GC ** hashmap_GC_to_array(struct hashmap_GC *t,
size_t *array_length) {
size_t array_size = 8;
*array_length = 0;
*array = ar_alloc(array_size * sizeof(struct node_GC));
struct node_GC ** 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 = ar_realloc(array, array_size * sizeof(struct node_GC*));
}
(*array)[(*array_length)++] = t->inline_values[i];
array[(*array_length)++] = &t->inline_values[i];
}
for (size_t i = 0; i < t->size; i++) {
if (!t->list[i]) continue;
struct node_GC *list = t->list[i];
struct node_GC *temp = list;
while (temp) {
if (*array_length >= array_size) {
array_size *= 2;
*array=ar_realloc(*array, array_size * sizeof(struct node_GC));
array = ar_realloc(array, array_size * sizeof(struct node_GC*));
}
(*array)[(*array_length)++] = *t->list[i];
array[(*array_length)++] = temp;
temp = temp->next;
}
}
qsort(array, *array_length, sizeof(struct node_GC*), compare_node_asc);
return array;
}
void clear_hashmap_GC(struct hashmap_GC *t) {
if (!t->count)
return;

View File

@@ -33,7 +33,7 @@ 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,
struct node_GC ** hashmap_GC_to_array(struct hashmap_GC *t,
size_t *array_length);
int hashCode_GC(struct hashmap_GC *t, uint64_t hash);

View File

@@ -45,18 +45,20 @@ ArgonObject *create_ARGON_DICTIONARY_TYPE___string__(size_t argc,
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);
size_t nodes_length;
struct node_GC ** nodes = hashmap_GC_to_array(object->value.as_hashmap, &nodes_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;
for (size_t i = 0; i < nodes_length; i++) {
struct node_GC *node = nodes[i];
ArgonObject *key = node->key;
ArgonObject *value = node->val;
if (!key) { fprintf(stderr, "NULL key at node %zu\n", i); continue; }
if (!value) { fprintf(stderr, "NULL value at node %zu\n", i); continue; }
ArgonObject *string_convert_method = get_builtin_field_for_class(
get_builtin_field(key, __class__), __repr__, key);
@@ -102,7 +104,7 @@ ArgonObject *create_ARGON_DICTIONARY_TYPE___string__(size_t argc,
string_length += length;
}
if (i != keys_length - 1) {
if (i != nodes_length - 1) {
char *string_obj = ", ";
size_t length = strlen(string_obj);
string = realloc(string, string_length + length);
@@ -120,14 +122,14 @@ ArgonObject *create_ARGON_DICTIONARY_TYPE___string__(size_t argc,
return result;
}
ArgonObject *create_ARGON_DICTIONARY_TYPE___get_attr__(size_t argc,
ArgonObject *create_ARGON_DICTIONARY_TYPE___getattr__(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);
"__getattr__ expects 2 argument, got %" PRIu64, argc);
return ARGON_NULL;
}
ArgonObject *object = argv[0];
@@ -146,14 +148,62 @@ ArgonObject *create_ARGON_DICTIONARY_TYPE___get_attr__(size_t argc,
}
ArgonObject *create_ARGON_DICTIONARY_TYPE___set_attr__(size_t argc,
ArgonObject *create_ARGON_DICTIONARY_TYPE___setattr__(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);
"__setattr__ 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;
}
ArgonObject *create_ARGON_DICTIONARY_TYPE___getitem__(size_t argc,
ArgonObject **argv,
ArErr *err,
RuntimeState *state) {
(void)state;
if (argc != 2) {
*err = create_err(0, 0, 0, "", "Runtime Error",
"__getitem__ 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) {
char *object_str = argon_object_to_null_terminated_string(key, err, state);
*err = create_err(0, 0, 0, NULL, "Attribute Error",
"Dictionary has no item '%s'", object_str);
return ARGON_NULL;
}
return result;
}
ArgonObject *create_ARGON_DICTIONARY_TYPE___setitem__(size_t argc,
ArgonObject **argv,
ArErr *err,
RuntimeState *state) {
(void)state;
if (argc != 3) {
*err = create_err(0, 0, 0, "", "Runtime Error",
"__setitem__ expects 2 argument, got %" PRIu64, argc);
return ARGON_NULL;
}
ArgonObject *object = argv[0];
@@ -175,13 +225,21 @@ void create_ARGON_DICTIONARY_TYPE() {
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__));
ARGON_DICTIONARY_TYPE, __getattr__,
create_argon_native_function("__getattr__",
create_ARGON_DICTIONARY_TYPE___getattr__));
add_builtin_field(
ARGON_DICTIONARY_TYPE, __set_attr__,
create_argon_native_function("__set_attr__",
create_ARGON_DICTIONARY_TYPE___set_attr__));
ARGON_DICTIONARY_TYPE, __setattr__,
create_argon_native_function("__setattr__",
create_ARGON_DICTIONARY_TYPE___setattr__));
add_builtin_field(
ARGON_DICTIONARY_TYPE, __setitem__,
create_argon_native_function("__setitem__",
create_ARGON_DICTIONARY_TYPE___setitem__));
add_builtin_field(
ARGON_DICTIONARY_TYPE, __getitem__,
create_argon_native_function("__getitem__",
create_ARGON_DICTIONARY_TYPE___getitem__));
add_builtin_field(ARGON_DICTIONARY_TYPE, __string__,
create_argon_native_function(
"__string__", create_ARGON_DICTIONARY_TYPE___string__));

View File

@@ -32,12 +32,12 @@ void load_argon_function(Translated *translated, RuntimeState *state,
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 *
uint64_t number_of_parameters = pop_bytecode(translated, state);
object->value.argon_fn = ar_alloc(sizeof(struct argon_function_struct)+number_of_parameters *
sizeof(struct string_struct));
object->value.argon_fn->parameters = (struct string_struct*)((char*)object->value.argon_fn+sizeof(struct argon_function_struct));
object->value.argon_fn->translated = *translated;
object->value.argon_fn->number_of_parameters = number_of_parameters;
for (size_t i = 0; i < object->value.argon_fn->number_of_parameters; i++) {
offset = pop_bytecode(translated, state);
length = pop_bytecode(translated, state);

View File

@@ -11,6 +11,7 @@
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
ArgonObject *ARGON_NUMBER_TYPE;
@@ -268,6 +269,64 @@ ArgonObject *ARGON_NUMBER_TYPE___multiply__(size_t argc, ArgonObject **argv,
}
}
static inline uint64_t mix64(uint64_t x) {
x ^= x >> 33;
x *= 0xff51afd7ed558ccdULL;
x ^= x >> 33;
x *= 0xc4ceb9fe1a85ec53ULL;
x ^= x >> 33;
return x;
}
uint64_t hash_mpz(const mpz_t z) {
// Export to raw bytes (big-endian for consistency)
size_t count;
unsigned char *data = mpz_export(NULL, &count, 1, 1, 1, 0, z);
// FNV-1a over bytes
uint64_t h = 1469598103934665603ULL;
for (size_t i = 0; i < count; i++) {
h ^= data[i];
h *= 1099511628211ULL;
}
// Include sign bit
if (mpz_sgn(z) < 0)
h = ~h;
// Free the temporary buffer allocated by mpz_export
free(data);
return mix64(h);
}
uint64_t hash_mpq(mpq_t q) {
uint64_t h_num = hash_mpz(mpq_numref(q));
uint64_t h_den = hash_mpz(mpq_denref(q));
// Combine using a standard 64-bit hash mix (boost-style)
uint64_t h = h_num ^ (h_den + 0x9e3779b97f4a7c15ULL + (h_num << 6) + (h_num >> 2));
return mix64(h);
}
ArgonObject *ARGON_NUMBER_TYPE___hash__(size_t argc, ArgonObject **argv,
ArErr *err, RuntimeState *state) {
(void)state;
if (argc != 1) {
*err = create_err(0, 0, 0, "", "Runtime Error",
"__hash__ expects 1 arguments, got %" PRIu64, argc);
}
uint64_t hash;
if (argv[0]->value.as_number->is_int64) {
hash = mix64(argv[0]->value.as_number->n.i64);
} else {
hash = hash_mpq(*argv[0]->value.as_number->n.mpq);
}
return new_number_object_from_int64(hash);
}
ArgonObject *ARGON_NUMBER_TYPE___division__(size_t argc, ArgonObject **argv,
ArErr *err, RuntimeState *state) {
(void)state;
@@ -497,7 +556,6 @@ 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);
@@ -520,6 +578,9 @@ void create_ARGON_NUMBER_TYPE() {
add_builtin_field(
ARGON_NUMBER_TYPE, __number__,
create_argon_native_function("__number__", ARGON_NUMBER_TYPE___number__));
add_builtin_field(
ARGON_NUMBER_TYPE, __hash__,
create_argon_native_function("__hash__", ARGON_NUMBER_TYPE___hash__));
add_builtin_field(ARGON_NUMBER_TYPE, __boolean__,
create_argon_native_function(
"__boolean__", ARGON_NUMBER_TYPE___boolean__));

View File

@@ -35,6 +35,8 @@ const char *built_in_field_names[BUILT_IN_FIELDS_COUNT] = {
"__class__",
"__name__",
"", // above is anything that gets stored in built in slots
"__binding__",
"__function__",
"__add__",
"__string__",
"__subtract__",
@@ -43,15 +45,15 @@ const char *built_in_field_names[BUILT_IN_FIELDS_COUNT] = {
"__new__",
"__init__",
"__boolean__",
"__get_attr__",
"__binding__",
"__function__",
"__getattr__",
"__getitem__",
"address",
"__call__",
"__number__",
"length",
"__getattribute__",
"__set_attr__",
"__setattr__",
"__setitem__",
"__hash__",
"__repr__"};
@@ -77,9 +79,7 @@ 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;
return (int64_t)object;
}
ArgonObject *hash_result = argon_call(hash_function, 0, NULL, err, state);
if (hash_result->type != TYPE_NUMBER ||

View File

@@ -13,6 +13,8 @@
extern ArgonObject *BASE_CLASS;
extern const char *built_in_field_names[BUILT_IN_FIELDS_COUNT];
typedef struct ArgonObject ArgonObject;
ArgonObject *new_class();

View File

@@ -5,6 +5,7 @@
*/
#include "string.h"
#include "../../call/call.h"
#include "../number/number.h"
#include "../object.h"
#include <ctype.h>
@@ -14,14 +15,12 @@
ArgonObject *ARGON_STRING_TYPE = NULL;
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;
if (!out)
return NULL;
size_t j = 0;
out[j++] = '"';
@@ -31,15 +30,25 @@ char *c_quote_string(const char *input, size_t len) {
switch (c) {
case '\n':
out[j++] = '\\'; out[j++] = 'n'; break;
out[j++] = '\\';
out[j++] = 'n';
break;
case '\t':
out[j++] = '\\'; out[j++] = 't'; break;
out[j++] = '\\';
out[j++] = 't';
break;
case '\r':
out[j++] = '\\'; out[j++] = 'r'; break;
out[j++] = '\\';
out[j++] = 'r';
break;
case '\\':
out[j++] = '\\'; out[j++] = '\\'; break;
out[j++] = '\\';
out[j++] = '\\';
break;
case '\"':
out[j++] = '\\'; out[j++] = '\"'; break;
out[j++] = '\\';
out[j++] = '\"';
break;
default:
if (isprint(c)) {
out[j++] = c;
@@ -55,10 +64,9 @@ char *c_quote_string(const char *input, size_t len) {
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));
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 = ar_alloc(sizeof(struct string_struct));
object->value.as_str->data = data;
@@ -69,8 +77,8 @@ void init_string(ArgonObject*object,char *data, size_t length, uint64_t prehash,
object->as_bool = length;
}
ArgonObject *new_string_object_without_memcpy(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 *object = new_instance(ARGON_STRING_TYPE);
init_string(object, data, length, prehash, hash);
return object;
@@ -83,6 +91,28 @@ ArgonObject *new_string_object(char *data, size_t length, uint64_t prehash,
return new_string_object_without_memcpy(data_copy, length, prehash, hash);
}
char *argon_object_to_null_terminated_string(ArgonObject *object, ArErr *err,
RuntimeState *state) {
ArgonObject *string_convert_method = get_builtin_field_for_class(
get_builtin_field(object, __class__), __repr__, object);
if (!string_convert_method)
return "<object>";
ArgonObject *string_object =
argon_call(string_convert_method, 0, NULL, err, state);
if (err->exists)
return NULL;
if (string_object->type != TYPE_STRING) return "<object>";
char *string = ar_alloc(string_object->value.as_str->length+1);
string[string_object->value.as_str->length] = '\0';
memcpy(string, string_object->value.as_str->data, string_object->value.as_str->length);
return string;
}
ArgonObject *new_string_object_null_terminated(char *data) {
return new_string_object(data, strlen(data), 0, 0);
}

View File

@@ -12,13 +12,17 @@ extern ArgonObject *ARGON_STRING_TYPE;
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);
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);
char *argon_object_to_null_terminated_string(ArgonObject *object, ArErr *err,
RuntimeState *state);
ArgonObject *new_string_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

View File

@@ -22,7 +22,7 @@
#include "objects/term/term.h"
#include "objects/type/type.h"
#include <fcntl.h>
#include <gc/gc.h>
#include <gc.h>
#include <gmp.h>
#include <inttypes.h>
#include <stddef.h>
@@ -72,11 +72,11 @@ ArgonObject *BASE_CLASS___getattribute__(size_t argc, ArgonObject **argv,
access->value.as_str->length, true, false);
if (value)
return value;
ArgonObject *cls__get_attr__ = get_builtin_field_for_class(
get_builtin_field(to_access, __class__), __get_attr__, to_access);
if (cls__get_attr__) {
ArgonObject *cls__getattr__ = get_builtin_field_for_class(
get_builtin_field(to_access, __class__), __getattr__, to_access);
if (cls__getattr__) {
value =
argon_call(cls__get_attr__, 1, (ArgonObject *[]){access}, err, state);
argon_call(cls__getattr__, 1, (ArgonObject *[]){access}, err, state);
if (err->exists) {
return ARGON_NULL;
}
@@ -285,12 +285,12 @@ ArgonObject *BASE_CLASS___init__(size_t argc, ArgonObject **argv, ArErr *err,
return ARGON_NULL;
}
ArgonObject *BASE_CLASS___set_attr__(size_t argc, ArgonObject **argv, ArErr *err,
ArgonObject *BASE_CLASS___setattr__(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 3 argument, got %" PRIu64, argc);
"__setattr__ expects 3 argument, got %" PRIu64, argc);
}
if (!argv[1]->value.as_str->hash)
argv[1]->value.as_str->hash =
@@ -615,7 +615,6 @@ void bootstrap_types() {
add_builtin_field(ARGON_METHOD_TYPE, __base__, BASE_CLASS);
add_builtin_field(ARGON_METHOD_TYPE, __name__,
new_string_object_null_terminated("method"));
create_ARGON_NUMBER_TYPE();
add_builtin_field(
BASE_CLASS, __new__,
@@ -692,12 +691,14 @@ void bootstrap_types() {
create_argon_native_function("__getattribute__",
BASE_CLASS___getattribute__));
add_builtin_field(
BASE_CLASS, __set_attr__,
create_argon_native_function("__set_attr__", BASE_CLASS___set_attr__));
BASE_CLASS, __setattr__,
create_argon_native_function("__setattr__", BASE_CLASS___setattr__));
create_ARGON_DICTIONARY_TYPE();
create_ARGON_NUMBER_TYPE();
}
void add_to_hashmap(struct hashmap_GC *hashmap, char *name, ArgonObject *value) {
void add_to_hashmap(struct hashmap_GC *hashmap, char *name,
ArgonObject *value) {
size_t length = strlen(name);
uint64_t hash = siphash64_bytes(name, length, siphash_key);
ArgonObject *key = new_string_object(name, length, 0, hash);
@@ -722,12 +723,12 @@ void bootstrap_globals() {
create_argon_native_function("log", term_log));
add_to_scope(Global_Scope, "term", create_dictionary(argon_term));
struct hashmap_GC *environment_variables = createHashmap_GC();
#if defined(_WIN32)
// Windows: use WinAPI
LPCH env = GetEnvironmentStringsA();
if (!env) return;
if (!env)
return;
for (LPCH var = env; *var; var += strlen(var) + 1) {
// Each string is like "KEY=VALUE"
@@ -742,7 +743,8 @@ void bootstrap_globals() {
const char *value = getenv(key);
add_to_hashmap(environment_variables, key,
value?new_string_object_null_terminated((char *)value):ARGON_NULL);
value ? new_string_object_null_terminated((char *)value)
: ARGON_NULL);
}
}
@@ -761,7 +763,8 @@ void bootstrap_globals() {
const char *value = getenv(key);
add_to_hashmap(environment_variables, key,
value?new_string_object_null_terminated((char *)value):ARGON_NULL);
value ? new_string_object_null_terminated((char *)value)
: ARGON_NULL);
}
}
#endif
@@ -885,7 +888,10 @@ void runtime(Translated _translated, RuntimeState _state, Stack *stack,
[OP_MULTIPLICATION] = &&DO_MULTIPLICATION,
[OP_DIVISION] = &&DO_DIVISION,
[OP_NOT] = &&DO_NOT,
[OP_LOAD_SETATTR_METHOD] = &&DO_LOAD_SETATTR_METHOD};
[OP_LOAD_SETATTR_METHOD] = &&DO_LOAD_SETATTR_METHOD,
[OP_CREATE_DICTIONARY] = &&DO_CREATE_DICTIONARY,
[OP_LOAD_SETITEM_METHOD] = &&DO_LOAD_SETITEM_METHOD,
[OP_LOAD_GETITEM_METHOD] = &&DO_LOAD_GETITEM_METHOD};
_state.head = 0;
StackFrame *currentStackFrame = ar_alloc(sizeof(StackFrame));
@@ -1257,13 +1263,41 @@ void runtime(Translated _translated, RuntimeState _state, Stack *stack,
}
DO_LOAD_SETATTR_METHOD: {
state->registers[0] = get_builtin_field_for_class(
get_builtin_field(state->registers[0], __class__), __set_attr__,
get_builtin_field(state->registers[0], __class__), __setattr__,
state->registers[0]);
if (!state->registers[0]) {
*err = create_err(
state->source_location.line, state->source_location.column,
state->source_location.length, state->path, "Runtime Error",
"unable to get __set_attr__ from objects class");
"unable to get __setattr__ from objects class");
}
continue;
}
DO_CREATE_DICTIONARY: {
state->registers[0] = create_dictionary(createHashmap_GC());
continue;
}
DO_LOAD_SETITEM_METHOD: {
state->registers[0] = get_builtin_field_for_class(
get_builtin_field(state->registers[0], __class__), __setitem__,
state->registers[0]);
if (!state->registers[0]) {
*err = create_err(
state->source_location.line, state->source_location.column,
state->source_location.length, state->path, "Runtime Error",
"unable to get __setitem__ from objects class");
}
continue;
}
DO_LOAD_GETITEM_METHOD: {
state->registers[0] = get_builtin_field_for_class(
get_builtin_field(state->registers[0], __class__), __getitem__,
state->registers[0]);
if (!state->registers[0]) {
*err = create_err(
state->source_location.line, state->source_location.column,
state->source_location.length, state->path, "Runtime Error",
"unable to get __getitem__ from objects class");
}
continue;
}

View File

@@ -15,6 +15,8 @@
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define MAX_REGISTERS 256
extern ArgonObject *ARGON_METHOD_TYPE;
extern Stack *Global_Scope;

View File

@@ -23,6 +23,7 @@
#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;
@@ -41,6 +42,46 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) {
return tmp;
}
// Only define ssize_t if it doesn't already exist
#ifndef _SSIZE_T_DEFINED
typedef long ssize_t;
#define _SSIZE_T_DEFINED
#endif
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
@@ -171,7 +212,6 @@ char *read_all_stdin(size_t *out_len) {
}
int shell() {
Stack *main_scope = create_scope(Global_Scope, true);
if (!isatty(STDIN_FILENO)) {
@@ -274,7 +314,8 @@ int shell() {
if (resp) {
continue;
}
if (runtime_state.registers[0]&&runtime_state.registers[0] != ARGON_NULL) {
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,

View File

@@ -122,11 +122,11 @@ sets the source location onto the runtime
## OP_LOAD_GETATTRIBUTE_METHOD
loads the \_\_getattribute\_\_ method from the objects class in register 1 and put it into register 1
loads the \_\_getattribute\_\_ method from the objects class in register 0 and put it into register 0
## OP_LOAD_BOOL
loads a boolean into register 1
loads a boolean into register 0
1. byte representing true or false (1 or 0) *
@@ -187,4 +187,16 @@ 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
loads the \_\_setattr\_\_ method from the objects class in register 0 and put it into register 0
## OP_CREATE_DICTIONARY
create a dictionary object into register 0.
## OP_LOAD_GETITEM_METHOD
loads the \_\_getitem\_\_ method from the objects class in register 0 and put it into register 0
## OP_LOAD_SETITEM_METHOD
loads the \_\_setitem\_\_ method from the objects class in register 0 and put it into register 0

View File

@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2025 William Bell
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "item_access.h"
#include <stddef.h>
size_t translate_item_access(Translated *translated, ParsedItemAccess *access,
ArErr *err) {
set_registers(translated, 1);
uint64_t first = translate_parsed(translated, access->to_access, err);
if (err->exists)
return 0;
push_instruction_byte(translated, OP_LOAD_GETITEM_METHOD);
push_instruction_byte(translated, OP_INIT_CALL);
push_instruction_code(translated, access->itemc);
for (size_t i = 0; i < access->itemc; i++) {
translate_parsed(translated, access->items[i], err);
if (err->exists)
return 0;
push_instruction_byte(translated, OP_INSERT_ARG);
push_instruction_code(translated, i);
}
push_instruction_byte(translated, OP_SOURCE_LOCATION);
push_instruction_code(translated, access->line);
push_instruction_code(translated, access->column);
push_instruction_code(translated, access->length);
push_instruction_byte(translated, OP_CALL);
return first;
}

View File

@@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2025 William Bell
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef translator_item_access_H
#define translator_item_access_H
#include "../../parser/assignable/item/item.h"
#include "../translator.h"
size_t translate_item_access(Translated *translated, ParsedItemAccess *access,
ArErr *err);
#endif // translator_item_access_H

View File

@@ -6,8 +6,11 @@
#include "translator.h"
#include "../hash_data/hash_data.h"
#include "../parser/dictionary/dictionary.h"
#include "../parser/not/not.h"
#include "access/access.h"
#include "../parser/assignable/item/item.h"
#include "item_access/item_access.h"
#include "assignment/assignment.h"
#include "call/call.h"
#include "declaration/declaration.h"
@@ -162,6 +165,8 @@ size_t translate_parsed(Translated *translated, ParsedValue *parsedValue,
err);
case AST_ACCESS:
return translate_access(translated, (ParsedAccess *)parsedValue->data, err);
case AST_ITEM_ACCESS:
return translate_item_access(translated, (ParsedItemAccess *)parsedValue->data, err);
case AST_OPERATION:
return translate_operation(translated, (ParsedOperation *)parsedValue->data,
err);
@@ -176,6 +181,67 @@ size_t translate_parsed(Translated *translated, ParsedValue *parsedValue,
push_instruction_byte(translated, OP_NOT);
return first;
}
case AST_DICTIONARY: {
DArray *dictionaryDarray = parsedValue->data;
size_t first = push_instruction_byte(translated, OP_CREATE_DICTIONARY);
uint8_t dictionaryRegister = translated->registerAssignment++;
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
push_instruction_byte(translated, 0);
push_instruction_byte(translated, dictionaryRegister);
push_instruction_byte(translated, OP_LOAD_SETITEM_METHOD);
uint8_t setitemRegister = translated->registerAssignment++;
set_registers(translated, translated->registerAssignment);
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
push_instruction_byte(translated, 0);
push_instruction_byte(translated, setitemRegister);
for (size_t i = 0; i < dictionaryDarray->size; i++) {
ParsedDictionaryEntry *entry = darray_get(dictionaryDarray, i);
if (i != 0) {
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
push_instruction_byte(translated, setitemRegister);
push_instruction_byte(translated, 0);
}
push_instruction_byte(translated, OP_INIT_CALL);
push_instruction_code(translated, 2);
translate_parsed(translated, entry->key, err);
if (err->exists)
return first;
push_instruction_byte(translated, OP_INSERT_ARG);
push_instruction_code(translated, 0);
translate_parsed(translated, entry->value, err);
if (err->exists)
return first;
push_instruction_byte(translated, OP_INSERT_ARG);
push_instruction_code(translated, 1);
push_instruction_byte(translated, OP_CALL);
}
push_instruction_byte(translated, OP_COPY_TO_REGISTER);
push_instruction_byte(translated, dictionaryRegister);
push_instruction_byte(translated, 0);
push_instruction_byte(translated, OP_LOAD_NULL);
push_instruction_byte(translated, dictionaryRegister);
push_instruction_byte(translated, OP_LOAD_NULL);
push_instruction_byte(translated, setitemRegister);
translated->registerAssignment-=2;
return first;
}
default:
fprintf(stderr, "panic: undefined translation\n");
exit(EXIT_FAILURE);
}
return 0;
}

View File

@@ -40,7 +40,10 @@ typedef enum {
OP_MULTIPLICATION,
OP_DIVISION,
OP_NOT,
OP_LOAD_SETATTR_METHOD
OP_LOAD_SETATTR_METHOD,
OP_CREATE_DICTIONARY,
OP_LOAD_GETITEM_METHOD,
OP_LOAD_SETITEM_METHOD
} OperationType;
void arena_resize(ConstantArena *arena, size_t new_size);

View File

@@ -1 +1,9 @@
term.log("hello world")
term.log(()=10)
term.log((x)=10)
term.log((x,y)=10)
term.log((x,y,z)=10)
term.log(a()=10)
term.log(b(x)=10)
term.log(c(x,y)=10)
term.log(d(x,y,z)=10)

5
tests/class_method.ar Normal file
View File

@@ -0,0 +1,5 @@
string.cool(self) = do
term.log(self, self.length)
return 10
'hello world'.cool()
string.cool('goodbye world')

16
tests/diff.ar Normal file
View File

@@ -0,0 +1,16 @@
let h = 1e-1000
let diff(f) = (x) = (f(x + h) - f(x)) / h
let f(x) = x*x*x*x*x*x*x*x*x*x+2*x*x*x*x*x*x*x*x*x+3*x*x*x*x*x*x*x*x+4*x*x*x*x*x*x*x+5*x*x*x*x*x*x+6*x*x*x*x*x+7*x*x*x*x+8*x*x*x+9*x*x+10*x+11
let x = 100
let d = 0
do
while (true) do
let n = f(x)
term.log("f"+string(d)+"("+string(x)+") = "+string(n))
if (n) do
f = diff(f)
d = d + 1
else return

10
tests/factorial.ar Normal file
View File

@@ -0,0 +1,10 @@
let factorial(x) = do
let n = x-1
if (n) return x*factorial(n)
return 1
let loop = 501
let x = 500
while (x) do
factorial(loop-x)
x=x-1

View File

@@ -0,0 +1,2 @@
while (true) do
{"z": 1, "b": 2, "c": 3, "a": 7}

23
tests/hashmap_order.ar Normal file
View File

@@ -0,0 +1,23 @@
let x = {'p':1}
x.z = 1
x.b = 2
x.c = 3
x.a = 7
term.log(x)
let y = {}
y.a = 1
y.b = 2
y.c = 3
y.d = 7
y.e = 2
y.f = 3
y.g = 7
y.h = 1
y.i = 2
y.j = 3
y.k = 7
y.l = 2
y.m = 3
y.n = 7
term.log(y)

View File

@@ -0,0 +1,2 @@
while (true) do
term.log(global)

View File

@@ -1,4 +0,0 @@
term.log(global)
let i = 1e8
while (i) do
i=i-1

View File

@@ -1,3 +0,0 @@
i = 1000000
while i:
i=i-1

3
tests/iteration.ar Normal file
View File

@@ -0,0 +1,3 @@
let i = 1e7
while (i) do
i=i-1

View File

@@ -1,6 +1,6 @@
let i = 10
let x = 1
let n = 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000 * 1e1000000
while (i) do
x = x * 1e100000000
x = x*n
term.log(i=i-1)
term.log(x)