/* string.c by Michael Thorpe 2017-05-28 */ #include #include #include #include #include #include "lisp.h" static void drop_string(obj *o) { free(o->value.s.data); } static obj *get_char(obj *str,obj *idx) { obj *c; if(str->type != &string_objtype) { DECREF(idx); return(throwtypeerror(str)); } if(idx->type != &integer_objtype) { DECREF(str); return(throwtypeerror(idx)); } if(idx->value.i<0 || str->value.s.len<=idx->value.i) { DECREF(str); return(throwrangecheck(idx)); } c=new_object(&integer_objtype); if(c) c->value.i=(unsigned char)str->value.s.data[idx->value.i]; DECREF(str); DECREF(idx); return(c); } static obj *put_char(obj *str,obj *idx,obj *c) { if(str->type != &string_objtype) { DECREF(idx); DECREF(c); return(throwtypeerror(str)); } if(idx->type != &integer_objtype) { DECREF(str); DECREF(c); return(throwtypeerror(idx)); } if(idx->value.i<0 || str->value.s.len<=idx->value.i) { DECREF(str); DECREF(c); return(throwrangecheck(idx)); } if(c->type != &integer_objtype) { DECREF(str); DECREF(idx); return(throwtypeerror(c)); } if(c->value.i<0 || UCHAR_MAXvalue.i) { DECREF(str); DECREF(idx); return(throwrangecheck(c)); } str->value.s.data[idx->value.i]=c->value.i; DECREF(idx); DECREF(c); return(str); } static obj *string_to_integer(obj *str) { obj *i; char buf[32]; char *s; unsigned long l; if(str->type != &string_objtype) return(throwtypeerror(str)); #ifdef USE_BIGINT if(10value.s.len) return(string_to_bigint(str)); #endif if(str->value.s.len>sizeof(buf)-1) return(throwtypeerror(str)); memcpy(buf,str->value.s.data,str->value.s.len); buf[str->value.s.len]='\0'; if(!isdigit(buf[0]) && '-' != buf[0]) return(throwtypeerror(str)); l=strtol(buf,&s,0); if(s != buf+str->value.s.len) return(throwtypeerror(str)); if(l==LONG_MIN || l==LONG_MAX) return(throwrangecheck(str)); DECREF(str); i=new_object(&integer_objtype); if(i) i->value.i=l; return(i); } static obj *string_to_symbol(obj *str) { obj *sym; if(str->type != &string_objtype) return(throwtypeerror(str)); sym=new_word(str->value.s.data,str->value.s.len); DECREF(str); return(sym); } static obj *string_tostring(obj *in) { obj *o; int i,j,k,prevquoted; prevquoted=0; for(i=j=0;ivalue.s.len;i++) { switch(in->value.s.data[i]) { case '\0': j++; prevquoted=1; break; case '\\': case '"': j++; prevquoted=0; break; default: if(!isprint(in->value.s.data[i]) || (prevquoted && isxdigit(in->value.s.data[i]))) { j+=3; prevquoted=1; } else prevquoted=0; break; } } o=new_string(0,in->value.s.len+2+j); if(!o) return(throwoom()); o->value.s.data[0]='"'; prevquoted=0; for(i=0,j=1;ivalue.s.len;i++) { switch(in->value.s.data[i]) { case '\0': o->value.s.data[j++]='\\'; o->value.s.data[j++]='0'; prevquoted=1; break; case '\\': case '"': o->value.s.data[j++]='\\'; o->value.s.data[j++]=in->value.s.data[i]; prevquoted=0; break; default: if(!isprint(in->value.s.data[i]) || (prevquoted && isxdigit(in->value.s.data[i]))) { o->value.s.data[j++]='\\'; o->value.s.data[j++]='x'; k=(in->value.s.data[i]>>4)&0xF; o->value.s.data[j++]=k+((k<10)?'0':'A'-10); k=in->value.s.data[i]&0xF; o->value.s.data[j++]=k+((k<10)?'0':'A'-10); prevquoted=1; } else { o->value.s.data[j++]=in->value.s.data[i]; prevquoted=0; } break; } } o->value.s.data[j++]='"'; assert(o->value.s.len==j); DECREF(in); return(o); } static obj *string_length(obj *str) { obj *l; if(str->type != &string_objtype) return(throwtypeerror(str)); l=new_object(&integer_objtype); if(l) l->value.i=str->value.s.len; DECREF(str); return(l); } static obj *substring(obj *str,obj *offobj,obj *lenobj) { obj *substr; signed long off,len; int no_truncate=0; if(&string_objtype != str->type) { DECREF(offobj); DECREF(lenobj); return(throwtypeerror(str)); } if(&integer_objtype==offobj->type) { off=offobj->value.i; } else { DECREF(str); DECREF(lenobj); return(throwtypeerror(offobj)); } if(&nilobj==lenobj) { no_truncate=1; len=0; } else if(&integer_objtype==lenobj->type) { len=lenobj->value.i; } else { DECREF(str); DECREF(offobj); return(throwtypeerror(lenobj)); } if(off<0) off+=str->value.s.len; if(off<0 || off>str->value.s.len) { DECREF(lenobj); DECREF(str); return(throwrangecheck(offobj)); } DECREF(offobj); if(no_truncate || len<0) len+=str->value.s.len-off; if(len<0) { DECREF(str); return(throwrangecheck(lenobj)); } if(off+len<0 || off+len>str->value.s.len) { DECREF(str); return(throwrangecheck(lenobj)); } DECREF(lenobj); substr=new_string(str->value.s.data+off,len); DECREF(str); return(substr); } obj *new_string(const char *data,unsigned long len) { obj *o; char *s; s=(char *)malloc(len); if(!s) return(throwoom()); o=new_object(&string_objtype); if(!o) { free(s); return(0); } o->value.s.data=s; o->value.s.len=len; if(data) memcpy(o->value.s.data,data,len); else memset(o->value.s.data,0,len); return(o); } static obj *newstring(obj *o) { obj *r; if(o->type != &integer_objtype) return(throwtypeerror(o)); if(o->value.i<0) return(throwrangecheck(o)); r=new_string(0,o->value.i); DECREF(o); return(r); } static obj *tostring(obj *o) { return((o)->type->tostring(o)); } static int string_init() { if(store_builtin_ns1("->string",tostring) || store_builtin_ns2("get-char",get_char) || store_builtin_ns1("new-string",newstring) || store_builtin_ns3("put-char",put_char) || store_builtin_ns1("string->integer",string_to_integer) || store_builtin_ns1("string->symbol",string_to_symbol) || store_builtin_ns1("string-length",string_length) || store_builtin_ns3("substring",substring)) return(1); return(0); } objtype string_objtype={ name: "string", tostring: string_tostring, dropobj: drop_string, init: string_init, };