<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/subex/parse.go
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-04-20 11:42:47 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-04-20 11:42:47 +0100
commitc1c33227ab72de1e5f21a08ee74c3df667148343 (patch)
tree6eb70d5bfa54a69129cdce14ea1b8902f23364c6 /subex/parse.go
parenta0a416e7762fcdcc066617da8083b0372b87155c (diff)
downloadstred-go-c1c33227ab72de1e5f21a08ee74c3df667148343.tar
Adds non-string literal syntax to subex
Diffstat (limited to 'subex/parse.go')
-rw-r--r--subex/parse.go83
1 files changed, 83 insertions, 0 deletions
diff --git a/subex/parse.go b/subex/parse.go
index e6efc2e..f95fd9f 100644
--- a/subex/parse.go
+++ b/subex/parse.go
@@ -2,6 +2,8 @@ package subex
import (
"main/walk"
+ "strconv"
+ "strings"
)
type RuneReader interface {
@@ -45,6 +47,68 @@ func parseTerminatorAtomLiteral(termType rune, l RuneReader) walk.Atom {
}
}
+func isNumericRune(r rune) bool {
+ return '0' <= r && r <= '9' || r == '.'
+}
+
+// Having just parsed a `, read until the next ` and parse the contents into a list of non-string atoms
+func parseNonStringLiteral(l RuneReader) (literals []walk.Atom) {
+ for {
+ r := l.Next()
+ if isNumericRune(r) {
+ var builder strings.Builder
+ builder.WriteRune(r)
+ for {
+ r := l.Next()
+ if !isNumericRune(r) {
+ l.Rewind()
+ break
+ }
+ builder.WriteRune(r)
+ }
+ numberString := builder.String()
+ number, err := strconv.ParseFloat(numberString, 64)
+ if err != nil {
+ panic("Invalid number literal")
+ }
+ literals = append(literals, walk.ValueNumber(number))
+ continue
+ }
+ switch r {
+ case '`':
+ return literals
+ case ' ', '\t':
+ continue
+ case 'n':
+ if accept(l, "u") && accept(l, "l") && accept(l, "l") {
+ literals = append(literals, walk.ValueNull{})
+ } else {
+ panic("Invalid literal")
+ }
+ case 't':
+ if accept(l, "r") && accept(l, "u") && accept(l, "e") {
+ literals = append(literals, walk.ValueBool(true))
+ } else {
+ panic("Invalid literal")
+ }
+ case 'f':
+ if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") {
+ literals = append(literals, walk.ValueBool(false))
+ } else {
+ panic("Invalid literal")
+ }
+ case '{':
+ literals = append(literals, walk.MapBegin)
+ case '}':
+ literals = append(literals, walk.MapEnd)
+ case '[':
+ literals = append(literals, walk.ArrayBegin)
+ case ']':
+ literals = append(literals, walk.ArrayEnd)
+ }
+ }
+}
+
func charIsDigit(c rune) bool {
return '0' <= c && c <= '9'
}
@@ -124,6 +188,11 @@ func parseReplacement(l RuneReader) (output []OutputContent) {
output = append(output, OutputLoad{slot: slot})
case '@', '~', '#':
output = append(output, OutputAtomLiteral{atom: parseTerminatorAtomLiteral(r, l)})
+ case '`':
+ literals := parseNonStringLiteral(l)
+ for _, literal := range literals {
+ output = append(output, OutputAtomLiteral {literal})
+ }
default:
output = append(output, OutputAtomLiteral{atom: walk.StringAtom(r)})
}
@@ -145,6 +214,10 @@ func parseRangeSubex(l RuneReader) map[walk.Atom]walk.Atom {
} else if fromsStart == '=' {
hasTo = true
break
+ } else if fromsStart == '`' {
+ literals := parseNonStringLiteral(l)
+ froms = append(froms, literals...)
+ continue
} else {
atom := parseTerminatorAtomLiteral(fromsStart, l)
if atom != nil {
@@ -175,6 +248,10 @@ func parseRangeSubex(l RuneReader) map[walk.Atom]walk.Atom {
tosStart := l.Next()
if tosStart == ']' {
break
+ } else if tosStart == '`' {
+ literals := parseNonStringLiteral(l)
+ tos = append(tos, literals...)
+ continue
} else {
atom := parseTerminatorAtomLiteral(tosStart, l)
if atom != nil {
@@ -232,6 +309,12 @@ func parseSubex(l RuneReader, minPower int) SubexAST {
lhs = SubexASTCopyAny{}
case '@', '#', '~':
lhs = SubexASTCopyAtom{atom: parseTerminatorAtomLiteral(r, l)}
+ case '`':
+ literals := parseNonStringLiteral(l)
+ lhs = SubexASTEmpty{}
+ for _, literal := range literals {
+ lhs = SubexASTConcat {lhs, SubexASTCopyAtom {literal}}
+ }
default:
lhs = SubexASTCopyAtom{atom: walk.StringAtom(r)}
}