--- /dev/null
+
+#include "../config.h"
+#include "../types.h"
+#include "../builtins.h"
+#include "../sexpr.h"
+#include "../eval.h"
+#include "../dict.h"
+#include "strings.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+Sexpr* s_strlen(Sexpr* b, Sexpr* rest, Sexpr* env) {
+ if(STRINGS_STRLEN_ARGS != u64_get_num_args(b)) {
+ return cons(b, rest);
+ }
+ Sexpr* arg = car(b->value.b.args);
+#ifdef TYPECHECK
+ if(unquote(arg)->type != STR) {
+ ERR("strlen: ", "argument not string");
+ sexpr_free(b);
+ return cons(from_nil(), rest);
+ }
+#endif // typecheck
+ size_t sl = strlen(arg->value.str);
+ sexpr_free(b);
+ return cons (from_uint(sl), rest);
+}
+
+Sexpr* s_strcat(Sexpr* b, Sexpr* rest, Sexpr* env) {
+ if(STRINGS_STRCAT_ARGS != u64_get_num_args(b)) {
+ return cons(b, rest);
+ }
+ Sexpr* args = b->value.b.args;
+ Sexpr* snd = eval(clone(car(args)), env);
+ Sexpr* fst = eval(clone(car(cdr(args))), env);
+#ifdef TYPECHECK
+ if(snd->type != STR || fst->type != STR) {
+ ERR("strcat: ", "arguments not string");
+ sexpr_free(snd);
+ sexpr_free(fst);
+ sexpr_free(b);
+ return cons(from_nil(), rest);
+ }
+#endif // typecheck
+ char* fs = fst->value.str;
+ char* ss = snd->value.str;
+ char* out = malloc(sizeof(char)*(strlen(fs)+strlen(ss)));
+ strcpy(out, fs);
+ strcat(out, ss);
+ sexpr_free(snd);
+ sexpr_free(fst);
+ sexpr_free(b);
+ Sexpr* outval = cons(from_string(out), rest);
+ free(out);
+ return outval;
+}
+
+Sexpr* s_strat(Sexpr* b, Sexpr* rest, Sexpr* env) {
+ // wait, do i want this to be 1- or 0- indexed? currently 1...
+ if(STRINGS_STRAT_ARGS != u64_get_num_args(b)) {
+ return cons(b, rest);
+ }
+ Sexpr* args = b->value.b.args;
+ Sexpr* index = eval(clone(car(cdr(args))), env);
+ Sexpr* str = eval(clone(car(args)), env);
+#ifdef TYPECHECK
+ if(str->type != STR || index->type != UINT) {
+ ERR("strat: ", "arguments not string");
+ sexpr_free(index);
+ sexpr_free(str);
+ sexpr_free(b);
+ return cons(from_nil(), rest);
+ }
+#endif // typecheck
+ size_t len = strlen(str->value.str);
+ size_t idx = index->value.u;
+ if(len < idx) {
+ WARN("", "index out of bounds");
+ return cons(from_nil(), rest);
+ }
+ char at[] = { '\0', '\0'}; // for uh string stuff?
+ at[0] = str->value.str[idx-1];
+ sexpr_free(b);
+ sexpr_free(index);
+ sexpr_free(str);
+ return cons(from_string(at), rest);
+}
+
+Sexpr* s_strexpand(Sexpr* b, Sexpr* rest, Sexpr* env) {
+ if(STRINGS_STREXPAND_ARGS != u64_get_num_args(b)) {
+ return cons(b, rest);
+ }
+ Sexpr* args = b->value.b.args;
+ Sexpr* arg = eval(clone(car(args)), env);
+#ifdef TYPECHECK
+ if(arg->type != STR) {
+ ERR("strexpand: ", "argument not string");
+ sexpr_free(arg);
+ sexpr_free(b);
+ return cons(from_nil(), rest);
+ }
+#endif // typecheck
+ size_t len = strlen(arg->value.str);
+ Sexpr* toret = from_nil();
+ char arr[] = {'\0', '\0'};
+ while(len > 0) {
+ arr[0] = arg->value.str[len - 1];
+ toret = cons(from_string(arr), toret);
+ len--;
+ }
+ sexpr_free(arg);
+ sexpr_free(b);
+ return cons(from_quote(toret), rest);
+}
+
+Sexpr* s_substr(Sexpr* b, Sexpr* rest, Sexpr* env) {
+ if(STRINGS_SUBSTR_ARGS != u64_get_num_args(b)) {
+ return cons(b, rest);
+ }
+ Sexpr* args = b->value.b.args;
+ Sexpr* subex = eval(clone(car(cdr(args))), env);
+ Sexpr* strex = eval(clone(car(args)), env);
+#ifdef TYPECHECK
+ if(subex->type != STR || strex->type != STR) {
+ ERR("substr: ", "arguments not strings");
+ sexpr_free(subex);
+ sexpr_free(strex);
+ sexpr_free(b);
+ return cons(from_nil(), rest);
+ }
+#endif
+ Sexpr* result = from_nil();
+ char* sub = subex->value.str;
+ char* str = strex->value.str;
+ char* currstr = str;
+ char* res;
+ size_t idx;
+ while((res = strstr(currstr, sub)) != NULL) {
+ idx = res - str;
+ result = cons(from_uint(idx + 1), result);
+ // the plus one is to be consistent with strat
+ // not decided if that's how I want it to be though
+ currstr = res + 1;
+ }
+ sexpr_free(subex);
+ sexpr_free(strex);
+ sexpr_free(b);
+ Sexpr* newres = reverse(result);
+ sexpr_free(result);
+ return cons(from_quote(newres), rest);
+}
+
+Sexpr* x_strings_dispatch(Sexpr* b, Sexpr* rest, Sexpr* env) {
+ uint64_t code = b->value.b.opcode & 0xff;
+ switch(code) {
+ case STRINGS_STRLEN:
+ return s_strlen(b, rest, env);
+ case STRINGS_STRCAT:
+ return s_strcat(b, rest, env);
+ case STRINGS_STRAT:
+ return s_strat(b, rest, env);
+ case STRINGS_STREXPAND:
+ return s_strexpand(b, rest, env);
+ case STRINGS_SUBSTR:
+ return s_substr(b, rest, env);
+ default:
+ return from_nil();
+ }
+ return from_nil();
+}
+
+
+Sexpr* load_strings_env(Sexpr* env) {
+ load_builtin(STRINGS_STRLEN_STR, (STRINGS_PREFIX << 8) | STRINGS_STRLEN, env);
+ load_builtin(STRINGS_STRCAT_STR, (STRINGS_PREFIX << 8) | STRINGS_STRCAT, env);
+ load_builtin(STRINGS_STRAT_STR, (STRINGS_PREFIX << 8) | STRINGS_STRAT, env);
+ load_builtin(STRINGS_STREXPAND_STR, (STRINGS_PREFIX << 8) | STRINGS_STREXPAND, env);
+ load_builtin(STRINGS_SUBSTR_STR, (STRINGS_PREFIX << 8) | STRINGS_SUBSTR, env);
+ return env;
+}