Compare commits

13 Commits

Author SHA1 Message Date
4e5b12b5b2 Updated README.md. 2025-09-01 00:18:59 -04:00
0e6bb7aa16 Cleaned up. 2025-08-28 11:12:19 -04:00
14ddf51f3c Bump version in README.md.
NOTE: changed versioning system slightly.
2025-08-27 12:30:28 -04:00
ac1d76361f Updated README.md. 2025-08-27 01:14:49 -04:00
b789193484 Recursion works.
FINALLY YESSSS 🎉🎉🎉
2025-08-27 01:10:58 -04:00
ab97f78fab Work on conditionals and equalities. 2025-08-27 00:41:48 -04:00
7c0b212ab4 Added a bit of familiar sugar to conditionals. 2025-08-26 23:50:06 -04:00
80c8038374 Added basic conditionals. 2025-08-26 23:47:15 -04:00
e3cd78e1b4 Added basic implementation of booleans. 2025-08-26 22:58:58 -04:00
b7b90f528b Created an awful hack for a clean exit on die(). 2025-08-26 18:30:08 -04:00
0ef44be808 Fixed some builtin arithmetic logic. 2025-08-26 18:24:28 -04:00
8924818ec4 Updated README.md. 2025-08-26 16:37:48 -04:00
73efa7e136 Cleaned up a bit. 2025-08-26 16:37:38 -04:00
14 changed files with 281 additions and 84 deletions

View File

@@ -1,20 +1,20 @@
# SCL: Simple CAS Language # SCL: Simple CAS Language
Version v1.0-beta *v0.3*
SCL aims to be a human-friendly Computer Algebra System (CAS) inspired by 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 [maxima](https://maxima.sourceforge.io/) that feels like writing on paper. In
its current state, SCL can be used as a basic 4-function calculator with order its current state, SCL can be used as a functional programming language capable
of operations and local variables. The codebase is about 1,400 lines of C, of performing simple arithmetic. The codebase is about 2,000 lines of
including a parser, interpreter, and runtime. It uses a linked environment handwritten C, including a parser, interpreter, and runtime. It uses a linked
scoping model. environment scoping model.
## Usage ## Usage
To download and run: To download and run:
```bash ```bash
git clone https://git.signorovitch.org/jacob/scl -b stable && cd scl git clone https://git.signorovitch.org/scl/scl -b stable && cd scl
make release make release
./scl.out ./scl.out
``` ```
@@ -22,75 +22,44 @@ make release
### For Development ### For Development
```bash ```bash
git clone git@signorovitch.org:jacob/scl --recurse-submodules && cd scl git clone git@signorovitch.org:scl/scl --recurse-submodules && cd scl
make all test make all test
./scl.out ./scl.out
``` ```
If you wish to run tests, make sure to run `git clone --recurse-submodules` to 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. include the [Unity](https://github.com/ThrowTheSwitch/Unity) test framework.
*Note that tests are currently in poor use. I hope to amend this in the future.*
## Syntax ## Syntax
As one would expect, you can evaluate simple infix expressions: SCL's syntax will feel familiar to other functional programming languages.
```scl ```scl
> 1 + 1 > x = 3 + 3 * 3; x + 1
= 13
> f(x) x + 1
> f(1)
= 2 = 2
``` > (\(x) 2 * x)(5)
= 10
You can also define your own functions and variables: > f(g) g(2)
> f(\(x) 2 * x)
```scl
> f(x) = 2x
> n = 3
> f(n)
= 6
```
As SCL uses a linked environment model for scope, arguments are passed by
reference by default. If you would like to pass by value (i.e., a copy) you may
use the syntax:
```scl
> f(x) = x = 1
> n = 4
> f($n) # Pass a copy of n.
= 1
> n
= 4 = 4
> f(n) # Pass a reference to n.
= 1
> n
> 1
``` ```
Symbolic algebra is done in the following manner: Here's a simple factorial function, using recursion:
```scl ```scl
> f(x) = x^4 > factorial(n) {
> diff(f, x:sym, 2) > if (n == 0) { 1 }
= 12x^2 > else { n * factorial(n - 1) }
> }
``` ```
SCL will dynamically decide on types, but you can state them explicitly as SCL's syntax is quite flexible. The above function could be more concisely
well: written as:
```scl ```scl
> f(x: int): int = 2x > factorial(n) ? n == 0 1 n * factorial(n - 1)
> f(3)
= 6
> f(3.1)
! Traceback:
! In call to `f(x: int): int`:
! TypeError (58): Argument `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.
``` ```

View File

@@ -1,5 +1,7 @@
1. Differentiate parameters and arguments -- params for function definitions, EXCEPTION HANDLING: exception ast type should have as data a giant enum of
arguments for function calls 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. Change editor to GNU Readline.
Make variables persist through lines in the editor. Make variables persist through lines in the editor.

View File

@@ -1,3 +1,2 @@
f(n) 2 * n apply(f, x) f(x)
apply(\(x) x + 1, 2)
f(5)

View File

@@ -40,6 +40,7 @@ void ast_destroy(AST* ast) {
switch (ast->type) { switch (ast->type) {
case AST_TYPE_NUM: ast_num_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_CALL: ast_call_data_destroy(ast->data); break;
case AST_TYPE_VREF: ast_vref_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_VDEF: ast_vdef_data_destroy(ast->data); break;
@@ -66,6 +67,16 @@ ASTNumData* ast_num_data_init(double val) {
void ast_num_data_destroy(ASTNumData* num) { 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_bool_data_destroy(ASTBoolData* bol) { free(bol); }
ASTExcData* ast_exc_data_init(const char* msg, AST* trace) { ASTExcData* ast_exc_data_init(const char* msg, AST* trace) {
ASTExcData* data = malloc(sizeof(ASTExcData)); ASTExcData* data = malloc(sizeof(ASTExcData));
data->msg = msg; data->msg = msg;

View File

@@ -8,6 +8,7 @@
static char* asttype_names[] = { static char* asttype_names[] = {
[AST_TYPE_CALL] = "FUNC CALL", [AST_TYPE_CALL] = "FUNC CALL",
[AST_TYPE_NUM] = "NUMBER", [AST_TYPE_NUM] = "NUMBER",
[AST_TYPE_BOOL] = "BOOLEAN",
[AST_TYPE_VREF] = "VAR REFERENCE", [AST_TYPE_VREF] = "VAR REFERENCE",
[AST_TYPE_VDEF] = "VAR DEFINITION", [AST_TYPE_VDEF] = "VAR DEFINITION",
[AST_TYPE_BLOCK] = "BLOCK", [AST_TYPE_BLOCK] = "BLOCK",
@@ -28,12 +29,17 @@ void ast_print_i(AST* ast, int i) {
INDENT_TITLE("AST", ast); INDENT_TITLE("AST", ast);
INDENT_FIELD("type", "%s", asttype_names[ast->type]); INDENT_FIELD("type", "%s", asttype_names[ast->type]);
// INDENT_FIELD("scope", "%p", ast->scope);
INDENT_FIELD_EXT_NONL_START("data"); INDENT_FIELD_EXT_NONL_START("data");
switch (ast->type) { switch (ast->type) {
case AST_TYPE_NUM: case AST_TYPE_NUM:
printf("%s %lf\n", INDENT_spacing->buf, *(ASTNumData*)ast->data); printf("%s %lf\n", INDENT_spacing->buf, *(ASTNumData*)ast->data);
break; 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_CALL: ast_call_print(ast->data, i + 2); break;
case AST_TYPE_EXC: ast_exc_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_VREF: ast_vref_print(ast->data, i + 2); break;
@@ -57,6 +63,14 @@ void ast_num_print(ASTNumData* data, int i) {
INDENT_END; 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) { void ast_exc_print(ASTExcData* data, int i) {
INDENT_BEGIN(i); INDENT_BEGIN(i);

View File

@@ -1,10 +1,10 @@
#include "include/builtin.h"
#include "include/ast.h"
#include "include/exec.h"
#include "include/util.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include "include/ast.h"
#include "include/builtin.h"
#include "include/exec.h"
AST* builtin_sum(size_t argc, AST** argv, Scope* parent) { AST* builtin_sum(size_t argc, AST** argv, Scope* parent) {
ASTNumData total = 0; ASTNumData total = 0;
@@ -28,7 +28,8 @@ AST* builtin_sum(size_t argc, AST** argv, Scope* parent) {
} }
AST* builtin_sub(size_t argc, AST** argv, Scope* parent) { AST* builtin_sub(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here"); if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent); AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC) if (first->type == AST_TYPE_EXC)
return ast_init( return ast_init(
@@ -63,7 +64,8 @@ AST* builtin_sub(size_t argc, AST** argv, Scope* parent) {
} }
AST* builtin_mul(size_t argc, AST** argv, Scope* parent) { AST* builtin_mul(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here"); if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent); AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC) if (first->type == AST_TYPE_EXC)
return ast_init( return ast_init(
@@ -98,7 +100,8 @@ AST* builtin_mul(size_t argc, AST** argv, Scope* parent) {
} }
AST* builtin_div(size_t argc, AST** argv, Scope* parent) { AST* builtin_div(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here"); if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent); AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC) if (first->type == AST_TYPE_EXC)
return ast_init( return ast_init(
@@ -131,3 +134,77 @@ AST* builtin_div(size_t argc, AST** argv, Scope* parent) {
return ast_init(AST_TYPE_NUM, ast_num_data_init(total)); 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.
}
}

View File

@@ -1,9 +1,9 @@
#include "include/dlist.h"
#include "include/util.h"
#include <stddef.h> #include <stddef.h>
#include <stdio.h> // IWYU pragma: keep. Req by util macros. #include <stdio.h> // IWYU pragma: keep. Req by util macros.
#include "include/dlist.h"
#include "include/util.h"
DList* dlist_init(void) { DList* dlist_init(void) {
DList* dlist = malloc(sizeof(DList)); DList* dlist = malloc(sizeof(DList));

View File

@@ -38,6 +38,10 @@ AST* exec_exp(AST* ast, Scope* parent) {
return ast_init( return ast_init(
AST_TYPE_NUM, ast_num_data_init(*(ASTNumData*)ast->data) 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_VREF: return exec_vref(ast, parent);
case AST_TYPE_VDEF: return exec_vdef(ast, parent); case AST_TYPE_VDEF: return exec_vdef(ast, parent);
case AST_TYPE_FDEF: return exec_fdef(ast, parent); case AST_TYPE_FDEF: return exec_fdef(ast, parent);
@@ -107,7 +111,8 @@ AST* exec_vref(AST* ast, Scope* parent) {
return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL)); return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL));
} }
return exec_exp(found, ast->scope); // return exec_exp(found, ast->scope);
return found;
} }
AST* exec_fdef(AST* ast, Scope* parent) { AST* exec_fdef(AST* ast, Scope* parent) {
@@ -124,7 +129,7 @@ AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent) {
ASTLambdaData* lambda = (ASTLambdaData*)exp->data; ASTLambdaData* lambda = (ASTLambdaData*)exp->data;
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
char* key = ((ASTArgData*)lambda->parv[i]->data)->name; char* key = ((ASTArgData*)lambda->parv[i]->data)->name;
AST* val = argv[i]; AST* val = exec_exp(argv[i], parent);
scope_add(callscope, key, val); scope_add(callscope, key, val);
} }

View File

@@ -28,6 +28,12 @@
%define parse.error verbose %define parse.error verbose
%token BOOLT // Boolean true (TRUE or T).
%token BOOLF // Boolean false (FALSE or F).
%token IF // if or ?.
%token ELSE // else or :.
%token BLOCKS // Block start {. %token BLOCKS // Block start {.
%token BLOCKE // Block end }. %token BLOCKE // Block end }.
@@ -36,6 +42,7 @@
%token SEP // Seperator ,. %token SEP // Seperator ,.
%token EQ // Equals =. %token EQ // Equals =.
%token DEQ // Double equals ==.
%token EXPSEP // Expression seperator ;. %token EXPSEP // Expression seperator ;.
@@ -129,6 +136,46 @@ exp:
// Number. // Number.
NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init($1)); } NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init($1)); }
| BOOLT { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(1)); }
| BOOLF { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(0)); }
| exp DEQ exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_eq))
));
}
| IF exp exp exp {
AST** argv = calloc(3, sizeof(AST*));
argv[0] = $2;
argv[1] = $3;
argv[2] = $4;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
3,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
));
}
| IF exp exp ELSE exp {
AST** argv = calloc(3, sizeof(AST*));
argv[0] = $2;
argv[1] = $3;
argv[2] = $5;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
3,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
));
}
// Variable reference. // Variable reference.
| WORD { | WORD {
$$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1)); $$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1));
@@ -158,6 +205,17 @@ exp:
)); ));
} }
// 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. // Function definitions. Convert to VDef of Lambda.
| WORD GROUPS arg GROUPE exp { | WORD GROUPS arg GROUPE exp {
size_t parc = $3->ln; size_t parc = $3->ln;

View File

@@ -7,11 +7,12 @@
// The type of an `AST`. // The type of an `AST`.
typedef enum { typedef enum {
// Primitive types. // Primitive types.
AST_TYPE_NUM, // A number (float). AST_TYPE_NUM, // A number (float).
AST_TYPE_STR, // A string AST_TYPE_STR, // A string
AST_TYPE_INT, // An integer. AST_TYPE_INT, // An integer.
AST_TYPE_SYM, // A symbol. AST_TYPE_BOOL, // A boolean.
AST_TYPE_EXC, // Exception. AST_TYPE_SYM, // A symbol.
AST_TYPE_EXC, // Exception.
// Collection types: // Collection types:
AST_TYPE_VEC, // A vector (fixed size, fixed type). AST_TYPE_VEC, // A vector (fixed size, fixed type).
@@ -51,6 +52,14 @@ ASTNumData* ast_num_data_init(double val);
// Destroy an `ASTNumData`. // Destroy an `ASTNumData`.
void ast_num_data_destroy(ASTNumData* num); void ast_num_data_destroy(ASTNumData* num);
// A boolean.
typedef int ASTBoolData;
// Create a new `ASTBoolData`.
ASTBoolData* ast_bool_data_init(int val);
// Destroy an `ASTBoolData`.
void ast_bool_data_destroy(ASTBoolData* bol);
// An exception. // An exception.
typedef struct ASTEXCDATA { typedef struct ASTEXCDATA {
const char* msg; // The exception message. const char* msg; // The exception message.
@@ -96,8 +105,8 @@ void ast_lambda_data_destroy(ASTLambdaData*);
// A call. // A call.
typedef struct { typedef struct {
ARGS; // The arguments the call is made with. ARGS; // The arguments the call is made with.
AST* exp; // The expression the call is to. AST* exp; // The expression the call is to.
} ASTCallData; } ASTCallData;
// Create a new `ASTCallData`. // Create a new `ASTCallData`.

View File

@@ -11,6 +11,9 @@ void ast_print_i(AST* ast, int i);
// Print an `ASTNumData`. // Print an `ASTNumData`.
void ast_num_print(ASTNumData*, int i); void ast_num_print(ASTNumData*, int i);
// Print an `ASTBoolData`.
void ast_bool_print(ASTBoolData*, int i);
// Print an `ASTExecData`. // Print an `ASTExecData`.
void ast_exc_print(ASTExcData*, int i); void ast_exc_print(ASTExcData*, int i);

View File

@@ -2,6 +2,7 @@
#define BUILTIN_H #define BUILTIN_H
#include "ast.h" #include "ast.h"
#include <stddef.h>
// Sum some nums. // Sum some nums.
AST* builtin_sum(size_t argc, AST** argv, Scope* parent); AST* builtin_sum(size_t argc, AST** argv, Scope* parent);
@@ -15,6 +16,15 @@ AST* builtin_mul(size_t argc, AST** argv, Scope* parent);
// Divide nums. // Divide nums.
AST* builtin_div(size_t argc, AST** argv, Scope* parent); 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 { struct builtin_data {
char* name; char* name;
AST* (*fn)(size_t argc, AST** argv, Scope* parent); AST* (*fn)(size_t argc, AST** argv, Scope* parent);
@@ -25,6 +35,9 @@ static struct builtin_data BUILTIN_FNS[] = {
{ "sub", builtin_sub }, { "sub", builtin_sub },
{ "mul", builtin_mul }, { "mul", builtin_mul },
{ "div", builtin_div }, { "div", builtin_div },
{ "die", builtin_die },
{"_if", builtin_if},
{"eq", builtin_eq},
}; };
#define BUILTIN_FNS_LN (arrln(BUILTIN_FNS)) #define BUILTIN_FNS_LN (arrln(BUILTIN_FNS))

View File

@@ -3,6 +3,7 @@
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "include/dstr.h" #include "include/dstr.h"
#include "include/lexer.h" #include "include/lexer.h"
@@ -72,7 +73,20 @@ double acc_float(int c) {
char* acc_word(int c) { char* acc_word(int c) {
Dstr* val = dstr_init(); Dstr* val = dstr_init();
while (isalpha(*inp)) { 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)); dstr_appendch(val, *(inp - 1));
inp++; inp++;
} }
@@ -98,8 +112,22 @@ int yylex() {
return NUM; return NUM;
} }
if (isalpha(c)) { if (c == '=') {
yylval.strval = acc_deq(c);
if (!strcmp(yylval.strval, "=")) return EQ;
if (!strcmp(yylval.strval, "==")) return DEQ;
}
if (isalpha(c) || c == '_') {
yylval.strval = acc_word(c); 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; return WORD;
} }
@@ -115,8 +143,10 @@ int yylex() {
case ';': return EXPSEP; case ';': return EXPSEP;
case '{': return BLOCKS; case '{': return BLOCKS;
case '}': return BLOCKE; case '}': return BLOCKE;
case '=': return EQ; // case '=': return EQ;
case '\\': return BACKSLASH; case '\\': return BACKSLASH;
case '?': return IF;
case ':': return ELSE;
default: fprintf(stderr, "Unexpected character: %c\n", c); default: fprintf(stderr, "Unexpected character: %c\n", c);
} }

View File

@@ -62,6 +62,13 @@ int main(int argc, char** argv) {
AST* eval = exec_start(root); AST* eval = exec_start(root);
ast_print(eval); 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(); gc_hack_free();
} }