Adds support for backtick escaping in a raw string
This commit is contained in:
parent
b90b979856
commit
2e303aea73
|
@ -58,8 +58,9 @@ slope recognizes a few types, that are generally created and used dynamically:
|
||||||
- as `#t` and `#f`
|
- 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)
|
- anything can be cast to bool with a `#t` value, except `#f` (the only truly falsy value without using `~bool`, see below)
|
||||||
- `string`
|
- `string`
|
||||||
- anything in "quotes"
|
- 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)
|
- 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`
|
- `list`/`pair`
|
||||||
- includes the empty list (null)
|
- includes the empty list (null)
|
||||||
- lists are the primary data structure
|
- lists are the primary data structure
|
||||||
|
|
19
helpers.go
19
helpers.go
|
@ -542,20 +542,29 @@ func createTimeFormatString(s string) string {
|
||||||
|
|
||||||
// Used by REPL to know if another input line
|
// Used by REPL to know if another input line
|
||||||
// should be offered before parsing
|
// should be offered before parsing
|
||||||
func stringParensMatch(s string) bool {
|
func stringParensMatch(s string) (bool, bool) {
|
||||||
count := 0
|
count := 0
|
||||||
inString := false
|
inString := false
|
||||||
prevPrev := rune(0)
|
prevPrev := rune(0)
|
||||||
|
inrawString := false
|
||||||
prev := ' '
|
prev := ' '
|
||||||
|
|
||||||
for _, c := range s {
|
for _, c := range s {
|
||||||
switch c {
|
switch c {
|
||||||
case '(':
|
case '(':
|
||||||
if !inString && prev != '\'' {
|
if !inString && !inrawString && prev != '\'' {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
case ')':
|
case ')':
|
||||||
if !inString {
|
if !inString && !inrawString {
|
||||||
|
count--
|
||||||
|
}
|
||||||
|
case '`':
|
||||||
|
if !inrawString {
|
||||||
|
inrawString = true
|
||||||
|
count++
|
||||||
|
} else if prev != '\\' || (prev == '\\' && prevPrev == '\\') {
|
||||||
|
inrawString = false
|
||||||
count--
|
count--
|
||||||
}
|
}
|
||||||
case '"':
|
case '"':
|
||||||
|
@ -570,12 +579,12 @@ func stringParensMatch(s string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
return false
|
return false, inrawString
|
||||||
}
|
}
|
||||||
|
|
||||||
// If count is negative still return true, the
|
// If count is negative still return true, the
|
||||||
// parser will handle erroring
|
// parser will handle erroring
|
||||||
return true
|
return true, inrawString
|
||||||
}
|
}
|
||||||
|
|
||||||
func variadic(l []expression) int {
|
func variadic(l []expression) int {
|
||||||
|
|
|
@ -548,6 +548,8 @@ func eval(exp expression, en *env) (value expression) {
|
||||||
}
|
}
|
||||||
var text strings.Builder
|
var text strings.Builder
|
||||||
var cont bool
|
var cont bool
|
||||||
|
var raw bool
|
||||||
|
var match bool
|
||||||
for {
|
for {
|
||||||
globalenv.vars[symbol("slope-interactive?")] = true
|
globalenv.vars[symbol("slope-interactive?")] = true
|
||||||
if linerState != nil {
|
if linerState != nil {
|
||||||
|
@ -560,12 +562,14 @@ func eval(exp expression, en *env) (value expression) {
|
||||||
if in == "uninspect" || in == "(uninspect)" {
|
if in == "uninspect" || in == "(uninspect)" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(strings.TrimSpace(in)) == 0 {
|
if len(strings.TrimSpace(in)) == 0 && !raw {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
text.WriteString(in)
|
text.WriteString(in)
|
||||||
text.WriteRune('\n')
|
text.WriteRune('\n')
|
||||||
if !stringParensMatch(text.String()) {
|
|
||||||
|
match, raw = stringParensMatch(text.String())
|
||||||
|
if !match {
|
||||||
cont = true
|
cont = true
|
||||||
} else {
|
} else {
|
||||||
cont = false
|
cont = false
|
||||||
|
|
12
lexer.go
12
lexer.go
|
@ -47,11 +47,17 @@ func eatString(r *strings.Reader, delim rune) string {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Lexing error while eating string")
|
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) {
|
if c == delim && (escapeCount == 0 || escapeCount%2 == 0) {
|
||||||
break
|
break
|
||||||
} else if c == '\n' || (c != delim && r.Len() == 0) {
|
} else if (delim == '"' && c == '\n') || (c != delim && r.Len() == 0) {
|
||||||
fmt.Println(delim, c, r.Len())
|
|
||||||
panic(fmt.Sprintf("Parse error: Unclosed string on line %d ", lexLine))
|
panic(fmt.Sprintf("Parse error: Unclosed string on line %d ", lexLine))
|
||||||
}
|
}
|
||||||
if c == '\\' {
|
if c == '\\' {
|
||||||
|
|
8
main.go
8
main.go
|
@ -141,6 +141,8 @@ func Repl() {
|
||||||
|
|
||||||
var text strings.Builder
|
var text strings.Builder
|
||||||
var cont bool
|
var cont bool
|
||||||
|
var raw bool
|
||||||
|
var match bool
|
||||||
for {
|
for {
|
||||||
globalenv.vars[symbol("slope-interactive?")] = true
|
globalenv.vars[symbol("slope-interactive?")] = true
|
||||||
if linerTerm != nil {
|
if linerTerm != nil {
|
||||||
|
@ -150,12 +152,14 @@ func Repl() {
|
||||||
if initialTerm != nil {
|
if initialTerm != nil {
|
||||||
initialTerm.ApplyMode()
|
initialTerm.ApplyMode()
|
||||||
}
|
}
|
||||||
if len(strings.TrimSpace(in)) == 0 {
|
if len(strings.TrimSpace(in)) == 0 && !raw {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
text.WriteString(in)
|
text.WriteString(in)
|
||||||
text.WriteRune('\n')
|
text.WriteRune('\n')
|
||||||
if !stringParensMatch(text.String()) {
|
|
||||||
|
match, raw = stringParensMatch(text.String())
|
||||||
|
if !match {
|
||||||
cont = true
|
cont = true
|
||||||
} else {
|
} else {
|
||||||
cont = false
|
cont = false
|
||||||
|
|
Loading…
Reference in New Issue