diff options
| -rw-r--r-- | subex/main.go | 6 | ||||
| -rw-r--r-- | subex/subexstate.go | 43 | ||||
| -rw-r--r-- | 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 | 
