+/**
+ * Parse a wide string literal and set lexer_token.
+ */
+static void parse_wide_string_literal(void)
+{
+ const unsigned start_linenr = lexer_token.source_position.linenr;
+
+ assert(c == '"');
+ next_char();
+
+ while(1) {
+ switch(c) {
+ case '\\': {
+ wchar_rep_t tc = parse_escape_sequence();
+ obstack_grow(&symbol_obstack, &tc, sizeof(tc));
+ break;
+ }
+
+ case EOF: {
+ source_position_t source_position;
+ source_position.input_name = lexer_token.source_position.input_name;
+ source_position.linenr = start_linenr;
+ errorf(source_position, "string has no end");
+ lexer_token.type = T_ERROR;
+ return;
+ }
+
+ case '"':
+ next_char();
+ goto end_of_string;
+
+ default: {
+ wchar_rep_t tc = c;
+ obstack_grow(&symbol_obstack, &tc, sizeof(tc));
+ next_char();
+ break;
+ }
+ }
+ }
+
+end_of_string:;
+
+ /* TODO: concatenate multiple strings separated by whitespace... */
+
+ /* add finishing 0 to the string */
+ wchar_rep_t nul = L'\0';
+ obstack_grow(&symbol_obstack, &nul, sizeof(nul));
+ const size_t size = (size_t)obstack_object_size(&symbol_obstack) / sizeof(wchar_rep_t);
+ const wchar_rep_t *const string = obstack_finish(&symbol_obstack);
+
+#if 0 /* TODO hash */
+ /* check if there is already a copy of the string */
+ const wchar_rep_t *const result = strset_insert(&stringset, string);
+ if(result != string) {
+ obstack_free(&symbol_obstack, string);
+ }
+#else
+ const wchar_rep_t *const result = string;
+#endif
+
+ lexer_token.type = T_WIDE_STRING_LITERAL;
+ lexer_token.v.wide_string.begin = result;
+ lexer_token.v.wide_string.size = size;
+}
+
+/**
+ * Parse a character constant and set lexer_token.
+ */
+static void parse_character_constant(void)
+{
+ const unsigned start_linenr = lexer_token.source_position.linenr;
+
+ eat('\'');
+
+ int tc;
+ while(1) {
+ switch(c) {
+ case '\\':
+ tc = parse_escape_sequence();
+ obstack_1grow(&symbol_obstack, (char) tc);
+ break;
+
+ MATCH_NEWLINE(
+ parse_error("newline while parsing character constant");
+ break;
+ )
+
+ case EOF: {
+ source_position_t source_position;
+ source_position.input_name = lexer_token.source_position.input_name;
+ source_position.linenr = start_linenr;
+ errorf(source_position, "EOF while parsing character constant");
+ lexer_token.type = T_ERROR;
+ return;
+ }
+
+ case '\'':
+ next_char();
+ goto end_of_char_constant;
+
+ default:
+ obstack_1grow(&symbol_obstack, (char) c);
+ next_char();
+ break;
+
+ }
+ }
+
+end_of_char_constant:;
+ const size_t size = (size_t)obstack_object_size(&symbol_obstack);
+ const char *const string = obstack_finish(&symbol_obstack);
+
+ lexer_token.type = T_CHARS;
+ lexer_token.v.string.begin = string;
+ lexer_token.v.string.size = size;
+ lexer_token.datatype = type_int;
+}
+
+/**
+ * Skip a multiline comment.
+ */