From bed0e712deda5038f52e495bacae003098df7a55 Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Fri, 21 Jul 2023 16:42:49 +0100 Subject: Reimplements inserting basic values using subexes --- subex/parse.go | 128 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 63 insertions(+), 65 deletions(-) (limited to 'subex/parse.go') diff --git a/subex/parse.go b/subex/parse.go index 6c19df4..2392b22 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -27,54 +27,47 @@ func isNumericRune(r rune) bool { } // 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.Scalar) { - for { - r := l.Next() - if isNumericRune(r) { - var builder strings.Builder +func parseScalarLiteral(l RuneReader) (walk.Scalar, bool) { + 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) - 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") + } + return walk.NumberScalar(number), true + } + switch r { + case 'n': + if accept(l, "u") && accept(l, "l") && accept(l, "l") { + return walk.NullScalar{}, true + } else { + panic("Invalid literal") } - numberString := builder.String() - number, err := strconv.ParseFloat(numberString, 64) - if err != nil { - panic("Invalid number literal") + case 't': + if accept(l, "r") && accept(l, "u") && accept(l, "e") { + return walk.BoolScalar(true), true + } else { + panic("Invalid literal") } - literals = append(literals, walk.NumberScalar(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.NullScalar{}) - } else { - panic("Invalid literal") - } - case 't': - if accept(l, "r") && accept(l, "u") && accept(l, "e") { - literals = append(literals, walk.BoolScalar(true)) - } else { - panic("Invalid literal") - } - case 'f': - if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") { - literals = append(literals, walk.BoolScalar(false)) - } else { - panic("Invalid literal") - } - default: + case 'f': + if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") { + return walk.BoolScalar(false), true + } else { panic("Invalid literal") - } + } + default: + panic("Invalid literal") } } @@ -140,14 +133,16 @@ func parseRepeatRange(l RuneReader) (output []ConvexRange) { return output } -func parseReplacement(l RuneReader) (output []OutputContentAST) { +// TODO: Consider if it's worth making better use of the go type system to enforce output being all runes or all values +func parseReplacement(l RuneReader, runic bool) (output []OutputContentAST) { // TODO escaping + // TODO add arrays, maps and strings loop: for { r := l.Next() switch r { case eof: - panic("Missing closing \"") - case '=', '^', ':': + panic("Missing closing `") + case '`': break loop case '$': slot := l.Next() @@ -155,14 +150,17 @@ func parseReplacement(l RuneReader) (output []OutputContentAST) { panic("Missing slot character") } output = append(output, OutputLoadAST{slot: slot}) - case '`': - literals := parseNonStringLiteral(l) - for _, literal := range literals { - output = append(output, OutputValueLiteralAST {literal}) - } default: - panic("Invalid value to insert") - //output = append(output, OutputValueLiteralAST{atom: walk.NewAtomStringRune(r)}) + if runic { + output = append(output, OutputRuneLiteralAST {walk.StringRuneAtom(r)}) + } else { + l.Rewind() + scalar, ok := parseScalarLiteral(l) + if !ok { + panic("Invalid scalar literal") + } + output = append(output, OutputValueLiteralAST {scalar}) + } } } return output @@ -265,15 +263,9 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { case ')', ']', '"', '|', ';', '{', '+', '-', '*', '/', '!', '$': l.Rewind() return SubexASTEmpty{} - case '=': - replacement := parseReplacement(l) - lhs = SubexASTOutput{replacement} - case '`': - literals := parseNonStringLiteral(l) - lhs = SubexASTEmpty{} - for _, literal := range literals { - lhs = SubexASTConcat {lhs, SubexASTCopyScalar {literal}} - } + // case '=': + // replacement := parseReplacement(l) + // lhs = SubexASTOutput{replacement} // case '^': // replacement := parseReplacement(l) // replacement = append( @@ -307,6 +299,8 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { panic("Missing matching ]") } } + case '`': + lhs = SubexASTOutput {parseReplacement(l, runic)} case '~': if runic { lhs = SubexASTCopyRune {'~'} @@ -339,8 +333,12 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { if runic { lhs = SubexASTCopyRune {r} } else { - // TODO: Allow whitespace outside of runic sections - panic("Tried to match rune outside of string") + l.Rewind() + scalar, ok := parseScalarLiteral(l) + if !ok { + panic("Invalid subex") + } + lhs = SubexASTCopyScalar {scalar} } } loop: for { -- cgit v1.2.3