diff --git a/src/scanner.c b/src/scanner.c index 36792cf..8ef4b22 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -23,6 +23,7 @@ static Token identifier(Scanner *scanner); static Token number(Scanner *scanner); static Token string(Scanner *scanner); static void skipWhitespace(Scanner *scanner); +static Token interpolatedString(Scanner *scanner); /** * Initialize the scanner's state @@ -111,6 +112,11 @@ Token scanToken(Scanner *scanner) { return makeToken(scanner, TOKEN_MODULO); case '"': return string(scanner); + case '$': + if (match(scanner, '"')) { + return interpolatedString(scanner); + } + break; } return errorToken(scanner, "Unexpected character."); @@ -277,6 +283,20 @@ static Token number(Scanner *scanner) { return makeToken(scanner, TOKEN_NUMBER); } +static Token interpolatedString(Scanner *scanner) { + while (peek(scanner) != '"' && !isAtEnd(scanner)) { + advance(scanner); + } + + if (isAtEnd(scanner)) { + return errorToken(scanner, "Unterminated interpolated string."); + } + + advance(scanner); // closing quote + + return makeToken(scanner, TOKEN_INTERPOLATED_STRING); +} + static Token string(Scanner *scanner) { TRACELN("scanner.string()"); diff --git a/src/token.c b/src/token.c index c55e63f..271c4f0 100644 --- a/src/token.c +++ b/src/token.c @@ -44,6 +44,8 @@ const char *tokenTypeToString(TokenType type) { return "TOKEN_IDENTIFIER"; case TOKEN_STRING: return "TOKEN_STRING"; + case TOKEN_INTERPOLATED_STRING: + return "TOKEN_INTERPOLATED_STRING"; case TOKEN_NUMBER: return "TOKEN_NUMBER"; case TOKEN_AND: diff --git a/src/token.h b/src/token.h index b3cc1c0..12fbd1c 100644 --- a/src/token.h +++ b/src/token.h @@ -30,6 +30,7 @@ typedef enum { // Literals. TOKEN_IDENTIFIER, TOKEN_STRING, + TOKEN_INTERPOLATED_STRING, TOKEN_NUMBER, // Keywords. TOKEN_AND, diff --git a/tests/strings/string_interpolation.lox b/tests/strings/string_interpolation.lox new file mode 100644 index 0000000..e49dfb6 --- /dev/null +++ b/tests/strings/string_interpolation.lox @@ -0,0 +1,3 @@ +var name = "World"; + +print $"Hello {name}"; diff --git a/tests/strings/string_interpolation.lox.err b/tests/strings/string_interpolation.lox.err new file mode 100644 index 0000000..c064f2c --- /dev/null +++ b/tests/strings/string_interpolation.lox.err @@ -0,0 +1,81 @@ +== is == +0000 2 OP_GET_GLOBAL 0 'typeof' +0002 | OP_GET_LOCAL 1 +0004 | OP_CALL 1 +0006 | OP_GET_LOCAL 2 +0008 | OP_EQUAL +0009 | OP_RETURN +0010 3 OP_NIL +0011 | OP_RETURN +== is_function == +0000 6 OP_GET_GLOBAL 0 'is' +0002 | OP_GET_LOCAL 1 +0004 | OP_CONSTANT 1 'function' +0006 | OP_CALL 2 +0008 | OP_RETURN +0009 7 OP_NIL +0010 | OP_RETURN +== is_number == +0000 10 OP_GET_GLOBAL 0 'is' +0002 | OP_GET_LOCAL 1 +0004 | OP_CONSTANT 1 'number' +0006 | OP_CALL 2 +0008 | OP_RETURN +0009 11 OP_NIL +0010 | OP_RETURN +== is_string == +0000 14 OP_GET_GLOBAL 0 'is' +0002 | OP_GET_LOCAL 1 +0004 | OP_CONSTANT 1 'string' +0006 | OP_CALL 2 +0008 | OP_RETURN +0009 15 OP_NIL +0010 | OP_RETURN +== is_bool == +0000 18 OP_GET_GLOBAL 0 'is' +0002 | OP_GET_LOCAL 1 +0004 | OP_CONSTANT 1 'bool' +0006 | OP_CALL 2 +0008 | OP_RETURN +0009 19 OP_NIL +0010 | OP_RETURN +== is_nil == +0000 22 OP_GET_GLOBAL 0 'is' +0002 | OP_GET_LOCAL 1 +0004 | OP_CONSTANT 1 'nil' +0006 | OP_CALL 2 +0008 | OP_RETURN +0009 23 OP_NIL +0010 | OP_RETURN +== is_empty == +0000 26 OP_GET_GLOBAL 0 'is_string' +0002 | OP_GET_LOCAL 1 +0004 | OP_CALL 1 +0006 | OP_JUMP_IF_FALSE 6 -> 19 +0009 | OP_POP +0010 | OP_GET_GLOBAL 1 'len' +0012 | OP_GET_LOCAL 1 +0014 | OP_CALL 1 +0016 | OP_CONSTANT 2 '0.000000' +0018 | OP_EQUAL +0019 | OP_RETURN +0020 27 OP_NIL +0021 | OP_RETURN +==