From c6905e06ba73f47495ba86cebe1cb42f141f308d Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Wed, 19 Apr 2023 09:48:04 +0100 Subject: Adds casting strings to numbers in the sum operator --- subex/main.go | 6 +++- subex/subexstate.go | 43 ++++++++++++++++++------- walk/walk.go | 91 ++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 109 insertions(+), 31 deletions(-) diff --git a/subex/main.go b/subex/main.go index 593dbbd..2a5f5ad 100644 --- a/subex/main.go +++ b/subex/main.go @@ -118,7 +118,11 @@ func Main() { return } - valueOut := walk.MemoryCompound(output) + valueOut, error := walk.MemoryCompound(output) + if error != nil { + fmt.Println(error.Error()) + return + } for _, value := range valueOut { fmt.Println(value) } diff --git a/subex/subexstate.go b/subex/subexstate.go index 62e9d1a..855d44a 100644 --- a/subex/subexstate.go +++ b/subex/subexstate.go @@ -2,6 +2,8 @@ package subex import ( "main/walk" + "strconv" + "errors" ) // A state of execution for the transducer @@ -236,41 +238,54 @@ func (state SubexRangeState) accepting(store Store) [][]walk.Atom { return nil } -func sumValues(values []walk.Atom) walk.ValueNumber { +func sumValues(atoms []walk.Atom) (walk.ValueNumber, error) { var sum float64 = 0 + values, err := walk.MemoryCompound(atoms) + if err != nil { + return 0, err + } for _, value := range values { switch v := value.(type) { + case walk.ValueNull: case walk.ValueBool: if (bool(v)) { sum += 1 } case walk.ValueNumber: sum += float64(v) - case rune: - if '0' <= v && v <= '9' { - sum += float64(v - '0') + case walk.ValueString: + num, err := strconv.ParseFloat(string(v), 64) + if err == nil { + sum += num + } else { + return 0, errors.New("Tried to sum non-castable string") } default: + return 0, errors.New("Tried to sum non-number") } } - return walk.ValueNumber(sum) + return walk.ValueNumber(sum), nil } // Run the inputState machine and sum any values output, output the sum -// Cast non numbers into numbers, ignore anything uncastable +// Cast non numbers into numbers, branch dies if it is not castable type SubexSumState struct { inputState SubexState next SubexState - sum walk.ValueNumber + acc []walk.Atom } func (state SubexSumState) child() SubexState { return state.inputState } func (state SubexSumState) nextAcc(output []walk.Atom) interface{} { - return sumValues(append(output, state.sum)) + return walk.ConcatData(state.acc, output) } func (state SubexSumState) feedNext(acc interface{}, store Store, char walk.Atom) []SubexBranch { - total := acc.(walk.ValueNumber) + childOutput := acc.([]walk.Atom) + total, err := sumValues(childOutput) + if err != nil { + return nil + } output := []walk.Atom{total} nextStates := state.next.eat(store.clone(), char) for i := range nextStates { @@ -279,7 +294,11 @@ func (state SubexSumState) feedNext(acc interface{}, store Store, char walk.Atom return nextStates } func (state SubexSumState) acceptNext(acc interface{}, store Store) [][]walk.Atom { - total := acc.(walk.ValueNumber) + childOutput := acc.([]walk.Atom) + total, err := sumValues(childOutput) + if err != nil { + return nil + } output := []walk.Atom{total} outputs := state.next.accepting(store.clone()) for i := range outputs { @@ -288,11 +307,11 @@ func (state SubexSumState) acceptNext(acc interface{}, store Store) [][]walk.Ato return outputs } func (state SubexSumState) nextParent(child SubexState, acc interface{}) SubexState { - sum := acc.(walk.ValueNumber) + childOutput := acc.([]walk.Atom) return &SubexSumState { inputState: child, next: state.next, - sum: sum, + acc: childOutput, } } func (state SubexSumState) eat(store Store, char walk.Atom) (nextStates []SubexBranch) { diff --git a/walk/walk.go b/walk/walk.go index 4da8040..36e9805 100644 --- a/walk/walk.go +++ b/walk/walk.go @@ -390,41 +390,92 @@ func Atomise(in <-chan WalkValue) <-chan Atom { return out } -func Compound(in <-chan Atom) <-chan WalkValue { - out := make(chan WalkValue) - go func(out chan<- WalkValue, in <-chan Atom) { - for { +func MemoryAtomise(in []WalkValue) (out []Atom) { + inChan := make(chan WalkValue) + go func(in []WalkValue, out chan<- WalkValue) { + for _, value := range in { + out<-value + } + close(out) + }(in, inChan) + outChan := Atomise(inChan) + for atom := range outChan { + out = append(out, atom) + } + return out +} + +type CompoundError int + +const ( + CompoundRuneOutsideString CompoundError = iota + CompoundEndStringOutsideString + CompoundUnknownAtom + CompoundMissingEnd + CompoundInvalidStringAtom +) + +func (err CompoundError) Error() string { + switch err { + case CompoundRuneOutsideString: + return "Compound Error: Rune Outside String" + case CompoundEndStringOutsideString: + return "Compound Error: End String Outside String" + case CompoundUnknownAtom: + return "Compound Error: Unknown Atom" + case CompoundMissingEnd: + return "Compound Error: Missing End" + case CompoundInvalidStringAtom: + return "Compound Error: Invalid String Atom" + default: + panic("Invalid CompoundError") + } +} + +type CompoundResult struct { + value WalkValue + error error +} + +func Compound(in <-chan Atom) <-chan CompoundResult { + out := make(chan CompoundResult) + go func(out chan<- CompoundResult, in <-chan Atom) { + outer: for { atom, hasAtom := <-in if !hasAtom { break } switch v := atom.(type) { case TerminalValue: - out<-v + out<-CompoundResult{v, nil} continue case ValueNull: - out<-v + out<-CompoundResult{v, nil} continue case ValueBool: - out<-v + out<-CompoundResult{v, nil} continue case ValueNumber: - out<-v + out<-CompoundResult{v, nil} continue case rune: - panic("Error! Rune output by subex but not in a string") + out<-CompoundResult{nil, CompoundRuneOutsideString} + break outer case EndString: - panic("Error! subex output an EndString before BeginString") + out<-CompoundResult{nil, CompoundEndStringOutsideString} + break outer case StartString: default: - panic("Unknown atom type") + out<-CompoundResult{nil, CompoundUnknownAtom} + break outer } // Handle string start var builder strings.Builder loop: for { atom, hasAtom := <-in if !hasAtom { - panic("Missing EndString") + out<-CompoundResult{nil, CompoundMissingEnd} + break outer } switch v := atom.(type) { case EndString: @@ -432,17 +483,18 @@ func Compound(in <-chan Atom) <-chan WalkValue { case rune: builder.WriteRune(v) default: - panic("Invalid atom in string") + out<-CompoundResult{nil, CompoundInvalidStringAtom} + break outer } } - out<-ValueString(builder.String()) + out<-CompoundResult{ValueString(builder.String()), nil} } close(out) }(out, in) return out } -func MemoryCompound(in []Atom) (out []WalkValue) { +func MemoryCompound(in []Atom) (out []WalkValue, err error) { inChan := make(chan Atom) go func(in []Atom, out chan<- Atom) { for _, atom := range in { @@ -451,8 +503,11 @@ func MemoryCompound(in []Atom) (out []WalkValue) { close(out) }(in, inChan) outChan := Compound(inChan) - for value := range outChan { - out = append(out, value) + for result := range outChan { + if result.error != nil { + return out, result.error + } + out = append(out, result.value) } - return out + return out, nil } \ No newline at end of file -- cgit v1.2.3