Fixes issues with string unescaping re: literal backslashes

This commit is contained in:
sloum 2021-09-06 15:40:15 -07:00
parent efb7ec71fd
commit 57b0a419f7
6 changed files with 33 additions and 8 deletions

View File

@ -208,7 +208,7 @@ So...
Implemented:
`length`, `cons`, `car`, `cdr`, `append`, `list`, `map`, `for-each`, `list->string`, `list-ref`, `list-sort`, `reverse`, `assoc`, `member?`
`length`, `cons`, `car`, `cdr`, `append`, `list`, `map`, `for-each`, `list->string`, `list-ref`, `list-sort`, `reverse`, `assoc`, `member?`, `list-seed`
- `list` can be shorthanded by using square brackets: `[1 2 3]` vs `(list 1 2 3)`
- `length` and `reverse` will take a string or a list.
@ -233,6 +233,7 @@ Implemented:
<li><code>(reverse [list|string])</code>: <code>list</code></li>
<li><code>(assoc [assoc-list] [key: value] [[value: value]])</code>: <code>value</code></li>
<li><code>(member? [list] [value])</code>: <code>bool</code></li>
<li><code>(list-seed [length: number] [value])</code>: <code>list</code> More efficient than recursion for building large lists and wont run into stack overflows</li>
</ul>
</details>

View File

@ -158,6 +158,7 @@ func unescapeString(s string) string {
var slash bool
for _, c := range []rune(s) {
fmt.Printf("%c\n", c)
if slash && !altNum {
switch c {
case 't':
@ -172,6 +173,8 @@ func unescapeString(s string) string {
out.WriteRune('\a')
case 'b':
out.WriteRune('\b')
case '\\':
out.WriteRune('\\')
case 'f':
out.WriteRune('\f')
case '0':
@ -522,6 +525,7 @@ func createTimeFormatString(s string) string {
func stringParensMatch(s string) bool {
count := 0
inString := false
prevPrev := rune(0)
prev := ' '
for _, c := range s {
@ -537,10 +541,11 @@ func stringParensMatch(s string) bool {
case '"':
if !inString {
inString = true
} else if prev != '\\' {
} else if prev != '\\' || (prev == '\\' && prevPrev == '\\') {
inString = false
}
}
prevPrev = c
prev = c
}

View File

@ -41,19 +41,20 @@ func eatWhiteSpace(r *strings.Reader) {
func eatString(r *strings.Reader) string {
var buf strings.Builder
buf.WriteRune('"')
prevPrev := rune(0)
previous := '"'
for r.Len() > 0 {
c, _, err := r.ReadRune()
if err != nil {
panic("Lexing error while eating string")
}
if c == '\n' || (c != '"' && r.Len() == 0) {
buf.WriteRune(c)
if c == '"' && (previous != '\\' || previous == '\\' && prevPrev == '\\') {
break
} else if c == '\n' || (c != '"' && r.Len() == 0) {
panic(fmt.Sprintf("Parse error: Unclosed string on line %d ", lexLine))
}
buf.WriteRune(c)
if c == '"' && previous != '\\' {
break
}
prevPrev = previous
previous = c
}
return buf.String()

17
lib.go
View File

@ -732,6 +732,23 @@ var stdLibrary = vars{
}
return strings.Join(sSlice, joinOn)
},
"list-seed": func(a ...expression) expression {
if len(a) < 2 {
return exception("insufficient number of arguments given to 'list-seed', expected number and value")
}
count, ok := a[0].(number)
if !ok {
return exception("'list-seed' expected a number as its first argument, but was given a non-number value")
}
if count < 1 {
return exception("'list-seed' expects a positive non-zero number as its first argument, a number less than 1 was given")
}
l := make([]expression, int(count))
for i := range l {
l[i] = a[1] // TODO may need to do a deep copy for slices that are passed this way
}
return l
},
"list-sort": func(a ...expression) expression {
if len(a) == 0 {
return exception("insufficient number of arguments given to 'list-sort', expected a list")

View File

@ -49,7 +49,7 @@ func String(v expression, rawString bool) string {
if rawString {
return fmt.Sprintf("\"%s\"", escapeString(v))
}
return unescapeString(v)
return v
case exception:
return string(v)
case bool:

View File

@ -76,6 +76,7 @@ var usageStrings = map[string]string{
"list": "(list [value...]) => list",
"list?": "(list? [value]) => bool",
"list-ref": "(list-ref [list] [index: number]) => value\n\nList indexing starts at 0",
"list-seed": "(list-seed [length: number] [value]) => list\n\nUsing list seed to create lists filled with a certain element is more efficient than recursion for large lists and will not overflow the stack\n\n`length` should be a positive number larger than zero. If a floating point number is given, it will be floored",
"list-sort": "(list-sort [list] [[sub-list-index: number]]) => list\n\nIf `list` is a list of lists, sorting can be done by the index value of a sublist if desired. `list-sort` always sorts the list in ascending order, but can be reversed with `reverse`",
"list->string": "(list->string [list] [[join-on: string]]) => string",
"load": "(load [filepath: string...]) => symbol\n\n`load` will open the file represented by filepath and evaluate its contents as slope . It will run any within the file. `load` can accept a relative reference, which will be treated as relative to the current working directory. Any `define` statements within the laoded file will result in the symbol getting added to the global environment",