text/plain
•
10.41 KB
•
342 lines
/*
* Wolkenwelten - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
*
* This project uses the MIT license, a copy should be included under /LICENSE
*/
#include "string.h"
#include "../nujel.h"
#include "../datatypes/list.h"
#include "../datatypes/native-function.h"
#include "../datatypes/string.h"
#include "../datatypes/symbol.h"
#include "../datatypes/val.h"
#ifndef COSMOPOLITAN_H_
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
lVal *lnfStrlen(lClosure *c, lVal *v){
(void)c;
if(v == NULL){return lValInt(0);}
lVal *t = lCar(v);
if((t == NULL) || (t->type != ltString)){return lValInt(0);}
return lValInt(lStringLength(t->vString));
}
lVal *lnfTrim(lClosure *c, lVal *v){
(void)c;
if(v == NULL){return NULL;}
lVal *t = lCar(v);
if((t == NULL) || (t->type != ltString)){return NULL;}
const char *s;
for(s = t->vString->data;*s != 0 && isspace((u8)*s);s++){}
int len = lStringLength(t->vString) - (s - t->vString->data);
for(;len > 0 && isspace((u8)s[len-1]);len--){}
char *buf = malloc(len+1);
memcpy(buf,s,len);
buf[len] = 0;
lVal *ret = lValAlloc();
ret->type = ltString;
ret->vString = lStringAlloc();
if(ret->vString == NULL){return NULL;}
ret->vString->flags |= lfHeapAlloc;
ret->vString->buf = ret->vString->data = buf;
ret->vString->bufEnd = &ret->vString->buf[len];
return ret;
}
lVal *lnfStrDown(lClosure *c, lVal *v){
(void)c;
if(v == NULL){return NULL;}
lVal *t = lCar(v);
if((t == NULL) || (t->type != ltString)){return NULL;}
const int len = lStringLength(t->vString);
char *buf = malloc(len+1);
for(int i=0;i<len;i++){
buf[i] = tolower((u8)t->vString->data[i]);
}
buf[len] = 0;
lVal *ret = lValAlloc();
ret->type = ltString;
ret->vString = lStringAlloc();
if(ret->vString == NULL){return NULL;}
ret->vString->flags |= lfHeapAlloc;
ret->vString->buf = ret->vString->data = buf;
ret->vString->bufEnd = &ret->vString->buf[len];
return ret;
}
lVal *lnfStrUp(lClosure *c, lVal *v){
(void)c;
if(v == NULL){return NULL;}
lVal *t = lCar(v);
if((t == NULL) || (t->type != ltString)){return NULL;}
const int len = lStringLength(t->vString);
char *buf = malloc(len+1);
for(int i=0;i<len;i++){
buf[i] = toupper((u8)t->vString->data[i]);
}
buf[len] = 0;
lVal *ret = lValAlloc();
ret->type = ltString;
ret->vString = lStringAlloc();
if(ret->vString == NULL){return NULL;}
ret->vString->flags |= lfHeapAlloc;
ret->vString->buf = ret->vString->data = buf;
ret->vString->bufEnd = &ret->vString->buf[len];
return ret;
}
lVal *lnfStrCap(lClosure *c, lVal *v){
(void)c;
if(v == NULL){return NULL;}
lVal *t = lCar(v);
if((t == NULL) || (t->type != ltString)){return NULL;}
const int len = lStringLength(t->vString);
char *buf = malloc(len+1);
int cap = 1;
for(int i=0;i<len;i++){
if(isspace((u8)t->vString->data[i])){
cap = 1;
buf[i] = t->vString->data[i];
}else{
if(cap){
buf[i] = toupper((u8)t->vString->data[i]);
cap = 0;
}else{
buf[i] = tolower((u8)t->vString->data[i]);
}
}
}
buf[len] = 0;
lVal *ret = lValAlloc();
ret->type = ltString;
ret->vString = lStringAlloc();
if(ret->vString == 0){return NULL;}
ret->vString->flags |= lfHeapAlloc;
ret->vString->buf = ret->vString->data = buf;
ret->vString->bufEnd = &ret->vString->buf[len];
return ret;
}
lVal *lnfSubstr(lClosure *c, lVal *v){
(void)c;
const char *buf;
int start = 0;
int len = 0;
int slen = 0;
if(v == NULL){return NULL;}
lVal *str = lCar(v);
if(str == NULL) {return NULL;}
if(str->type != ltString){return NULL;}
if(str->vString == 0) {return NULL;}
buf = str->vString->data;
slen = len = lStringLength(str->vString);
if(lCdr(v) != NULL){
v = lCdr(v);
lVal *lStart = lCar(v);
if((lStart != NULL) && (lStart->type == ltInt)){
start = lStart->vInt;
}
if(lCdr(v) != NULL){
v = lCdr(v);
lVal *lLen = lCar(v);
if((lLen != NULL) && (lLen->type == ltInt)){
len = lLen->vInt;
}
}
}
if(start >= slen){return NULL;}
if(start < 0){start = slen + start;}
if(len < 0) {len = slen + len;}
len = MIN(slen,len-start);
lVal *ret = lValAlloc();
if(ret == NULL){return NULL;}
ret->type = ltString;
ret->vString = lStringNew(&buf[start], len);
return ret;
}
lVal *lnfCat(lClosure *c, lVal *v){
(void)c;
static char *tmpStringBuf = NULL;
static int tmpStringBufSize = 1<<8; // Start with 4K
if(tmpStringBuf == NULL){tmpStringBuf = malloc(tmpStringBufSize);}
if(tmpStringBuf == NULL){exit(99);}
char *buf = tmpStringBuf;
int i = 0;
forEach(sexpr,v){
lVal *t = lCar(sexpr);
int clen = 0;
if(t == NULL){continue;}
restart:
switch(t->type){
default: break;
case ltInf:
clen = snprintf(buf,tmpStringBufSize - (buf-tmpStringBuf),"#inf");
break;
case ltSymbol:
clen = snprintf(buf,tmpStringBufSize - (buf-tmpStringBuf),"%s",t->vSymbol->c);
break;
case ltFloat:
clen = snprintf(buf,tmpStringBufSize - (buf-tmpStringBuf),"%.5f",t->vFloat);
for(;buf[clen-1] == '0';clen--){buf[clen]=0;}
if(buf[clen] == '0'){buf[clen] = 0;}
if(buf[clen-1] == '.'){buf[clen++] = '0';}
break;
case ltInt:
clen = snprintf(buf,tmpStringBufSize - (buf-tmpStringBuf),"%i",t->vInt);
break;
case ltBool:
clen = snprintf(buf,tmpStringBufSize - (buf-tmpStringBuf),"%s",t->vBool ? "#t" : "#f");
break;
case ltString:
if(t->vString == NULL){continue;}
clen = snprintf(buf,tmpStringBufSize - (buf-tmpStringBuf),"%s",t->vString->data);
break;
}
if(clen < 0){
return NULL;
}
if((i + clen) >= tmpStringBufSize){
tmpStringBufSize *= 2;
tmpStringBuf = realloc(tmpStringBuf,tmpStringBufSize);
buf = &tmpStringBuf[i];
goto restart;
}
buf += clen;
i += clen;
}
*buf = 0;
return lValString(tmpStringBuf);
}
lVal *lnfIndexOf(lClosure *c, lVal *v){
const char *haystack = NULL;
const char *needle = NULL;
v = getLArgS(c,v,&haystack);
if(haystack == NULL) {return lValInt(-1);}
const int haystackLength = strlen(haystack);
v = getLArgS(c,v,&needle);
if(needle == NULL) {return lValInt(-2);}
const int needleLength = strlen(needle);
int pos = 0;
v = getLArgI(c,v,&pos);
if(pos > haystackLength-needleLength){return lValInt(-3);}
/* Empty strings just return the current position, this is so we can
* split an empty string into each character by passing an empty string
*/
if(needleLength <= 0){return lValInt(pos);}
for(const char *s = &haystack[pos]; *s != 0; s++){
if(strncmp(s,needle,needleLength) == 0){
return lValInt(s-haystack);
}
}
return lValInt(-4);
}
lVal *lnfLastIndexOf(lClosure *c, lVal *v){
const char *haystack = NULL;
const char *needle = NULL;
v = getLArgS(c,v,&haystack);
if(haystack == NULL) {return lValInt(-1);}
const int haystackLength = strlen(haystack);
v = getLArgS(c,v,&needle);
if(needle == NULL) {return lValInt(-2);}
const int needleLength = strlen(needle);
if(needleLength <= 0){return lValInt(-3);}
int pos = haystackLength - needleLength - 1;
v = getLArgI(c,v,&pos);
for(const char *s = &haystack[pos]; s > haystack; s--){
if(strncmp(s,needle,needleLength) == 0){
return lValInt(s-haystack);
}
}
return lValInt(-4);
}
lVal *lnfStrSym(lClosure *c, lVal *v){
(void)c;
v = lCar(v);
if(v == NULL){return NULL;}
if(v->type != ltString){return NULL;}
return lValSym(v->vString->data);
}
lVal *lnfSymStr(lClosure *c, lVal *v){
(void)c;
v = lCar(v);
if(v == NULL){return NULL;}
if(v->type != ltSymbol){return NULL;}
return lValString(v->vSymbol->c);
}
lVal *lnfWriteStr(lClosure *c, lVal *v){
(void)c;
static char *buf = NULL;
if(v == NULL){
return lValString("#nil");
}
if(buf == NULL){buf = malloc(1<<16);}
lSWriteVal(lCar(v), buf, &buf[1<<16],0,false);
buf[(1<<16)-1]=0;
return lValString(buf);
}
lVal *lnfCharAt(lClosure *c,lVal *v){
const char *str = NULL;
int pos = 0;
v = getLArgS(c,v,&str);
v = getLArgI(c,v,&pos);
if(str == NULL){return NULL;}
const int len = strlen(str);
if(pos >= len){return NULL;}
return lValInt(str[pos]);
}
lVal *lnfFromCharCode(lClosure *c,lVal *v){
int len = lListLength(v)+1;
char *buf = malloc(len);
int i=0,code=0;
while(v != NULL){
v = getLArgI(c,v,&code);
buf[i++] = code;
if(i >= len){break;}
}
v = lValString(buf);
free(buf);
return v;
}
void lOperationsString(lClosure *c){
lAddNativeFunc(c,"str/concatenate str/cat cat","[...args]", "ConCATenates ARGS into a single string", lnfCat);
lAddNativeFunc(c,"str/trim trim", "[str]", "Trim STR of any excessive whitespace", lnfTrim);
lAddNativeFunc(c,"str/length", "[str]", "Return length of STR", lnfStrlen);
lAddNativeFunc(c,"str/uppercase uppercase", "[str]", "Return STR uppercased", lnfStrUp);
lAddNativeFunc(c,"str/lowercase lowercase", "[str]", "Return STR lowercased", lnfStrDown);
lAddNativeFunc(c,"str/capitalize capitalize", "[str]", "Return STR capitalized", lnfStrCap);
lAddNativeFunc(c,"str/substr substr", "[str &start &stop]","Return STR starting at position START=0 and ending at &STOP=[str-len s]", lnfSubstr);
lAddNativeFunc(c,"str/index-of index-of", "[haystack needle &start]","Return the position of NEEDLE in HAYSTACK, searcing from START=0, or -1 if not found",lnfIndexOf);
lAddNativeFunc(c,"str/last-index-of", "[haystack needle &start]","Return the last position of NEEDLE in HAYSTACK, searcing from START=0, or -1 if not found",lnfLastIndexOf);
lAddNativeFunc(c,"str/char-at char-at", "[str pos]", "Return the character at position POS in STR", lnfCharAt);
lAddNativeFunc(c,"str/from-char-code from-char-code","[...codes]", "Construct a string out of ...CODE codepoints and return it", lnfFromCharCode);
lAddNativeFunc(c,"str->sym", "[str]", "Convert STR to a symbol", lnfStrSym);
lAddNativeFunc(c,"sym->str", "[sym]", "Convert SYM to a string", lnfSymStr);
lAddNativeFunc(c,"str/write", "[val]", "Write V into a string and return it", lnfWriteStr);
}