Compare commits

194 Commits

Author SHA1 Message Date
14ddf51f3c Bump version in README.md.
NOTE: changed versioning system slightly.
2025-08-27 12:30:28 -04:00
ac1d76361f Updated README.md. 2025-08-27 01:14:49 -04:00
b789193484 Recursion works.
FINALLY YESSSS 🎉🎉🎉
2025-08-27 01:10:58 -04:00
ab97f78fab Work on conditionals and equalities. 2025-08-27 00:41:48 -04:00
7c0b212ab4 Added a bit of familiar sugar to conditionals. 2025-08-26 23:50:06 -04:00
80c8038374 Added basic conditionals. 2025-08-26 23:47:15 -04:00
e3cd78e1b4 Added basic implementation of booleans. 2025-08-26 22:58:58 -04:00
b7b90f528b Created an awful hack for a clean exit on die(). 2025-08-26 18:30:08 -04:00
0ef44be808 Fixed some builtin arithmetic logic. 2025-08-26 18:24:28 -04:00
8924818ec4 Updated README.md. 2025-08-26 16:37:48 -04:00
73efa7e136 Cleaned up a bit. 2025-08-26 16:37:38 -04:00
4e8d7131d6 Merge branch 'lambda' 2025-08-26 16:26:06 -04:00
bfce18ab81 Completed lambda implementation of functions.
Hooray! 🎉
2025-08-26 16:25:20 -04:00
7b648c4bd7 Fixed negative expressions. 2025-08-26 16:17:54 -04:00
4ec5d1c075 Fixed everything.
- Cleaned up call stack
- Fixed builtin function execution
- Fixed lambda execution
- Fixed call parsing
2025-08-23 10:53:12 -04:00
5ba070ced7 Things are still in motion. Last thing worked on: call execution. 2025-07-05 11:00:11 -04:00
8256643c0b Updated example. 2025-06-30 01:24:26 -04:00
29a217928e Things are in motion. 2025-06-30 01:22:39 -04:00
482f2b4877 Big refactor to unclutter ast.h.
Moves all print functions to own file for sanity.

Also, just hit 2,000 lines; nice.
2025-06-28 12:25:10 -04:00
36fd838a8f Done with some things, saving here. 2025-06-28 12:05:07 -04:00
289243de38 Added lambda parsing. 2025-06-28 11:15:21 -04:00
67aafb3ead Updated syntax to abbreviate functions defs. 2025-06-28 10:55:15 -04:00
3b5bee0695 Added trap for GDB &c. 2025-06-18 17:21:45 -04:00
970fc39198 Cleaned up types with anonymous structs. 2025-06-14 11:22:07 -04:00
8d3e43d7dc Bump version in README.md. 2025-06-14 14:09:54 +00:00
d4293e87f3 Everything works. 2025-06-14 10:02:04 -04:00
abb8ff6b58 Finished garbage collector.
🎉
2025-06-14 09:23:49 -04:00
80122b6572 I try. 2025-05-31 19:13:26 -04:00
a4afd3b58a Fixed segfault on spacey input. 2025-05-31 18:25:11 -04:00
90c8c91410 Somethings working. 2025-05-31 18:19:04 -04:00
f5ab0e9cb0 Fixed memory leak. 2025-05-24 10:52:27 -04:00
0fb1f1d55f Began work on garbage collector. 2025-05-24 10:37:29 -04:00
1d83aa65a4 Well it mostly works now.
Fixed some memory leaks. Implemented some memory leaks.

May need to look into garbage collection in the future.
2025-05-17 11:01:20 -04:00
3f30662cde Fixed some memory bugs. 2025-05-10 10:43:36 -04:00
743d16f696 Fixed bad free on stack.
Still need to figure that one out.
2025-05-07 08:54:19 -04:00
7b99292547 Builtin functions now work with new scope. 2025-05-07 08:38:10 -04:00
4a516d8edb Updated builtin functions for scope. 2025-05-07 08:16:05 -04:00
5b0950cabb Finished linked env scope on user functions.
Doesn't work for builtin functions yet, so doesn't compile. TODO.
2025-04-26 11:04:21 -04:00
40051de9ae Fix deprecated version name.
Co-authored-by: bgiobbe-tcs <bgiobbe.tcs@gmail.com>
2025-04-26 10:58:09 -04:00
2d01b09ee9 Added scope structures.
Not yet used in exec.c.
2025-04-26 09:45:46 -04:00
ff68b756ef Updated for Scope. 2025-04-26 09:45:34 -04:00
c2f2658e9c Start removing old cursed stack frame scope code.
It never really worked anyway.
2025-04-23 13:12:03 -04:00
3e80f6430d Updated output formatting for scope. 2025-04-23 13:06:54 -04:00
5b50d423fa Updated README for new scoping system. 2025-04-14 09:07:28 -04:00
a04d318e45 Started implementing the linked env scope. 2025-04-14 08:58:41 -04:00
7258f162c9 Clean up build process and add more doc. 2025-04-12 14:36:43 -04:00
6e05f1334a Updated grammar rules for new scope system.
Need to implement scope fields still though.
2025-04-12 14:29:39 -04:00
4e66f7875a Added grammars for function definitions. 2025-04-12 09:47:34 -04:00
845a7f87b2 Updated exception execution.
Now represents a sort of traceback.
2025-03-30 17:05:29 -04:00
5b163b26dd Fixed static allocation of builting functions.
Co-authored-by: bgiobbe-tcs <bgiobbe.tcs@gmail.com>
2025-03-30 16:09:09 -04:00
f8ee7cc66e Oh boy do I love formatting. 2025-03-26 18:16:13 -04:00
d699f492fa Updated to use the builtin functions list.
Wow, the type declaration for builtin_fns[] is cursed.

Also, first commit from zed :D.
2025-03-26 18:06:17 -04:00
018404eea1 Wait why's that still there it's done already. 2025-03-25 15:07:22 -04:00
18c61d86dc Fixed crash on unknown var name. 2025-03-11 17:56:04 +09:00
eaf65697de Reformatted comments to be more consistent & clear. 2025-03-03 10:15:39 -05:00
ce6e558761 Updated README.md. 2025-02-28 13:10:19 -05:00
db6e591d77 Added back all the old functions. 2025-02-26 12:43:55 -05:00
58554e8727 Fixed some expression mishandling. 2025-02-25 08:17:17 -05:00
4b9f269938 Updated Unity. 2025-02-25 08:13:42 -05:00
3d0f534017 Plans. 2025-02-25 08:11:22 -05:00
38c6a9113c Added sub to the builtin functions. 2025-02-25 08:04:43 -05:00
109bcb3fa5 Fixed function calls on builtin.
Random pointer indirection.
2025-02-25 07:56:25 -05:00
79b60e4853 Fixed name error handling. 2025-02-25 07:52:02 -05:00
8cab531129 Fixed memory error in exception printing.
Conflation between char* & char**.
2025-02-25 07:47:08 -05:00
eb3fbca030 Some things are broken.
EVERYTHING IS BROKEN
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.
2025-02-15 11:03:51 -05:00
40b91b96bd Adds type infrastructure.
Only floats (nums) are available at the moment.
2025-02-08 11:01:56 -05:00
67f659e263 Merge branch 'master' into types. 2025-02-08 10:23:15 -05:00
b8ce193299 Scope works. 2025-02-08 10:21:22 -05:00
11401e75a7 Fixed. 2025-02-08 09:25:54 -05:00
a6be116a2d Adds test for integer arithmetic. 2025-02-07 17:33:18 -05:00
3678940537 Adds preliminary type enums. 2025-02-07 17:29:30 -05:00
cddf5eed76 Creates config.mk.
Moves a bunch of stuff clutter out of the Makefile.
2025-02-04 18:11:30 -05:00
c685d6e55f TODO.md. 2025-02-04 17:59:39 -05:00
6cbc28e092 Maybe fixed some memory leaks.
I don't know cauase lsan isn't working.
2025-02-04 17:55:47 -05:00
408a5a46c0 Can set and ref vars in main scope.
One can now assign and reference variables in the main scope, as it has
been put in a block through the parser.

Also added some tests for this purpose.
2025-02-04 17:28:06 -05:00
6903aeb383 Added Makefile rule to count LOC. 2025-02-04 17:03:55 -05:00
fbabf71d64 Variable references in blocks now work.
:D :D :D
2025-02-04 17:00:28 -05:00
78e31c3e27 Fixed block parsing for real this time.
I am such an idiot, ast_block_data_init() just ignored the given array
lol.
2025-02-04 16:50:04 -05:00
be3baee74e Fixed dlists.
Also added tests.
2025-02-04 16:40:58 -05:00
d5a07fae56 Started adding scope.
Doesn't really do anything yet as blocks aren't fully implemented.
2025-02-04 11:30:45 -05:00
4153903443 Added types example.
This is gonna be hard to implement...
2025-02-03 13:37:21 -05:00
77d58bd69e Updated STATUS.md. 2025-02-03 13:10:48 -05:00
47a3143752 Merge remote-tracking branch 'refs/remotes/origin/master' 2025-02-03 13:07:10 -05:00
4fb73b3c6f Fixed word accumulator. 2025-02-03 13:02:47 -05:00
e5c58d5fc5 Fixed tests.
Can't have spaces for some reason. Not a real fix.
2025-02-03 13:02:28 -05:00
5d81054cf6 Added variable definitions. 2025-02-03 11:28:47 -05:00
66c518fe43 Don't know why that last test is hanging.
Will fix later I guess.
2025-02-03 10:06:53 -05:00
5ecdf2d89a Sorted .gitignore again because that's important. 2025-02-01 21:38:45 -05:00
a5a86dc080 Basic blocks now work. 2025-02-01 18:40:43 -05:00
70393ef9ae Started work on blocks and the hash table. 2025-02-01 11:01:01 -05:00
efac7f7747 Added hashtable header. 2025-01-31 18:10:01 -05:00
3fcffc81ad Merge branch 'variables'. 2025-01-25 10:13:25 -05:00
27e61471a8 Fixed some things. 2025-01-25 10:12:15 -05:00
c94d7863a7 Fixed tests. 2025-01-25 09:22:31 -05:00
4dd1f2b5f1 Slightly broken. 2025-01-20 18:05:56 -05:00
1a2249a0da Fixed tests and Makefile for the last time. 2025-01-20 16:02:24 -05:00
8b01407374 Added initial support for variables.
They are now parsed correctly, though they cannot be defined manually
and all have a value of 42.42.
2025-01-20 14:47:58 -05:00
3ab2696705 Updated dstr sz. 2025-01-18 11:14:14 -05:00
0293c925d2 Fixed dstr test. 2025-01-18 11:12:07 -05:00
16d62f280f Fix Makefile assuming dirs. 2025-01-18 11:10:50 -05:00
0dbeff7077 Fix Makefile assuming dirs. 2025-01-18 11:07:46 -05:00
96038f4baa Updated README. 2025-01-18 11:05:58 -05:00
6d7ff5d43f Updated README. 2025-01-18 11:05:15 -05:00
868ee84aeb Updated README. 2025-01-18 11:01:56 -05:00
694eb43eab Bump version. 2025-01-18 10:47:10 -05:00
694d40124f Bump version. 2025-01-18 10:46:21 -05:00
39778ce08d Bump version. 2025-01-18 10:44:13 -05:00
cfd44621d5 Whoops a bit overzealous with search and replace. 2025-01-18 10:43:18 -05:00
4be71317b0 Fixed memory leaks. 2025-01-18 10:41:54 -05:00
49642553e1 Cleaned up Makefile. 2025-01-18 10:41:34 -05:00
1e11b5921d It all works! (except for mem leaks) 2025-01-18 09:42:28 -05:00
7343c3b9d9 Fixed some function parsing. 2025-01-17 11:49:51 -05:00
9e8410d4cf Parens. 2025-01-16 11:59:34 -05:00
bc0c4f33ad Cleaned up. 2025-01-14 15:11:20 -05:00
1098fa252f Added parenthesis.
Also updated some grammar rules for negatives to be more general.
2025-01-14 13:59:26 -05:00
0b1905429c Updated tests to include order of operations. 2025-01-12 20:47:24 -05:00
e7d3ea3697 Fixed testing recipe. 2025-01-12 20:47:06 -05:00
577bde6e57 Fixed list indentation. 2025-01-12 20:36:24 -05:00
60b9ed9eb2 Updated README and added STATUS. 2025-01-12 20:34:43 -05:00
d13bf883b5 Fixed some functions. 2025-01-11 11:05:26 -05:00
681e005a68 Generalized arithmetic functions
to arbitrary arguments.
2025-01-11 10:53:49 -05:00
2ce89fb39a Added arbitrary length functions. 2025-01-11 10:35:42 -05:00
907bc26264 Updated README.md. 2025-01-11 09:24:20 -05:00
e243e862ae Updated README.md. 2025-01-11 09:23:14 -05:00
5e930b9847 Updated README.
Why can't the link be in <pre>??
2025-01-09 11:43:49 -05:00
ed3ec885c0 Updated README.
OK so apparently I don't know how markdown links work...
2025-01-09 11:43:04 -05:00
835bcfe121 Updated README. 2025-01-09 11:42:25 -05:00
9432496875 Updated README. 2025-01-09 11:42:06 -05:00
0731e40e6a Fixed precedence. 2025-01-04 14:01:05 -05:00
35322de3ac Added validation tests to Makefile. 2025-01-04 10:48:01 -05:00
c3d8d6f8e5 Validation tests. 2025-01-04 10:45:34 -05:00
1f2bca5028 Not sure. 2025-01-04 10:45:11 -05:00
9a9e5cd3e0 Fixed parsing of negative numbers. 2025-01-04 09:53:39 -05:00
4d828500af Updated README. 2024-12-28 19:03:18 -05:00
deac5ca5b8 Updated README. 2024-12-28 19:02:36 -05:00
e05ebeef2a Added the ability to call infix function backend. 2024-12-28 18:59:04 -05:00
bdca40bae4 Made the infix ops call the normal functions. 2024-12-28 18:42:37 -05:00
b4cd46a1e7 Added division. 2024-12-28 18:39:33 -05:00
a57acc1176 Fixed stupid stupid GNU Make issue. 2024-12-28 12:18:36 -05:00
ffcf2fb013 Added tests for dstr. 2024-12-28 11:55:35 -05:00
c7a9c8215c Reorganized functions, consolidated. 2024-12-28 11:54:58 -05:00
ca4cf2cd68 Finished Unity test support.
Not really, it fails to build tests sometimes and I have no idea why.
2024-12-28 11:52:59 -05:00
e19fc8820a Added Unity in test. 2024-12-28 09:30:35 -05:00
ee3f2919c6 Update README.md. Updated main.c. 2024-12-21 11:10:01 -05:00
653736622f Add multiplication.
Supports negative numbers too.
2024-12-21 10:45:43 -05:00
e3afe52ab7 Added negative numbers. 2024-12-21 10:33:36 -05:00
905acacd07 Fixed float addition.
One can now add floats, will also print the AST.
2024-12-21 10:12:30 -05:00
8763fa35dd Updated README.md 2024-12-14 20:35:36 -05:00
feae5d560a Trimmed grammars. 2024-12-07 11:02:22 -05:00
ec268f6047 Nothing works. 2024-12-07 11:01:00 -05:00
6fff5e5cc8 Without token.c. 2024-12-07 10:33:30 -05:00
7b19e553f2 With token.c. 2024-12-07 10:33:16 -05:00
64ef797727 Things. 2024-12-07 09:16:17 -05:00
85e17ede84 Addition with floats is now entirely possible. 2024-11-30 11:15:18 -05:00
8e5b39a6e4 Can now do integer addition (with floats! :D). 2024-11-30 10:44:21 -05:00
4514d94be9 Basic addition with integers is now available. 2024-11-30 10:24:31 -05:00
4080d1a80a Cleaned up. 2024-11-23 10:21:34 -05:00
a36ae22d52 Added exec, grammar files. 2024-11-23 09:30:35 -05:00
ad8ac61b98 Parse. 2024-11-16 10:42:50 -05:00
e5bb4dfd96 Now compiles without warning. 2024-11-16 10:15:30 -05:00
363188d7d6 The. 2024-11-16 10:00:40 -05:00
139d6fcb22 Things. 2024-11-09 11:09:57 -05:00
92d9da14c2 Grammars are broken. 2024-11-09 10:27:03 -05:00
b43fd46260 Changes were made. 2024-11-09 04:37:56 -05:00
a6dc46149c README.md 2024-11-07 19:55:54 -05:00
120038ea8f Global lexer. 2024-11-07 19:41:14 -05:00
8cf09e43c9 Worked on yylex(). 2024-11-02 11:02:18 -04:00
ecc12f6f3b Begin bison-based parsing. 2024-11-02 10:31:55 -04:00
a1f210fee1 Something. 2024-10-31 16:44:17 -04:00
2662f54c51 Fixed more print formatting. 2024-10-31 16:05:04 -04:00
950c25bace Fixed print formatting. 2024-10-31 12:52:39 -04:00
1c0dd7aa0b Added stacks to parser. 2024-10-26 11:02:42 -04:00
5b345e6bf5 Added stack. 2024-10-26 10:56:11 -04:00
891d8bf7ef Finished initial AST structures.
Call and ints (nums).
2024-10-26 10:07:33 -04:00
68fc644ea6 Added more AST stuff. 2024-10-25 11:23:03 -04:00
ca9d2aabe4 Fixed more printing, added basic ast. 2024-10-25 11:20:07 -04:00
7a04ccfd9f Fixed some printing errors, introduced many more. 2024-10-19 10:59:05 -04:00
4df9808859 Fixed blinking functions.
Heh.
2024-10-19 09:15:03 -04:00
933418895e Something. 2024-10-19 09:09:37 -04:00
3c56290448 Numbers work. Calls do not work. 2024-10-16 08:13:32 -04:00
63f5064ba9 Fixed some things, broke others. 2024-10-13 23:46:03 -04:00
8e8b6233d6 Added dynamic strings. They do not yet work. 2024-10-10 16:09:25 -04:00
89e8674ced Update README.md. 2024-10-07 11:54:54 -04:00
77f40cf3c5 Changed things. 2024-10-07 11:48:53 -04:00
2008bab1f7 Fixed tests. 2024-10-05 09:35:33 -04:00
d244cfbfe1 Tests are slightly not. 2024-10-05 09:24:12 -04:00
6f283c1d12 Fixed some things. 2024-10-02 21:06:54 -04:00
aae8402403 Added .clang-format. 2024-10-02 21:04:54 -04:00
53c303095d ...whoops. 2024-10-02 20:05:46 -04:00
ce25c5fe9f Beginnings of the lexer. 2024-10-02 17:57:04 -04:00
d95c134a54 Test and stuff.
Need to fix testing functions so that every assert is run in each
function of the registry.
2024-09-30 20:55:46 -04:00
32386ccf58 Here's another commit.
Got the Makefile working with tests properly.
2024-09-30 09:00:37 -04:00
60d1edcccc Fixed something. 2024-09-28 09:38:16 -04:00
f674f47306 Update Makefile. 2024-09-28 09:38:01 -04:00
53 changed files with 2939 additions and 89 deletions

13
.clang-format Normal file
View File

@@ -0,0 +1,13 @@
---
AlignConsecutiveShortCaseStatements:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
IndentCaseLabels: true
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLoopsOnASingleLine: true
IndentWidth: 4
PointerAlignment: Left
AlignAfterOpenBracket: BlockIndent

2
.clangd Normal file
View File

@@ -0,0 +1,2 @@
CompileFlags:
Add: [-xc]

5
.gitignore vendored
View File

@@ -1,5 +1,8 @@
*.o *.o
*.so *.so
tags
*.out *.out
obj/ .cache
build/*
vgcore.*
compile_commands.json compile_commands.json

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "test/Unity"]
path = test/Unity
url = https://github.com/ThrowTheSwitch/Unity

107
Makefile
View File

@@ -1,46 +1,77 @@
NAME = scl include config.mk
CC = gcc all: $(TARGET)
CFLAGS_REG = -Wall -O2
CFLAGS_DBG = -ggdb -fsanitize=address -O0
LDFLAGS = -lm
SRC_DIR = src release: clean
OBJ_DIR = obj release: CFLAGS = -Wall -O2
TARGET = $(NAME).out release: $(TARGET)
SRC_FILES = $(wildcard $(SRC_DIR)/*.c) # Run the target.
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC_FILES)) run: $(TARGET)
./$(TARGET)
COL_BOLD = \x1b[37;1m # Generate grammars with bison.
COL_CLEAR = \x1b[0m $(GRAM_FILES): $(SRC_DIR)/grammar.y
@ mkdir -p $(GRAM_DIR)
@ $(PRINT) "$(WHITE_BOLD)Generating grammars...$(RESETCOLOR)"
$(BISON) $< -o$(GRAM_DIR)/grammar.tab.c -H$(GRAM_DIR)/grammar.tab.h
all: reg # Compile grammars.
$(OBJ_DIR)/grammar.o: $(GRAM_DIR)/grammar.tab.c $(GRAM_DIR)/grammar.tab.h $(OBJ_DIR)/lexer.o
reg: CFLAGS = $(CFLAGS_REG) @ $(PRINT) "$(WHITE_BOLD)Compiling grammars...$(RESETCOLOR)"
reg: $(TARGET)
dbg: CFLAGS = $(CFLAGS_DBG)
dbg: clean
dbg: $(TARGET)
msg_compiling:
@ echo -e "$(COL_BOLD)Compiling...$(COL_CLEAR)"
msg_linking:
@ echo -e "$(COL_BOLD)Linking...$(COL_CLEAR)"
msg_cleaning:
@ echo -e "$(COL_BOLD)Cleaning up...$(COL_CLEAR)"
$(TARGET): msg_compiling $(OBJ_FILES) msg_linking
$(CC) $(LDFLAGS) $(OBJ_FILES) -o $@
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
@ mkdir -p $(OBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
clean: msg_cleaning # Lexer depends on grammars.
rm -rf $(OBJ_DIR) $(TARGET) $(TARGET_DBG) $(OBJ_DIR)/lexer.o: $(SRC_DIR)/lexer.c $(GRAM_FILES)
@ mkdir -p $(OBJ_DIR)
@ $(PRINT) "$(WHITE_BOLD)Compiling source object $(WHITE)$@$(WHITE_BOLD)... $(RESETCOLOR)"
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: all clean reg dbg msg_compiling msg_linking msg_cleaning # Compile project source objects.
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(INC_DIR)/%.h
@ mkdir -p $(OBJ_DIR)
@ $(PRINT) "$(WHITE_BOLD)Compiling source object $(WHITE)$@$(WHITE_BOLD)... $(RESETCOLOR)"
$(CC) $(CFLAGS) -c $< -o $@
# Link to final binary.
$(TARGET): $(OBJ_DIR)/grammar.o $(OBJ_FILES)
@ $(PRINT) "$(WHITE_BOLD)Linking $(WHITE)$@$(WHITE_BOLD)...$(RESETCOLOR)"
$(LINK) -o $(TARGET) $(OBJ_FILES) $(OBJ_DIR)/grammar.o $(LDFLAGS)
# Compile Unity object.
$(UNITY_OBJ): $(UNITY_C) $(UNITY_H)
@ $(PRINT) "$(WHITE_BOLD)Compiling Unity...$(RESETCOLOR)"
$(CC) $(CFLAGS) -D UNITY_OUTPUT_COLOR -c $< -o $@
# Compile test object.
$(TEST_OBJ_DIR)/test_%.o: $(TEST_DIR)/test_%.c
@ mkdir -p $(TEST_OBJ_DIR)
@ $(PRINT) "$(WHITE_BOLD)Compiling test object $(WHITE)$@$(WHITE_BOLD)...$(RESETCOLOR)"
$(CC) $(CFLAGS) -c $< -o $@
# Link final test binary.
$(TEST_BUILD_DIR)/test_%.out: $(TEST_OBJ_DIR)/test_%.o $(OBJ_DIR)/grammar.o $(OBJ_FILES_NOMAIN) $(UNITY_OBJ)
@ mkdir -p $(TEST_BUILD_DIR)
@ $(PRINT) "$(WHITE_BOLD)Linking test binary $(WHITE)$@$(WHITE_BOLD)...$(RESETCOLOR)"
$(LINK) -o $@ $? $(LDFLAGS)
# Run the test files.
test: $(TARGET) $(TEST_BIN_FILES)
@ $(PRINT) "$(WHITE_BOLD)Running unit tests...$(RESETCOLOR)"
for test in $(TEST_BIN_FILES); do ./$${test} || echo -e "$(RED_BOLD) BAD EXIT ON $${test} $(RESETCOLOR)"; done
@ $(PRINT) "$(WHITE_BOLD)Running validation tests...$(RESETCOLOR)"
$(BATS) $(TEST_VAL_DIR)
# Clean out objects, binaries, and built artifacts.
clean:
@ $(PRINT) "$(WHITE_BOLD)Cleaning up...$(RESETCOLOR)"
rm -rf $(OBJ_DIR)/*.o $(TEST_OBJ_DIR)/*.o $(TEST_BUILD_DIR)/test.out $(TARGET) $(GRAM_DIR)/* $(UNITY_OBJ)
# Get LOC.
lines:
@ wc -l $(SRC_FILES) $(INC_FILES) $(GRAM_SRC)
.PHONY: all clean test nocolor release run lines
# Run this intermediary even though make thinks it's useless.
.PRECIOUS: $(TEST_OBJ_FILES)

View File

@@ -1 +1,74 @@
# SCL: Simple Calculator Language # SCL: Simple CAS Language
Version v0.3
SCL aims to be a human-friendly Computer Algebra System (CAS) inspired by
[maxima](https://maxima.sourceforge.io/) that feels like writing on paper. In
its current state, SCL can be used as a functional programming language capable
of performing simple arithmetic. The codebase is about 2,000 lines of C,
including a parser, interpreter, and runtime. It uses a linked environment
scoping model.
## Usage
To download and run:
```bash
git clone https://git.signorovitch.org/jacob/scl -b stable && cd scl
make release
./scl.out
```
### For Development
```bash
git clone git@signorovitch.org:jacob/scl --recurse-submodules && cd scl
make all test
./scl.out
```
If you wish to run tests, make sure to run `git clone --recurse-submodules` to
include the [Unity](https://github.com/ThrowTheSwitch/Unity) test framework.
## Syntax
As one would expect, you can evaluate simple infix expressions:
```scl
> 1 + 1
= 2
```
You can also define your own functions and variables:
```scl
> f(x) 2 * x
> n = 3
> f(n)
= 6
```
Being a functional programming language at heart, one can of course use lambda functions:
```scl
> (\(x) 2 * x)(5)
= 10
> f(g) g(2)
> f(\(x) 2 * x)
= 4
```
Here's a simple factorial function:
```scl
> factorial(n) {
> if (n == 0) { 1 }
> else { n * factorial(n - 1) }
> }
```
Or, using SCL's more concise syntax:
```scl
> factorial(n) ? n == 0 1 n * factorial(n - 1)
```

55
STATUS.md Normal file
View File

@@ -0,0 +1,55 @@
# SCL Design Status
- [x] Data definitions
- [x] Token Definitions
- [x] AST Definitions
- [ ] Parser
- [x] Parse numbers
- [x] Parse floats
- [x] Parse negative numbers
- [x] Parse infix operators
- [x] Order of operations
- [x] Parse function application
- [x] Parse order of operations with parenthesis
- [x] Parse variable invocation
- [x] Parse variable definition
- [ ] Parse types
- [x] Parse function definition
- [ ] Parse lists/arrays/vectors
- [x] Parse blocks
- [ ] Parse control flow
- [ ] Parse `if` statements
- [ ] Parse `loop`s
- [ ] Parse `for` loops
- [ ] Parse `while` loops
- [ ] Parse `case` statements
- [ ] Parse `goto` statements
- [ ] Parse lambda function definition
- [ ] Parse function calling with positional arguments
- [ ] Parse variadic functions
- [ ] Parse infix function definition
- [ ] Executer
- [x] Exec function calls
- [x] Exec variable use
- [x] Exec variable definition
- [x] Exec function definition
- [ ] Exec symbolic variables
- [ ] Exec control flow statements
- [ ] Exec variadic functions
- [ ] Exec lambda functions
- [ ] Exec lists
- [ ] Exec arrays
- [ ] Exec vectors
- [ ] Interface
- [ ] Interactive interpreter
- [ ] Use GNU readline
- [ ] Multi-line input
- [ ] Syntax highlighting
- [ ] Autocompletion/suggestion
- [ ] Command line interface
- [ ] Pass in a file
- [ ] Save AST to a file
- [ ] Run from AST file

9
TODO.md Normal file
View File

@@ -0,0 +1,9 @@
EXCEPTION HANDLING: exception ast type should have as data a giant enum of
possible types, rather than a char* message. A description of each type could be
handled under the exception type and print logic. For now, executor checks
message for special exceptions e.g. exit().
Change editor to GNU Readline.
Make variables persist through lines in the editor.
Return syntax errors as exceptions.

40
config.mk Normal file
View File

@@ -0,0 +1,40 @@
NAME = scl
TARGET = $(NAME).out
SRC_DIR = src
INC_DIR = $(SRC_DIR)/include
BUILD_DIR = build
OBJ_DIR = $(BUILD_DIR)/obj
GRAM_DIR = $(BUILD_DIR)/grammars
TEST_DIR = test
TEST_BUILD_DIR = $(BUILD_DIR)/test
TEST_OBJ_DIR = $(TEST_BUILD_DIR)/obj
CC = clang -std=c23
LINK = clang
CFLAGS = -Wall -DDBG -ggdb -fsanitize=leak
LDFLAGS = -lm
BATS = bats
BISON = bison
PRINT = echo -e
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
INC_FILES = $(wildcard $(INC_DIR)/*.h)
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC_FILES))
OBJ_FILES_NOMAIN = $(filter-out $(OBJ_DIR)/main.o, $(OBJ_FILES)) # Object files without main.c.
GRAM_SRC = $(SRC_DIR)/grammar.y
GRAM_FILES = $(GRAM_DIR)/grammar.tab.c $(GRAM_DIR)/grammar.tab.h
UNITY_DIR = $(TEST_DIR)/Unity
UNITY_C = $(UNITY_DIR)/src/unity.c
UNITY_H = $(UNITY_DIR)/src/unity.h
UNITY_OBJ = $(TEST_BUILD_DIR)/unity.o
TEST_SRC_FILES = $(wildcard $(TEST_DIR)/*.c)
TEST_OBJ_FILES = $(patsubst $(TEST_DIR)/%.c, $(TEST_OBJ_DIR)/%.o, $(TEST_SRC_FILES))
TEST_BIN_FILES = $(patsubst $(TEST_DIR)/%.c, $(TEST_BUILD_DIR)/%.out, $(TEST_SRC_FILES))
TEST_VAL_DIR = $(TEST_DIR)/val
RESETCOLOR = \033[0m
WHITE = $(RESETCOLOR)\033[37m
WHITE_BOLD = $(RESETCOLOR)\033[37;1m
RED_BOLD = $(RESETCOLOR)\033[31;1m

25
definitions.md Normal file
View File

@@ -0,0 +1,25 @@
f = \(x) 2 * x
g = \(h) \(x) h(h(x))
f(2) => 4
g(f)(2) => 8
CALL
argc: 1
argv: [ 2 ]
to:
CALL
argc: 1
argv: [
VREF
name: f
]
to:
VREF
name: g
fname: NULL
expression + arguments = call = expression
expression + parameters = lambda = expression
expression + name = variable = expression

2
examples/function.scl Normal file
View File

@@ -0,0 +1,2 @@
apply(f, x) f(x)
apply(\(x) x + 1, 2)

1
examples/test.scl Normal file
View File

@@ -0,0 +1 @@
1 + 1

48
examples/types.scl Normal file
View File

@@ -0,0 +1,48 @@
# Vectors
# - Must have fixed size and type
# Lists
# - Variable size, variable type
# Define a variable of type int:
n: int = 3
# Define v as a 3-dimensional vector of integers:
v: int<3> = <4, 5, 6>
# Define l as list of length 3:
l: [3] = [1, 2, "These can be any type at all"]
# This would also work, as length values for lists are optional (and mutable),
# unlike vectors:
l: [] = [1, 2, "whatever"]
# This is also the same, being more explicit about the any type:
l: any[] = [1, 2, "whatever"]
# This must be a list of integers, however:
l: int[] = [1, 2, 3]
# Define a list of either integers or strings:
l: union(int, str)[] = ["hello", 4, "world"]
# union() is a complex type generator; "returns" a union of the int and str types.
# Use vectors wherever possible, as they are much faster than lists.
# Matrix
# - Must have a fixed size and type (just like vectors)
m: int<2, 2> = <<0, 1>, <2, 3>>
# Can also be written as
m: int<2, 2> = <<0, 1, 2, 3>>
# When using this syntax, will fill remaining space with default value of type
# to make data rectangular. E.g., to fill a 2 x 2 matrix with 0:
m: int<2, 2> = <<>>
# To define custom data types, e.g. structs:
typedef(Vec2, struct( a: int, b: int ))
vector_two: Vec2 = Vec2(2, 3)
typedef(IntOrStr, union(str, int))
one: IntOrStr = 1
alsoOne: IntOrStr = "one"
# To define custom data types with type arguments:
typedef(Vec1Of(T), T<1>)
hello: Vec1Of(str) = <"Hello">

225
src/ast.c Normal file
View File

@@ -0,0 +1,225 @@
#include <inttypes.h>
#include <stdio.h>
#include "include/ast.h"
#include "include/gc.h"
#include "include/scope.h"
#include "include/util.h"
extern AST* root;
AST* ast_init(ASTType type, void* data) {
AST* ast = gc_alloc(sizeof(AST), GC_TYPE_AST);
ast->type = type;
ast->data = data;
ast->scope = NULL;
if (ast->type > AST_TYPE_MAX) {
log_dbgf(
"Attempted to create invalid AST (%i > %i) to GC: ast:%p",
ast->type, AST_TYPE_MAX, ast
);
}
return ast;
}
AST* ast_init_scope(ASTType type, void* data, Scope* scope) {
AST* ast = malloc(sizeof(AST));
ast->type = type;
ast->data = data;
ast->scope = scope;
return ast;
}
void ast_destroy(AST* ast) {
if (!ast) return;
switch (ast->type) {
case AST_TYPE_NUM: ast_num_data_destroy(ast->data); break;
case AST_TYPE_BOOL: ast_bool_data_destroy(ast->data); break;
case AST_TYPE_CALL: ast_call_data_destroy(ast->data); break;
case AST_TYPE_VREF: ast_vref_data_destroy(ast->data); break;
case AST_TYPE_VDEF: ast_vdef_data_destroy(ast->data); break;
case AST_TYPE_BLOCK: ast_block_data_destroy_psv(ast->data); break;
case AST_TYPE_FDEF: ast_fdef_data_destroy_psv(ast->data); break;
case AST_TYPE_ARG: ast_arg_data_destroy(ast->data); break;
case AST_TYPE_BIF: ast_bif_data_destroy(ast->data); break;
case AST_TYPE_EXC: ast_exc_data_destroy(ast->data); break;
case AST_TYPE_LAMBDA: ast_lambda_data_destroy(ast->data); break;
default:
log_dbgf("Unknown ast type %d (max: %d)", ast->type, AST_TYPE_MAX);
}
free(ast);
}
ASTNumData* ast_num_data_init(double val) {
talloc(ASTNumData, num);
*num = val;
return num;
}
void ast_num_data_destroy(ASTNumData* num) { free(num); }
ASTBoolData* ast_bool_data_init(int val) {
talloc(ASTBoolData, bol);
*bol = val;
return bol;
}
void ast_bool_data_destroy(ASTBoolData* bol) { free(bol); }
ASTExcData* ast_exc_data_init(const char* msg, AST* trace) {
ASTExcData* data = malloc(sizeof(ASTExcData));
data->msg = msg;
data->trace = trace;
return data;
}
void ast_exc_data_destroy(ASTExcData* exc) {
// `msg` is static, and `trace` will get freed in GC.
free(exc);
}
ASTBIFData* ast_bif_data_init(AST* fn(size_t, AST**, Scope*)) {
return (ASTBIFData*)fn;
}
void ast_bif_data_destroy(ASTBIFData* bif) { return; }
// Lambda.
ASTLambdaData* ast_lambda_data_init(size_t parc, AST** parv, AST* body) {
talloc(ASTLambdaData, lambda);
lambda->parc = parc;
lambda->parv = parv;
lambda->body = body;
return lambda;
}
void ast_lambda_data_destroy(ASTLambdaData* lambda) {
free(lambda->parv);
free(lambda);
}
// Call.
ASTCallData* ast_call_data_init(size_t argc, AST** argv, AST* exp) {
talloc(ASTCallData, call);
call->exp = exp;
call->argc = argc;
call->argv = argv;
return call;
}
void ast_call_data_destroy(ASTCallData* call) {
if (!call) return;
free(call->argv);
free(call);
}
// VDef.
ASTVDefData* ast_vdef_data_init(char* name, AST* exp) {
talloc(ASTVDefData, vdef);
vdef->name = name;
vdef->exp = exp;
return vdef;
}
void ast_vdef_data_destroy(ASTVDefData* vdef) {
free(vdef->name);
free(vdef);
}
// VRef.
ASTVrefData* ast_vref_data_init(char* to) {
talloc(ASTVrefData, vref);
vref->to = to;
return vref;
}
void ast_vref_data_destroy(ASTVrefData* vref) {
free(vref->to);
free(vref);
}
ASTBlockData* ast_block_data_init(AST** inside, size_t ln) {
ASTBlockData* block = malloc(sizeof(ASTBlockData));
block->inside = inside;
block->ln = ln;
return block;
}
void ast_block_data_destroy(ASTBlockData* block) {
for (size_t i = 0; i < block->ln; i++) { ast_destroy(block->inside[i]); }
free(block->inside);
free(block);
}
void ast_block_data_destroy_psv(ASTBlockData* block) {
free(block->inside);
free(block);
}
ASTFDefData*
ast_fdef_data_init(char* name, size_t argc, AST** argv, AST* body) {
ASTFDefData* fdef = malloc(sizeof(ASTFDefData));
fdef->name = name;
fdef->argc = argc;
fdef->argv = argv;
fdef->body = body;
return fdef;
}
void ast_fdef_data_destroy(ASTFDefData* fdef) {
free(fdef->name);
for (int i = 0; i < fdef->argc; ast_destroy(fdef->argv[i++]));
ast_destroy(fdef->body);
}
void ast_fdef_data_destroy_psv(ASTFDefData* fdef) {
free(fdef->name);
free(fdef->argv);
free(fdef);
}
ASTArgData* ast_arg_data_init(char* name) {
ASTArgData* arg = malloc(sizeof(ASTArgData));
arg->name = name;
return arg;
}
void ast_arg_data_destroy(ASTArgData* arg) { free(arg->name); }
AST* ast_find(Scope* scope, char* name) {
while (scope) {
AST* gotten = htab_get(scope->here, name);
if (gotten) return gotten;
else scope = scope->inherit;
}
return NULL;
}

175
src/ast_print.c Normal file
View File

@@ -0,0 +1,175 @@
#include "include/ast_print.h"
#include "include/ast.h"
#include "include/builtin.h"
#include "include/dstr.h"
#include "include/util.h"
#include <stdio.h>
static char* asttype_names[] = {
[AST_TYPE_CALL] = "FUNC CALL",
[AST_TYPE_NUM] = "NUMBER",
[AST_TYPE_BOOL] = "BOOLEAN",
[AST_TYPE_VREF] = "VAR REFERENCE",
[AST_TYPE_VDEF] = "VAR DEFINITION",
[AST_TYPE_BLOCK] = "BLOCK",
[AST_TYPE_EXC] = "EXCEPTION",
[AST_TYPE_FDEF] = "FUNCTION DEFINITION",
[AST_TYPE_ARG] = "DEFINITION ARGUMENT",
[AST_TYPE_LAMBDA] = "LAMBDA EXPRESSION",
[AST_TYPE_BIF] = "BUILTIN FUNCTION"
};
void ast_print(AST* ast) {
if (!ast) return;
ast_print_i(ast, 0);
}
void ast_print_i(AST* ast, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("AST", ast);
INDENT_FIELD("type", "%s", asttype_names[ast->type]);
INDENT_FIELD_EXT_NONL_START("data");
switch (ast->type) {
case AST_TYPE_NUM:
printf("%s %lf\n", INDENT_spacing->buf, *(ASTNumData*)ast->data);
break;
case AST_TYPE_BOOL:
printf(
"%s %s\n", INDENT_spacing->buf,
*(ASTBoolData*)ast->data ? "true" : "false"
);
break;
case AST_TYPE_CALL: ast_call_print(ast->data, i + 2); break;
case AST_TYPE_EXC: ast_exc_print(ast->data, i + 2); break;
case AST_TYPE_VREF: ast_vref_print(ast->data, i + 2); break;
case AST_TYPE_VDEF: ast_vdef_print(ast->data, i + 2); break;
case AST_TYPE_BLOCK: ast_block_print(ast->data, i + 2); break;
case AST_TYPE_FDEF: ast_fdef_print(ast->data, i + 2); break;
case AST_TYPE_ARG: ast_arg_print(ast->data, i + 2); break;
case AST_TYPE_LAMBDA: ast_lambda_print(ast->data, i + 2); break;
case AST_TYPE_BIF: ast_bif_print(ast->data, i + 2); break;
default: exit(1);
}
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_num_print(ASTNumData* data, int i) {
INDENT_BEGIN(i);
INDENT_FIELD("data", "%lf", *data);
INDENT_END;
}
void ast_bool_print(ASTBoolData* data, int i) {
INDENT_BEGIN(i);
INDENT_FIELD("data", "%s", *data ? "true" : "false");
INDENT_END;
}
void ast_exc_print(ASTExcData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTExcData", data);
INDENT_FIELD("msg", "\"%s\"", data->msg);
if (data->trace == NULL) {
INDENT_FIELD("trace", "%p", NULL)
} else {
INDENT_FIELD_EXT_NONL_START("trace");
ast_print_i(data->trace, i + 1);
INDENT_FIELD_NONL_END;
}
INDENT_END;
}
void ast_call_print(ASTCallData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTCallData", data);
INDENT_FIELD("argc", "%ld", data->argc);
INDENT_FIELD_LIST("argv", data->argv, data->argc, ast_print_i);
INDENT_FIELD_EXT_NONL_START("exp");
ast_print_i(data->exp, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_vdef_print(ASTVDefData* vdef, int depth) {
INDENT_BEGIN(depth);
INDENT_TITLE("ASTVDefData", vdef);
INDENT_FIELD("name", "%s", vdef->name);
INDENT_FIELD_EXT_NONL_START("exp");
ast_print_i(vdef->exp, depth + 2); // 2 because already indented.
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_vref_print(ASTVrefData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTVrefData", data);
INDENT_FIELD("to", "%s", data->to);
INDENT_END;
}
void ast_block_print(ASTBlockData* data, int depth) {
INDENT_BEGIN(depth);
INDENT_TITLE("ASTBlockData", data);
INDENT_FIELD("ln", "%ld", data->ln);
INDENT_FIELD_LIST("inside", data->inside, data->ln, ast_print_i);
INDENT_END;
}
void ast_fdef_print(ASTFDefData* fdef, int i) {
INDENT_BEGIN(i)
INDENT_TITLE("ASTFDefData", fdef);
INDENT_FIELD("name", "%s", fdef->name);
INDENT_FIELD("argc", "%ld", fdef->argc);
INDENT_FIELD_LIST("argv", fdef->argv, fdef->argc, ast_print_i);
INDENT_FIELD_EXT_NONL_START("body");
ast_print_i(fdef->body, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_arg_print(ASTArgData* arg, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTArgData", arg);
INDENT_FIELD("name", "%s", arg->name);
INDENT_END;
}
void ast_lambda_print(ASTLambdaData* lambda, int i) {
INDENT_BEGIN(i)
INDENT_TITLE("ASTLambdaData", lambda);
INDENT_FIELD("parc", "%ld", lambda->parc);
INDENT_FIELD_LIST("parv", lambda->parv, lambda->parc, ast_print_i);
INDENT_FIELD_EXT_NONL_START("body");
ast_print_i(lambda->body, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_bif_print(ASTBIFData* bif, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTBIFData", bif);
char* name = "unknown";
for (int i = 0; i < BUILTIN_FNS_LN; i++)
if ((void*)BUILTIN_FNS[i].fn == bif) {
name = BUILTIN_FNS[i].name;
break;
}
INDENT_FIELD("name", "%s", name);
INDENT_END;
}

211
src/builtin.c Normal file
View File

@@ -0,0 +1,211 @@
#include <stdarg.h>
#include <stdio.h>
#include "include/ast.h"
#include "include/ast_print.h"
#include "include/builtin.h"
#include "include/exec.h"
#include "include/util.h"
AST* builtin_sum(size_t argc, AST** argv, Scope* parent) {
ASTNumData total = 0;
for (int i = 0; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`sum` encountered an exception.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Sum can't sum some non-num arguments.", NULL)
);
total += *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_sub(size_t argc, AST** argv, Scope* parent) {
if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`sub` encountered an exception.", first)
);
if (first->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't subtract non-num arguments.", NULL)
);
ASTNumData total = *(ASTNumData*)first->data;
for (int i = 1; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`sub` encountered an exception.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't subtract non-num arguments.", NULL)
);
total -= *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_mul(size_t argc, AST** argv, Scope* parent) {
if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`mul` encountered an expection.", first)
);
if (first->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't multiply non-num arguments.", NULL)
);
ASTNumData total = *(ASTNumData*)first->data;
for (int i = 1; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`mul` encountered an execption.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't multiply non-num arguments.", NULL)
);
total *= *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_div(size_t argc, AST** argv, Scope* parent) {
if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`div` encountered an exception.", first)
);
if (first->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't divide non-num arguments.", NULL)
);
ASTNumData total = *(ASTNumData*)first->data;
for (int i = 1; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`div` encountered an exception.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't divide non-num arguments.", NULL)
);
total /= *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_die(size_t argc, AST** argv, Scope* parent) {
return ast_init(AST_TYPE_EXC, ast_exc_data_init("8", NULL));
}
AST* builtin_if(size_t argc, AST** argv, Scope* parent) {
if (argc != 3)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("If invoked with too few args.", NULL)
);
AST* pred = exec_exp(argv[0], parent);
AST* body = argv[1];
AST* alt = argv[2];
if (pred->type != AST_TYPE_BOOL) {
if (pred->type == AST_TYPE_EXC) {
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("if touched an error", pred)
);
} else {
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("if works on booleans idiot", NULL)
);
}
}
if (*(ASTBoolData*)pred->data) return exec_exp(body, parent);
else return exec_exp(alt, parent);
}
AST* builtin_eq(size_t argc, AST** argv, Scope* parent) {
if (argc < 1) return ast_init(AST_TYPE_EXC, ast_exc_data_init("bad", NULL));
else if (argc == 1) return ast_init(AST_TYPE_BOOL, ast_bool_data_init(1));
AST* first = exec_exp(argv[0], parent);
ASTType type = first->type;
AST* second = exec_exp(argv[1], parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("first was bad", first)
);
if (second->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("second was bad", second)
);
if (second->type != type)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("apples and oranges or something idk", NULL)
);
// Later when I put together an anctual type system I'll have this
// delegated to each type. For now this works.
switch (type) {
case AST_TYPE_NUM:
if (*(ASTNumData*)first->data == *(ASTNumData*)second->data)
return ast_init(AST_TYPE_BOOL, ast_bool_data_init(1));
else return ast_init(AST_TYPE_BOOL, ast_bool_data_init(0));
case AST_TYPE_BOOL:
if (*(ASTNumData*)first->data == *(ASTNumData*)second->data)
return ast_init(AST_TYPE_BOOL, ast_bool_data_init(1));
else return ast_init(AST_TYPE_BOOL, ast_bool_data_init(0));
default:
return ast_init(
AST_TYPE_BOOL, ast_bool_data_init(0)
); // Can't equate nonprimatives. I think. Maybe.
}
}

44
src/dlist.c Normal file
View File

@@ -0,0 +1,44 @@
#include <stddef.h>
#include <stdio.h> // IWYU pragma: keep. Req by util macros.
#include "include/dlist.h"
#include "include/util.h"
DList* dlist_init(void) {
DList* dlist = malloc(sizeof(DList));
dlist->sz = DLIST_INITSZ;
dlist->buf = malloc(DLIST_INITSZ);
dlist->ln = 0;
return dlist;
}
void dlist_destroy(DList* dlist) {
free(dlist->buf);
free(dlist);
}
void dlist_destroypsv(DList* dlist) { free(dlist); }
// Check whether the buffer is about to overflow and resize it if necessary.
void dlist_check_resz(DList* dlist) {
while ((dlist->ln + 1) * sizeof(void*) >= dlist->sz) {
// Double the buffer size when overflown.
dlist->sz *= 2;
dlist->buf = realloc(dlist->buf, dlist->sz);
log_dbgf(
"dlist @ %p doubled from %ld to %ld", dlist, dlist->sz / 2,
dlist->sz
);
}
}
void dlist_append(DList* dest, void* src) {
dlist_check_resz(dest);
log_dbgf("added %p to dlist@%p", src, dest);
dest->buf[dest->ln] = src;
dest->ln++;
}

54
src/dstr.c Normal file
View File

@@ -0,0 +1,54 @@
#include "include/dstr.h"
#include "include/util.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h>
Dstr* dstr_init(void) {
Dstr* dstr = malloc(sizeof(Dstr));
dstr->sz = DSTR_INITSZ;
dstr->buf = malloc(DSTR_INITSZ);
*dstr->buf = '\0';
dstr->ln = 0;
return dstr;
}
void dstr_destroy(Dstr* dstr) {
free(dstr->buf);
free(dstr);
}
void dstr_destroypsv(Dstr* dstr) { free(dstr); }
// Check whether the buffer is overflowing and resize it if necessary.
void dstr_check_resz(Dstr* dstr, size_t ln) {
while (dstr->ln + ln + 1 > dstr->sz) {
// Double the buffer size when overflown.
dstr->sz *= 2;
dstr->buf = realloc(dstr->buf, dstr->sz);
log_dbgf(
"dstr @ %p doubled from %ld to %ld", dstr, dstr->sz / 2, dstr->sz
);
}
}
void dstr_append(Dstr* dest, char* src, size_t ln) {
dstr_check_resz(dest, ln);
// Overwrites the \0 at the end of the string, keeps the null from the given
// string.
memcpy(dest->buf + dest->ln, src, ln + 1);
dest->ln += ln;
}
void dstr_appendch(Dstr* dest, char ch) {
dstr_check_resz(dest, 1);
// Overwrites the preexisting null terminator, and adds one of its own.
dest->buf[dest->ln] = ch;
dest->buf[dest->ln + 1] = '\0';
dest->ln += 1;
}

154
src/exec.c Normal file
View File

@@ -0,0 +1,154 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "include/ast.h"
#include "include/builtin.h"
#include "include/exec.h"
#include "include/htab.h"
#include "include/scope.h"
#include "include/util.h"
AST* exec_start(AST* ast) {
log_dbg("Started execution.");
if (!ast) return ast;
Scope* global = scope_init(NULL);
global->uses = 1;
for (int i = 0; i < BUILTIN_FNS_LN; i++)
htab_ins(
global->here, BUILTIN_FNS[i].name,
ast_init(AST_TYPE_BIF, ast_bif_data_init(BUILTIN_FNS[i].fn))
);
log_dbg("Completed startup sequence.");
AST* res = exec_exp(ast, global);
return res;
}
AST* exec_exp(AST* ast, Scope* parent) {
switch (ast->type) {
case AST_TYPE_BLOCK: return exec_block(ast, parent);
case AST_TYPE_CALL: return exec_call(ast, parent);
case AST_TYPE_NUM:
return ast_init(
AST_TYPE_NUM, ast_num_data_init(*(ASTNumData*)ast->data)
);
case AST_TYPE_BOOL:
return ast_init(
AST_TYPE_BOOL, ast_bool_data_init(*(ASTBoolData*)ast->data)
);
case AST_TYPE_VREF: return exec_vref(ast, parent);
case AST_TYPE_VDEF: return exec_vdef(ast, parent);
case AST_TYPE_FDEF: return exec_fdef(ast, parent);
case AST_TYPE_BIF:
case AST_TYPE_LAMBDA: return ast;
default: printf("what\n"); exit(1);
}
}
AST* exec_block(AST* ast, Scope* parent) {
ASTBlockData* block = (ASTBlockData*)ast->data;
exec_new_scope(ast, parent);
// Loop through all but last ast.
for (int i = 0; i < block->ln - 1; i++)
exec_exp(block->inside[i], ast->scope);
AST* last = exec_exp(block->inside[block->ln - 1], ast->scope);
return last;
}
AST* exec_call(AST* ast, Scope* parent) {
ASTCallData* calldata = (ASTCallData*)ast->data;
AST* exp = exec_exp(calldata->exp, parent);
switch (exp->type) {
case AST_TYPE_BIF:
ASTBIFData bifdata = exp->data;
return bifdata(calldata->argc, calldata->argv, parent);
case AST_TYPE_LAMBDA:
return exec_lambda(calldata->argc, calldata->argv, exp, parent);
default:
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("Uncallable.", NULL)
);
}
}
AST* exec_vdef(AST* ast, Scope* parent) {
// Use parent's scope.
exec_inherit_scope(ast, parent);
ASTVDefData* data = (ASTVDefData*)ast->data;
AST* val = data->exp;
char* key = data->name;
scope_add(parent, key, val); // Add variable definition to parent scope.
return exec_exp(val, parent);
}
AST* exec_vref(AST* ast, Scope* parent) {
// Use parent's scope.
exec_inherit_scope(ast, parent);
log_dbg("attempting to reference var");
ASTVrefData* vref = (ASTVrefData*)ast->data;
AST* found = ast_find(parent, vref->to);
if (found == NULL) {
// TODO: Better memory management here.
static char msg[256];
snprintf(
msg, sizeof(msg), "Could not find value in scope for `%s`.",
vref->to
);
return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL));
}
// return exec_exp(found, ast->scope);
return found;
}
AST* exec_fdef(AST* ast, Scope* parent) {
ast->scope = scope_init(parent);
ASTFDefData* fdef = (ASTFDefData*)ast->data;
AST* val = ast;
char* key = fdef->name;
scope_add(parent, key, val);
return fdef->body; // Function definitions return function body.
}
AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent) {
Scope* callscope = scope_init(parent);
ASTLambdaData* lambda = (ASTLambdaData*)exp->data;
for (int i = 0; i < argc; i++) {
char* key = ((ASTArgData*)lambda->parv[i]->data)->name;
AST* val = exec_exp(argv[i], parent);
scope_add(callscope, key, val);
}
return exec_exp(lambda->body, callscope);
}
void exec_print(double n) { printf("= %lf\n", n); }
inline void exec_new_scope(AST* ast, Scope* inherit) {
Scope* scope = scope_init(inherit);
ast->scope = scope;
// Update linked status.
scope->uses++;
}
inline void exec_inherit_scope(AST* ast, Scope* inherit) {
ast->scope = inherit;
// Update uses.
inherit->uses++;
}

14
src/fnv1a.c Normal file
View File

@@ -0,0 +1,14 @@
#include "include/fnv1a.h"
#include <stdlib.h>
uint64_t fnv1a_hash(char* key, size_t ln) {
uint64_t hash = FNV1A_BASIS_64;
for (size_t i = 0; i < ln; i++) {
hash ^= (unsigned char)key[i];
hash *= FNV1A_PRIME_64;
}
return hash;
}

57
src/gc.c Normal file
View File

@@ -0,0 +1,57 @@
#include "include/gc.h"
#include "include/ast.h"
#include "include/scope.h"
#include "include/util.h"
#include <assert.h>
#include <stdio.h>
#include <stddef.h>
GC* gclist = NULL;
void gc_destroy(GC* gc) { free(gc); }
void f() {}
void* gc_alloc(size_t sz, GCType type) {
assert(type <= GC_TYPE_MAX);
void* mem = malloc(sz);
GC* gc = malloc(sizeof(GC));
gc->p = mem;
gc->type = type;
gc->marked = false;
gc->nxt = gclist;
gclist = gc;
if (type == GC_TYPE_AST) {
log_dbgf("Alloc'd AST for GC: %p", mem);
} else if (type == GC_TYPE_SCOPE) {
log_dbgf("Alloc'd scope for GC: %p", mem);
}
return mem;
}
void gc_hack_free() {
while (gclist) {
GC* gc = gclist;
gclist = gclist->nxt;
switch (gc->type) {
case GC_TYPE_AST:
f();
if (((AST*)gc->p)->type > AST_TYPE_MAX) {
log_dbgf(
"Attempted to free invalid AST (%i > %i) to GC: gc:%p "
"ast:%p",
((AST*)gc->p)->type, AST_TYPE_MAX, gc, gc->p
);
}
ast_destroy(gc->p);
break;
case GC_TYPE_SCOPE: scope_destroy_psv(gc->p); break;
}
gc_destroy(gc);
}
}

6
src/global.c Normal file
View File

@@ -0,0 +1,6 @@
#include "include/global.h"
#include <stdlib.h>
// Global input text.
char* inp = NULL;

320
src/grammar.y Normal file
View File

@@ -0,0 +1,320 @@
%{
#include <string.h>
#include <stdio.h>
#include "../../src/include/ast.h"
#include "../../src/include/lexer.h"
#include "../../src/include/dlist.h"
#include "../../src/include/builtin.h"
int yylex(void);
void yyerror(char const*);
AST* root = NULL;
%}
%code requires {
#include "../../src/include/ast.h"
#include "../../src/include/dlist.h"
#include "../../src/include/builtin.h"
}
%union {
double fval;
char* strval;
AST* ast;
ArgArr* argarr;
DList* exps;
}
%define parse.error verbose
%token BOOLT // Boolean true (TRUE or T).
%token BOOLF // Boolean false (FALSE or F).
%token IF // if or ?.
%token ELSE // else or :.
%token BLOCKS // Block start {.
%token BLOCKE // Block end }.
%token GROUPS // Group start (.
%token GROUPE // Group end ).
%token SEP // Seperator ,.
%token EQ // Equals =.
%token DEQ // Double equals ==.
%token EXPSEP // Expression seperator ;.
%token<strval> WORD // Word, i.e. keyword.
%token<fval> NUM // Number.
%token SUB // Subtract -.
%token ADD // Addition *.
%token MUL // Multiplication *.
%token DIV // Division /.
%token NL // Newline.
%token BACKSLASH
%left ADD SUB
%left MUL DIV
%precedence NEG
%type<ast> exp;
%type<argarr> arg;
%type<argarr> argstart;
%type<exps> blockstart;
%type<exps> block;
%type<exps> inputstart
%type<exps> input
%start inputend // This makes no sense but w/e.
%%
inputstart:
exp {
DList* exps = dlist_init();
dlist_append(exps, $1);
$$ = exps;
}
;
input:
inputstart {
$$ = $1;
}
| input EXPSEP exp {
dlist_append($1, $3);
$$ = $1;
}
;
inputend:
%empty
| input {
root = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $1->buf, $1->ln));
dlist_destroypsv($1);
}
;
argstart:
exp {
ArgArr* argarr = argarr_init();
argarr_add(argarr, $1);
$$ = argarr;
}
;
arg:
argstart { $$ = $1; }
| arg SEP exp {
argarr_add($1, $3);
$$ = $1;
}
;
blockstart:
exp {
DList* exps = dlist_init(); // List of expressions.
dlist_append(exps, $1);
$$ = exps;
}
;
block:
blockstart { $$ = $1; }
| block EXPSEP exp {
dlist_append($1, $3);
$$ = $1;
}
;
exp:
// Number.
NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init($1)); }
| BOOLT { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(1)); }
| BOOLF { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(0)); }
| exp DEQ exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_eq))
));
}
| IF exp exp exp {
AST** argv = calloc(3, sizeof(AST*));
argv[0] = $2;
argv[1] = $3;
argv[2] = $4;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
3,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
));
}
| IF exp exp ELSE exp {
AST** argv = calloc(3, sizeof(AST*));
argv[0] = $2;
argv[1] = $3;
argv[2] = $5;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
3,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
));
}
// Variable reference.
| WORD {
$$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1));
}
// Call (general form).
| exp GROUPS arg GROUPE {
size_t argc = $3->ln;
AST** argv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
$1
));
}
// Call (convenient form).
| WORD GROUPS arg GROUPE {
size_t argc = $3->ln;
AST** argv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
ast_init(AST_TYPE_VREF, ast_vref_data_init($1))
));
}
// Call (hacky convenient form).
| WORD GROUPS GROUPE {
size_t argc = 0;
AST** argv = NULL;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
ast_init(AST_TYPE_VREF, ast_vref_data_init($1))
));
}
// Function definitions. Convert to VDef of Lambda.
| WORD GROUPS arg GROUPE exp {
size_t parc = $3->ln;
AST** parv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init(
$1,
ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init(
parc, parv, $5
))
));
}
// Lambda definitions.
| BACKSLASH GROUPS arg GROUPE exp {
size_t parc = $3->ln;
AST** parv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init(parc, parv, $5));
}
// Block.
| BLOCKS block BLOCKE {
$$ = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $2->buf, $2->ln));
}
// Negative.
| SUB exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-1));
argv[1] = $2;
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF,
ast_bif_data_init(builtin_mul)
)
)
);
}
// Group.
| GROUPS exp GROUPE { $$ = $2; }
// Variable definition.
| WORD EQ exp {
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init($1, $3));
}
| exp ADD exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_sum))
)
);
}
| exp SUB exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_sub))
)
);
}
| exp MUL exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_mul))
)
);
}
| exp DIV exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_div))
)
);
}
%%

36
src/htab.c Normal file
View File

@@ -0,0 +1,36 @@
#include "include/htab.h"
#include "include/fnv1a.h"
#include "include/util.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
HTab* htab_init() {
HTab* htab = calloc(1, sizeof(HTab));
log_dbgf("HTAB %p", htab);
return htab;
}
void htab_destroy(HTab* htab) { free(htab); }
// Get the index of a key.
size_t geti(char* key) {
uint64_t hash = fnv1a_hash(key, strlen(key));
size_t i = hash & (HTAB_SPACE - 1); // Magic.
return i;
}
void* htab_get(HTab* htab, char* key) {
size_t i = geti(key);
log_dbgf("Getting something from hash table @ index %lu", i);
return (*htab)[i];
}
void htab_ins(HTab* htab, char* key, void* data) {
size_t i = geti(key);
(*htab)[i] = data;
log_dbgf("Inserted something to hash table @ index %lu", i);
}

176
src/include/ast.h Normal file
View File

@@ -0,0 +1,176 @@
#ifndef AST_H
#define AST_H
#include "scope.h"
#include <stdlib.h>
// The type of an `AST`.
typedef enum {
// Primitive types.
AST_TYPE_NUM, // A number (float).
AST_TYPE_STR, // A string
AST_TYPE_INT, // An integer.
AST_TYPE_BOOL, // A boolean.
AST_TYPE_SYM, // A symbol.
AST_TYPE_EXC, // Exception.
// Collection types:
AST_TYPE_VEC, // A vector (fixed size, fixed type).
AST_TYPE_LIST, // A list (variable size, variable type).
// Misc. types.
AST_TYPE_BIF, // Built-in function.
AST_TYPE_CALL, // A function call.
AST_TYPE_VDEF, // A variable definition.
AST_TYPE_VREF, // A variable reference.
AST_TYPE_BLOCK, // A block of code (scope).
AST_TYPE_FDEF, // A function definition.
AST_TYPE_LAMBDA, // An anonymous function definition.
AST_TYPE_ARG, // A definition argument.
AST_TYPE_MAX = AST_TYPE_ARG,
} ASTType;
// An Abstract Syntax Tree.
typedef struct {
ASTType type; // The type of the `AST`.
void* data; // The data of the `AST`.
Scope* scope; // The scope of the `AST`.
} AST;
// Create a new `AST`.
AST* ast_init(ASTType type, void* data);
// Create a new `AST` with a specified scope.
AST* ast_init_scope(ASTType type, void* data, Scope* scope);
// Destroy an `AST`.
void ast_destroy(AST* ast);
// A number.
typedef double ASTNumData;
// Create a new `ASTNumData`.
ASTNumData* ast_num_data_init(double val);
// Destroy an `ASTNumData`.
void ast_num_data_destroy(ASTNumData* num);
// A boolean.
typedef int ASTBoolData;
// Create a new `ASTBoolData`.
ASTBoolData* ast_bool_data_init(int val);
// Destroy an `ASTBoolData`.
void ast_bool_data_destroy(ASTBoolData* bol);
// An exception.
typedef struct ASTEXCDATA {
const char* msg; // The exception message.
AST* trace; // The previous exception.
} ASTExcData;
// Create a new `ASTExecData. `msg` should be static.
ASTExcData* ast_exc_data_init(const char* msg, AST* trace);
// Destroy an `ASTExecData`.
void ast_exc_data_destroy(ASTExcData* exc);
// Argument list as anonymous struct.
#define ARGS \
struct { \
size_t argc; \
AST** argv; \
}
// Parameter list as anonymous struct.
#define PARS \
struct { \
size_t parc; \
AST** parv; \
}
// A built-in function.
typedef AST* (*ASTBIFData)(size_t argc, AST** argv, Scope* scope);
// Create a built-in function.
ASTBIFData* ast_bif_data_init(AST* fn(size_t, AST**, Scope*));
// Destroy an `ASTBIFData`.
void ast_bif_data_destroy(ASTBIFData* bif);
// A lambda.
typedef struct {
PARS; // The parameters the lambda can accept.
AST* body; // The body expression to be executed.
} ASTLambdaData;
// Creates a new `ASTLambdaData`.
ASTLambdaData* ast_lambda_data_init(size_t parc, AST** parv, AST* body);
// Destroy an `ASTLambdaData`.
void ast_lambda_data_destroy(ASTLambdaData*);
// A call.
typedef struct {
ARGS; // The arguments the call is made with.
AST* exp; // The expression the call is to.
} ASTCallData;
// Create a new `ASTCallData`.
ASTCallData* ast_call_data_init(size_t argc, AST** argv, AST* exp);
// Destroy an `ASTCallData`.
void ast_call_data_destroy(ASTCallData* call);
// A variable definition. Associates a name with an expression.
typedef struct {
char* name;
AST* exp;
} ASTVDefData;
// Create a new `ASTVDefData`.
ASTVDefData* ast_vdef_data_init(char* name, AST* exp);
// Destroy an `ASTVDefData`.
void ast_vdef_data_destroy(ASTVDefData* vdef);
// A variable reference.
typedef struct {
char* to; // What the reference's to.
} ASTVrefData;
// Create a new `ASTVRefData`.
ASTVrefData* ast_vref_data_init(char* to);
// Destroy an `ASTVRefData`.
void ast_vref_data_destroy(ASTVrefData* call);
// A code block.
typedef struct {
AST** inside; // What's inside the block.
size_t ln; // How many ASTs are in the block.
} ASTBlockData;
// Create a new `ASTBlockData`.
ASTBlockData* ast_block_data_init(AST** inside, size_t ln);
// Destroy an `ASTBlockData`, recursively.
void ast_block_data_destroy(ASTBlockData* block);
// Destroy an `ASTBlockData`.
void ast_block_data_destroy_psv(ASTBlockData* block);
typedef struct {
char* name; // Function name.
ARGS; // Function args.
AST* body; // Function body.
} ASTFDefData;
// Create a new `ASTFDefData`.
ASTFDefData* ast_fdef_data_init(char* name, size_t argc, AST** argv, AST* body);
// Destroy an `ASTFDefData`, recursively.
void ast_fdef_data_destroy(ASTFDefData* fdef);
// Destroy an `ASTFDefData`.
void ast_fdef_data_destroy_psv(ASTFDefData* fdef);
typedef struct {
char* name; // Argument name.
} ASTArgData;
// Create a new `ASTArgData`.
ASTArgData* ast_arg_data_init(char* name);
// Destroy an `ASTArgData`.
void ast_arg_data_destroy(ASTArgData* arg);
// Find the expression associated with a name in the nearest scope.
AST* ast_find(Scope* scope, char* name);
#endif

44
src/include/ast_print.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef AST_PRINT_H
#define AST_PRINT_H
#include "ast.h"
// Print an `AST`, recursively.
void ast_print(AST* ast);
// Helper function to `ast_print()`, where `i` is indentation level.
void ast_print_i(AST* ast, int i);
// Print an `ASTNumData`.
void ast_num_print(ASTNumData*, int i);
// Print an `ASTBoolData`.
void ast_bool_print(ASTBoolData*, int i);
// Print an `ASTExecData`.
void ast_exc_print(ASTExcData*, int i);
// Print an `ASTCallData`.
void ast_call_print(ASTCallData*, int i);
// Print an `ASTVDefData`.
void ast_vdef_print(ASTVDefData*, int depth);
// Print an `ASTVRefData`.
void ast_vref_print(ASTVrefData*, int i);
// Print an `ASTBlockData`.
void ast_block_print(ASTBlockData*, int i);
// Print an `ASTFDefData`.
void ast_fdef_print(ASTFDefData* fdef, int i);
// Print an `ASTArgData`.
void ast_arg_print(ASTArgData* arg, int i);
// Print an `ASTLambdaData`.
void ast_lambda_print(ASTLambdaData* arg, int i);
// Print an `ASTBIFData`.
void ast_bif_print(ASTBIFData* arg, int i);
#endif

44
src/include/builtin.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef BUILTIN_H
#define BUILTIN_H
#include "ast.h"
#include <stddef.h>
// Sum some nums.
AST* builtin_sum(size_t argc, AST** argv, Scope* parent);
// Subtract nums.
AST* builtin_sub(size_t argc, AST** argv, Scope* parent);
// Multiply nums.
AST* builtin_mul(size_t argc, AST** argv, Scope* parent);
// Divide nums.
AST* builtin_div(size_t argc, AST** argv, Scope* parent);
// Die.
AST* builtin_die(size_t argc, AST** argv, Scope* parent);
// If statement.
AST* builtin_if(size_t argc, AST** argv, Scope* parent);
// Equality.
AST* builtin_eq(size_t argc, AST** argv, Scope* parent);
struct builtin_data {
char* name;
AST* (*fn)(size_t argc, AST** argv, Scope* parent);
};
static struct builtin_data BUILTIN_FNS[] = {
{ "sum", builtin_sum },
{ "sub", builtin_sub },
{ "mul", builtin_mul },
{ "div", builtin_div },
{ "die", builtin_die },
{"_if", builtin_if},
{"eq", builtin_eq},
};
#define BUILTIN_FNS_LN (arrln(BUILTIN_FNS))
#endif

24
src/include/dlist.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef DLIST_H
#define DLIST_H
#include <stdlib.h>
#define DLIST_INITSZ 128 * sizeof(void*)
typedef struct {
void** buf; // The buffer containing the list.
size_t sz; // The size of the buffer.
size_t ln; // The number of elements in the list.
} DList;
// Create a new `DList`.
DList* dlist_init(void);
// Destroy a `DList`.
void dlist_destroy(DList* dstr);
// Destroy `DList` structure but preserve `->buf`.
void dlist_destroypsv(DList* dstr);
// Append `src` to `dest`.
void dlist_append(DList* dest, void* src);
#endif

27
src/include/dstr.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef DSTR_H
#define DSTR_H
#include <stdlib.h>
#define DSTR_INITSZ 128
typedef struct {
char* buf; // The buffer containing the string.
size_t sz; // The size of the buffer.
size_t ln; // The number of characters in the buffer.
} Dstr;
// Initialize a `DStr`.
Dstr* dstr_init(void);
// Destroy a `DStr`.
void dstr_destroy(Dstr* dstr);
// Destroy `DStr` structure but preserve `DStr->buf`.
void dstr_destroypsv(Dstr* dstr);
// Append `ln` characters of `src` to `dest`.
void dstr_append(Dstr* dest, char* src, size_t ln);
// Append `ch` to `dest`.
void dstr_appendch(Dstr* dest, char ch);
#endif

32
src/include/exec.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef EXEC_H
#define EXEC_H
#include "ast.h"
#include "scope.h"
// Start executing at the root of the AST. Initialize the `scope`.
AST* exec_start(AST* ast);
// Execute an expression. Delegates to the other executor functions.
AST* exec_exp(AST* ast, Scope* parent);
// Execute the expressions of a block.
AST* exec_block(AST* ast, Scope* parent);
// Execute a call.
AST* exec_call(AST* ast, Scope* parent);
// Execute a variable definition.
AST* exec_vdef(AST* ast, Scope* parent);
// Execute a variable reference.
AST* exec_vref(AST* ast, Scope* parent);
// Execute a function definition.
AST* exec_fdef(AST* ast, Scope* parent);
// Execute a lambda expression.
AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent);
// Print the result of an execution.
void exec_print(double n);
// Create a new scope and mark it as linked. Also update inherited scope.
void exec_new_scope(AST* ast, Scope* inherit);
// Inherit from another scope and mark it as linked.
void exec_inherit_scope(AST* ast, Scope* inherit);
#endif

18
src/include/fnv1a.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef FNV1A_H
#define FNV1A_H
// Implements the FNV-1a hash algorithm.
#include <stdint.h>
#include <stdlib.h>
// FNV prime.
#define FNV1A_PRIME_64 0x00000100000001b3u
// Offset basis.
#define FNV1A_BASIS_64 0xcbf29ce484222325u
// Hash a string `str` of length `ln`.
uint64_t fnv1a_hash(char* str, size_t ln);
#endif

31
src/include/gc.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef GC_H
#define GC_H
#include <stdlib.h>
// The type a GC can refer to.
typedef enum {
GC_TYPE_AST,
GC_TYPE_SCOPE,
GC_TYPE_MAX = GC_TYPE_SCOPE
} GCType;
// Added to each AST and Scope; keep track of what's actually still accessible.
typedef struct GC_STRUCT {
void* p; // Pointer to the data.
struct GC_STRUCT* nxt; // The next GC in the linked list.
GCType type; // What type of data.
bool marked; // Whether the data is still accessible.
} GC;
GC* gc_init(void* p, GCType type, GC*);
// Does not free ->p or ->nxt.
void gc_destroy(GC* gc);
// Allocate for an object in the heap, and keep track of it in the GC.
void* gc_alloc(size_t sz, GCType type);
// Free everything, immediately.
void gc_hack_free();
#endif

4
src/include/global.h Normal file
View File

@@ -0,0 +1,4 @@
#ifndef GLOBAL_H
#define GLOBAL_H
#endif

29
src/include/htab.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef HTAB_H
#define HTAB_H
#include <stddef.h>
#include <stdint.h>
#define HTAB_FN fnv1a_hash // Function used for hashing.
#define HTAB_SPACE \
1024 // Number of entries possible in the hash table; must be
// power of 2.
// Hash Table.
typedef void* HTab[HTAB_SPACE];
// Create a new hash table.
HTab* htab_init();
// Destroy a hash table, but not its elements.
void htab_destroy(HTab* htab);
// Get element at `hash`. Return its contents, or NULL if nothing found.
void* htab_get(HTab* htab, char* str);
// Insert `data` at index `hash`.
void htab_ins(HTab* htab, char* key, void* data);
// Gets the length of the hash table.
size_t htab_ln(HTab* htab);
#endif

39
src/include/lexer.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef LEXER_H
#define LEXER_H
#include <assert.h>
#include <stddef.h>
#include "ast.h"
#define ARLN 8
extern char* inp;
typedef struct {
size_t sz;
size_t ln;
AST** buf;
} ArgArr;
ArgArr* argarr_init();
void argarr_destroy(ArgArr* argarr);
// Destroy ArgArr structure but preserve -> buf.
void argarr_destroypsv(ArgArr* argarr);
void argarr_add(ArgArr* argarr, AST* arg);
#include "../../build/grammars/grammar.tab.h"
extern YYSTYPE yylval;
// Accumulate an integer.
int acc_int(int c);
// Accumulate a floating-point number.
double acc_float(int c);
// Called by `yyparse()` (in bison-generated files.)
int yylex();
void yyerror(char const* s);
#endif

View File

@@ -1,6 +1,2 @@
#ifndef MAIN_H // This file serves no purpose but because I've written my makefile this way it
#define MAIN_H // has to exist for things to compile :P. TODO: Fix this.
#include <stdio.h>
#endif

View File

@@ -1,30 +0,0 @@
#ifndef PARSER_H
#define PARSER_H
// Expression one of:
// - Operation
// - Number
// Operation contains:
// - Type
// - Expression 1
// - Expression 2
typedef enum OpType {
OPTYPE_PLUS,
OPTYPE_MINUS
} optype_t;
typedef union Exp {
typedef struct Op {
optype_t type;
Exp* exp1;
Exp* exp2;
} op_t;
int n;
} exp_t;
#endif

22
src/include/scope.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef SCOPE_H
#define SCOPE_H
#include "htab.h"
// Represents the reverse linked tree of scope.
typedef struct SCOPE_T {
HTab* here; // This scope's hash table.
struct SCOPE_T* inherit; // The scope to inherit from.
int uses; // How many `AST`s are linked to this scope. // TODO: REMOVE.
} Scope;
// Create a new `Scope`. Creates new empty `HTab` for current scope.
Scope* scope_init(Scope* inherit);
// Destroy all linked `Scope`s this inherits from.
void scope_destroy(Scope* scope);
// Destroy the current `Scope` only.
void scope_destroy_psv(Scope *scope);
// Insert a key/val pair into the `HTab` of a `Scope`.
void scope_add(Scope* scope, char* key, void* val);
#endif

24
src/include/stack.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef STACK_H
#define STACK_H
#include <stdlib.h>
#define STACK_MAX 64
typedef struct {
size_t ln; // The length of the stack (i.e., how many elements).
void* buf[STACK_MAX]; // The stack itself.
} Stack;
// Create a `Stack`.
Stack* stack_init();
// Destroy a `Stack`.
// Note that `->i` must be `0`, i.e. the `Stack` must be empty.
void stack_destroy(Stack* stack);
// Push a value to the `Stack`.
void stack_push(Stack* stack, void* val);
// Pop a value from the `Stack`.
void* stack_pop(Stack* stack);
#endif

View File

@@ -1,12 +0,0 @@
#ifndef UTIL_H
#define UTIL_H
// Utilies.
#include <stdlib.h>
#include <stdio.h>
// Exit with an error. Returns int for ease of use, but should be treated as void.
int die(char* msg);
#endif

115
src/include/util.h Normal file
View File

@@ -0,0 +1,115 @@
#ifndef UTIL_H
#define UTIL_H
#include <signal.h>
// Most of this file is cursed printing macros for `ast_print()`. Do not attempt
// to comprehend.
// Allocate a pointer with a type.
#define talloc(T, X) T* X = malloc(sizeof(T));
// Get the length of an array.
#define arrln(A) (sizeof(A)/sizeof(*A))
// Trap GDB &c.
#define TRAP() RAISE(SIGTRAP)
#ifdef DBG // Debug macros
// Log a message.
#define log_dbg(msg) \
printf( \
"\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__ \
);
// Log a message with formatting.
#define log_dbgf(msg, ...) \
printf( \
"\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__, __VA_ARGS__ \
);
#else // ifdef DBG
#define log_dbg(msg)
#define log_dbgf(msg, ...)
#endif // ifdef DBG else
// Resent color code.
#define COL_RESET "\e[0m"
// Regular color codes.
#define COL_BLA "\e[0;30m"
#define COL_RED "\e[0;31m"
#define COL_GRE "\e[0;32m"
#define COL_YEL "\e[0;33m"
#define COL_BLU "\e[0;34m"
#define COL_MAG "\e[0;35m"
#define COL_CYA "\e[0;36m"
#define COL_WHI "\e[0;37m"
// Bold color codes.
#define COL_BBLA "\e[1;30m"
#define COL_BRED "\e[1;31m"
#define COL_BGRE "\e[1;32m"
#define COL_BYEL "\e[1;33m"
#define COL_BBLU "\e[1;34m"
#define COL_BMAG "\e[1;35m"
#define COL_BCYA "\e[1;36m"
#define COL_BWHI "\e[1;37m"
// Start in indent block.
#define INDENT_BEGIN(ILVL) \
__attribute__((unused)) int INDENT_lvl = ILVL; \
Dstr* INDENT_spacing = dstr_init(); \
for (int INDENT_j = 0; INDENT_j < ILVL; INDENT_j++) \
dstr_appendch(INDENT_spacing, ' ');
// Print & indent the title of a section.
#define INDENT_TITLE(THING, WHERE) \
printf( \
"%s" COL_BCYA THING COL_RESET " @" COL_MAG " %p\n" COL_RESET, \
INDENT_spacing->buf, WHERE \
);
// Print & indent a thing.
#define INDENT_FIELD(FIELD, VAL, ...) \
printf( \
"%s " COL_BWHI FIELD ": " COL_RESET COL_WHI VAL COL_RESET "\n", \
INDENT_spacing->buf, __VA_ARGS__ \
);
// Print & indent a thing with a newline before the val.
#define INDENT_FIELD_NL(FIELD, VAL, ...) \
printf( \
"%s " COL_BWHI FIELD ":" COL_RESET "\n %s " COL_WHI VAL COL_RESET \
"\n", \
INDENT_spacing->buf, INDENT_spacing->buf, __VA_ARGS__ \
);
// Print & indent a thing without any newline.
#define INDENT_FIELD_EXT_NONL_START(FIELD) \
printf("%s " COL_BWHI FIELD ":\n" COL_RESET COL_WHI, INDENT_spacing->buf);
#define INDENT_FIELD_NONL_END printf("\n" COL_RESET);
// Print an array A of N things, by calling the function F.
#define INDENT_FIELD_LIST(FIELD, A, N, F) \
printf("%s " COL_BWHI FIELD ": [\n" COL_RESET, INDENT_spacing->buf); \
for (int INDENT_i = 0; INDENT_i < N; INDENT_i++) { \
F(A[INDENT_i], INDENT_lvl + 2); \
} \
printf(COL_BWHI "%s ]\n" COL_RESET, INDENT_spacing->buf);
// End an indent block.
#define INDENT_END \
printf(COL_RESET); \
dstr_destroy(INDENT_spacing);
#define INDENT_FIELD_LIST_OPEN(FIELD) \
printf("%s " COL_BWHI FIELD ": [\n" COL_RESET, INDENT_spacing->buf);
#define INDENT_FIELD_LIST_CLOSE \
printf(COL_BWHI "%s ]\n" COL_RESET, INDENT_spacing->buf);
#endif

155
src/lexer.c Normal file
View File

@@ -0,0 +1,155 @@
#include <complex.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "include/dstr.h"
#include "include/lexer.h"
#include "include/util.h"
ArgArr* argarr_init() {
ArgArr* argarr = malloc(sizeof(ArgArr));
argarr->sz = ARLN * sizeof(AST*);
argarr->ln = 0;
argarr->buf = malloc(argarr->sz);
return argarr;
}
void argarr_destroy(ArgArr* argarr) {
free(argarr->buf);
free(argarr);
}
void argarr_destroypsv(ArgArr* argarr) { free(argarr); }
void argarr_add(ArgArr* argarr, AST* arg) {
if ((argarr->ln + 1) * sizeof(AST*) > argarr->sz) {
argarr->sz *= 2;
argarr->buf = realloc(argarr->buf, argarr->sz);
log_dbgf(
"ArgArr @ %p doubled from %ld to %ld", argarr, argarr->sz / 2,
argarr->sz
);
}
argarr->buf[argarr->ln++] = arg;
}
int acc_int(int c) {
int value = c - '0';
while (isdigit(*inp)) {
value = value * 10 + (*inp - '0'); // Accumulate value.
inp++;
}
return value;
}
double acc_float(int c) {
int dplaces = 0;
double value = (double)(c - '0');
// Grab everything prior to '.'.
while (isdigit(*inp)) {
value = value * 10 + (*inp - '0'); // Accumulate value.
inp++;
}
if (*inp == '.') {
inp++;
while (isdigit(*inp)) {
value = value * 10 + (*inp - '0'); // Accumulate value.
dplaces++;
inp++;
}
value = value / pow(10, dplaces);
}
return value;
}
char* acc_word(int c) {
Dstr* val = dstr_init();
while (isalpha(*inp) || *inp == '_') {
dstr_appendch(val, *(inp - 1));
inp++;
}
dstr_appendch(val, *(inp - 1));
char* ret = val->buf;
dstr_destroypsv(val);
return ret;
}
char* acc_deq(int c) {
Dstr* val = dstr_init();
while (*inp == '=') {
dstr_appendch(val, *(inp - 1));
inp++;
}
dstr_appendch(val, *(inp - 1));
char* ret = val->buf;
dstr_destroypsv(val);
return ret;
}
int yylex() {
if (*inp == '\0') return YYEOF;
// Skip all whitespace.
while (*inp == ' ' || *inp == '\t') inp++;
// Assign & consume current character.
int c = *inp++;
// Check for NUM.
if (isdigit(c)) {
yylval.fval = acc_float(c); // Set the token value.
return NUM;
}
if (c == '=') {
yylval.strval = acc_deq(c);
if (!strcmp(yylval.strval, "=")) return EQ;
if (!strcmp(yylval.strval, "==")) return DEQ;
}
if (isalpha(c) || c == '_') {
yylval.strval = acc_word(c);
if (!strcmp(yylval.strval, "TRUE") || !strcmp(yylval.strval, "T"))
return BOOLT;
if (!strcmp(yylval.strval, "FALSE") || !strcmp(yylval.strval, "F"))
return BOOLF;
if (!strcmp(yylval.strval, "if")) return IF;
if (!strcmp(yylval.strval, "else")) return ELSE;
return WORD;
}
switch (c) {
case '+': return ADD;
case '\n': return NL;
case '-': return SUB;
case '*': return MUL;
case '/': return DIV;
case '(': return GROUPS;
case ')': return GROUPE;
case ',': return SEP;
case ';': return EXPSEP;
case '{': return BLOCKS;
case '}': return BLOCKE;
// case '=': return EQ;
case '\\': return BACKSLASH;
case '?': return IF;
case ':': return ELSE;
default: fprintf(stderr, "Unexpected character: %c\n", c);
}
return 0;
}
void yyerror(char const* s) { fprintf(stderr, "Parse error: %s\n", s); }

View File

@@ -1,6 +1,80 @@
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "include/ast.h"
#include "include/ast_print.h"
#include "include/dstr.h"
#include "include/exec.h"
#include "include/gc.h"
#include "include/lexer.h"
#include "include/util.h"
#include "../build/grammars/grammar.tab.h"
// Global Abstract Syntax Tree.
extern AST* root;
extern char* inp;
extern int yyparse();
int main(int argc, char** argv) { int main(int argc, char** argv) {
printf("Hello, world!");
if (argc - 1 && strlen(argv[1]) > 0 && (inp = argv[1]) && !yyparse()) {
log_dbg("Parsed successfully!\n");
ast_print(root);
AST* eval = exec_start(root);
ast_print(eval);
// ast_destroy(eval);
// ast_destroy(root);
gc_hack_free();
exit(0);
}
while (1) {
Dstr* ln = dstr_init();
char c;
printf("> ");
fflush(stdout);
// Accumulate line.
do {
c = getc(stdin);
switch (c) {
case EOF: dstr_destroy(ln); goto lnskip;
case '\n': goto lnend;
default: dstr_appendch(ln, c);
}
} while (1);
lnend:
log_dbgf("cline: %s", ln->buf);
if (ln->ln > 0) {
inp = ln->buf;
if (yyparse() == 0) {
log_dbg("Parsed successfully!\n");
} else printf("Parse error.\n");
#ifdef DBG
ast_print(root);
#endif
AST* eval = exec_start(root);
ast_print(eval);
// Awful hack to exit when die() is called, until proper exception
// handling is implemented. TODO TODO TODO PLSFIX.
if (eval->type == AST_TYPE_EXC &&
((ASTExcData*)eval->data)->msg[0] == '8') {
gc_hack_free();
exit(1);
}
gc_hack_free();
}
dstr_destroy(ln);
}
lnskip:;
return 0; return 0;
} }

37
src/scope.c Normal file
View File

@@ -0,0 +1,37 @@
#include "include/scope.h"
#include "include/gc.h"
#include "include/htab.h"
#include "include/util.h"
#include <stdio.h>
#include <stdlib.h>
Scope* scope_init(Scope* inherit) {
Scope* scope = gc_alloc(sizeof(Scope), GC_TYPE_SCOPE);
scope->here = htab_init();
scope->inherit = inherit;
scope->uses = 0;
log_dbgf("%p: new scope, inherits from %p", scope, inherit);
return scope;
}
void scope_destroy(Scope* scope) {
if (!scope) return;
htab_destroy(scope->here);
if (scope->inherit != NULL) scope_destroy(scope->inherit);
free(scope);
}
void scope_destroy_psv(Scope* scope) {
if (!scope) return;
log_dbgf("%p: destroying", scope);
htab_destroy(scope->here);
scope->inherit = NULL;
free(scope);
}
inline void scope_add(Scope* scope, char* key, void* val) {
htab_ins(scope->here, key, val);
}

41
src/stack.c Normal file
View File

@@ -0,0 +1,41 @@
#include <assert.h>
#include <stdio.h> // IWYU pragma: keep. Req by util macros.
#include <string.h>
#include "include/stack.h"
#include "include/util.h"
Stack* stack_init() {
talloc(Stack, stack);
memset(stack->buf, 0, sizeof(void*) * STACK_MAX);
stack->ln = 0;
return stack;
}
void stack_destroy(Stack* stack) {
// Can only free an empty stack.
assert(stack->ln == 0);
free(stack);
}
void stack_push(Stack* stack, void* val) {
if (stack->ln >= STACK_MAX) {
log_dbgf("Ran out of stack (max: %d)", STACK_MAX);
return;
}
stack->buf[stack->ln] = val;
stack->ln++;
log_dbgf("pushed to stack, inc ln to %ld", stack->ln);
}
void* stack_pop(Stack* stack) {
if (stack->ln <= 0) {
log_dbg("Can't pop empty stack.");
return (void*)-1;
}
return stack->buf[--stack->ln];
}

1
src/util.c Normal file
View File

@@ -0,0 +1 @@
#include "include/util.h"

1
test/Unity Submodule

Submodule test/Unity added at 73237c5d22

59
test/test_ast.c Normal file
View File

@@ -0,0 +1,59 @@
#include "../src/include/ast.h"
#include "Unity/src/unity.h"
#include "Unity/src/unity_internals.h"
#include <string.h>
void setUp() {}
void tearDown() {}
void test_ast_num() {
ASTNumData* num = ast_num_data_init(12.0);
AST* ast = ast_init(AST_TYPE_NUM, num);
TEST_ASSERT_EQUAL(AST_TYPE_NUM, ast->type);
TEST_ASSERT_EQUAL(12.0, *(ASTNumData*)ast->data);
ast_destroy(ast);
}
void test_ast_call() {
AST** argv = malloc(2*sizeof(AST*));
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(1.0));
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init(2.0));
char* name = malloc(2);
strcpy(name, "f");
ASTCallData* call = ast_call_data_init(name, 2, argv);
AST* ast = ast_init(AST_TYPE_CALL, call);
TEST_ASSERT_EQUAL(AST_TYPE_CALL, ast->type);
TEST_ASSERT_EQUAL(name, ((ASTCallData*)ast->data)->to);
TEST_ASSERT_EQUAL(2, ((ASTCallData*)ast->data)->argc);
TEST_ASSERT_EQUAL(1.0, ((ASTCallData*)ast->data)->argv[0]);
TEST_ASSERT_EQUAL(2.0, ((ASTCallData*)ast->data)->argv[1]);
ast_destroy(ast);
}
void test_ast_vref() {
char* s = malloc(2);
strcpy(s, "x");
ASTVrefData* vref = ast_vref_data_init(s);
AST* ast = ast_init(AST_TYPE_VREF, vref);
TEST_ASSERT_EQUAL(AST_TYPE_VREF, ast->type);
ASTVrefData data = *(ASTVrefData*)ast->data;
TEST_ASSERT_EQUAL_STRING("x", data.to);
//ast_destroy(ast);
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_ast_num);
//RUN_TEST(test_ast_call);
//RUN_TEST(test_ast_vref);
return UNITY_END();
}

41
test/test_dlist.c Normal file
View File

@@ -0,0 +1,41 @@
#include "../src/include/dlist.h"
#include "Unity/src/unity.h"
void setUp() {};
void tearDown() {};
void test_dlist_init() {
DList* dlist = dlist_init();
TEST_ASSERT_EQUAL(0, dlist->ln);
TEST_ASSERT_EQUAL(DLIST_INITSZ, dlist->sz);
}
void test_dlist_append() {
DList* dlist;
// Test simple appending.
dlist = dlist_init();
int* n = malloc(sizeof(int));
*n = 1;
dlist_append(dlist, n);
TEST_ASSERT_EQUAL(n, dlist->buf[0]);
TEST_ASSERT_EQUAL(1, dlist->ln);
dlist_destroy(dlist);
// Test buffer doubling.
dlist = dlist_init();
for (int i = 0; i < 129; i++) dlist_append(dlist, &i);
TEST_ASSERT_EQUAL(129, dlist->ln);
TEST_ASSERT_EQUAL(DLIST_INITSZ*2, dlist->sz);
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_dlist_init);
RUN_TEST(test_dlist_append);
return UNITY_END();
}

78
test/test_dstr.c Normal file
View File

@@ -0,0 +1,78 @@
#include "../src/include/dstr.h"
#include "Unity/src/unity.h"
#include <string.h>
void setUp() {};
void tearDown() {};
void test_dstr_init() {
Dstr* dstr = dstr_init();
TEST_ASSERT_EQUAL(0, strlen(dstr->buf));
TEST_ASSERT_EQUAL(0, dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->sz);
}
void test_dstr_append() {
Dstr* dstr;
// Test simple appending.
dstr = dstr_init();
char* hello_world = "Hello, world!";
dstr_append(dstr, hello_world, strlen(hello_world));
TEST_ASSERT_EQUAL_STRING(hello_world, dstr->buf);
TEST_ASSERT_EQUAL(strlen(hello_world), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->sz);
dstr_destroy(dstr);
// Test buffer doubling.
dstr = dstr_init();
char h[DSTR_INITSZ + 20];
memset(h, 'h', DSTR_INITSZ + 19);
h[DSTR_INITSZ + 19] = '\0';
dstr_append(dstr, h, strlen(h));
TEST_ASSERT_EQUAL_STRING(h, dstr->buf);
TEST_ASSERT_EQUAL(strlen(h), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ * 2, dstr->sz);
}
void test_dstr_appendch() {
Dstr* dstr;
// Test simple character appending.
dstr = dstr_init();
char c = 'c';
char* c_str = "c";
dstr_appendch(dstr, c);
TEST_ASSERT_EQUAL_STRING(c_str, dstr->buf);
TEST_ASSERT_EQUAL(strlen(c_str), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->sz);
dstr_destroy(dstr);
// Test buffer doubling.
dstr = dstr_init();
// Test against this string.
char h[DSTR_INITSZ + 20];
memset(h, 'h', DSTR_INITSZ + 19);
h[DSTR_INITSZ + 19] = '\0';
for (int i = 0; i < DSTR_INITSZ + 19; i++) dstr_appendch(dstr, 'h');
TEST_ASSERT_EQUAL_STRING(h, dstr->buf);
TEST_ASSERT_EQUAL(strlen(h), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ * 2, dstr->sz);
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_dstr_init);
RUN_TEST(test_dstr_append);
RUN_TEST(test_dstr_appendch);
return UNITY_END();
}

27
test/test_htab.c Normal file
View File

@@ -0,0 +1,27 @@
#include "../src/include/htab.h"
#include "Unity/src/unity.h"
#include "Unity/src/unity_internals.h"
#include <string.h>
void setUp() {}
void tearDown() {}
void test_htab() {
char* key = "hello";
char* data = "world";
HTab* htab = htab_init();
htab_ins(htab, key, data);
TEST_ASSERT_EQUAL_STRING(data, htab_get(htab, key));
TEST_ASSERT_NOT_EQUAL(data, htab_get(htab, "h"));
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_htab);
return UNITY_END();
}

131
test/val/test.bats Normal file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env bats
bin() { ./scl.out $1 | tail -n1; }
@test "simple addition" {
run bin "1+1"
[ "$output" = "= 2.000000" ]
run bin "-1+1"
echo $output
[ "$output" = "= 0.000000" ]
run bin "1+-1"
[ "$output" = "= 0.000000" ]
run bin "-1+-1"
[ "$output" = "= -2.000000" ]
}
@test "simple subtraction" {
run bin "1-1"
[ "$output" = "= 0.000000" ]
run bin "-1-1"
[ "$output" = "= -2.000000" ]
run bin "1--1"
[ "$output" = "= 2.000000" ]
run bin "-1--1"
[ "$output" = "= 0.000000" ]
}
@test "simple multiplication" {
run bin "1*2"
[ "$output" = "= 2.000000" ]
run bin "-1*2"
[ "$output" = "= -2.000000" ]
run bin "1*-1"
[ "$output" = "= -1.000000" ]
run bin "-1*-1"
[ "$output" = "= 1.000000" ]
}
@test "simple division" {
run bin "1/2"
[ "$output" = "= 0.500000" ]
run bin "-1/2"
[ "$output" = "= -0.500000" ]
run bin "1/-1"
[ "$output" = "= -1.000000" ]
run bin "-1/-1"
[ "$output" = "= 1.000000" ]
}
@test "order of operations" {
run bin "1+2*3"
[ "$output" = "= 7.000000" ]
run bin "2*3+1"
[ "$output" = "= 7.000000" ]
run bin "6/2-1"
[ "$output" = "= 2.000000" ]
run bin "1-6/2"
[ "$output" = "= -2.000000" ]
}
@test "order of operations with parenthesis" {
run bin "(1+2)*3"
[ "$output" = "= 9.000000" ]
run bin "-(1+2*3)"
[ "$output" = "= -7.000000" ]
run bin "-(-(1+2)*3)"
[ "$output" = "= 9.000000" ]
}
@test "basic blocks" {
run bin "{1}"
[ "$output" = "= 1.000000" ]
run bin "2+{3;4}"
[ "$output" = "= 6.000000" ]
run bin "5*{1+1;5*2}"
echo $output
[ "$output" = "= 50.000000" ]
}
@test "variable definition" {
run bin "x=1"
[ "$output" = "= 1.000000" ]
run bin "x=1;x+1"
[ "$output" = "= 2.000000" ]
run bin "h=7;j=2;k=8;l=4;h*h-l+j*k"
echo $output
[ "$output" = "= 61.000000" ]
}
#@test "function definition" {
# run bin "f(n)=2*n; f(2)"
# [ "$output" = "= 4.000000" ]
#}
@test "type stuff" {
run bin "x:int=1"
[ "$output" = "= 1" ]
run bin "x=1.5; type(x)"
[ "$output" = "= num"]
run bin "x:int=1.5; type(x)"
[ "$output" = "= int" ]
run bin "x:int=1.5"
[ "$output" = "= 1" ]
run bin "print(\"Hello, world!\")"
[ "$output" = "= Hello, world!" ]
}

21
type-notes.md Normal file
View File

@@ -0,0 +1,21 @@
Primitive Types
- num
- bool
- str
```
fact(x) = ? (x <= 1) 1 : f(x-1) * x
> lambda
fact(5) == 120
> True
```
Ordered collections.
| Name | Homogeneous | Fixed Size | Unique |
|---|---|---|---|
| Collection | No | No | No |
| List | Yes | No | No |
| Array | Yes | Yes | No |
| Group | No | Yes | No |
| Set | Yes | No | Yes |
| Perm | Yes | Yes | Yes |