Fixes issues with string unescaping re: literal backslashes
This commit is contained in:
parent
efb7ec71fd
commit
57b0a419f7
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
11
lexer.go
11
lexer.go
|
@ -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
17
lib.go
|
@ -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")
|
||||
|
|
2
main.go
2
main.go
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue
Block a user