Skip to content

Commit

Permalink
Add a compilation error for invalid escapes in string literals.
Browse files Browse the repository at this point in the history
This helps to prevent bugs wherein the user believes they have
correctly written an escape code.

Prior to this commit, invalid escape code were treated as if
they were literal characters, including the backslash character.

See discussion in #348
  • Loading branch information
jemc committed Sep 8, 2022
1 parent 207a4f4 commit 97dee89
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 11 deletions.
19 changes: 19 additions & 0 deletions spec/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,25 @@ describe Savi::Parser do
end
end

it "complains when a string literal has an unknown escape character" do
source = Savi::Source.new_example <<-SOURCE
:actor Main
:new
greeting = "Hello, World\\?"
SOURCE

expected = <<-MSG
This is an invalid escape character:
from (example):3:
greeting = "Hello, World\\?"
^
MSG

expect_raises Savi::Error, expected do
Savi::Parser.parse(source)
end
end

it "handles nifty heredoc string literals" do
source = Savi::Source.new_example <<-SOURCE
:actor Main
Expand Down
30 changes: 19 additions & 11 deletions src/savi/parser/builder/state.cr
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ module Savi::Parser::Builder
)
end

def pos_single_with_offset(
token : Pegmatite::Token,
offset : Int32,
) : Source::Pos
kind, start, finish = token

start = start + offset
finish = start + 1

pos({kind, start, finish})
end

def slice(token : Pegmatite::Token)
kind, start, finish = token
slice(start...finish)
Expand All @@ -58,11 +70,7 @@ module Savi::Parser::Builder
end

def slice_with_escapes(token : Pegmatite::Token)
kind, start, finish = token
slice_with_escapes(start...finish)
end

def slice_with_escapes(range : Range)
range = token[1]...token[2]
string = slice(range)
reader = Char::Reader.new(string)

Expand Down Expand Up @@ -91,7 +99,8 @@ module Savi::Parser::Builder
elsif 'A' <= hex_char <= 'F'
10 + (hex_char - 'A')
else
raise "invalid escape hex character: #{hex_char}"
Error.at pos_single_with_offset(token, reader.pos),
"This is an invalid escape hex character"
end
byte_value = 16 * byte_value + hex_value
end
Expand All @@ -108,7 +117,8 @@ module Savi::Parser::Builder
elsif 'A' <= hex_char <= 'F'
10 + (hex_char - 'A')
else
raise "invalid unicode escape hex character: #{hex_char}"
Error.at pos_single_with_offset(token, reader.pos),
"This is an invalid unicode escape hex character"
end
codepoint = 16 * codepoint + hex_value
end
Expand All @@ -126,10 +136,8 @@ module Savi::Parser::Builder
reader.next_char
end
else
# Not a valid escape character - pass it on as a literal slash
# followed by that literal character, as if not an escape.
result << '\\'
result << reader.current_char
Error.at pos_single_with_offset(token, reader.pos),
"This is an invalid escape character"
end
else
result << reader.current_char
Expand Down

0 comments on commit 97dee89

Please sign in to comment.