Adds support for backtick escaping in a raw string

This commit is contained in:
sloum 2022-10-16 15:01:33 -07:00
parent b90b979856
commit 2e303aea73
5 changed files with 38 additions and 14 deletions

View File

@ -58,8 +58,9 @@ slope recognizes a few types, that are generally created and used dynamically:
- as `#t` and `#f`
- anything can be cast to bool with a `#t` value, except `#f` (the only truly falsy value without using `~bool`, see below)
- `string`
- anything in "quotes"
- allows for backslash escapes within the quotes (for chars by number or shortcuts to tab, newline, etc)
- anything in "quotes" is a standard string literal
- allows for backslash escapes within the quotes (for chars by number or shortcuts to tab, newline, etc)
- anything in `backticks` is a raw string literal, no escapes occur and strings can span multiple lines
- `list`/`pair`
- includes the empty list (null)
- lists are the primary data structure

View File

@ -542,20 +542,29 @@ func createTimeFormatString(s string) string {
// Used by REPL to know if another input line
// should be offered before parsing
func stringParensMatch(s string) bool {
func stringParensMatch(s string) (bool, bool) {
count := 0
inString := false
prevPrev := rune(0)
inrawString := false
prev := ' '
for _, c := range s {
switch c {
case '(':
if !inString && prev != '\'' {
if !inString && !inrawString && prev != '\'' {
count++
}
case ')':
if !inString {
if !inString && !inrawString {
count--
}
case '`':
if !inrawString {
inrawString = true
count++
} else if prev != '\\' || (prev == '\\' && prevPrev == '\\') {
inrawString = false
count--
}
case '"':
@ -570,12 +579,12 @@ func stringParensMatch(s string) bool {
}
if count > 0 {
return false
return false, inrawString
}
// If count is negative still return true, the
// parser will handle erroring
return true
return true, inrawString
}
func variadic(l []expression) int {

View File

@ -548,6 +548,8 @@ func eval(exp expression, en *env) (value expression) {
}
var text strings.Builder
var cont bool
var raw bool
var match bool
for {
globalenv.vars[symbol("slope-interactive?")] = true
if linerState != nil {
@ -560,12 +562,14 @@ func eval(exp expression, en *env) (value expression) {
if in == "uninspect" || in == "(uninspect)" {
break
}
if len(strings.TrimSpace(in)) == 0 {
if len(strings.TrimSpace(in)) == 0 && !raw {
continue
}
text.WriteString(in)
text.WriteRune('\n')
if !stringParensMatch(text.String()) {
match, raw = stringParensMatch(text.String())
if !match {
cont = true
} else {
cont = false

View File

@ -47,11 +47,17 @@ func eatString(r *strings.Reader, delim rune) string {
if err != nil {
panic("Lexing error while eating string")
}
buf.WriteRune(c)
if delim == '`' && c == delim && (escapeCount > 0 && escapeCount%2 != 0) {
out := buf.String()
buf.Reset()
buf.WriteString(out[:len(out)-1])
buf.WriteRune(c)
} else {
buf.WriteRune(c)
}
if c == delim && (escapeCount == 0 || escapeCount%2 == 0) {
break
} else if c == '\n' || (c != delim && r.Len() == 0) {
fmt.Println(delim, c, r.Len())
} else if (delim == '"' && c == '\n') || (c != delim && r.Len() == 0) {
panic(fmt.Sprintf("Parse error: Unclosed string on line %d ", lexLine))
}
if c == '\\' {

View File

@ -141,6 +141,8 @@ func Repl() {
var text strings.Builder
var cont bool
var raw bool
var match bool
for {
globalenv.vars[symbol("slope-interactive?")] = true
if linerTerm != nil {
@ -150,12 +152,14 @@ func Repl() {
if initialTerm != nil {
initialTerm.ApplyMode()
}
if len(strings.TrimSpace(in)) == 0 {
if len(strings.TrimSpace(in)) == 0 && !raw {
continue
}
text.WriteString(in)
text.WriteRune('\n')
if !stringParensMatch(text.String()) {
match, raw = stringParensMatch(text.String())
if !match {
cont = true
} else {
cont = false