Compare commits
13 Commits
4e8d7131d6
...
master
Author | SHA1 | Date | |
---|---|---|---|
4e5b12b5b2 | |||
0e6bb7aa16 | |||
14ddf51f3c | |||
ac1d76361f | |||
b789193484 | |||
ab97f78fab | |||
7c0b212ab4 | |||
80c8038374 | |||
e3cd78e1b4 | |||
b7b90f528b | |||
0ef44be808 | |||
8924818ec4 | |||
73efa7e136 |
81
README.md
81
README.md
@@ -1,20 +1,20 @@
|
||||
# SCL: Simple CAS Language
|
||||
|
||||
Version v1.0-beta
|
||||
*v0.3*
|
||||
|
||||
SCL aims to be a human-friendly Computer Algebra System (CAS) inspired by
|
||||
[maxima](https://maxima.sourceforge.io/) that feels like writing on paper. In
|
||||
its current state, SCL can be used as a basic 4-function calculator with order
|
||||
of operations and local variables. The codebase is about 1,400 lines of C,
|
||||
including a parser, interpreter, and runtime. It uses a linked environment
|
||||
scoping model.
|
||||
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
|
||||
handwritten C, including a parser, interpreter, and runtime. It uses a linked
|
||||
environment scoping model.
|
||||
|
||||
## Usage
|
||||
|
||||
To download and run:
|
||||
|
||||
```bash
|
||||
git clone https://git.signorovitch.org/jacob/scl -b stable && cd scl
|
||||
git clone https://git.signorovitch.org/scl/scl -b stable && cd scl
|
||||
make release
|
||||
./scl.out
|
||||
```
|
||||
@@ -22,75 +22,44 @@ make release
|
||||
### For Development
|
||||
|
||||
```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
|
||||
./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.
|
||||
*Note that tests are currently in poor use. I hope to amend this in the future.*
|
||||
|
||||
## Syntax
|
||||
|
||||
As one would expect, you can evaluate simple infix expressions:
|
||||
SCL's syntax will feel familiar to other functional programming languages.
|
||||
|
||||
```scl
|
||||
> 1 + 1
|
||||
> x = 3 + 3 * 3; x + 1
|
||||
= 13
|
||||
> f(x) x + 1
|
||||
> f(1)
|
||||
= 2
|
||||
```
|
||||
|
||||
You can also define your own functions and variables:
|
||||
|
||||
```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
|
||||
> (\(x) 2 * x)(5)
|
||||
= 10
|
||||
> f(g) g(2)
|
||||
> f(\(x) 2 * x)
|
||||
= 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
|
||||
> 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:
|
||||
SCL's syntax is quite flexible. The above function could be more concisely
|
||||
written as:
|
||||
|
||||
```scl
|
||||
> f(x: int): int = 2x
|
||||
> 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.
|
||||
> factorial(n) ? n == 0 1 n * factorial(n - 1)
|
||||
```
|
||||
|
6
TODO.md
6
TODO.md
@@ -1,5 +1,7 @@
|
||||
1. Differentiate parameters and arguments -- params for function definitions,
|
||||
arguments for function calls
|
||||
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.
|
||||
|
@@ -1,3 +1,2 @@
|
||||
f(n) 2 * n
|
||||
|
||||
f(5)
|
||||
apply(f, x) f(x)
|
||||
apply(\(x) x + 1, 2)
|
||||
|
11
src/ast.c
11
src/ast.c
@@ -40,6 +40,7 @@ void ast_destroy(AST* ast) {
|
||||
|
||||
switch (ast->type) {
|
||||
case AST_TYPE_NUM: ast_num_data_destroy(ast->data); break;
|
||||
case AST_TYPE_BOOL: ast_bool_data_destroy(ast->data); break;
|
||||
case AST_TYPE_CALL: ast_call_data_destroy(ast->data); break;
|
||||
case AST_TYPE_VREF: ast_vref_data_destroy(ast->data); break;
|
||||
case AST_TYPE_VDEF: ast_vdef_data_destroy(ast->data); break;
|
||||
@@ -66,6 +67,16 @@ ASTNumData* ast_num_data_init(double val) {
|
||||
|
||||
void ast_num_data_destroy(ASTNumData* num) { free(num); }
|
||||
|
||||
ASTBoolData* ast_bool_data_init(int val) {
|
||||
talloc(ASTBoolData, bol);
|
||||
|
||||
*bol = val;
|
||||
|
||||
return bol;
|
||||
}
|
||||
|
||||
void ast_bool_data_destroy(ASTBoolData* bol) { free(bol); }
|
||||
|
||||
ASTExcData* ast_exc_data_init(const char* msg, AST* trace) {
|
||||
ASTExcData* data = malloc(sizeof(ASTExcData));
|
||||
data->msg = msg;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
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",
|
||||
@@ -28,12 +29,17 @@ void ast_print_i(AST* ast, int i) {
|
||||
|
||||
INDENT_TITLE("AST", ast);
|
||||
INDENT_FIELD("type", "%s", asttype_names[ast->type]);
|
||||
// INDENT_FIELD("scope", "%p", ast->scope);
|
||||
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;
|
||||
@@ -57,6 +63,14 @@ void ast_num_print(ASTNumData* data, int i) {
|
||||
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);
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
#include "include/builtin.h"
|
||||
#include "include/ast.h"
|
||||
#include "include/exec.h"
|
||||
#include "include/util.h"
|
||||
#include <stdarg.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) {
|
||||
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) {
|
||||
log_dbg("Got here");
|
||||
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(
|
||||
@@ -63,7 +64,8 @@ AST* builtin_sub(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);
|
||||
if (first->type == AST_TYPE_EXC)
|
||||
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) {
|
||||
log_dbg("Got here");
|
||||
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(
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
#include "include/dlist.h"
|
||||
#include "include/util.h"
|
||||
|
||||
#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));
|
||||
|
||||
|
@@ -38,6 +38,10 @@ AST* exec_exp(AST* ast, Scope* parent) {
|
||||
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);
|
||||
@@ -107,7 +111,8 @@ AST* exec_vref(AST* ast, Scope* parent) {
|
||||
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) {
|
||||
@@ -124,7 +129,7 @@ AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent) {
|
||||
ASTLambdaData* lambda = (ASTLambdaData*)exp->data;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
char* key = ((ASTArgData*)lambda->parv[i]->data)->name;
|
||||
AST* val = argv[i];
|
||||
AST* val = exec_exp(argv[i], parent);
|
||||
scope_add(callscope, key, val);
|
||||
}
|
||||
|
||||
|
@@ -28,6 +28,12 @@
|
||||
|
||||
%define parse.error verbose
|
||||
|
||||
%token BOOLT // Boolean true (TRUE or T).
|
||||
%token BOOLF // Boolean false (FALSE or F).
|
||||
|
||||
%token IF // if or ?.
|
||||
%token ELSE // else or :.
|
||||
|
||||
%token BLOCKS // Block start {.
|
||||
%token BLOCKE // Block end }.
|
||||
|
||||
@@ -36,6 +42,7 @@
|
||||
%token SEP // Seperator ,.
|
||||
|
||||
%token EQ // Equals =.
|
||||
%token DEQ // Double equals ==.
|
||||
|
||||
%token EXPSEP // Expression seperator ;.
|
||||
|
||||
@@ -129,6 +136,46 @@ exp:
|
||||
// Number.
|
||||
NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init($1)); }
|
||||
|
||||
| BOOLT { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(1)); }
|
||||
| BOOLF { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(0)); }
|
||||
|
||||
| exp DEQ exp {
|
||||
AST** argv = calloc(2, sizeof(AST*));
|
||||
argv[0] = $1;
|
||||
argv[1] = $3;
|
||||
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
|
||||
2,
|
||||
argv,
|
||||
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_eq))
|
||||
));
|
||||
}
|
||||
|
||||
| IF exp exp exp {
|
||||
AST** argv = calloc(3, sizeof(AST*));
|
||||
argv[0] = $2;
|
||||
argv[1] = $3;
|
||||
argv[2] = $4;
|
||||
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
|
||||
3,
|
||||
argv,
|
||||
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
|
||||
));
|
||||
}
|
||||
| IF exp exp ELSE exp {
|
||||
AST** argv = calloc(3, sizeof(AST*));
|
||||
argv[0] = $2;
|
||||
argv[1] = $3;
|
||||
argv[2] = $5;
|
||||
|
||||
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
|
||||
3,
|
||||
argv,
|
||||
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
|
||||
));
|
||||
}
|
||||
|
||||
// Variable reference.
|
||||
| WORD {
|
||||
$$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1));
|
||||
@@ -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.
|
||||
| WORD GROUPS arg GROUPE exp {
|
||||
size_t parc = $3->ln;
|
||||
|
@@ -10,6 +10,7 @@ typedef enum {
|
||||
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.
|
||||
|
||||
@@ -51,6 +52,14 @@ ASTNumData* ast_num_data_init(double val);
|
||||
// Destroy an `ASTNumData`.
|
||||
void ast_num_data_destroy(ASTNumData* num);
|
||||
|
||||
// A boolean.
|
||||
typedef int ASTBoolData;
|
||||
|
||||
// Create a new `ASTBoolData`.
|
||||
ASTBoolData* ast_bool_data_init(int val);
|
||||
// Destroy an `ASTBoolData`.
|
||||
void ast_bool_data_destroy(ASTBoolData* bol);
|
||||
|
||||
// An exception.
|
||||
typedef struct ASTEXCDATA {
|
||||
const char* msg; // The exception message.
|
||||
|
@@ -11,6 +11,9 @@ 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);
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "ast.h"
|
||||
#include <stddef.h>
|
||||
|
||||
// Sum some nums.
|
||||
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.
|
||||
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);
|
||||
@@ -25,6 +35,9 @@ static struct builtin_data BUILTIN_FNS[] = {
|
||||
{ "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))
|
||||
|
||||
|
36
src/lexer.c
36
src/lexer.c
@@ -3,6 +3,7 @@
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/dstr.h"
|
||||
#include "include/lexer.h"
|
||||
@@ -72,7 +73,20 @@ double acc_float(int c) {
|
||||
|
||||
char* acc_word(int c) {
|
||||
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));
|
||||
inp++;
|
||||
}
|
||||
@@ -98,8 +112,22 @@ int yylex() {
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -115,8 +143,10 @@ int yylex() {
|
||||
case ';': return EXPSEP;
|
||||
case '{': return BLOCKS;
|
||||
case '}': return BLOCKE;
|
||||
case '=': return EQ;
|
||||
// case '=': return EQ;
|
||||
case '\\': return BACKSLASH;
|
||||
case '?': return IF;
|
||||
case ':': return ELSE;
|
||||
default: fprintf(stderr, "Unexpected character: %c\n", c);
|
||||
}
|
||||
|
||||
|
@@ -62,6 +62,13 @@ int main(int argc, char** argv) {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user