diff options
| author | Charlie Stanton <charlie@shtanton.xyz> | 2023-04-21 16:22:16 +0100 | 
|---|---|---|
| committer | Charlie Stanton <charlie@shtanton.xyz> | 2023-04-21 16:22:16 +0100 | 
| commit | 12c1d179f32c38a929fcc9adb326a9f44c8288ae (patch) | |
| tree | d685922ed93708cbaf01357a95989283a7b86443 | |
| parent | 5d8582711936cae3c42f2645d0f304418b17fb7e (diff) | |
| download | stred-go-12c1d179f32c38a929fcc9adb326a9f44c8288ae.tar | |
Replaces the interfaces implementation of Atom with a tagged union based implementation
| -rw-r--r-- | subex/arithmetic.go | 32 | ||||
| -rw-r--r-- | subex/parse.go | 40 | ||||
| -rw-r--r-- | subex/subexast.go | 4 | ||||
| -rw-r--r-- | subex/subexstate.go | 13 | ||||
| -rw-r--r-- | 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()))  	} | 
