Compare commits
142 Commits
e19fc8820a
...
v0.3
Author | SHA1 | Date | |
---|---|---|---|
14ddf51f3c | |||
ac1d76361f | |||
b789193484 | |||
ab97f78fab | |||
7c0b212ab4 | |||
80c8038374 | |||
e3cd78e1b4 | |||
b7b90f528b | |||
0ef44be808 | |||
8924818ec4 | |||
73efa7e136 | |||
4e8d7131d6 | |||
bfce18ab81 | |||
7b648c4bd7 | |||
4ec5d1c075 | |||
5ba070ced7 | |||
8256643c0b | |||
29a217928e | |||
482f2b4877 | |||
36fd838a8f | |||
289243de38 | |||
67aafb3ead | |||
3b5bee0695 | |||
970fc39198 | |||
8d3e43d7dc | |||
d4293e87f3 | |||
abb8ff6b58 | |||
80122b6572 | |||
a4afd3b58a | |||
90c8c91410 | |||
f5ab0e9cb0 | |||
0fb1f1d55f | |||
1d83aa65a4 | |||
3f30662cde | |||
743d16f696 | |||
7b99292547 | |||
4a516d8edb | |||
5b0950cabb | |||
40051de9ae | |||
2d01b09ee9 | |||
ff68b756ef | |||
c2f2658e9c | |||
3e80f6430d | |||
5b50d423fa | |||
a04d318e45 | |||
7258f162c9 | |||
6e05f1334a | |||
4e66f7875a | |||
845a7f87b2 | |||
5b163b26dd | |||
f8ee7cc66e | |||
d699f492fa | |||
018404eea1 | |||
18c61d86dc | |||
eaf65697de | |||
ce6e558761 | |||
db6e591d77 | |||
58554e8727 | |||
4b9f269938 | |||
3d0f534017 | |||
38c6a9113c | |||
109bcb3fa5 | |||
79b60e4853 | |||
8cab531129 | |||
eb3fbca030 | |||
40b91b96bd | |||
67f659e263 | |||
b8ce193299 | |||
11401e75a7 | |||
a6be116a2d | |||
3678940537 | |||
cddf5eed76 | |||
c685d6e55f | |||
6cbc28e092 | |||
408a5a46c0 | |||
6903aeb383 | |||
fbabf71d64 | |||
78e31c3e27 | |||
be3baee74e | |||
d5a07fae56 | |||
4153903443 | |||
77d58bd69e | |||
47a3143752 | |||
4fb73b3c6f | |||
e5c58d5fc5 | |||
5d81054cf6 | |||
66c518fe43 | |||
5ecdf2d89a | |||
a5a86dc080 | |||
70393ef9ae | |||
efac7f7747 | |||
3fcffc81ad | |||
27e61471a8 | |||
c94d7863a7 | |||
4dd1f2b5f1 | |||
1a2249a0da | |||
8b01407374 | |||
3ab2696705 | |||
0293c925d2 | |||
16d62f280f | |||
0dbeff7077 | |||
96038f4baa | |||
6d7ff5d43f | |||
868ee84aeb | |||
694eb43eab | |||
694d40124f | |||
39778ce08d | |||
cfd44621d5 | |||
4be71317b0 | |||
49642553e1 | |||
1e11b5921d | |||
7343c3b9d9 | |||
9e8410d4cf | |||
bc0c4f33ad | |||
1098fa252f | |||
0b1905429c | |||
e7d3ea3697 | |||
577bde6e57 | |||
60b9ed9eb2 | |||
d13bf883b5 | |||
681e005a68 | |||
2ce89fb39a | |||
907bc26264 | |||
e243e862ae | |||
5e930b9847 | |||
ed3ec885c0 | |||
835bcfe121 | |||
9432496875 | |||
0731e40e6a | |||
35322de3ac | |||
c3d8d6f8e5 | |||
1f2bca5028 | |||
9a9e5cd3e0 | |||
4d828500af | |||
deac5ca5b8 | |||
e05ebeef2a | |||
bdca40bae4 | |||
b4cd46a1e7 | |||
a57acc1176 | |||
ffcf2fb013 | |||
c7a9c8215c | |||
ca4cf2cd68 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,5 +4,5 @@ tags
|
||||
*.out
|
||||
.cache
|
||||
build/*
|
||||
compile_commands.json
|
||||
vgcore.*
|
||||
compile_commands.json
|
||||
|
95
Makefile
95
Makefile
@@ -1,32 +1,4 @@
|
||||
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
|
||||
LINK = clang
|
||||
CFLAGS = -Wall -DDBG -ggdb
|
||||
LDFLAGS = -lm
|
||||
|
||||
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
|
||||
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_FILES = $(GRAM_DIR)/grammar.tab.c $(GRAM_DIR)/grammar.tab.h
|
||||
UNITY_C = $(TEST_DIR)/unity/unity.c
|
||||
TEST_SRC_FILES = $(wildcard $(TEST_DIR)/*.c)
|
||||
TEST_OBJ_FILES = $(patsubst $(TEST_DIR)/%.c, $(TEST_OBJ_DIR)/%.o, $(TEST_SRC_FILES))
|
||||
|
||||
RESETCOLOR = \033[0m
|
||||
WHITE = $(RESETCOLOR)\033[37m
|
||||
WHITE_BOLD = $(RESETCOLOR)\033[37;1m
|
||||
include config.mk
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
@@ -34,51 +6,72 @@ release: clean
|
||||
release: CFLAGS = -Wall -O2
|
||||
release: $(TARGET)
|
||||
|
||||
# Run the target.
|
||||
run: $(TARGET)
|
||||
@ echo -e "$(WHITE_BOLD)Running... $(RESETCOLOR)./$(TARGET)"
|
||||
@ ./$(TARGET)
|
||||
./$(TARGET)
|
||||
|
||||
# Generate grammars with bison.
|
||||
$(GRAM_FILES): $(SRC_DIR)/grammar.y
|
||||
@ mkdir -p $(GRAM_DIR)
|
||||
@ echo -e "$(WHITE_BOLD)Generating grammars...$(RESETCOLOR) bison $< -o$(GRAM_DIR)/grammar.tab.c -H$(GRAM_DIR)/grammar.tab.h"
|
||||
@ bison $< -o$(GRAM_DIR)/grammar.tab.c -H$(GRAM_DIR)/grammar.tab.h
|
||||
@ $(PRINT) "$(WHITE_BOLD)Generating grammars...$(RESETCOLOR)"
|
||||
$(BISON) $< -o$(GRAM_DIR)/grammar.tab.c -H$(GRAM_DIR)/grammar.tab.h
|
||||
|
||||
# Compile grammars.
|
||||
$(OBJ_DIR)/grammar.o: $(GRAM_DIR)/grammar.tab.c $(GRAM_DIR)/grammar.tab.h $(OBJ_DIR)/lexer.o
|
||||
@ $(PRINT) "$(WHITE_BOLD)Compiling grammars...$(RESETCOLOR)"
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Lexer depends on grammars.
|
||||
$(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 $@
|
||||
|
||||
# Compile project sources.
|
||||
# Compile project source objects.
|
||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(INC_DIR)/%.h
|
||||
@ mkdir -p $(OBJ_DIR)
|
||||
@ echo -e "$(WHITE_BOLD)Compiling $(WHITE)$<$(WHITE_BOLD)... $(RESETCOLOR)$(CC) $(CFLAGS) -c $< -o $@"
|
||||
@ $(CC) $(CFLAGS) -c $< -o $@
|
||||
@ $(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)
|
||||
@ echo -e "$(WHITE_BOLD)Linking $(WHITE)$(TARGET)$(WHITE_BOLD)...$(RESETCOLOR) $(CC) -o $(TARGET) $(OBJ_FILES) $(LDFLAGS)"
|
||||
@ $(LINK) -o $(TARGET) $(OBJ_FILES) $(OBJ_DIR)/grammar.o $(LDFLAGS)
|
||||
@ $(PRINT) "$(WHITE_BOLD)Linking $(WHITE)$@$(WHITE_BOLD)...$(RESETCOLOR)"
|
||||
$(LINK) -o $(TARGET) $(OBJ_FILES) $(OBJ_DIR)/grammar.o $(LDFLAGS)
|
||||
|
||||
# Compile test sources.
|
||||
$(TEST_OBJ_DIR)/%.o: $(TEST_DIR)/%.c
|
||||
# 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)
|
||||
@ echo -e "$(WHITE_BOLD)Compiling Test $(WHITE)$<$(WHITE_BOLD)... $(WHITE)$(CC) $(CFLAGS) -I$(SRC_DIR)/include -c $< -o $@$(RESETCOLOR)"
|
||||
@ $(CC) $(CFLAGS) -I$(SRC_DIR)/include -c $< -o $@
|
||||
@ $(PRINT) "$(WHITE_BOLD)Compiling test object $(WHITE)$@$(WHITE_BOLD)...$(RESETCOLOR)"
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Link the test executable.
|
||||
test: $(TEST_OBJ_FILES) $(OBJ_FILES_NOMAIN) $(UNITY_C)
|
||||
@ echo -e "$(WHITE_BOLD)Linking test binary$(WHITE)...$(RESETCOLOR)"
|
||||
@ $(LINK) -DUNITY_OUTPUT_COLOR $(TEST_OBJ_FILES) $(OBJ_FILES_NOMAIN) $(UNITY_C) -o $(TEST_BUILD_DIR)/test.out
|
||||
@ echo -e "$(WHITE_BOLD)Running tests$(WHITE)...$(RESETCOLOR)"
|
||||
@ ./$(TEST_BUILD_DIR)/test.out
|
||||
# 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:
|
||||
@ echo -e "$(WHITE_BOLD)Cleaning up...$(WHITE) $(OBJ_DIR)/*.o $(TEST_OBJ_DIR)/*.o $(TEST_BUILD_DIR)/test.out $(TARGET) $(GRAM_DIR)/* $(RESETCOLOR)"
|
||||
@ rm -rf $(OBJ_DIR)/*.o $(TEST_OBJ_DIR)/*.o $(TEST_BUILD_DIR)/test.out $(TARGET) $(GRAM_DIR)/*
|
||||
@ $(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)
|
||||
|
||||
.PHONY: all clean test nocolor release run
|
||||
# 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)
|
||||
|
79
README.md
79
README.md
@@ -1,15 +1,36 @@
|
||||
# SCL: Simple CAS Language
|
||||
|
||||
## Current State
|
||||
Version v0.3
|
||||
|
||||
The following things are possible:
|
||||
1. Floating-point numbers
|
||||
2. Negative numbers
|
||||
3. Addition
|
||||
4. Subtraction
|
||||
5. Multiplication
|
||||
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.
|
||||
|
||||
## Syntax (Planned)
|
||||
## 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:
|
||||
|
||||
@@ -18,36 +39,36 @@ As one would expect, you can evaluate simple infix expressions:
|
||||
= 2
|
||||
```
|
||||
|
||||
You can also define your own functions:
|
||||
You can also define your own functions and variables:
|
||||
|
||||
```scl
|
||||
> f(x) = 2x
|
||||
> f(2)
|
||||
> 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
|
||||
```
|
||||
|
||||
Symbolic algebra is done in the following manner:
|
||||
Here's a simple factorial function:
|
||||
|
||||
```scl
|
||||
> f(x) = x^4
|
||||
> diff(f, x:sym, 2)
|
||||
= 12x^2
|
||||
> factorial(n) {
|
||||
> if (n == 0) { 1 }
|
||||
> else { n * factorial(n - 1) }
|
||||
> }
|
||||
```
|
||||
|
||||
SCL will dynamically decide on types, but you can state them explicitly as
|
||||
well:
|
||||
Or, using SCL's more concise syntax:
|
||||
|
||||
```scl
|
||||
> f(x:int) = 2x
|
||||
> f(2.2)
|
||||
! f(x:int): x must be of type int.
|
||||
```
|
||||
|
||||
Variables can be defined, with several attributes:
|
||||
|
||||
```scl
|
||||
> a = 1 // Interpret type automatically.
|
||||
> b:int = 1 // Must be int.
|
||||
> c:const:int = 1 // Constant: value can never change.
|
||||
> x:sym // Treated symbolicaly.
|
||||
> factorial(n) ? n == 0 1 n * factorial(n - 1)
|
||||
```
|
||||
|
55
STATUS.md
Normal file
55
STATUS.md
Normal 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
9
TODO.md
Normal 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
40
config.mk
Normal 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
25
definitions.md
Normal 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
2
examples/function.scl
Normal file
@@ -0,0 +1,2 @@
|
||||
apply(f, x) f(x)
|
||||
apply(\(x) x + 1, 2)
|
48
examples/types.scl
Normal file
48
examples/types.scl
Normal 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">
|
219
src/ast.c
219
src/ast.c
@@ -1,21 +1,36 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "include/ast.h"
|
||||
#include "include/dstr.h"
|
||||
#include "include/gc.h"
|
||||
#include "include/scope.h"
|
||||
#include "include/util.h"
|
||||
|
||||
extern AST* root;
|
||||
|
||||
static char* asttype_names[] = {
|
||||
[AST_TYPE_CALL] = "CALL",
|
||||
[AST_TYPE_NUM] = "NUMBER",
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -24,60 +39,85 @@ 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_CALL: ast_call_data_destroy(ast->data); break;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void ast_print(AST* ast) { 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_CALL: ast_call_print(ast->data, i + 2); break;
|
||||
default: exit(1);
|
||||
}
|
||||
INDENT_FIELD_NONL_END;
|
||||
INDENT_END;
|
||||
free(ast);
|
||||
}
|
||||
|
||||
ASTNumData* ast_num_data_init(double val) {
|
||||
talloc(ASTNumData, num);
|
||||
|
||||
log_dbgf("val: %lf", val);
|
||||
|
||||
*num = val;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
void ast_num_data_destroy(ASTNumData* num) {
|
||||
if (!num) return free(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_num_print(ASTNumData* data, int i) {
|
||||
INDENT_BEGIN(i);
|
||||
void ast_bool_data_destroy(ASTBoolData* bol) { free(bol); }
|
||||
|
||||
INDENT_FIELD("data", "%lf", *data);
|
||||
|
||||
INDENT_END;
|
||||
ASTExcData* ast_exc_data_init(const char* msg, AST* trace) {
|
||||
ASTExcData* data = malloc(sizeof(ASTExcData));
|
||||
data->msg = msg;
|
||||
data->trace = trace;
|
||||
return data;
|
||||
}
|
||||
|
||||
ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv) {
|
||||
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);
|
||||
|
||||
log_dbgf("to: %s", to);
|
||||
|
||||
call->to = to;
|
||||
call->exp = exp;
|
||||
call->argc = argc;
|
||||
call->argv = argv;
|
||||
|
||||
@@ -85,18 +125,101 @@ ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv) {
|
||||
}
|
||||
|
||||
void ast_call_data_destroy(ASTCallData* call) {
|
||||
if (!call) return free(call->to);
|
||||
for (size_t i = 0; i < call->argc; i++) free(call->argv[i]);
|
||||
if (!call) return;
|
||||
free(call->argv);
|
||||
free(call);
|
||||
}
|
||||
|
||||
void ast_call_print(ASTCallData* data, int i) {
|
||||
INDENT_BEGIN(i);
|
||||
// VDef.
|
||||
|
||||
INDENT_TITLE("ASTCallData", data);
|
||||
INDENT_FIELD("to", "%s", data->to);
|
||||
INDENT_FIELD("argc", "%ld", data->argc);
|
||||
INDENT_FIELD_LIST("argv", data->argv, data->argc, ast_print_i);
|
||||
ASTVDefData* ast_vdef_data_init(char* name, AST* exp) {
|
||||
talloc(ASTVDefData, vdef);
|
||||
|
||||
INDENT_END;
|
||||
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
175
src/ast_print.c
Normal 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
211
src/builtin.c
Normal 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
44
src/dlist.c
Normal 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++;
|
||||
}
|
31
src/dstr.c
31
src/dstr.c
@@ -1,13 +1,14 @@
|
||||
#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->bufsz = DSTR_INITSZ;
|
||||
dstr->sz = DSTR_INITSZ;
|
||||
dstr->buf = malloc(DSTR_INITSZ);
|
||||
*dstr->buf = '\0';
|
||||
dstr->ln = 0;
|
||||
@@ -20,16 +21,22 @@ void dstr_destroy(Dstr* dstr) {
|
||||
free(dstr);
|
||||
}
|
||||
|
||||
void dstr_append(Dstr* dest, char* src, size_t ln) {
|
||||
while (dest->ln + ln + 1 > dest->bufsz) {
|
||||
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.
|
||||
dest->bufsz *= 2;
|
||||
dest->buf = realloc(dest->buf, dest->bufsz);
|
||||
dstr->sz *= 2;
|
||||
dstr->buf = realloc(dstr->buf, dstr->sz);
|
||||
log_dbgf(
|
||||
"dstr @ %p doubled from %ld to %ld", dest, dest->bufsz / 2,
|
||||
dest->bufsz
|
||||
"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.
|
||||
@@ -38,15 +45,7 @@ void dstr_append(Dstr* dest, char* src, size_t ln) {
|
||||
}
|
||||
|
||||
void dstr_appendch(Dstr* dest, char ch) {
|
||||
if (dest->ln + 1 + 1 > dest->bufsz) {
|
||||
// Double the buffer size when overflown.
|
||||
dest->bufsz *= 2;
|
||||
dest->buf = realloc(dest->buf, dest->bufsz);
|
||||
log_dbgf(
|
||||
"dstr @ %p doubled from %ld to %ld", dest, dest->bufsz / 2,
|
||||
dest->bufsz
|
||||
);
|
||||
}
|
||||
dstr_check_resz(dest, 1);
|
||||
|
||||
// Overwrites the preexisting null terminator, and adds one of its own.
|
||||
dest->buf[dest->ln] = ch;
|
||||
|
160
src/exec.c
160
src/exec.c
@@ -1,44 +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"
|
||||
|
||||
extern AST* root;
|
||||
|
||||
ASTNumData exec_expr(AST* ast) {
|
||||
// ast_print(ast);
|
||||
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_CALL: return exec_call(ast);
|
||||
case AST_TYPE_NUM: return *(ASTNumData*)ast->data;
|
||||
default: printf("what\n");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
ASTNumData exec_call(AST* ast) {
|
||||
log_dbg("Started call execution.");
|
||||
fflush(stdout);
|
||||
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;
|
||||
if (!strcmp(calldata->to, "+") && calldata->argc == 2) {
|
||||
|
||||
ASTNumData n1 = exec_expr(calldata->argv[0]);
|
||||
ASTNumData n2 = exec_expr(calldata->argv[1]);
|
||||
AST* exp = exec_exp(calldata->exp, parent);
|
||||
|
||||
return n1 + n2;
|
||||
} else if (!strcmp(calldata->to, "-") && calldata->argc == 2) {
|
||||
ASTNumData n1 = exec_expr(calldata->argv[0]);
|
||||
ASTNumData n2 = exec_expr(calldata->argv[1]);
|
||||
|
||||
return n1 - n2;
|
||||
} else if (!strcmp(calldata->to, "*") && calldata->argc == 2) {
|
||||
ASTNumData n1 = exec_expr(calldata->argv[0]);
|
||||
ASTNumData n2 = exec_expr(calldata->argv[1]);
|
||||
|
||||
return n1 * n2;
|
||||
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)
|
||||
);
|
||||
}
|
||||
return -1000;
|
||||
}
|
||||
|
||||
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
14
src/fnv1a.c
Normal 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
57
src/gc.c
Normal 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
6
src/global.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "include/global.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
// Global input text.
|
||||
char* inp = NULL;
|
332
src/grammar.y
332
src/grammar.y
@@ -1,7 +1,10 @@
|
||||
%{
|
||||
#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*);
|
||||
@@ -11,84 +14,307 @@
|
||||
|
||||
%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<strval> CALL
|
||||
%token<fval> NUM
|
||||
%token BOOLT // Boolean true (TRUE or T).
|
||||
%token BOOLF // Boolean false (FALSE or F).
|
||||
|
||||
%token NEG
|
||||
%token PLUS
|
||||
%token MULT
|
||||
%token IF // if or ?.
|
||||
%token ELSE // else or :.
|
||||
|
||||
%token NL
|
||||
%type<ast> exp
|
||||
%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
|
||||
| exp { root = $1; }
|
||||
| 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)); }
|
||||
| NEG NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init(-$2)); }
|
||||
| NUM PLUS NUM {
|
||||
|
||||
| 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] = ast_init(AST_TYPE_NUM, ast_num_data_init($1));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init($3));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("+", 2, argv));
|
||||
}
|
||||
| NEG NUM PLUS NUM {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-$2));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init($4));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("+", 2, argv));
|
||||
}
|
||||
| NUM NEG NUM {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init($1));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init($3));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("-", 2, argv));
|
||||
}
|
||||
| NEG NUM NEG NUM {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-$2));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init($4));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("-", 2, argv));
|
||||
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))
|
||||
));
|
||||
}
|
||||
|
||||
| NUM MULT NUM {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init($1));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init($3));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("*", 2, argv));
|
||||
| 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))
|
||||
));
|
||||
}
|
||||
| NEG NUM MULT NUM {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-$2));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init($4));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("*", 2, argv));
|
||||
}
|
||||
| NEG NUM MULT NEG NUM {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-$2));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init(-$5));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("*", 2, argv));
|
||||
}
|
||||
| NUM MULT NEG NUM {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init($1));
|
||||
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init(-$4));
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init("*", 2, argv));
|
||||
| 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
36
src/htab.c
Normal 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);
|
||||
}
|
@@ -1,38 +1,176 @@
|
||||
#ifndef AST_H
|
||||
#define AST_H
|
||||
|
||||
#include "scope.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// The type of an `AST`.
|
||||
typedef enum {
|
||||
AST_TYPE_NUM,
|
||||
AST_TYPE_CALL,
|
||||
AST_TYPE_MAX = AST_TYPE_CALL
|
||||
// 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;
|
||||
void* data;
|
||||
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);
|
||||
void ast_print(AST* ast);
|
||||
void ast_print_i(AST* ast, int i);
|
||||
|
||||
// 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);
|
||||
void ast_num_print(ASTNumData*, int i);
|
||||
|
||||
// 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 {
|
||||
char* to; // What the call's to.
|
||||
size_t argc; // Argument count.
|
||||
AST** argv; // Argument vector.
|
||||
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;
|
||||
|
||||
ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv);
|
||||
// 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);
|
||||
void ast_call_print(ASTCallData*, int i);
|
||||
|
||||
// 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
44
src/include/ast_print.h
Normal 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
44
src/include/builtin.h
Normal 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
24
src/include/dlist.h
Normal 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
|
@@ -6,18 +6,22 @@
|
||||
#define DSTR_INITSZ 128
|
||||
|
||||
typedef struct {
|
||||
char* buf; // The buffer containing the string.
|
||||
size_t bufsz; // The size of the buffer.
|
||||
size_t ln; // The number of characters in the buffer.
|
||||
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.
|
||||
// Append `ln` characters of `src` to `dest`.
|
||||
void dstr_append(Dstr* dest, char* src, size_t ln);
|
||||
|
||||
// Append ch to dest.
|
||||
// Append `ch` to `dest`.
|
||||
void dstr_appendch(Dstr* dest, char ch);
|
||||
|
||||
#endif
|
||||
|
@@ -2,9 +2,31 @@
|
||||
#define EXEC_H
|
||||
|
||||
#include "ast.h"
|
||||
#include "scope.h"
|
||||
|
||||
ASTNumData exec_expr(AST* ast);
|
||||
ASTNumData exec_call(AST* ast);
|
||||
// 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
18
src/include/fnv1a.h
Normal 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
31
src/include/gc.h
Normal 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
4
src/include/global.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#ifndef GLOBAL_H
|
||||
#define GLOBAL_H
|
||||
|
||||
#endif
|
29
src/include/htab.h
Normal file
29
src/include/htab.h
Normal 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
|
@@ -2,20 +2,29 @@
|
||||
#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);
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include("../../build/grammars/grammar.tab.h")
|
||||
#include "../../build/grammars/grammar.tab.h"
|
||||
#else
|
||||
#warn "Build resources not present!"
|
||||
#endif
|
||||
#else
|
||||
#warn "Not sure whether build-time resources are present."
|
||||
#include "../../build/grammars/grammar.tab.h"
|
||||
#endif
|
||||
|
||||
extern YYSTYPE yylval;
|
||||
extern char* inp;
|
||||
|
||||
// Accumulate an integer.
|
||||
int acc_int(int c);
|
||||
|
@@ -1,4 +1,2 @@
|
||||
// This file serves no purpose but because I've written my makefile this way it
|
||||
// has to exist for things to compile :P. TODO: Fix this.
|
||||
|
||||
int four() { return 4; }
|
||||
|
22
src/include/scope.h
Normal file
22
src/include/scope.h
Normal 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
|
@@ -6,18 +6,19 @@
|
||||
#define STACK_MAX 64
|
||||
|
||||
typedef struct {
|
||||
size_t i; // Current index in the stack.
|
||||
void* val[STACK_MAX]; // The stack itself.
|
||||
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 `stack->i` must be `0`.
|
||||
// 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.
|
||||
// Push a value to the `Stack`.
|
||||
void stack_push(Stack* stack, void* val);
|
||||
// Pop a value from the stack.
|
||||
// Pop a value from the `Stack`.
|
||||
void* stack_pop(Stack* stack);
|
||||
|
||||
#endif
|
||||
|
@@ -1,17 +1,35 @@
|
||||
#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__);
|
||||
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__);
|
||||
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)
|
||||
@@ -50,23 +68,30 @@
|
||||
|
||||
// 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);
|
||||
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__);
|
||||
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__);
|
||||
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) \
|
||||
#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);
|
||||
#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) \
|
||||
@@ -77,9 +102,14 @@
|
||||
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_END \
|
||||
printf(COL_RESET); \
|
||||
dstr_destroy(INDENT_spacing);
|
||||
|
||||
// Allocate a pointer with a type.
|
||||
#define talloc(T, X) T* X = malloc(sizeof(T));
|
||||
#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
|
||||
|
114
src/lexer.c
114
src/lexer.c
@@ -1,9 +1,43 @@
|
||||
#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';
|
||||
@@ -28,30 +62,46 @@ double acc_float(int c) {
|
||||
inp++;
|
||||
|
||||
while (isdigit(*inp)) {
|
||||
// TODO:
|
||||
// Accumulate as int, divide once at end.
|
||||
// value = value + (((double)(*inp - '0'))/pow(10.0l,
|
||||
// (double)(inp-oinp))); // Accumulate value.
|
||||
value = value * 10 + (*inp - '0'); // Accumulate value.
|
||||
dplaces++;
|
||||
inp++;
|
||||
}
|
||||
value = value / pow(10, dplaces);
|
||||
}
|
||||
|
||||
// > 1.20000
|
||||
// = 1.0 + 2/10
|
||||
|
||||
// > 1.23
|
||||
// = 1.2 + 3/100
|
||||
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++; }
|
||||
while (*inp == ' ' || *inp == '\t') inp++;
|
||||
|
||||
// Assign & consume current character.
|
||||
int c = *inp++;
|
||||
@@ -62,15 +112,43 @@ int yylex() {
|
||||
return NUM;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '+': return PLUS;
|
||||
case '\n': return NL;
|
||||
case '-': return NEG;
|
||||
case '*': return MULT;
|
||||
default: return CALL;
|
||||
if (c == '=') {
|
||||
yylval.strval = acc_deq(c);
|
||||
if (!strcmp(yylval.strval, "=")) return EQ;
|
||||
if (!strcmp(yylval.strval, "==")) return DEQ;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unexpected character: %c\n", c);
|
||||
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;
|
||||
}
|
||||
|
35
src/main.c
35
src/main.c
@@ -1,8 +1,11 @@
|
||||
#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"
|
||||
|
||||
@@ -10,14 +13,22 @@
|
||||
|
||||
// Global Abstract Syntax Tree.
|
||||
extern AST* root;
|
||||
|
||||
// Global input text.
|
||||
char* inp = NULL;
|
||||
|
||||
extern char* inp;
|
||||
extern int yyparse();
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
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;
|
||||
@@ -31,7 +42,7 @@ int main(int argc, char** argv) {
|
||||
switch (c) {
|
||||
case EOF: dstr_destroy(ln); goto lnskip;
|
||||
case '\n': goto lnend;
|
||||
default: dstr_appendch(ln, c); log_dbgf("cchar: %c", c);
|
||||
default: dstr_appendch(ln, c);
|
||||
}
|
||||
} while (1);
|
||||
|
||||
@@ -45,13 +56,25 @@ int main(int argc, char** argv) {
|
||||
log_dbg("Parsed successfully!\n");
|
||||
} else printf("Parse error.\n");
|
||||
|
||||
exec_print(exec_expr(root));
|
||||
#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;
|
||||
}
|
||||
|
37
src/scope.c
Normal file
37
src/scope.c
Normal 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);
|
||||
}
|
19
src/stack.c
19
src/stack.c
@@ -1,5 +1,5 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio.h> // IWYU pragma: keep. Req by util macros.
|
||||
#include <string.h>
|
||||
|
||||
#include "include/stack.h"
|
||||
@@ -8,33 +8,34 @@
|
||||
Stack* stack_init() {
|
||||
talloc(Stack, stack);
|
||||
|
||||
memset(stack->val, 0, sizeof(void*) * STACK_MAX);
|
||||
stack->i = 0;
|
||||
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->i == 0);
|
||||
assert(stack->ln == 0);
|
||||
free(stack);
|
||||
}
|
||||
|
||||
void stack_push(Stack* stack, void* val) {
|
||||
if (stack->i >= STACK_MAX) {
|
||||
if (stack->ln >= STACK_MAX) {
|
||||
log_dbgf("Ran out of stack (max: %d)", STACK_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
stack->val[stack->i] = val;
|
||||
stack->i++;
|
||||
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->i <= 0) {
|
||||
if (stack->ln <= 0) {
|
||||
log_dbg("Can't pop empty stack.");
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
return stack->val[--stack->i];
|
||||
return stack->buf[--stack->ln];
|
||||
}
|
||||
|
59
test/test_ast.c
Normal file
59
test/test_ast.c
Normal 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
41
test/test_dlist.c
Normal 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
78
test/test_dstr.c
Normal 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
27
test/test_htab.c
Normal 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
131
test/val/test.bats
Normal 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
21
type-notes.md
Normal 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 |
|
Reference in New Issue
Block a user