diff --git a/compiler/src/Parse/String.hs b/compiler/src/Parse/String.hs index efdc1d532..84d601505 100644 --- a/compiler/src/Parse/String.hs +++ b/compiler/src/Parse/String.hs @@ -170,7 +170,7 @@ singleString pos end row col initialPos revChunks = -- MULTI STRINGS multiString :: Ptr Word8 -> Ptr Word8 -> Row -> Col -> Ptr Word8 -> Row -> Col -> StringResult -multiString pos end row col initialPos sr sc = +multiString pos end row _ _ sr sc = if pos >= end then Err sr sc E.StringEndless_Multi else @@ -179,7 +179,7 @@ multiString pos end row col initialPos sr sc = then let !pos1 = plusPtr pos 1 in countLeadingWhiteSpaceThenMultiString 0 pos1 end (row + 1) 1 pos1 sr sc - else countLeadingWhiteSpaceThenMultiString 0 pos end row col initialPos sr sc + else Err sr sc E.StringMultilineWithoutLeadingNewline countLeadingWhiteSpaceThenMultiString :: Int -> Ptr Word8 -> Ptr Word8 -> Row -> Col -> Ptr Word8 -> Row -> Col -> StringResult countLeadingWhiteSpaceThenMultiString count pos end row col initialPos sr sc = diff --git a/compiler/src/Reporting/Error/Syntax.hs b/compiler/src/Reporting/Error/Syntax.hs index e403f9fbf..5db10e526 100644 --- a/compiler/src/Reporting/Error/Syntax.hs +++ b/compiler/src/Reporting/Error/Syntax.hs @@ -196,7 +196,8 @@ data CustomType -- EXPRESSIONS data Expr - = Let Let Row Col + = ExpressionBadEnd Row Col + | Let Let Row Col | Case Case Row Col | If If Row Col | Parenthesized Parenthesized Row Col @@ -440,6 +441,7 @@ data String = StringEndless_Single | StringEndless_Multi | StringEscape Escape + | StringMultilineWithoutLeadingNewline deriving (Show) data Escape @@ -2597,6 +2599,8 @@ isWithin desiredNode context = toExprReport :: Code.Source -> Context -> Expr -> Row -> Col -> Report.Report toExprReport source context expr startRow startCol = case expr of + ExpressionBadEnd row col -> + toWeirdEndReport source row col Let let_ row col -> toLetReport source context let_ row col Case case_ row col -> @@ -2948,18 +2952,7 @@ toStringReport source string row col = D.toSimpleNote $ "For a string that spans multiple lines, you can use the multi-line string\ \ syntax like this:", - D.dullyellow $ - D.indent 4 $ - D.vcat $ - [ "\"\"\"", - "# Multi-line Strings", - "", - "- start with triple double quotes", - "- write whatever you want", - "- no need to escape newlines or double quotes", - "- end with triple double quotes", - "\"\"\"" - ] + D.dullyellow $ D.indent 3 validMultilineStringExample ] ) StringEndless_Multi -> @@ -2975,22 +2968,38 @@ toStringReport source string row col = [ D.reflow "Add a \"\"\" somewhere after this to end the string.", D.toSimpleNote $ "Here is a valid multi-line string for reference:", - D.dullyellow $ - D.indent 4 $ - D.vcat $ - [ "\"\"\"", - "# Multi-line Strings", - "", - "- start with triple double quotes", - "- write whatever you want", - "- no need to escape newlines or double quotes", - "- end with triple double quotes", - "\"\"\"" - ] + D.dullyellow $ D.indent 4 validMultilineStringExample ] ) StringEscape escape -> toEscapeReport source escape row col + StringMultilineWithoutLeadingNewline -> + let region = toRegion row col + in Report.Report "MULTILINE STRING WITHOUT LEADING NEWLINE" region [] $ + Code.toSnippet + source + region + Nothing + ( D.reflow "The contents of a multiline sting must start on a new line", + D.stack + [ D.reflow "Add a \"\"\" a new line right after the opening quotes.", + D.toSimpleNote "Here is a valid multi-line string for reference:", + D.dullyellow $ D.indent 4 validMultilineStringExample + ] + ) + +validMultilineStringExample :: D.Doc +validMultilineStringExample = + D.vcat + [ "\"\"\"", + "# Multi-line Strings", + "", + "- start with triple double quotes", + "- write whatever you want", + "- no need to escape newlines or double quotes", + "- end with triple double quotes", + "\"\"\"" + ] -- ESCAPES diff --git a/tests/Parse/MultilineStringSpec.hs b/tests/Parse/MultilineStringSpec.hs index f1b30ba10..a4daef731 100644 --- a/tests/Parse/MultilineStringSpec.hs +++ b/tests/Parse/MultilineStringSpec.hs @@ -7,7 +7,9 @@ import Data.ByteString qualified as BS import Data.Utf8 qualified as Utf8 import Helpers.Instances () import Helpers.Parse qualified as Helpers +import Parse.Expression qualified as Expression import Parse.Pattern qualified as Pattern +import Reporting.Error.Syntax (Expr (ExpressionBadEnd)) import Reporting.Error.Syntax qualified as Error.Syntax import Test.Hspec (Spec, describe, it) @@ -17,12 +19,12 @@ spec = do it "regression test" $ parse "normal string" - "\"\"\"normal string\"\"\"" + "\"\"\"\nnormal string\"\"\"" it "mixing quotes work" $ do parse "string with \" in it" - "\"\"\"string with \" in it\"\"\"" + "\"\"\"\nstring with \" in it\"\"\"" it "first newline, and leading whitespace, is dropped" $ do parse @@ -39,6 +41,11 @@ spec = do "this is\\na test" "\"\"\"\n this is\n a test\n\"\"\"" + it "does not allow non-newline characters on the first line" $ do + let isCorrectError ((Error.Syntax.String Error.Syntax.StringMultilineWithoutLeadingNewline _ _)) = True + isCorrectError _ = False + Helpers.checkParseError Expression.expression ExpressionBadEnd isCorrectError "\"\"\"this is not allowed\"\"\"" + parse :: String -> BS.ByteString -> IO () parse expectedStr = let isExpectedString :: Src.Pattern_ -> Bool