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 ++++++++++++++++++++++++++-------------------------- subex/subexast.go | 9 +++- subex/subexstate.go | 43 ++++++++++++++---- 3 files changed, 105 insertions(+), 75 deletions(-) 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 { diff --git a/subex/subexast.go b/subex/subexast.go index 31c77ba..b1ac931 100644 --- a/subex/subexast.go +++ b/subex/subexast.go @@ -246,7 +246,14 @@ type OutputValueLiteralAST struct { atom walk.Value } func (ast OutputValueLiteralAST) compile(slotMap *SlotMap) OutputContent { - return OutputAtomLiteral {ast.atom} + return OutputValueLiteral {ast.atom} +} + +type OutputRuneLiteralAST struct { + rune walk.StringRuneAtom +} +func (ast OutputRuneLiteralAST) compile(slotMap *SlotMap) OutputContent { + return OutputRuneLiteral {ast.rune} } // Output a series of Atoms without reading anything from input diff --git a/subex/subexstate.go b/subex/subexstate.go index 7ffd592..0b21c93 100644 --- a/subex/subexstate.go +++ b/subex/subexstate.go @@ -121,29 +121,54 @@ func (state SubexStoreEndState) accepting(aux auxiliaryState) []OutputStack { // A part of an output literal, either an Atom or a slot from which to load type OutputContent interface { - // Given the current store, return the []Atom produced by the TransducerOutput - build(Store) walk.ValueList + // Given the current store, return the ValueList produced by the TransducerOutput + buildValues(Store) walk.ValueList + // Given the current store, return the RuneList produced by the TransducerOutput + buildRunes(Store) walk.RuneList } -// An OutputContent which is just an Atom literal -type OutputAtomLiteral struct { - atom walk.Value +// An OutputContent which is just a Value literal +type OutputValueLiteral struct { + value walk.Value } -func (replacement OutputAtomLiteral) build(store Store) walk.ValueList { - return walk.ValueList{replacement.atom} +func (replacement OutputValueLiteral) buildValues(store Store) walk.ValueList { + return walk.ValueList{replacement.value} +} +func (replacement OutputValueLiteral) buildRunes(store Store) walk.RuneList { + // TODO: serialise to JSON + panic("Unimplemented!") +} + +// An OutputContent which is just a rune literal +type OutputRuneLiteral struct { + rune walk.StringRuneAtom +} +func (replacement OutputRuneLiteral) buildValues(store Store) walk.ValueList { + // TODO: Try to deserialise + panic("Unimplemented!") +} +func (replacement OutputRuneLiteral) buildRunes(store Store) walk.RuneList { + return walk.RuneList {replacement.rune} } // An OutputContent which is a slot that is loaded from type OutputLoad struct { slot int } -func (replacement OutputLoad) build(store Store) walk.ValueList { +func (replacement OutputLoad) buildValues(store Store) walk.ValueList { values, isValues := store[replacement.slot].(walk.ValueList) if !isValues { panic("Tried to output non-values list") } return values } +func (replacement OutputLoad) buildRunes(store Store) walk.RuneList { + runes, isRunes := store[replacement.slot].(walk.RuneList) + if !isRunes { + panic("Tried to output non-runes as runes") + } + return runes +} // Don't read in anything, just output the series of data and slots specified type SubexOutputState struct { @@ -155,7 +180,7 @@ type SubexOutputState struct { func (state SubexOutputState) build(store Store) walk.ValueList { var result walk.ValueList for _, part := range state.content { - result = append(result, part.build(store)...) + result = append(result, part.buildValues(store)...) } return result } -- cgit v1.2.3