Build a symbol from a C string in C?


#1

I wonder how it’s possible to create a valid symbol from an existing C string within an extension function. I want to pass it to the cons() function to receive something like this in uLisp:

(mysymbol ((param . 1) (param . 2)))

I do not want to pass “mysymbol” as a lispstring back to uLisp in this case.
Any help is greatly appreciated!


#2

I’m not quite sure if this is what you mean, but to create a Lisp string from a C string call lispstring():

/*
  lispstring - converts a C string to a Lisp string
*/
object *lispstring (char *s) {
  object *obj = newstring();
  object *tail = obj;
  while(1) {
    char ch = *s++;
    if (ch == 0) break;
    if (ch == '\\') ch = *s++;
    buildstring(ch, &tail);
  }
  return obj;
}

To convert the Lisp string into a symbol you would then call intern():

/*
  intern - looks through the workspace for an existing occurrence of symbol name and returns it,
  otherwise calls symbol(name) to create a new symbol.
*/
object *intern (symbol_t name) {
  for (int i=0; i<WORKSPACESIZE; i++) {
    object *obj = &Workspace[i];
    if (obj->type == SYMBOL && obj->name == name) return obj;
  }
  return symbol(name);
}

#3

Thanks a lot for helping! Your suggestion probably does the trick; but I’m still struggling with the question what a “name” of an object actually is - what does symbol_t refer to? I couldn’t figure that out. So - when I convert a Lisp string to a symbol using “intern”, what do I actually have to pass as the “name”?
Thanks again!


#4

It’s explained a bit here:

Implementation - Symbols


#5

I’ve actually read that already. Sorry if I’m looking dumb - do I just need to pass mylispstring-> name to “intern”? Do I need to use “twist” beforehand since the Lisp string would probably qualify as a “long symbol”? Or is a Lisp string’s “name” already coded as a “long symbol” in general?
My apologies for not getting it completely and thanks for your help! I really tried hard to wrap my head around it reading the extensive documentation on your website and looking into the source code.


#6

You don’t need to use twist() and untwist(); they are internal to uLisp. It would help to know a bit more about what you’re aiming to do. You may be able to achieve what you want to do using higher level uLisp functions. For example, if you want to read in a C string defining a uLisp function call, you could just process it with read-from-string.


#7

Thank you. What I’m trying to do is this:
In my extension, I create elements with a number of properties stored within persistent C structures. I could certainly pass on the property category name just as a string when I make information about the elements available to uLisp to be able to reference the elements from there. But currently I think it will be more consistent and useful to pass and store dotted pairs for further use in uLisp like this…

(message . “mytext”)

… as opposed to …

(“message” . “mytext”)

That’s just a conjecture right now and may prove wrong, but it would still be interesting if and how it’s possible to create a dotted pair of the (symbol . value) kind from within a C function, bypassing the need of creating (string . value) pairs and therefore bypassing string operations in uLisp when accessing the elements.


#8

I think you can do this with the lispstring() and intern() functions. The advantage of (message . "mytext") over ("message" . "mytext") is that every occurrence of message will just be a pointer to the symbol, but every "message" will use up space for the string. One thought: I think you need internlong(), not intern().


#9

EDIT 3: Trying the short symbol trick like this…

char myname[] = “short”;
object* propname = intern(lispstring(myname)->name);

… does not crash uLisp and results in a “visually correct” dotted pair (short . "text"), but the dotted pair still isn’t found by (assoc 'short mylist-with-pair-in-it). Looks like I’m partially stuck - not totally, since the whole thing now works with longer symbol names.

EDIT 2: I’ve tracked it down. First, internlong(“message”) needs to be replaced with a proper char[] initilized with the string:

char myname[] = “message”;
object* propname = internlong(myname);

When doing it this way, uLisp does not crash trying to access the symbol. Second, quite logical, “internlong” can only be used for symbol names that qualify as “long” - if I use “internlong” on shorter words (which I did), the equality check of assoc fails. I’ll try to use just “intern” with those and will report back.

EDIT: Rejoycing below was too early - something must be missing, because assoc fails to identify the dotted pair in uLisp, and when I try to access the symbol using car, uLisp crashes… sorry to bother you again.

That worked, thanks so much! I just did the following:

object* mysym = internlong(“message”);

The resulting object is correctly consed as a symbol into the list I return to uLisp.


#10

The string “short” could create a valid short symbol or a valid long symbol, but as you’ve discovered, they are not considered equal by uLisp.

So I think you need to do something like:

if (strlen(buffer) <= 6 && valid40(buffer)) return intern(twist(pack40(buffer)));
else return internlong(buffer);

This is what read() does, to ensure that strings that could make a valid short symbol get packed into one word as a short symbol.

Alternatively you could ensure that your assoc keys are all long symbols by including a character, such as “!”, that isn’t valid in a radix40 short string; see toradix40().


#11

That worked, of course! Thank you very much for taking the time to explain this - I’m getting better at reading and understanding the inner workings of uLisp, but obviously I wasn’t able to figure out this one.
I really enjoy being able to exchange all kinds of uLisp objects between C and uLisp. To me, this makes uLisp even more valuable!


#12

Great - good luck with your project!