commit 520534baedc529ed128876adbf70be26eb0de73e
parent c12ed3e0974d52dd93162ce5ba0538a292840d1d
Author: Demonstrandum <moi@knutsen.co>
Date: Wed, 24 Jun 2020 22:14:57 +0100
Added implicit casting, local variable searching, and more operators.
Diffstat:
10 files changed, 216 insertions(+), 52 deletions(-)
diff --git a/src/builtin.c b/src/builtin.c
@@ -75,29 +75,41 @@ NumberNode *upcast_pair(NumberNode lhs, NumberNode rhs)
return pair;
}
-NumberNode *num_add(NumberNode lhs, NumberNode rhs)
-{
- NumberNode *upcasted = upcast_pair(lhs, rhs);
- if (upcasted == NULL)
- return NULL;
+#define BINARY_FUNCTION(NAME, OP) \
+NumberNode *num_ ## NAME (NumberNode lhs, NumberNode rhs) \
+{ \
+ NumberNode *upcasted = upcast_pair(lhs, rhs); \
+ if (upcasted == NULL) \
+ return NULL; \
+ \
+ NumberNode *result = upcasted + 0; \
+ \
+ switch (result->type) { \
+ case FLOAT: \
+ result->value.f = upcasted[0].value.f OP upcasted[1].value.f; \
+ break; \
+ case INT: \
+ result->value.i = upcasted[0].value.i OP upcasted[1].value.i; \
+ break; \
+ default: { \
+ ERROR_TYPE = EXECUTION_ERROR; \
+ strcpy(ERROR_MSG, "Unsupported number type."); \
+ return NULL; \
+ } \
+ } \
+ result = realloc(result, sizeof(NumberNode)); \
+ return result; \
+}
- NumberNode *result = upcasted + 0;
+BINARY_FUNCTION(add, +) // `num_add` function.
+BINARY_FUNCTION(sub, -) // `num_sub` function.
+BINARY_FUNCTION(mul, *) // `num_mul` function.
- switch (result->type) {
- case FLOAT:
- result->value.f = upcasted[0].value.f + upcasted[1].value.f;
- break;
- case INT:
- result->value.i = upcasted[0].value.i + upcasted[1].value.i;
- break;
- default: {
- ERROR_TYPE = EXECUTION_ERROR;
- strcpy(ERROR_MSG, "Unsupported number type.");
- return NULL;
- }
- }
- result = realloc(result, sizeof(NumberNode));
+// `num_div` function is different, it always gives a float.
+NumberNode *num_div(NumberNode lhs, NumberNode rhs)
+{
+ NumberNode *result = malloc(sizeof(NumberNode));
+ result->type = FLOAT;
+ result->value.f = num_to_float(lhs).value.f / num_to_float(rhs).value.f;
return result;
}
-
-
diff --git a/src/builtin.h b/src/builtin.h
@@ -7,5 +7,9 @@
NumberNode num_to_float(NumberNode);
NumberNode num_to_int(NumberNode);
NumberNode *upcast_pair(NumberNode, NumberNode);
+
NumberNode *num_add(NumberNode, NumberNode);
+NumberNode *num_sub(NumberNode, NumberNode);
+NumberNode *num_mul(NumberNode, NumberNode);
+NumberNode *num_div(NumberNode, NumberNode);
diff --git a/src/displays.c b/src/displays.c
@@ -8,6 +8,22 @@
#include "execute.h"
#include "displays.h"
+char *display_numbernode(NumberNode num)
+{
+ char *str = malloc(sizeof(char) * 128); // Hope that's enough.
+ switch (num.type) {
+ case INT:
+ sprintf(str, "%ld", num.value.i);
+ break;
+ case FLOAT:
+ sprintf(str, "%.15LG", num.value.f);
+ break;
+ default:
+ strcpy(str, "undisplayable-number-type");
+ }
+ return str;
+}
+
char *display_parampos(ParamPos pos)
{
switch (pos) {
@@ -43,10 +59,7 @@ char *display_parsetree(const ParseNode *tree)
return tree->node.ident.value;
}
case NUMBER_NODE: {
- // TODO: Check the number type (int, float, etc.).
- char *number = malloc(sizeof(char) * 64); // Guess?
- sprintf(number, "%ld", tree->node.number.value.i);
- return number;
+ return display_numbernode(tree->node.number);
}
case UNARY_NODE: {
UnaryNode unary = tree->node.unary;
@@ -78,14 +91,13 @@ char *display_parsetree(const ParseNode *tree)
return binary_str;
}
default:
- return "[Unknown Parse Node]";
+ return "[unknown-parse-node]";
}
}
char *display_datavalue(const DataValue *data)
{
- // Safe bet.
- char *string = malloc(sizeof(char) * 512);
+ char *string;
if (data == NULL)
return "internal-null-pointer";
@@ -95,11 +107,20 @@ char *display_datavalue(const DataValue *data)
if (data->value == NULL)
return "number-with-null-value";
NumberNode *num = data->value;
- // TODO: Handle more than just INT type.
- sprintf(string, "%ld", num->value.i);
+ return display_numbernode(*num);
+ }
+ case T_STRING: {
+ char *inside = data->value;
+ usize len = strlen(inside);
+ string = malloc(len + 2);
+ strcpy(string + 1, inside);
+ string[0] = '"';
+ string[len + 1] = '"';
+ string[len + 2] = '\0';
break;
}
default:
+ string = malloc(sizeof(char) * 128); // Safe bet.
sprintf(string, "<%s at 0x%p>",
display_datatype(data->type),
data->value);
diff --git a/src/displays.h b/src/displays.h
@@ -1,6 +1,7 @@
#include "parse.h"
#include "execute.h"
+char *display_numbernode(NumberNode _);
char *display_parampos(ParamPos _);
char *display_datatype(DataType _);
char *display_parsetree(const ParseNode *);
diff --git a/src/execute.c b/src/execute.c
@@ -4,6 +4,21 @@
#include "builtin.h"
#include "displays.h"
+#define BINARY_OPERATION(OPERATION) do { \
+ NumberNode *l_num = type_check("+", LHS, T_NUMBER, lhs); \
+ NumberNode *r_num = type_check("+", RHS, T_NUMBER, rhs); \
+ if (l_num == NULL || r_num == NULL) \
+ return NULL; \
+ data->type = T_NUMBER; \
+ data->value = num_ ##OPERATION (*l_num, *r_num); \
+} while (0);
+
+void free_datavalue(DataValue *data)
+{
+ free(data->value);
+ free(data);
+}
+
/// Takes in an execution context (ctx) and a
/// statement as produced by the parser (stmt).
/// Returns what it evaluates to.
@@ -11,6 +26,39 @@ DataValue *execute(Context *ctx, const ParseNode *stmt)
{
DataValue *data = malloc(sizeof(DataValue));
switch (stmt->type) {
+ case IDENT_NODE: {
+ // Resolve variables by searching through local
+ // variables. If that fails, search through the
+ // superior sopes varaibles, repeat until there
+ // are no more superior scopes, if the local is
+ // found yield its corresponding value, or else
+ // throw an execution error.
+ char *ident_name = stmt->node.ident.value;
+
+ free(data);
+ data = NULL;
+
+ Context *current_ctx = ctx;
+ while (current_ctx != NULL) {
+ for (usize i = 0; i < current_ctx->locals_count; ++i) {
+ Local *local = ¤t_ctx->locals[i];
+ if (strcmp(local->name, ident_name) == 0) {
+ data = &local->value;
+ goto finished_search;
+ }
+ }
+ current_ctx = current_ctx->superior;
+ }
+
+finished_search:
+ if (data == NULL) {
+ ERROR_TYPE = EXECUTION_ERROR;
+ sprintf(ERROR_MSG, "Could not find variable `%s'\n"
+ " in any local or superior scope.", ident_name);
+ return NULL;
+ }
+ break;
+ }
case NUMBER_NODE: {
data->type = T_NUMBER;
data->value = malloc(sizeof(NumberNode));
@@ -30,15 +78,13 @@ DataValue *execute(Context *ctx, const ParseNode *stmt)
DataValue *rhs = execute(ctx, stmt->node.binary.right);
if (strcmp(op, "+") == 0) {
- NumberNode *l_num = type_check("+", LHS, T_NUMBER, lhs);
- NumberNode *r_num = type_check("+", RHS, T_NUMBER, rhs);
- if (l_num == NULL || r_num == NULL)
- return NULL;
- // Finally, the addition is performed.
- data->type = T_NUMBER;
- data->value = num_add(*l_num, *r_num);
+ BINARY_OPERATION(add);
} else if (strcmp(op, "-") == 0) {
-
+ BINARY_OPERATION(sub);
+ } else if (strcmp(op, "*") == 0) {
+ BINARY_OPERATION(mul);
+ } else if (strcmp(op, "/") == 0) {
+ BINARY_OPERATION(div);
} else {
ERROR_TYPE = EXECUTION_ERROR;
sprintf(ERROR_MSG, "Do not know how to evaluate"
@@ -75,10 +121,13 @@ void *type_check(char *function_name, ParamPos pos,
ERROR_TYPE = TYPE_ERROR;
sprintf(ERROR_MSG, "Wrong type for %s of `%s' operation,\n"
- " needed type of `%s'.",
+ " expected type of `%s', got type of `%s'.",
display_parampos(pos),
function_name,
- display_datatype(type));
+ display_datatype(type),
+ value == NULL
+ ? "null-pointer"
+ : display_datatype(value->type));
return NULL;
}
diff --git a/src/execute.h b/src/execute.h
@@ -34,6 +34,7 @@ typedef enum {
ARG, LHS, RHS
} ParamPos;
+void free_datavalue(DataValue *);
void *type_check(char *, ParamPos, DataType, DataValue *);
DataValue *execute(Context *, const ParseNode *);
Local *make_local(char *, DataType, void *);
diff --git a/src/main.c b/src/main.c
@@ -88,7 +88,7 @@ int main(int argc, char **argv)
printf("\033[%luC\033[1A",
strlen(PROMPT)
+ strlen(response));
- printf(" ≡ %s\n", display_parsetree(tree));
+ printf("\033[2m ≡ %s\n\033[0m", display_parsetree(tree));
DataValue *result = execute(ctx, tree);
@@ -99,7 +99,6 @@ int main(int argc, char **argv)
printf("#=> %s\n", display_datavalue(result));
- //free_datavalue(result);
free_parsenode(tree);
} while (true);
diff --git a/src/parse.c b/src/parse.c
@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <math.h>
#include "error.h"
#include "displays.h"
@@ -45,7 +46,7 @@ Token *new_token(TokenType type, const char *value)
return t;
}
-TokenType char_token_type(char c, TokenType last_token_type)
+TokenType char_token_type(char c, char last_char, TokenType last_token_type)
{
if (c <= '9' && c >= '0') {
if (last_token_type == TT_IDENTIFIER)
@@ -61,9 +62,13 @@ TokenType char_token_type(char c, TokenType last_token_type)
&& (last_token_type == TT_OPERATOR
|| last_token_type == TT_NONE))
return TT_NUMERIC;
- if ((c == '.' || c == 'E')
+ if ((c == '.' || c == 'E') // Scientific notation.
&& last_token_type == TT_NUMERIC)
return TT_NUMERIC;
+ if ((c == '+' || c == '-') && last_char == 'E')
+ return TT_NUMERIC;
+ if ((c == 'x' || c == 'o') && last_char == '0')
+ return TT_NUMERIC;
if (c == '(')
return TT_LPAREN;
if (c == ')')
@@ -93,14 +98,14 @@ Token *lex(char **source)
if (**source == '\0')
return NULL; // No more tokens.
- TokenType tt = char_token_type(**source, TT_NONE);
+ TokenType tt = char_token_type(**source, ' ', TT_NONE);
// Skip over TT_NONE tokens (spaces, tabs, etc.).
while (tt == TT_NONE) {
(*source)++;
if (**source == '\0')
return NULL;
- tt = char_token_type(**source, tt);
+ tt = char_token_type(**source, *(*source - 1), tt);
}
// First of all, check if it matches an operator.
@@ -139,7 +144,10 @@ Token *lex(char **source)
} else {
while (tt == previous_tt) {
span++;
- previous_tt = char_token_type(*(*source + span), previous_tt);
+ previous_tt = char_token_type(
+ *(*source + span),
+ *(*source + span - (span == 0 ? 0 : 1)),
+ previous_tt);
}
}
@@ -170,12 +178,64 @@ void node_into_ident(const char *str, ParseNode *node)
node->node.ident = *ident;
}
-// TODO: Support more than just INTs.
NumberNode *parse_number(const char *str)
{
NumberNode *number = malloc(sizeof(NumberNode));
- number->type = INT;
- number->value.i = atoi(str);
+
+ char *exponent_ptr = strstr(str, "E");
+ char *neg_exponent_ptr = strstr(str, "E-");
+ char *decimal_point_ptr = strstr(str, ".");
+
+ // Sanity.
+ if (exponent_ptr != NULL) {
+ // No trailing 'E'.
+ if (*(exponent_ptr + 1) == '\0')
+ return NULL;
+ // No trailing 'E+' or 'E-'.
+ if ((*(exponent_ptr + 1) == '+'
+ || *(exponent_ptr + 1) == '-')
+ && *(exponent_ptr + 2) == '\0')
+ return NULL;
+ // No repreated 'E' and no decimal point ('.') after 'E'.
+ if (strstr(exponent_ptr + 1, "E") != NULL
+ || strstr(exponent_ptr + 1, ".") != NULL)
+ return NULL;
+ }
+ if (decimal_point_ptr != NULL) {
+ // No trailing decimal point ('.').
+ if (*(decimal_point_ptr + 1) == '\0')
+ return NULL;
+ // No decimal point ('.') after first decimal point.
+ if (strstr(decimal_point_ptr + 1, ".") != NULL)
+ return NULL;
+ }
+
+ // No negative exponent and no decimal point, means
+ // the number literal is certainly an integer.
+ if (neg_exponent_ptr == NULL && decimal_point_ptr == NULL) {
+ number->type = INT;
+ ssize significand = strtoll(str, NULL, 0);
+ if (exponent_ptr == NULL) { // No power-term.
+ number->value.i = significand;
+ return number;
+ }
+
+ usize exponent = strtoull(exponent_ptr + 1, NULL, 10);
+ ssize power_term = ipow(10, exponent);
+ if (power_term >= 0 && exponent <= 18) {
+ // Probably didn't overflow.
+ number->value.i = significand * power_term;
+ return number;
+ }
+ // Fallback to float.
+ }
+
+ number->type = FLOAT;
+ fsize power_term = 1;
+
+ fsize significand = strtold(str, NULL);
+ number->value.f = significand * power_term;
+
return number;
}
@@ -188,7 +248,7 @@ ParseNode *parse_prefix(const Token *token, char **rest)
NumberNode *num = parse_number(token->value);
if (num == NULL) {
ERROR_TYPE = SYNTAX_ERROR;
- sprintf(ERROR_MSG, "Malformatted number literal (`%s').",
+ sprintf(ERROR_MSG, "Malformed number literal (`%s').",
token->value);
return NULL;
}
diff --git a/src/prelude.c b/src/prelude.c
@@ -1,5 +1,20 @@
#include "prelude.h"
+ssize ipow(ssize base, usize exp)
+{
+ ssize result = 1;
+ do {
+ if (exp & 1)
+ result *= base;
+ exp >>= 1;
+ if (!exp)
+ break;
+ base *= base;
+ } while (true);
+
+ return result;
+}
+
char *trim(char *str)
{
char *p = strdup(str);
diff --git a/src/prelude.h b/src/prelude.h
@@ -29,6 +29,8 @@ typedef float f32;
typedef double f64;
typedef long double fsize;
+ssize ipow(ssize, usize);
+
char *trim(char *);
char *downcase(char *);