text/plain
•
14.39 KB
•
545 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 "array.h"
#include "casting.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *ansiRS = "\033[0m";
char *ansiFG[16] = {
"\033[0;30m",
"\033[0;31m",
"\033[0;32m",
"\033[0;33m",
"\033[0;34m",
"\033[0;35m",
"\033[0;36m",
"\033[0;37m",
"\033[1;30m",
"\033[1;31m",
"\033[1;32m",
"\033[1;33m",
"\033[1;34m",
"\033[1;35m",
"\033[1;36m",
"\033[1;37m"
};
int bufPrintFloat(float v, char *buf, int t, int len){
t = snprintf(buf,len,"%.5f",v);
for(;buf[t-1] == '0';t--){buf[t]=0;}
if(buf[t] == '0'){buf[t] = 0;}
if(buf[t-1] == '.'){buf[t++] = '0';}
return t;
}
int bufWriteString(char *buf, int len, const char *data){
u8 c;
char *out = buf;
char *end = &buf[len-2];
if(buf >= end){return 0;}
*out++ = '\"';
while(out < end){
switch(c = *data++){
case 0:
goto bufWriteStringExit;
case '\a': // Bell
*out++ = '\\'; *out++ = 'a';
break;
case '\b': // Backspace
*out++ = '\\'; *out++ = 'b';
break;
case '\t': // Horiz. Tab
*out++ = '\\'; *out++ = 't';
break;
case '\n': // Line Feed
*out++ = '\\'; *out++ = 'n';
break;
case '\v': // Vert. Tab
*out++ = '\\'; *out++ = 'v';
break;
case '\f': // Form Feed
*out++ = '\\'; *out++ = 'f';
break;
case '\r': // Carriage Return
*out++ = '\\'; *out++ = 'r';
break;
case '\e': // Escape
*out++ = '\\'; *out++ = 'e';
break;
case '"':
*out++ = '\\'; *out++ = '"';
break;
case '\'':
*out++ = '\\'; *out++ = '\'';
break;
case '\\':
*out++ = '\\'; *out++ = '\\';
break;
default:
*out++ = c;
break;
}
}
bufWriteStringExit:
*out++ = '\"';
return out-buf;
}
char *lSIndent(char *buf, char *bufEnd, int indentLevel){
for(int i=0;i<indentLevel;i++){
if(buf >= bufEnd){return buf;}
*buf++ = ' ';
}
return buf;
}
char *lSWriteVal(lVal *v, char *buf, char *bufEnd, int indentLevel, bool display){
*buf = 0;
if(v == NULL){return buf;}
char *cur = buf;
int t = 0;
int len = bufEnd-buf;
switch(v->type){
case ltNoAlloc:
t = snprintf(buf,len,"#zzz");
break;
case ltBool:
if(v->vBool){
t = snprintf(buf,len,"#t");
}else{
t = snprintf(buf,len,"#f");
}
break;
case ltLambda: {
lClosure *cl = &lClosureList[v->vCdr & CLO_MASK];
if(cl->flags & lfObject){
t = snprintf(cur,bufEnd-cur,"[ω ");
indentLevel += 2;
}else if(cl->flags & lfDynamic){
t = snprintf(cur,bufEnd-cur,"[δ [");
indentLevel += 3;
}else{
t = snprintf(cur,bufEnd-cur,"[λ [");
indentLevel += 3;
}
if(t > 0){cur += t;}
lVal *cloData = lCloData(v->vCdr);
forEach(n,cloData){
if(lCaar(n) == NULL){continue;}
if(n != cloData){
if(cl->flags & lfObject){
*cur++ = '\n';
for(int i=indentLevel;i>=0;i--){*cur++=' ';}
}else{
*cur++ = ' ';
}
}
lVal *cv = NULL;
if(lCadar(n) != NULL){
cv = lCar(n);
}else{
cv = lCaar(n);
}
if(cl->flags & lfObject){
cv = lCons(lValSym("def"),cv);
}
cur = lSWriteVal(cv,cur,bufEnd,indentLevel,display);
}
if(!(cl->flags & lfObject)){*cur++ = ']';}
lVal *cloText = lCloText(v->vCdr);
forEach(n,cloText){
*cur++ = '\n';
for(int i=indentLevel;i>=0;i--){*cur++=' ';}
cur = lSWriteVal(lCar(n),cur,bufEnd,indentLevel,display);
}
indentLevel -= 2;
t = snprintf(cur,bufEnd-cur,"]");
break; }
case ltPair: {
int indentStyle = 0;
int oldIndent = indentLevel;
lVal *carSym = lCar(v);
if((carSym != NULL) && (carSym->type == ltSymbol) && (lCdr(v) != NULL)){
lSymbol *sym = lvSym(carSym->vCdr);
if(sym == symQuote){
v = lCdar(v);
*cur++ = '\'';
}else if(sym == symCond){
indentStyle = 1;
indentLevel += 6;
}else if(sym == symWhen){
indentStyle = 1;
indentLevel += 6;
}else if(sym == symUnless){
indentStyle = 1;
indentLevel += 8;
}else if(sym == symIf){
indentStyle = 1;
indentLevel += 4;
}else if(sym == symLet){
indentStyle = 1;
indentLevel += 5;
}
}
t = snprintf(cur,bufEnd-cur,"[");
if(t > 0){cur += t;}
for(lVal *n = v;n != NULL; n = lCdr(n)){
if(n->type == ltPair){
cur = lSWriteVal(lCar(n),cur,bufEnd,indentLevel,display);
if(lCdr(n) != NULL){
if((indentStyle == 1) && (n != v)){
*cur++ = '\n';
for(int i=indentLevel;i>=0;i--){*cur++=' ';}
}else{
*cur++ = ' ';
}
}
}else{
*cur++ = '.';
*cur++ = ' ';
cur = lSWriteVal(n,cur,bufEnd,indentLevel,display);
break;
}
}
t = snprintf(cur,bufEnd-cur,"]");
indentLevel = oldIndent;
break; }
case ltArray: {
t = snprintf(cur,bufEnd-cur,"#[");
if(t > 0){cur += t;}
if(lArrData(v) != NULL){
const int arrLen = lArrLength(v);
for(int i=0;i<arrLen;i++){
cur = lSWriteVal(lValD(lArrData(v)[i]),cur,bufEnd,indentLevel,display);
if(i < (lArrLength(v)-1)){*cur++ = ' ';}
}
}
t = snprintf(cur,bufEnd-cur,"]");
break; }
case ltInt:
t = snprintf(buf,len,"%i",v->vInt);
break;
case ltFloat:
t = bufPrintFloat(v->vFloat,buf,t,len);
break;
case ltVec:
t = snprintf(buf,len,"[vec ");
t += bufPrintFloat(lVecV(v->vCdr).x,&buf[t],t,len);
buf[t++] = ' ';
t += bufPrintFloat(lVecV(v->vCdr).y,&buf[t],t,len);
buf[t++] = ' ';
t += bufPrintFloat(lVecV(v->vCdr).z,&buf[t],t,len);
t += snprintf(&buf[t],len,"]");
break;
case ltString:
if(display){
t = snprintf(buf,len,"%s",lStrData(v));
}else{
t = bufWriteString(buf,len,lStrData(v));
}
break;
case ltSymbol:
t = snprintf(buf,len,"%s",lvSym(v->vCdr)->c);
break;
case ltNativeFunc:
t = snprintf(buf,len,"#cfn_%u",v->vCdr);
break;
case ltInf:
t = snprintf(buf,len,"#inf");
break;
case ltGUIWidget:
t = snprintf(buf,len,"#gui_%u",v->vCdr);
break;
}
if(t > 0){cur += t;}
*cur = 0;
return cur;
}
lVal *lnfStrlen(lClosure *c, lVal *v){
if(v == NULL){return lValInt(0);}
lVal *t = lEval(c,lCar(v));
if((t == NULL) || (t->type != ltString)){return lValInt(0);}
if(lStrNull(t)){return lValInt(0);}
return lValInt(lStringLength(&lStr(t)));
}
lVal *lnfTrim(lClosure *c, lVal *v){
if(v == NULL){return NULL;}
lVal *t = lEval(c,lCar(v));
if((t == NULL) || (t->type != ltString)){return NULL;}
if(lStrNull(t)){return lValInt(0);}
const char *s;
for(s = lStrData(t);*s != 0 && isspace((u8)*s);s++){}
int len = lStringLength(&lStr(t)) - (s - lStrData(t));
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->vCdr = lStringAlloc();
if(ret->vCdr == 0){return NULL;}
lStr(ret).flags |= lfHeapAlloc;
lStr(ret).buf = lStr(ret).data = buf;
lStr(ret).bufEnd = &lStrBuf(ret)[len];
return ret;
}
lVal *lnfStrDown(lClosure *c, lVal *v){
if(v == NULL){return NULL;}
lVal *t = lEval(c,lCar(v));
if((t == NULL) || (t->type != ltString)){return NULL;}
if(lStrNull(t)){return lValInt(0);}
const int len = lStringLength(&lStr(t));
char *buf = malloc(len+1);
for(int i=0;i<len;i++){
buf[i] = tolower((u8)lStrData(t)[i]);
}
buf[len] = 0;
lVal *ret = lValAlloc();
ret->type = ltString;
ret->vCdr = lStringAlloc();
if(ret->vCdr == 0){return NULL;}
lStr(ret).flags |= lfHeapAlloc;
lStr(ret).buf = lStr(ret).data = buf;
lStr(ret).bufEnd = &lStrBuf(ret)[len];
return ret;
}
lVal *lnfStrUp(lClosure *c, lVal *v){
if(v == NULL){return NULL;}
lVal *t = lEval(c,lCar(v));
if((t == NULL) || (t->type != ltString)){return NULL;}
if(lStrNull(t)){return lValInt(0);}
const int len = lStringLength(&lStr(t));
char *buf = malloc(len+1);
for(int i=0;i<len;i++){
buf[i] = toupper((u8)lStrData(t)[i]);
}
buf[len] = 0;
lVal *ret = lValAlloc();
ret->type = ltString;
ret->vCdr = lStringAlloc();
if(ret->vCdr == 0){return NULL;}
lStrFlags(ret) |= lfHeapAlloc;
lStrBuf(ret) = lStrData(ret) = buf;
lStrEnd(ret) = &lStrBuf(ret)[len];
return ret;
}
lVal *lnfStrCap(lClosure *c, lVal *v){
if(v == NULL){return NULL;}
lVal *t = lEval(c,lCar(v));
if((t == NULL) || (t->type != ltString)){return NULL;}
if(lStrNull(t)){return lValInt(0);}
const int len = lStringLength(&lStr(t));
char *buf = malloc(len+1);
int cap = 1;
for(int i=0;i<len;i++){
if(isspace((u8)lStrData(t)[i])){
cap = 1;
buf[i] = lStrData(t)[i];
}else{
if(cap){
buf[i] = toupper((u8)lStrData(t)[i]);
cap = 0;
}else{
buf[i] = tolower((u8)lStrData(t)[i]);
}
}
}
buf[len] = 0;
lVal *ret = lValAlloc();
ret->type = ltString;
ret->vCdr = lStringAlloc();
if(ret->vCdr == 0){return NULL;}
lStrFlags(ret) |= lfHeapAlloc;
lStrBuf(ret) = lStrData(ret) = buf;
lStrEnd(ret) = &lStrBuf(ret)[len];
return ret;
}
lVal *lnfSubstr(lClosure *c, lVal *v){
const char *buf;
int start = 0;
int len = 0;
int slen = 0;
if(v == NULL){return NULL;}
lVal *str = lEval(c,lCar(v));
if(str == NULL) {return NULL;}
if(str->type != ltString){return NULL;}
if(str->vCdr == 0) {return NULL;}
buf = lStrData(str);
slen = len = lStringLength(&lStr(str));
if(lCdr(v) != NULL){
v = lCdr(v);
lVal *lStart = lEval(c,lCar(v));
if((lStart != NULL) && (lStart->type == ltInt)){
start = lStart->vInt;
}
if(lCdr(v) != NULL){
v = lCdr(v);
lVal *lLen = lEval(c,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->vCdr = lStringNew(&buf[start], len);
return ret;
}
lVal *lnfCat(lClosure *c, lVal *v){
char tmpStringBuf[8192];
char *buf = tmpStringBuf;
forEach(sexpr,v){
lVal *t = lEval(c,lCar(sexpr));
int clen = 0;
if(t == NULL){continue;}
switch(t->type){
default: break;
case ltInf: {
clen = snprintf(buf,sizeof(tmpStringBuf) - (buf-tmpStringBuf),"#inf");
break; }
case ltSymbol: {
clen = snprintf(buf,sizeof(tmpStringBuf) - (buf-tmpStringBuf),"%s",lvSym(t->vCdr)->c);
break; }
case ltFloat: {
clen = snprintf(buf,sizeof(tmpStringBuf) - (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,sizeof(tmpStringBuf) - (buf-tmpStringBuf),"%i",t->vInt);
break; }
case ltBool: {
clen = snprintf(buf,sizeof(tmpStringBuf) - (buf-tmpStringBuf),"%s",t->vBool ? "#t" : "#f");
break; }
case ltString:
if(t->vCdr == 0){continue;}
clen = snprintf(buf,sizeof(tmpStringBuf) - (buf-tmpStringBuf),"%s",lStrData(t));
break;
}
if(clen > 0){
buf += clen;
}
}
*buf = 0;
return lValString(tmpStringBuf);
}
lVal *lnfIndexOf(lClosure *c, lVal *v){
const char *haystack = NULL;
const char *needle = NULL;
int pos = 0;
v = getLArgS(c,v,&haystack);
v = getLArgS(c,v,&needle);
v = getLArgI(c,v,&pos);
if(needle == NULL) {return lValInt(-1);}
if(haystack == NULL) {return lValInt(-1);}
const int needleLength = strlen(needle);
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(-1);
}
lVal *lnfStrSym(lClosure *c, lVal *v){
v = lEval(c,lCar(v));
if(v == NULL){return NULL;}
if(v->type != ltString){return NULL;}
return lValSym(lStrData(v));
}
lVal *lnfSymStr(lClosure *c, lVal *v){
v = lEval(c,lCar(v));
if(v == NULL){return NULL;}
if(v->type != ltSymbol){return NULL;}
return lValString(lvSym(v->vCdr)->c);
}
lVal *lnfWriteStr(lClosure *c, lVal *v){
static char *buf = NULL;
lVal *t = lApply(c,v,lEval);
if(t == NULL){
return lValString("#nil");
}
if(buf == NULL){buf = malloc(1<<16);}
lSWriteVal(lCar(t), buf, &buf[1<<16],0,false);
buf[(1<<16)-1]=0;
t = lValString(buf);
return t;
}
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 lAddStringFuncs(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/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);
}