From 12c1d179f32c38a929fcc9adb326a9f44c8288ae Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Fri, 21 Apr 2023 16:22:16 +0100 Subject: Replaces the interfaces implementation of Atom with a tagged union based implementation --- subex/arithmetic.go | 32 +++++------ subex/parse.go | 40 +++++++------- subex/subexast.go | 4 +- subex/subexstate.go | 13 ++--- walk/walk.go | 150 ++++++++++++++++++++++++++++++++++++++++------------ 5 files changed, 157 insertions(+), 82 deletions(-) diff --git a/subex/arithmetic.go b/subex/arithmetic.go index a7dc73a..1ebd1a6 100644 --- a/subex/arithmetic.go +++ b/subex/arithmetic.go @@ -39,9 +39,9 @@ func sumValues(atoms []walk.Atom) ([]walk.Atom, error) { } } if allBools { - return []walk.Atom{walk.ValueBool(any)}, nil + return []walk.Atom{walk.NewAtomBool(any)}, nil } else { - return []walk.Atom{walk.ValueNumber(sum)}, nil + return []walk.Atom{walk.NewAtomNumber(sum)}, nil } } @@ -80,9 +80,9 @@ func multiplyValues(atoms []walk.Atom) ([]walk.Atom, error) { } } if allBools { - return []walk.Atom{walk.ValueBool(all)}, nil + return []walk.Atom{walk.NewAtomBool(all)}, nil } else { - return []walk.Atom{walk.ValueNumber(product)}, nil + return []walk.Atom{walk.NewAtomNumber(product)}, nil } } @@ -96,19 +96,19 @@ func negateValues(atoms []walk.Atom) ([]walk.Atom, error) { for _, value := range values { switch v := value.(type) { case walk.ValueNull: - negatedNumbers = append(negatedNumbers, walk.ValueNumber(0)) + negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(0)) case walk.ValueBool: if bool(v) { - negatedNumbers = append(negatedNumbers, walk.ValueNumber(-1)) + negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(-1)) } else { - negatedNumbers = append(negatedNumbers, walk.ValueNumber(0)) + negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(0)) } case walk.ValueNumber: - negatedNumbers = append(negatedNumbers, walk.ValueNumber(-v)) + negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(-float64(v))) case walk.ValueString: num, err := strconv.ParseFloat(string(v), 64) if err == nil { - negatedNumbers = append(negatedNumbers, walk.ValueNumber(-num)) + negatedNumbers = append(negatedNumbers, walk.NewAtomNumber(-num)) } else { return nil, errors.New("Tried to negate non-castable string") } @@ -133,16 +133,16 @@ func reciprocalValues(atoms []walk.Atom) ([]walk.Atom, error) { return nil, errors.New("Tried to take reciprocal of null") case walk.ValueBool: if bool(v) { - reciprocals = append(reciprocals, walk.ValueNumber(1)) + reciprocals = append(reciprocals, walk.NewAtomNumber(1)) } else { return nil, errors.New("Tried to take reciprocal of false") } case walk.ValueNumber: - reciprocals = append(reciprocals, walk.ValueNumber(1 / v)) + reciprocals = append(reciprocals, walk.NewAtomNumber(1 / float64(v))) case walk.ValueString: num, err := strconv.ParseFloat(string(v), 64) if err == nil { - reciprocals = append(reciprocals, walk.ValueNumber(1 / num)) + reciprocals = append(reciprocals, walk.NewAtomNumber(1 / num)) } else { return nil, errors.New("Tried to take reciprocal of non-castable string") } @@ -163,13 +163,13 @@ func notValues(atoms []walk.Atom) (notted []walk.Atom, err error) { for _, value := range values { switch v := value.(type) { case walk.ValueNull: - notted = append(notted, walk.ValueBool(true)) + notted = append(notted, walk.NewAtomBool(true)) case walk.ValueBool: - notted = append(notted, walk.ValueBool(!v)) + notted = append(notted, walk.NewAtomBool(!bool(v))) case walk.ValueNumber: - notted = append(notted, walk.ValueBool(v == 0)) + notted = append(notted, walk.NewAtomBool(v == 0)) case walk.ValueString: - notted = append(notted, walk.ValueBool(len(v) == 0)) + notted = append(notted, walk.NewAtomBool(len(v) == 0)) default: return nil, errors.New("Tried to NOT non-boolean") } diff --git a/subex/parse.go b/subex/parse.go index 4e7a3f6..de53e2a 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -57,7 +57,7 @@ func parseNonStringLiteral(l RuneReader) (literals []walk.Atom) { if err != nil { panic("Invalid number literal") } - literals = append(literals, walk.ValueNumber(number)) + literals = append(literals, walk.NewAtomNumber(number)) continue } switch r { @@ -67,30 +67,30 @@ func parseNonStringLiteral(l RuneReader) (literals []walk.Atom) { continue case 'n': if accept(l, "u") && accept(l, "l") && accept(l, "l") { - literals = append(literals, walk.ValueNull{}) + literals = append(literals, walk.NewAtomNull()) } else { panic("Invalid literal") } case 't': if accept(l, "r") && accept(l, "u") && accept(l, "e") { - literals = append(literals, walk.ValueBool(true)) + literals = append(literals, walk.NewAtomBool(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)) + literals = append(literals, walk.NewAtomBool(false)) } else { panic("Invalid literal") } case '{': - literals = append(literals, walk.MapBegin) + literals = append(literals, walk.NewAtomTerminal(walk.MapBegin)) case '}': - literals = append(literals, walk.MapEnd) + literals = append(literals, walk.NewAtomTerminal(walk.MapEnd)) case '[': - literals = append(literals, walk.ArrayBegin) + literals = append(literals, walk.NewAtomTerminal(walk.ArrayBegin)) case ']': - literals = append(literals, walk.ArrayEnd) + literals = append(literals, walk.NewAtomTerminal(walk.ArrayEnd)) default: panic("Invalid literal") } @@ -180,9 +180,9 @@ func parseReplacement(l RuneReader) (output []OutputContent) { output = append(output, OutputAtomLiteral {literal}) } case '"': - output = append(output, OutputAtomLiteral {walk.StringTerminal{}}) + output = append(output, OutputAtomLiteral {walk.NewAtomStringTerminal()}) default: - output = append(output, OutputAtomLiteral{atom: walk.StringAtom(r)}) + output = append(output, OutputAtomLiteral{atom: walk.NewAtomStringRune(r)}) } } return output @@ -207,7 +207,7 @@ func parseRangeSubex(l RuneReader) map[walk.Atom]walk.Atom { froms = append(froms, literals...) continue } else if fromsStart == '"' { - froms = append(froms, walk.StringTerminal{}) + froms = append(froms, walk.NewAtomStringTerminal()) continue } if accept(l, "-") { @@ -217,10 +217,10 @@ func parseRangeSubex(l RuneReader) map[walk.Atom]walk.Atom { fromsEnd = fromsStart } for i := fromsStart; i <= fromsEnd; i += 1 { - froms = append(froms, walk.StringAtom(i)) + froms = append(froms, walk.NewAtomStringRune(i)) } } else { - froms = append(froms, walk.StringAtom(fromsStart)) + froms = append(froms, walk.NewAtomStringRune(fromsStart)) } } if len(froms) == 0 { @@ -238,7 +238,7 @@ func parseRangeSubex(l RuneReader) map[walk.Atom]walk.Atom { tos = append(tos, literals...) continue } else if tosStart == '"' { - tos = append(tos, walk.StringTerminal{}) + tos = append(tos, walk.NewAtomStringTerminal()) continue } if accept(l, "-") { @@ -248,10 +248,10 @@ func parseRangeSubex(l RuneReader) map[walk.Atom]walk.Atom { tosEnd = tosStart } for i := tosStart; i <= tosEnd; i += 1 { - tos = append(tos, walk.StringAtom(i)) + tos = append(tos, walk.NewAtomStringRune(i)) } } else { - tos = append(tos, walk.StringAtom(tosStart)) + tos = append(tos, walk.NewAtomStringRune(tosStart)) } } } else { @@ -296,12 +296,12 @@ func parseSubex(l RuneReader, minPower int) SubexAST { case '^': replacement := parseReplacement(l) replacement = append( - []OutputContent{OutputAtomLiteral {walk.StringTerminal{}}}, + []OutputContent{OutputAtomLiteral {walk.NewAtomStringTerminal()}}, replacement... ) replacement = append( replacement, - OutputAtomLiteral {walk.StringTerminal{}}, + OutputAtomLiteral {walk.NewAtomStringTerminal()}, ) lhs = SubexASTOutput {replacement} case '.': @@ -317,7 +317,7 @@ func parseSubex(l RuneReader, minPower int) SubexAST { case ',': lhs = SubexASTCopyValue{} case '"': - lhs = SubexASTCopyAtom {walk.StringTerminal{}} + lhs = SubexASTCopyAtom {walk.NewAtomStringTerminal()} case '~': literals := parseNonStringLiteral(l) var replacement []OutputContent @@ -326,7 +326,7 @@ func parseSubex(l RuneReader, minPower int) SubexAST { } lhs = SubexASTOutput {replacement} default: - lhs = SubexASTCopyAtom{Atom: walk.StringAtom(r)} + lhs = SubexASTCopyAtom{Atom: walk.NewAtomStringRune(r)} } loop: for { if minPower <= 20 { diff --git a/subex/subexast.go b/subex/subexast.go index dd98aa9..92c099a 100644 --- a/subex/subexast.go +++ b/subex/subexast.go @@ -179,14 +179,14 @@ func (ast SubexASTCopyString) compileWith(next SubexState) SubexState { } stringContentState := &SubexGroupState { &SubexCopyAtomState { - atom: walk.StringTerminal{}, + atom: walk.NewAtomStringTerminal(), next: next, }, stringAtomState, } stringAtomState.next = stringContentState return &SubexCopyAtomState { - atom: walk.StringTerminal{}, + atom: walk.NewAtomStringTerminal(), next: stringContentState, } } diff --git a/subex/subexstate.go b/subex/subexstate.go index b5e1e9b..56063c0 100644 --- a/subex/subexstate.go +++ b/subex/subexstate.go @@ -153,8 +153,7 @@ type SubexCopyBoolState struct { next SubexState } func (state SubexCopyBoolState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch { - _, isBool := char.(walk.ValueBool) - if isBool { + if char.Typ == walk.AtomBool { return []SubexBranch{{ state: state.next, outputStack: topAppend(outputStack, []walk.Atom{char}), @@ -172,8 +171,7 @@ type SubexCopyNumberState struct { next SubexState } func (state SubexCopyNumberState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch { - _, isNumber := char.(walk.ValueNumber) - if isNumber { + if char.Typ == walk.AtomNumber { return []SubexBranch{{ state: state.next, outputStack: topAppend(outputStack, []walk.Atom{char}), @@ -191,8 +189,7 @@ type SubexCopyStringAtomState struct { next SubexState } func (state SubexCopyStringAtomState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch { - _, isStringAtom := char.(walk.StringAtom) - if isStringAtom { + if char.Typ == walk.AtomStringRune { return []SubexBranch{{ state: state.next, outputStack: topAppend(outputStack, []walk.Atom{char}), @@ -210,9 +207,7 @@ type SubexCopyNonStringAtomState struct { next SubexState } func (state SubexCopyNonStringAtomState) eat(store Store, outputStack OutputStack, char walk.Atom) []SubexBranch { - _, isStringAtom := char.(walk.StringAtom) - _, isStringTerminal := char.(walk.StringTerminal) - if isStringAtom || isStringTerminal { + if char.Typ == walk.AtomStringRune || char.Typ == walk.AtomStringTerminal { return nil } return []SubexBranch{{ diff --git a/walk/walk.go b/walk/walk.go index 490a6f2..949b6a2 100644 --- a/walk/walk.go +++ b/walk/walk.go @@ -50,7 +50,7 @@ const ( MapEnd ) func (value TerminalValue) Atomise(in []Atom) []Atom { - return append(in, value) + return append(in, NewAtomTerminal(value)) } func (value TerminalValue) String() string { switch value { @@ -69,7 +69,7 @@ func (value TerminalValue) String() string { type ValueNull struct {} func (value ValueNull) Atomise(in []Atom) []Atom { - return append(in, value) + return append(in, NewAtomNull()) } func (value ValueNull) String() string { return "null" @@ -77,7 +77,7 @@ func (value ValueNull) String() string { type ValueBool bool func (value ValueBool) Atomise(in []Atom) []Atom { - return append(in, value) + return append(in, NewAtomBool(bool(value))) } func (value ValueBool) String() string { if value { @@ -89,38 +89,113 @@ func (value ValueBool) String() string { type ValueNumber float64 func (value ValueNumber) Atomise(in []Atom) []Atom { - return append(in, value) + return append(in, NewAtomNumber(float64(value))) } func (value ValueNumber) String() string { v := float64(value) return fmt.Sprintf("%f", v) } -type StringTerminal struct {} -func (value StringTerminal) String() string { - return "\"" -} - -type StringAtom rune -func (value StringAtom) String() string { - return string(value) -} - type ValueString string func (value ValueString) Atomise(in []Atom) []Atom { - in = append(in, StringTerminal{}) + in = append(in, NewAtomStringTerminal()) for _, char := range value { - in = append(in, StringAtom(char)) + in = append(in, NewAtomStringRune(char)) } - in = append(in, StringTerminal{}) + in = append(in, NewAtomStringTerminal()) return in } func (value ValueString) String() string { return fmt.Sprintf("\"%s\"", string(value)) } -type Atom interface { - String() string +type AtomType int64 +const ( + AtomNull AtomType = iota + AtomBool + AtomNumber + AtomTerminal + AtomStringTerminal + AtomStringRune +) +type Atom struct { + Typ AtomType + data uint64 +} +func NewAtomNull() Atom { + return Atom { + Typ: AtomNull, + data: 0, + } +} +func NewAtomBool(v bool) Atom { + if v { + return Atom { + Typ: AtomBool, + data: 1, + } + } else { + return Atom { + Typ: AtomBool, + data: 0, + } + } +} +func NewAtomNumber(v float64) Atom { + return Atom { + Typ: AtomNumber, + data: math.Float64bits(v), + } +} +func NewAtomTerminal(v TerminalValue) Atom { + return Atom { + Typ: AtomTerminal, + data: uint64(v), + } +} +func NewAtomStringTerminal() Atom { + return Atom { + Typ: AtomStringTerminal, + data: 0, + } +} +func NewAtomStringRune(v rune) Atom { + return Atom { + Typ: AtomStringRune, + data: uint64(v), + } +} +func (v Atom) String() string { + switch v.Typ { + case AtomNull: + return "null" + case AtomBool: + if v.data == 0 { + return "false" + } + return "true" + case AtomNumber: + return fmt.Sprintf("%v", math.Float64frombits(v.data)) + case AtomTerminal: + switch TerminalValue(v.data) { + case MapBegin: + return "{" + case MapEnd: + return "}" + case ArrayBegin: + return "[" + case ArrayEnd: + return "]" + default: + panic("Invalid terminal atom") + } + case AtomStringTerminal: + return "\"" + case AtomStringRune: + return string(rune(v.data)) + default: + panic("Invalid atom type") + } } type WalkValue interface { @@ -481,12 +556,12 @@ func Compound(in []Atom) (out []WalkValue, error error) { i := 0 inString := false for _, atom := range in { - switch atom.(type) { - case TerminalValue, ValueNull, ValueBool, ValueNumber: + switch atom.Typ { + case AtomNull, AtomBool, AtomNumber, AtomTerminal: if !inString { numValues++ } - case StringTerminal: + case AtomStringTerminal: if inString { numValues++ } @@ -501,32 +576,37 @@ func Compound(in []Atom) (out []WalkValue, error error) { } atom := in[i] i++ - switch v := atom.(type) { - case TerminalValue, ValueNull, ValueBool, ValueNumber: - out = append(out, v.(WalkValue)) + switch atom.Typ { + case AtomNull: + out = append(out, ValueNull{}) continue - case StringAtom: + case AtomBool: + out = append(out, ValueBool(atom.data != 0)) + continue + case AtomNumber: + out = append(out, ValueNumber(math.Float64frombits(atom.data))) + continue + case AtomTerminal: + out = append(out, TerminalValue(atom.data)) + continue + case AtomStringRune: return nil, CompoundRuneOutsideString - case StringTerminal: + case AtomStringTerminal: default: return nil, CompoundUnknownAtom } // Handle string start var builder strings.Builder - loop: for { + for { if i >= len(in) { return nil, CompoundMissingEnd } atom := in[i] i++ - switch v := atom.(type) { - case StringTerminal: - break loop - case StringAtom, ValueNull, ValueBool, ValueNumber, TerminalValue: - builder.WriteString(v.String()) - default: - return nil, CompoundInvalidStringAtom + if atom.Typ == AtomStringTerminal { + break } + builder.WriteString(atom.String()) } out = append(out, ValueString(builder.String())) } -- cgit v1.2.3