From c1c33227ab72de1e5f21a08ee74c3df667148343 Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Thu, 20 Apr 2023 11:42:47 +0100 Subject: Adds non-string literal syntax to subex --- subex/parse.go | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'subex/parse.go') 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)} } -- cgit v1.2.3