aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--src/mds-kbdc/builtin-functions.c55
-rw-r--r--src/mds-kbdc/builtin-functions.h4
-rw-r--r--src/mds-kbdc/compile-layout.c73
-rw-r--r--src/mds-kbdc/include-stack.c4
-rw-r--r--src/mds-kbdc/include-stack.h2
-rw-r--r--src/mds-kbdc/tree.c16
-rw-r--r--src/mds-kbdc/tree.h4
-rw-r--r--src/mds-kbdc/variables.c197
-rw-r--r--src/mds-kbdc/variables.h80
10 files changed, 393 insertions, 44 deletions
diff --git a/Makefile b/Makefile
index 805d8c2..ee56ac2 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ OBJ_mds-registry_ = mds-registry util globals reexec registry signals \
OBJ_mds-kbdc_ = mds-kbdc globals raw-data builtin-functions string \
tree make-tree parse-error simplify-tree parsed \
process-includes validate-tree eliminate-dead-code \
- paths include-stack compile-layout
+ paths include-stack compile-layout variables
OBJ_mds-server = $(foreach O,$(OBJ_mds-server_),obj/mds-server/$(O).o)
OBJ_mds-registry = $(foreach O,$(OBJ_mds-registry_),obj/mds-registry/$(O).o)
diff --git a/src/mds-kbdc/builtin-functions.c b/src/mds-kbdc/builtin-functions.c
index cfac8ac..0cb7aa6 100644
--- a/src/mds-kbdc/builtin-functions.c
+++ b/src/mds-kbdc/builtin-functions.c
@@ -17,6 +17,8 @@
*/
#include "builtin-functions.h"
+#include "variables.h"
+
#include <stdlib.h>
#include <string.h>
@@ -258,8 +260,45 @@ static char32_t* builtin_function_less_2(const char32_t** restrict args)
}
-/* static char32_t* builtin_function_set_3(const char32_t** restrict args); (variable index value) */
-/* static char32_t* builtin_function_get_2(const char32_t** restrict args); (variable index) */
+/**
+ * Definition of the built-in function get/2
+ *
+ * @param args The arguments passed to the function
+ * @return The return value of the function, `NULL` on error
+ */
+static char32_t* builtin_function_get_2(const char32_t** restrict args)
+{
+ const char32_t* restrict a = *args++;
+ const char32_t* restrict b = *args++;
+ size_t n = (size_t)*b;
+ mds_kbdc_tree_t* value = variables_get((size_t)*a);
+ while (n--)
+ value = value->next;
+ return string_dup(value->compiled_string.string);
+}
+
+
+/**
+ * Definition of the built-in function set/3
+ *
+ * @param args The arguments passed to the function
+ * @return The return value of the function, `NULL` on error
+ */
+static char32_t* builtin_function_set_3(const char32_t** restrict args)
+{
+ const char32_t* restrict a = *args++;
+ const char32_t* restrict b = *args++;
+ const char32_t* restrict c = *args++;
+ size_t n = (size_t)*b;
+ mds_kbdc_tree_t* value = variables_get((size_t)*a);
+ while (n--)
+ value = value->next;
+ free(value->compiled_string.string);
+ value->compiled_string.string = string_dup(c);
+ if (value->compiled_string.string == NULL)
+ return NULL;
+ return string_dup(c);
+}
#undef define_1
@@ -298,6 +337,10 @@ int builtin_function_defined(const char* restrict name, size_t arg_count)
/**
* Invoke a builtin function
*
+ * The function will abort if an non-builtin function is addressed
+ *
+ * Before invoking set/3 or get/2, please check the arguments
+ *
* @param name The name of the function
* @param arg_count The number of arguments to pass to the function
* @param args The arguments to pass
@@ -307,14 +350,14 @@ char32_t* builtin_function_invoke(const char* restrict name, size_t arg_count, c
{
if (arg_count == 3)
if (!strcmp(name, "set"))
- return NULL; /* TODO builtin_function_set_3(args) */
+ return builtin_function_set_3(args);
if (arg_count == 1)
if (!strcmp(name, "not"))
return builtin_function_not_1(args);
if (arg_count != 2)
- return NULL;
+ abort();
if (!strcmp(name, "add")) return builtin_function_add_2(args);
if (!strcmp(name, "sub")) return builtin_function_sub_2(args);
@@ -329,8 +372,8 @@ char32_t* builtin_function_invoke(const char* restrict name, size_t arg_count, c
if (!strcmp(name, "equals")) return builtin_function_equals_2(args);
if (!strcmp(name, "greater")) return builtin_function_greater_2(args);
if (!strcmp(name, "less")) return builtin_function_less_2(args);
- if (!strcmp(name, "get")) return NULL; /* TODO builtin_function_get_2(args) */
+ if (!strcmp(name, "get")) return builtin_function_get_2(args);
- return NULL;
+ abort();
}
diff --git a/src/mds-kbdc/builtin-functions.h b/src/mds-kbdc/builtin-functions.h
index ebe3935..5c78f0e 100644
--- a/src/mds-kbdc/builtin-functions.h
+++ b/src/mds-kbdc/builtin-functions.h
@@ -38,6 +38,10 @@ int builtin_function_defined(const char* restrict name, size_t arg_count) __attr
/**
* Invoke a builtin function
*
+ * The function will abort if an non-builtin function is addressed
+ *
+ * Before invoking set/3 or get/2, please check the arguments
+ *
* @param name The name of the function
* @param arg_count The number of arguments to pass to the function
* @param args The arguments to pass
diff --git a/src/mds-kbdc/compile-layout.c b/src/mds-kbdc/compile-layout.c
index cdf8df6..3239482 100644
--- a/src/mds-kbdc/compile-layout.c
+++ b/src/mds-kbdc/compile-layout.c
@@ -21,6 +21,7 @@
#include "include-stack.h"
#include "builtin-functions.h"
#include "string.h"
+#include "variables.h"
#include <stdlib.h>
#include <errno.h>
@@ -144,27 +145,53 @@ static size_t parse_variable(mds_kbdc_tree_t* restrict tree, const char* restric
return 0; /* TODO */
}
+
+/**
+ * Assign a value to a variable, and define or shadow it in the process
+ *
+ * @param variable The variable index
+ * @param string The variable's new value, must be `NULL` iff `value != NULL`
+ * @param value The variable's new value, must be `NULL` iff `string != NULL`
+ * @param statement The statement where the variable is assigned, may be `NULL`
+ * @param lineoff The offset of the line for where the string selecting the variable begins
+ * @param possibile_shadow_attempt Whether `statement` is of a type that does not shadow variables,
+ * but could easily be mistaked for one that does
+ * @return Zero on success, -1 on error
+ */
static int let(size_t variable, const char32_t* restrict string, const mds_kbdc_tree_t* restrict value,
mds_kbdc_tree_t* restrict statement, size_t lineoff, int possibile_shadow_attempt)
{
- (void) variable;
- (void) string;
- (void) value;
- (void) statement;
- (void) lineoff;
- (void) possibile_shadow_attempt;
- return 0; /* TODO */
-}
-
-static int push_stack(void)
-{
- return 0; /* TODO */
+ mds_kbdc_tree_t* tree = NULL;
+ int saved_errno;
+
+ /* Warn if this is a possible shadow attempt. */
+ if (possibile_shadow_attempt && variables_let_will_override(variable) &&
+ statement && (statement->processed != PROCESS_LEVEL))
+ {
+ statement->processed = PROCESS_LEVEL;
+ NEW_ERROR(statement, WARNING, "will not shadow existing definition");
+ error->start = lineoff;
+ error->end = lineoff + (size_t)snprintf(NULL, 0, "\\%zu", variable);
+ }
+
+ /* Duplicate value. */
+ if (value)
+ fail_if ((tree = mds_kbdc_tree_dup(value), tree == NULL));
+ if (value == NULL)
+ {
+ fail_if ((tree = mds_kbdc_tree_create(C(COMPILED_STRING)), tree == NULL));
+ tree->compiled_string.string = string_dup(string);
+ fail_if (tree->compiled_string.string == NULL);
+ }
+
+ /* Assign variable. */
+ fail_if (variables_let(variable, tree));
+ return 0;
+ FAIL_BEGIN;
+ mds_kbdc_tree_free(tree);
+ FAIL_END;
}
-static int pop_stack(void)
-{
- return 0; /* TODO */
-}
static int set_macro(const mds_kbdc_tree_macro_t* restrict macro,
mds_kbdc_include_stack_t* macro_include_stack)
@@ -1072,7 +1099,7 @@ static int compile_let(mds_kbdc_tree_let_t* restrict tree)
/* Set the value of the variable. */
fail_if (let(variable, NULL, value, NULL, 0, 0));
- free(value);
+ mds_kbdc_tree_free(value);
return 0;
FAIL_BEGIN;
free(value);
@@ -1339,7 +1366,7 @@ static int compile_macro_call(mds_kbdc_tree_macro_call_t* restrict tree)
/* Push call stack and set parameters. */
- fail_if (push_stack());
+ variables_stack_push();
for (arg_ = arg; arg_; arg_ = arg_->next)
fail_if (let(variable, NULL, arg_, NULL, 0, 0));
@@ -1355,7 +1382,7 @@ static int compile_macro_call(mds_kbdc_tree_macro_call_t* restrict tree)
mds_kbdc_include_stack_free(our_include_stack), our_include_stack = NULL;
/* Pop call stack. */
- fail_if (pop_stack());
+ variables_stack_pop();
done:
@@ -1446,10 +1473,14 @@ static int compile_subtree(mds_kbdc_tree_t* restrict tree)
*/
int compile_layout(mds_kbdc_parsed_t* restrict result_)
{
- int r;
+ int r, saved_errno;
mds_kbdc_include_stack_begin(result = result_);
r = compile_subtree(result_->tree);
- return mds_kbdc_include_stack_end(), r;
+ saved_errno = errno;
+ mds_kbdc_include_stack_end();
+ variables_terminate();
+ errno = saved_errno;
+ return r;
}
diff --git a/src/mds-kbdc/include-stack.c b/src/mds-kbdc/include-stack.c
index dd2bc98..d86374e 100644
--- a/src/mds-kbdc/include-stack.c
+++ b/src/mds-kbdc/include-stack.c
@@ -109,18 +109,14 @@ void mds_kbdc_include_stack_begin(mds_kbdc_parsed_t* restrict result_)
/**
* Mark the root of the tree as no longer being visited,
* and release clean up after the use of this module
- *
- * This function is guaranteed not to modify `errno`
*/
void mds_kbdc_include_stack_end(void)
{
- int saved_errno = errno;
latest_save = NULL;
result->pathname = original_pathname;
result->source_code = original_source_code;
free(includes), includes = NULL;
includes_size = includes_ptr = 0;
- errno = saved_errno;
}
diff --git a/src/mds-kbdc/include-stack.h b/src/mds-kbdc/include-stack.h
index 49a6248..5d51a61 100644
--- a/src/mds-kbdc/include-stack.h
+++ b/src/mds-kbdc/include-stack.h
@@ -112,8 +112,6 @@ void mds_kbdc_include_stack_begin(mds_kbdc_parsed_t* restrict result);
/**
* Mark the root of the tree as no longer being visited,
* and release clean up after the use of this module
- *
- * This function is guaranteed not to modify `errno`
*/
void mds_kbdc_include_stack_end(void);
diff --git a/src/mds-kbdc/tree.c b/src/mds-kbdc/tree.c
index 7493ceb..e5af0b9 100644
--- a/src/mds-kbdc/tree.c
+++ b/src/mds-kbdc/tree.c
@@ -277,9 +277,9 @@ void mds_kbdc_tree_free(mds_kbdc_tree_t* restrict this)
*
* @param LOWERCASE:identifer The name of subtype
*/
-#define NODE(LOWERCASE) \
- mds_kbdc_tree_##LOWERCASE##_t* n = (mds_kbdc_tree_##LOWERCASE##_t*)node; \
- mds_kbdc_tree_##LOWERCASE##_t* t = (mds_kbdc_tree_##LOWERCASE##_t*)this
+#define NODE(LOWERCASE) \
+ mds_kbdc_tree_##LOWERCASE##_t* n = (mds_kbdc_tree_##LOWERCASE##_t*)node; \
+ const mds_kbdc_tree_##LOWERCASE##_t* t = (const mds_kbdc_tree_##LOWERCASE##_t*)this
/**
@@ -288,7 +288,7 @@ void mds_kbdc_tree_free(mds_kbdc_tree_t* restrict this)
* @param this The tree node
* @return A duplicate of `this`, `NULL` on error
*/
-mds_kbdc_tree_t* mds_kbdc_tree_dup(mds_kbdc_tree_t* restrict this)
+mds_kbdc_tree_t* mds_kbdc_tree_dup(const mds_kbdc_tree_t* restrict this)
{
mds_kbdc_tree_t* node = NULL;
int saved_errno;
@@ -357,8 +357,8 @@ mds_kbdc_tree_t* mds_kbdc_tree_dup(mds_kbdc_tree_t* restrict this)
* @param NOTATION:const char* The notation for the subtype
*/
#define NODE(LOWERCASE, NOTATION) \
- mds_kbdc_tree_##LOWERCASE##_t* node; \
- node = (mds_kbdc_tree_##LOWERCASE##_t*)this; \
+ const mds_kbdc_tree_##LOWERCASE##_t* node; \
+ node = (const mds_kbdc_tree_##LOWERCASE##_t*)this; \
fprintf(output, "%*.s(\033[01m%s\033[00m", indent, "", NOTATION); \
fprintf(output, " \033[36m(@ %zu %zu-%zu)\033[00m", \
node->loc_line + 1, node->loc_start, node->loc_end)
@@ -502,7 +502,7 @@ mds_kbdc_tree_t* mds_kbdc_tree_dup(mds_kbdc_tree_t* restrict this)
* @param output The output file
* @param indent The indent
*/
-static void mds_kbdc_tree_print_indented(mds_kbdc_tree_t* restrict this, FILE* output, int indent)
+static void mds_kbdc_tree_print_indented(const mds_kbdc_tree_t* restrict this, FILE* output, int indent)
{
again:
if (this == NULL)
@@ -605,7 +605,7 @@ static void mds_kbdc_tree_print_indented(mds_kbdc_tree_t* restrict this, FILE* o
* @param this The tree node
* @param output The output file
*/
-void mds_kbdc_tree_print(mds_kbdc_tree_t* restrict this, FILE* output)
+void mds_kbdc_tree_print(const mds_kbdc_tree_t* restrict this, FILE* output)
{
mds_kbdc_tree_print_indented(this, output, 0);
}
diff --git a/src/mds-kbdc/tree.h b/src/mds-kbdc/tree.h
index 1c5d8d3..885839a 100644
--- a/src/mds-kbdc/tree.h
+++ b/src/mds-kbdc/tree.h
@@ -831,7 +831,7 @@ void mds_kbdc_tree_free(mds_kbdc_tree_t* restrict this);
* @param this The tree node
* @return A duplicate of `this`, `NULL` on error
*/
-mds_kbdc_tree_t* mds_kbdc_tree_dup(mds_kbdc_tree_t* restrict this);
+mds_kbdc_tree_t* mds_kbdc_tree_dup(const mds_kbdc_tree_t* restrict this);
/**
@@ -840,7 +840,7 @@ mds_kbdc_tree_t* mds_kbdc_tree_dup(mds_kbdc_tree_t* restrict this);
* @param this The tree node
* @param output The output file
*/
-void mds_kbdc_tree_print(mds_kbdc_tree_t* restrict this, FILE* output);
+void mds_kbdc_tree_print(const mds_kbdc_tree_t* restrict this, FILE* output);
diff --git a/src/mds-kbdc/variables.c b/src/mds-kbdc/variables.c
new file mode 100644
index 0000000..b53569a
--- /dev/null
+++ b/src/mds-kbdc/variables.c
@@ -0,0 +1,197 @@
+/**
+ * mds — A micro-display server
+ * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "variables.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+
+/**
+ * The state of a variable
+ */
+typedef struct variable
+{
+ /**
+ * The current value of the variable
+ */
+ mds_kbdc_tree_t* value;
+
+ /**
+ * The previous version of variable,
+ * before it was shadowed
+ */
+ struct variable* restrict previous;
+
+ /**
+ * The original scope the current shadow
+ * of the variable was created in
+ */
+ size_t scope;
+
+} variable_t;
+
+
+
+/**
+ * Map (by index) of defined variables
+ */
+static variable_t** restrict variables = NULL;
+
+/**
+ * The size of `variables`
+ */
+static size_t variable_count = 0;
+
+/**
+ * The current scope, the number of
+ * times the variable-stakc has been
+ * pushed without being popped
+ */
+static size_t current_scope = 0;
+
+
+
+/**
+ * Destroy the variable storage
+ */
+void variables_terminate(void)
+{
+ size_t i;
+ variable_t* old;
+ for (i = 0; i < variable_count; i++)
+ while (variables[i])
+ {
+ old = variables[i];
+ variables[i] = variables[i]->previous;
+ mds_kbdc_tree_free(old->value);
+ free(old);
+ }
+ free(variables), variables = NULL;
+ variable_count = current_scope = 0;
+}
+
+
+/**
+ * Push the variable-stack, making it
+ * possible to shadow all variables
+ */
+void variables_stack_push(void)
+{
+ current_scope++;
+}
+
+
+/**
+ * Undo the actions of `variables_stack_push`
+ * and all additions to the variable storage
+ * since it was last called (without a
+ * corresponding call to this function)
+ */
+void variables_stack_pop(void)
+{
+ size_t i;
+ variable_t* old;
+ for (i = 0; i < variable_count; i++)
+ if (variables[i] && (variables[i]->scope == current_scope))
+ {
+ old = variables[i];
+ variables[i] = variables[i]->previous;
+ mds_kbdc_tree_free(old->value);
+ free(old);
+ }
+ current_scope--;
+}
+
+
+/**
+ * Check whether a let will override a variable
+ * rather the define or shadow it
+ *
+ * @param variable The variable index
+ * @return Whether a let will override the variable
+ */
+int variables_let_will_override(size_t variable)
+{
+ if (variable >= variable_count) return 0;
+ if (variables[variable] == NULL) return 0;
+ return variables[variable]->scope == current_scope;
+}
+
+
+/**
+ * Assign a value to a variable, and define or shadow it in the process
+ *
+ * @param variable The variable index
+ * @param value The variable's new value
+ * @return Zero on success, -1 on error
+ */
+int variables_let(size_t variable, mds_kbdc_tree_t* restrict value)
+{
+ variable_t** new;
+ variable_t* previous;
+
+ /* Grow the table if necessary to fit the variable. */
+ if (variable >= variable_count)
+ {
+ new = realloc(variables, (variable + 1) * sizeof(variable_t*));
+ if (new == NULL)
+ return -1;
+ variables = new;
+ memset(variables, 0, (variable + 1 - variable_count) * sizeof(variable_t*));
+ variable_count = variable + 1;
+ }
+
+ if (variables_let_will_override(variable))
+ {
+ /* Override. */
+ mds_kbdc_tree_free(variables[variable]->value);
+ variables[variable]->value = value;
+ }
+ else
+ {
+ /* Shadow or define. */
+ previous = variables[variable];
+ variables[variable] = malloc(sizeof(variable_t));
+ if (variables[variable] == NULL)
+ return variables[variable] = previous, -1;
+ variables[variable]->value = value;
+ variables[variable]->previous = previous;
+ variables[variable]->scope = current_scope;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Get the value currently assigned to a variable
+ *
+ * The function cannot fail, `NULL` is however returned
+ * if the variable is not defined
+ *
+ * @param variable The variable index
+ * @return The variable's value, `NULL` if not defined
+ */
+mds_kbdc_tree_t* variables_get(size_t variable)
+{
+ if (variable >= variable_count) return NULL;
+ if (variables[variable] == NULL) return NULL;
+ return variables[variable]->value;
+}
+
diff --git a/src/mds-kbdc/variables.h b/src/mds-kbdc/variables.h
new file mode 100644
index 0000000..26d3c72
--- /dev/null
+++ b/src/mds-kbdc/variables.h
@@ -0,0 +1,80 @@
+/**
+ * mds — A micro-display server
+ * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef MDS_MDS_KBDC_VARIABLES_H
+#define MDS_MDS_KBDC_VARIABLES_H
+
+
+#include "parsed.h"
+#include "string.h"
+
+
+
+/**
+ * Destroy the variable storage
+ */
+void variables_terminate(void);
+
+/**
+ * Push the variable-stack, making it
+ * possible to shadow all variables
+ *
+ * @return Zero on success, -1 on error
+ */
+void variables_stack_push(void);
+
+/**
+ * Undo the actions of `variables_stack_push`
+ * and all additions to the variable storage
+ * since it was last called (without a
+ * corresponding call to this function)
+ */
+void variables_stack_pop(void);
+
+/**
+ * Check whether a let will override a variable
+ * rather the define or shadow it
+ *
+ * @param variable The variable index
+ * @return Whether a let will override the variable
+ */
+int variables_let_will_override(size_t variable) __attribute__((pure));
+
+/**
+ * Assign a value to a variable, and define or shadow it in the process
+ *
+ * @param variable The variable index
+ * @param value The variable's new value
+ * @return Zero on success, -1 on error
+ */
+int variables_let(size_t variable, mds_kbdc_tree_t* restrict value);
+
+/**
+ * Get the value currently assigned to a variable
+ *
+ * The function cannot fail, `NULL` is however returned
+ * if the variable is not defined
+ *
+ * @param variable The variable index
+ * @return The variable's value, `NULL` if not defined
+ */
+mds_kbdc_tree_t* variables_get(size_t variable) __attribute__((pure));
+
+
+
+#endif
+