Reading Strings
Now we need to add support for parsing strings. As usual this requires first adding a new grammar rule called string
and adding it to our parser.
The rule we are going to use that represents a string is going to be the same as for C style strings. This means a string is essentially a series of escape characters, or normal characters, between two quotation marks ""
. We can specify this as a regular expression inside our grammar string as follows.
string : /\"(\\\\.|[^\"])*\"/ ;
This looks complicated but makes a lot more sense when explained in parts. It reads like this. A string is a "
character, followed by zero or more of either a backslash \\
followed by any other character .
, or anything that isn’t a "
character [^\\"]
. Finally it ends with another "
character.
We also need to add a case to deal with this in the lval_read
function.
if (strstr(t->tag, "string")) { return lval_read_str(t); }
Because the input string is input in an escaped form we need to create a function lval_read_str
which deals with this. This function is a little tricky because it has to do a few tasks. First it must strip the input string of the "
characters on either side. Then it must unescape the string, converting series of characters such as \n
to their actual encoded characters. Finally it has to create a new lval
and clean up anything that has happened in-between.
lval* lval_read_str(mpc_ast_t* t) {
/* Cut off the final quote character */
t->contents[strlen(t->contents)-1] = '\0';
/* Copy the string missing out the first quote character */
char* unescaped = malloc(strlen(t->contents+1)+1);
strcpy(unescaped, t->contents+1);
/* Pass through the unescape function */
unescaped = mpcf_unescape(unescaped);
/* Construct a new lval using the string */
lval* str = lval_str(unescaped);
/* Free the string and return */
free(unescaped);
return str;
}
If this all works we should be able to play around with strings in the prompt. Next we’ll add functions which can actually make use of them.
lispy> "hello"
"hello"
lispy> "hello\n"
"hello\n"
lispy> "hello\""
"hello\""
lispy> head {"hello" "world"}
{"hello"}
lispy> eval (head {"hello" "world"})
"hello"
lispy>