<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-04-21 16:22:16 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-04-21 16:22:16 +0100
commit12c1d179f32c38a929fcc9adb326a9f44c8288ae (patch)
treed685922ed93708cbaf01357a95989283a7b86443
parent5d8582711936cae3c42f2645d0f304418b17fb7e (diff)
downloadstred-go-12c1d179f32c38a929fcc9adb326a9f44c8288ae.tar
Replaces the interfaces implementation of Atom with a tagged union based implementation
-rw-r--r--subex/arithmetic.go32
-rw-r--r--subex/parse.go40
-rw-r--r--subex/subexast.go4
-rw-r--r--subex/subexstate.go13
-rw-r--r--walk/walk.go150
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()))
}