feat: REPL environment
This commit is contained in:
parent
9b7c2a7e99
commit
87f3a94155
8 changed files with 150 additions and 84 deletions
71
README.md
71
README.md
|
@ -17,47 +17,50 @@ I didn't really study how others languages works beforehand, I'm just guessing h
|
||||||
|
|
||||||
ToDo List:
|
ToDo List:
|
||||||
|
|
||||||
- [X] pow operator
|
- [X] feat: pow operator
|
||||||
- [X] binary operators
|
- [X] feat: binary operators
|
||||||
- [X] implement basic math functions
|
- [X] feat: basic math functions
|
||||||
- [X] implement random_int(min, max)
|
- [X] feat: random_int(min, max)
|
||||||
- [X] implement print_number(message)
|
- [X] feat: print_number(message)
|
||||||
- [X] add unit tests
|
- [X] base of unit tests
|
||||||
- [X] allow to set variables
|
- [X] feat: set variables
|
||||||
- [X] read line comments
|
- [X] feat: read line comments
|
||||||
- [X] add modulus operator '%'
|
- [X] feat: modulus operator '%'
|
||||||
- [X] add input_number() std function
|
- [X] feat: input_number() std function
|
||||||
- [X] add NULL type (dirty way to handle no returns and some errors)
|
- [X] feat: NULL type (dirty way to handle no returns and some errors)
|
||||||
- [X] add type() std function and others type checking functions
|
- [X] feat: type() std function and others type checking functions
|
||||||
- [X] add ceil() and floor() std functions
|
- [X] feat: ceil() and floor() std functions
|
||||||
- [X] base of the CLI
|
- [X] feat: base of the CLI
|
||||||
- [X] read a file
|
- [X] feat: process from a file
|
||||||
- [X] if statements
|
- [X] feat: if statements
|
||||||
- [X] while statements (with break and continue)
|
- [X] feat: while statements (with break and continue)
|
||||||
|
|
||||||
- [ ] add multiple characters operators
|
- [ ] feat(Evaluator): multiple characters operators
|
||||||
- [ ] add inclusive operators like '!=', '>=', '<='
|
- [ ] feat(Evaluator): inclusive operators like '!=', '>=', '<='
|
||||||
- [ ] add functions support
|
- [ ] feat: functions support
|
||||||
- [ ] add priority operators
|
- [ ] feat(Evaluator): priority operators
|
||||||
- [ ] add multiline expressions
|
- [ ] feat: multiline expressions
|
||||||
- [ ] add short hand if without 'end'
|
- [ ] feat: short hand if statement without 'end'
|
||||||
- [ ] add repeat statement
|
- [ ] feat: repeat statement
|
||||||
- [ ] add static string support (just for ui)
|
- [ ] feat: static string support (just for ui)
|
||||||
- [ ] add print_string function
|
- [ ] feat: print_string function
|
||||||
- [ ] add basic number list support
|
- [ ] feat: basic number list support
|
||||||
- [ ] add fully features strings support
|
- [ ] feat: fully features strings support
|
||||||
- [ ] add function to access to environment variables
|
- [ ] feat: function to access to environment variables
|
||||||
|
|
||||||
- [ ] add REPL environment
|
- [X] feat: REPL environment
|
||||||
|
- [ ] feat: add history to REPL
|
||||||
- [ ] evaluate expression from stdin
|
- [ ] evaluate expression from stdin
|
||||||
- [X] add config header file
|
- [X] feat: config header file
|
||||||
- [X] ability to modify keywords and customize the lang
|
- [X] ability to modify keywords and customize the lang
|
||||||
- [ ] add more config options
|
- [ ] more config options
|
||||||
|
|
||||||
- [ ] add [classic problem solving](https://rosettacode.org) with code examples
|
- [ ] add [classic problem solving](https://rosettacode.org) with code examples
|
||||||
- [ ] add Web Assembly support and publish a demo website
|
- [ ] feat: web Assembly support and publish a demo website
|
||||||
|
|
||||||
- [ ] refactor: use malloc for list
|
- [ ] refactor: add 'Literal' struct with type and data
|
||||||
|
- [ ] refactor(Evaluator): if branching around token identification
|
||||||
|
- [ ] refactor(List): use malloc
|
||||||
- [ ] refactor: remove inconsistency on how functions returns error codes
|
- [ ] refactor: remove inconsistency on how functions returns error codes
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
|
@ -13,6 +13,11 @@ int main () {
|
||||||
struct VariableStore* store = var_store_init();
|
struct VariableStore* store = var_store_init();
|
||||||
printf("%d", var_store_hash_name(store, "print_number index"));
|
printf("%d", var_store_hash_name(store, "print_number index"));
|
||||||
|
|
||||||
|
int i = -1;
|
||||||
|
if (i < 0) {
|
||||||
|
printf("lol jpp \n");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
//struct List l1;
|
//struct List l1;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#if SYNTAX_PRESET == PRESET_ENGLISH
|
#if SYNTAX_PRESET == PRESET_ENGLISH
|
||||||
#define SYNTAX_END "end"
|
#define SYNTAX_END "end"
|
||||||
|
#define SYNTAX_EXIT "exit"
|
||||||
|
|
||||||
#define SYNTAX_SET_START "set"
|
#define SYNTAX_SET_START "set"
|
||||||
#define SYNTAX_SET_STOP "to"
|
#define SYNTAX_SET_STOP "to"
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
|
|
||||||
#if SYNTAX_PRESET == PRESET_FRENCH
|
#if SYNTAX_PRESET == PRESET_FRENCH
|
||||||
#define SYNTAX_END "fin"
|
#define SYNTAX_END "fin"
|
||||||
|
#define SYNTAX_EXIT "sortir"
|
||||||
|
|
||||||
#define SYNTAX_SET_START "definir"
|
#define SYNTAX_SET_START "definir"
|
||||||
#define SYNTAX_SET_STOP "comme"
|
#define SYNTAX_SET_STOP "comme"
|
||||||
|
|
|
@ -532,6 +532,7 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: refactor to better branching
|
||||||
if (st != 0) {
|
if (st != 0) {
|
||||||
// identify token
|
// identify token
|
||||||
// first try a variable then a func name
|
// first try a variable then a func name
|
||||||
|
|
|
@ -309,34 +309,12 @@ int process_line(struct StateContainer* state, char* str)
|
||||||
{
|
{
|
||||||
if (LINE_PROCESSING_DEBUG_LEVEL >= 1) printf("\n ======= PROCESSING LINE '%s' =======\n", str);
|
if (LINE_PROCESSING_DEBUG_LEVEL >= 1) printf("\n ======= PROCESSING LINE '%s' =======\n", str);
|
||||||
if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("skipping: %d, blockStackLen: %d \n", state->skipping, state->blockStack->length);
|
if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("skipping: %d, blockStackLen: %d \n", state->skipping, state->blockStack->length);
|
||||||
// process
|
|
||||||
|
state->lastEvaluationType = TYPE_NULL;
|
||||||
|
state->lastEvaluationResult = 0;
|
||||||
|
|
||||||
int len = strlen(str);
|
int len = strlen(str);
|
||||||
// int startPos = 0;
|
|
||||||
// int stopPos = len-1;
|
|
||||||
// // modifiy the start and stop pos to trim spaces
|
|
||||||
// int startTrimOffset = 0;
|
|
||||||
// int stopTrimOffset = 0;
|
|
||||||
// for (int z = 0; z < len; z++) {
|
|
||||||
// if (inputStr[startPos+z] != ' ') {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// startTrimOffset++;
|
|
||||||
// }
|
|
||||||
// for (int z = 1; z < len; z++) {
|
|
||||||
// if (inputStr[stopPos-z] != ' ') {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// stopTrimOffset++;
|
|
||||||
// }
|
|
||||||
// startPos += startTrimOffset;
|
|
||||||
// stopPos -= stopTrimOffset;
|
|
||||||
|
|
||||||
// check for comment
|
|
||||||
// if (str[startPos] == '#') {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
//int stat = 0;
|
|
||||||
if (recognize_comment_statement(str)) {
|
if (recognize_comment_statement(str)) {
|
||||||
if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("Comment recognized \n");
|
if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("Comment recognized \n");
|
||||||
state->linePtr++;
|
state->linePtr++;
|
||||||
|
@ -448,6 +426,12 @@ int process_line(struct StateContainer* state, char* str)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recognize_word(str, SYNTAX_EXIT)) {
|
||||||
|
// exit the whole script
|
||||||
|
state->running = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (recognize_word(str, SYNTAX_CONTINUE)) {
|
if (recognize_word(str, SYNTAX_CONTINUE)) {
|
||||||
int lastLoopLine;
|
int lastLoopLine;
|
||||||
if (!int_stack_pop(state->loopStack, &lastLoopLine)) {
|
if (!int_stack_pop(state->loopStack, &lastLoopLine)) {
|
||||||
|
@ -468,6 +452,7 @@ int process_line(struct StateContainer* state, char* str)
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recognize_word(str, SYNTAX_BREAK)) {
|
if (recognize_word(str, SYNTAX_BREAK)) {
|
||||||
int lastLoopLine;
|
int lastLoopLine;
|
||||||
if (!int_stack_pop(state->loopStack, &lastLoopLine)) {
|
if (!int_stack_pop(state->loopStack, &lastLoopLine)) {
|
||||||
|
@ -527,7 +512,7 @@ int process_line(struct StateContainer* state, char* str)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we evaluate but we dont care about the result
|
// we evaluate the expression and we register it in the state in case of a REPL that want to get the result of the expression
|
||||||
int res;
|
int res;
|
||||||
byte resType;
|
byte resType;
|
||||||
int evalStat = evaluate(state, str, &res, &resType);
|
int evalStat = evaluate(state, str, &res, &resType);
|
||||||
|
@ -535,6 +520,9 @@ int process_line(struct StateContainer* state, char* str)
|
||||||
printf("Error: could not evaluate expression \"%s\".\n", str);
|
printf("Error: could not evaluate expression \"%s\".\n", str);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state->lastEvaluationType = resType;
|
||||||
|
state->lastEvaluationResult = res;
|
||||||
state->linePtr++;
|
state->linePtr++;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
105
src/main.c
105
src/main.c
|
@ -1,5 +1,8 @@
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "./config.h"
|
#include "./config.h"
|
||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
|
@ -12,6 +15,11 @@
|
||||||
#include "./var_store.h"
|
#include "./var_store.h"
|
||||||
#include "./line_processing.h"
|
#include "./line_processing.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
This is the binding of the interpreter to a CLI
|
||||||
|
The interpreter should be uncoupled from the rest of the code
|
||||||
|
*/
|
||||||
|
|
||||||
#define HELP_FLAG 1
|
#define HELP_FLAG 1
|
||||||
#define VERSION_FLAG 4
|
#define VERSION_FLAG 4
|
||||||
#define INTERACTIVE_FLAG 8
|
#define INTERACTIVE_FLAG 8
|
||||||
|
@ -34,8 +42,36 @@ int version_mode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int interactive_mode() {
|
int interactive_mode() {
|
||||||
printf("Interactive not implemented yet ¯\\_(ツ)_/¯ \n");
|
struct StateContainer* state = state_init();
|
||||||
return 1;
|
|
||||||
|
while (state->running) {
|
||||||
|
printf("? ");
|
||||||
|
|
||||||
|
char* line;
|
||||||
|
size_t len = 0;
|
||||||
|
size_t lineSize = 0;
|
||||||
|
|
||||||
|
lineSize = getline(&line, &len, stdin);
|
||||||
|
if (lineSize == (size_t) -1) {
|
||||||
|
// we received some kind of interrupt?
|
||||||
|
printf("\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char toEvaluate[lineSize+1];
|
||||||
|
// remove the new line at the end
|
||||||
|
str_extract((char*) toEvaluate, line, 0, lineSize-1);
|
||||||
|
|
||||||
|
int stat = process_line(state, toEvaluate);
|
||||||
|
if (!stat) {
|
||||||
|
printf("Processing that line failed.\n");
|
||||||
|
}
|
||||||
|
if (state->lastEvaluationType != TYPE_NULL) {
|
||||||
|
printf("%s\n", get_repr(state->lastEvaluationType, &state->lastEvaluationResult));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stdin_expression_mode() {
|
int stdin_expression_mode() {
|
||||||
|
@ -43,31 +79,56 @@ int stdin_expression_mode() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int file_mode(char* fileName) {
|
|
||||||
FILE* f = fopen(fileName, "rb");
|
|
||||||
|
|
||||||
if (!f) {
|
char* slurp_file(char* filePath, size_t* size) {
|
||||||
fprintf(stderr, "Cannot load file '%s' status: %d\n", fileName, (int) f);
|
char* buffer = NULL;
|
||||||
return 1;
|
|
||||||
}
|
FILE* f = fopen(filePath, "rb");
|
||||||
fseek(f, 0, SEEK_END);
|
if (f == NULL) goto error;
|
||||||
long length = ftell(f);
|
|
||||||
fseek(f, 0, SEEK_SET);
|
if (fseek(f, 0, SEEK_END) < 0) goto error;
|
||||||
|
|
||||||
|
long m = ftell(f);
|
||||||
|
if (m < 0) goto error;
|
||||||
|
|
||||||
|
buffer = malloc(sizeof(char) * m);
|
||||||
|
if (buffer == NULL) goto error;
|
||||||
|
|
||||||
|
if (fseek(f, 0, SEEK_SET) < 0) goto error;
|
||||||
|
|
||||||
|
size_t n = fread(buffer, 1, m, f);
|
||||||
|
assert(n == (size_t) m);
|
||||||
|
|
||||||
|
if (ferror(f)) goto error;
|
||||||
|
|
||||||
|
if (size) *size = n;
|
||||||
|
|
||||||
char* buff = (char*) malloc(length);
|
|
||||||
if (!buff) {
|
|
||||||
fprintf(stderr, "Could not allocate buffer to process file content\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (buff) {
|
|
||||||
fread(buff, 1, length, f);
|
|
||||||
}
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (f) fclose(f);
|
||||||
|
|
||||||
|
if (buffer) free(buffer);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_mode(char* fileName) {
|
||||||
|
// printf("Open file mode...\n");
|
||||||
|
size_t size;
|
||||||
|
char* buff = slurp_file(fileName, &size);
|
||||||
|
|
||||||
|
if (!buff) {
|
||||||
|
fprintf(stderr, "Error: Cannot load file '%s' errno: %d '%s'.\n", fileName, errno, strerror(errno));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
struct StateContainer* state = state_init();
|
struct StateContainer* state = state_init();
|
||||||
process_script(state, buff);
|
process_script(state, buff);
|
||||||
|
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main (int argc, char** argv) {
|
int main (int argc, char** argv) {
|
||||||
|
@ -97,7 +158,7 @@ int main (int argc, char** argv) {
|
||||||
// no flag match
|
// no flag match
|
||||||
if (str_starts_with("--", argv[i])) {
|
if (str_starts_with("--", argv[i])) {
|
||||||
fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
|
fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
|
||||||
return 1;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
nonFlagArgumentCount++;
|
nonFlagArgumentCount++;
|
||||||
nonFlagArgumentPos = i;
|
nonFlagArgumentPos = i;
|
||||||
|
@ -110,5 +171,5 @@ int main (int argc, char** argv) {
|
||||||
if (flags & STDIN_EXP_FLAG) return stdin_expression_mode();
|
if (flags & STDIN_EXP_FLAG) return stdin_expression_mode();
|
||||||
|
|
||||||
help_mode(cmdName);
|
help_mode(cmdName);
|
||||||
return 1;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,5 +18,8 @@ struct StateContainer* state_init()
|
||||||
|
|
||||||
ptr->running = 1;
|
ptr->running = 1;
|
||||||
|
|
||||||
|
ptr->lastEvaluationType = TYPE_NULL;
|
||||||
|
ptr->lastEvaluationResult = 0;
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ struct StateContainer {
|
||||||
|
|
||||||
// struct FunctionStore* funcStore;
|
// struct FunctionStore* funcStore;
|
||||||
// struct StringStore* strStore ??
|
// struct StringStore* strStore ??
|
||||||
|
|
||||||
|
int lastEvaluationResult;
|
||||||
|
byte lastEvaluationType;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StateContainer* state_init();
|
struct StateContainer* state_init();
|
||||||
|
|
Loading…
Reference in a new issue