diff options
| author | Charlie Stanton <charlie@shtanton.xyz> | 2023-07-21 16:42:49 +0100 | 
|---|---|---|
| committer | Charlie Stanton <charlie@shtanton.xyz> | 2023-07-21 16:42:49 +0100 | 
| commit | bed0e712deda5038f52e495bacae003098df7a55 (patch) | |
| tree | 09f3b86927efc6a0cef4f7fdf509c1731cc4b86d | |
| parent | c52794f9d420e319900f27e9f16c8444e8842e92 (diff) | |
| download | stred-go-bed0e712deda5038f52e495bacae003098df7a55.tar | |
Reimplements inserting basic values using subexes
| -rw-r--r-- | subex/parse.go | 128 | ||||
| -rw-r--r-- | subex/subexast.go | 9 | ||||
| -rw-r--r-- | subex/subexstate.go | 43 | 
3 files changed, 105 insertions, 75 deletions
| diff --git a/subex/parse.go b/subex/parse.go index 6c19df4..2392b22 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -27,54 +27,47 @@ func isNumericRune(r rune) bool {  }  // Having just parsed a `, read until the next ` and parse the contents into a list of non-string atoms -func parseNonStringLiteral(l RuneReader) (literals []walk.Scalar) { -	for { -		r := l.Next() -		if isNumericRune(r) { -			var builder strings.Builder +func parseScalarLiteral(l RuneReader) (walk.Scalar, bool) { +	r := l.Next() +	if isNumericRune(r) { +		var builder strings.Builder +		builder.WriteRune(r) +		for { +			r := l.Next() +			if !isNumericRune(r) { +				l.Rewind() +				break +			}  			builder.WriteRune(r) -			for { -				r := l.Next() -				if !isNumericRune(r) { -					l.Rewind() -					break -				} -				builder.WriteRune(r) +		} +		numberString := builder.String() +		number, err := strconv.ParseFloat(numberString, 64) +		if err != nil { +			panic("Invalid number literal") +		} +		return walk.NumberScalar(number), true +	} +	switch r { +		case 'n': +			if accept(l, "u") && accept(l, "l") && accept(l, "l") { +				return walk.NullScalar{}, true +			} else { +				panic("Invalid literal")  			} -			numberString := builder.String() -			number, err := strconv.ParseFloat(numberString, 64) -			if err != nil { -				panic("Invalid number literal") +		case 't': +			if accept(l, "r") && accept(l, "u") && accept(l, "e") { +				return walk.BoolScalar(true), true +			} else { +				panic("Invalid literal")  			} -			literals = append(literals, walk.NumberScalar(number)) -			continue -		} -		switch r { -			case '`', '~': -				return literals -			case ' ', '\t': -				continue -			case 'n': -				if accept(l, "u") && accept(l, "l") && accept(l, "l") { -					literals = append(literals, walk.NullScalar{}) -				} else { -					panic("Invalid literal") -				} -			case 't': -				if accept(l, "r") && accept(l, "u") && accept(l, "e") { -					literals = append(literals, walk.BoolScalar(true)) -				} else { -					panic("Invalid literal") -				} -			case 'f': -				if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") { -					literals = append(literals, walk.BoolScalar(false)) -				} else { -					panic("Invalid literal") -				} -			default: +		case 'f': +			if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") { +				return walk.BoolScalar(false), true +			} else {  				panic("Invalid literal") -		} +			} +		default: +			panic("Invalid literal")  	}  } @@ -140,14 +133,16 @@ func parseRepeatRange(l RuneReader) (output []ConvexRange) {  	return output  } -func parseReplacement(l RuneReader) (output []OutputContentAST) { +// TODO: Consider if it's worth making better use of the go type system to enforce output being all runes or all values +func parseReplacement(l RuneReader, runic bool) (output []OutputContentAST) {  	// TODO escaping +	// TODO add arrays, maps and strings  	loop: for {  		r := l.Next()  		switch r {  			case eof: -				panic("Missing closing \"") -			case '=', '^', ':': +				panic("Missing closing `") +			case '`':  				break loop  			case '$':  				slot := l.Next() @@ -155,14 +150,17 @@ func parseReplacement(l RuneReader) (output []OutputContentAST) {  					panic("Missing slot character")  				}  				output = append(output, OutputLoadAST{slot: slot}) -			case '`': -				literals := parseNonStringLiteral(l) -				for _, literal := range literals { -					output = append(output, OutputValueLiteralAST {literal}) -				}  			default: -				panic("Invalid value to insert") -				//output = append(output, OutputValueLiteralAST{atom: walk.NewAtomStringRune(r)}) +				if runic { +					output = append(output, OutputRuneLiteralAST {walk.StringRuneAtom(r)}) +				} else { +					l.Rewind() +					scalar, ok := parseScalarLiteral(l) +					if !ok { +						panic("Invalid scalar literal") +					} +					output = append(output, OutputValueLiteralAST {scalar}) +				}  		}  	}  	return output @@ -265,15 +263,9 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST {  		case ')', ']', '"', '|', ';', '{', '+', '-', '*', '/', '!', '$':  			l.Rewind()  			return SubexASTEmpty{} -		case '=': -			replacement := parseReplacement(l) -			lhs = SubexASTOutput{replacement} -		case '`': -			literals := parseNonStringLiteral(l) -			lhs = SubexASTEmpty{} -			for _, literal := range literals { -				lhs = SubexASTConcat {lhs, SubexASTCopyScalar {literal}} -			} +		// case '=': +		// 	replacement := parseReplacement(l) +		// 	lhs = SubexASTOutput{replacement}  		// case '^':  		// 	replacement := parseReplacement(l)  		// 	replacement = append( @@ -307,6 +299,8 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST {  					panic("Missing matching ]")  				}  			} +		case '`': +			lhs = SubexASTOutput {parseReplacement(l, runic)}  		case '~':  			if runic {  				lhs = SubexASTCopyRune {'~'} @@ -339,8 +333,12 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST {  			if runic {  				lhs = SubexASTCopyRune {r}  			} else { -				// TODO: Allow whitespace outside of runic sections -				panic("Tried to match rune outside of string") +				l.Rewind() +				scalar, ok := parseScalarLiteral(l) +				if !ok { +					panic("Invalid subex") +				} +				lhs = SubexASTCopyScalar {scalar}  			}  	}  	loop: for { diff --git a/subex/subexast.go b/subex/subexast.go index 31c77ba..b1ac931 100644 --- a/subex/subexast.go +++ b/subex/subexast.go @@ -246,7 +246,14 @@ type OutputValueLiteralAST struct {  	atom walk.Value  }  func (ast OutputValueLiteralAST) compile(slotMap *SlotMap) OutputContent { -	return OutputAtomLiteral {ast.atom} +	return OutputValueLiteral {ast.atom} +} + +type OutputRuneLiteralAST struct { +	rune walk.StringRuneAtom +} +func (ast OutputRuneLiteralAST) compile(slotMap *SlotMap) OutputContent { +	return OutputRuneLiteral {ast.rune}  }  // Output a series of Atoms without reading anything from input diff --git a/subex/subexstate.go b/subex/subexstate.go index 7ffd592..0b21c93 100644 --- a/subex/subexstate.go +++ b/subex/subexstate.go @@ -121,29 +121,54 @@ func (state SubexStoreEndState) accepting(aux auxiliaryState) []OutputStack {  // A part of an output literal, either an Atom or a slot from which to load  type OutputContent interface { -	// Given the current store, return the []Atom produced by the TransducerOutput -	build(Store) walk.ValueList +	// Given the current store, return the ValueList produced by the TransducerOutput +	buildValues(Store) walk.ValueList +	// Given the current store, return the RuneList produced by the TransducerOutput +	buildRunes(Store) walk.RuneList  } -// An OutputContent which is just an Atom literal -type OutputAtomLiteral struct { -	atom walk.Value +// An OutputContent which is just a Value literal +type OutputValueLiteral struct { +	value walk.Value  } -func (replacement OutputAtomLiteral) build(store Store) walk.ValueList { -	return walk.ValueList{replacement.atom} +func (replacement OutputValueLiteral) buildValues(store Store) walk.ValueList { +	return walk.ValueList{replacement.value} +} +func (replacement OutputValueLiteral) buildRunes(store Store) walk.RuneList { +	// TODO: serialise to JSON +	panic("Unimplemented!") +} + +// An OutputContent which is just a rune literal +type OutputRuneLiteral struct { +	rune walk.StringRuneAtom +} +func (replacement OutputRuneLiteral) buildValues(store Store) walk.ValueList { +	// TODO: Try to deserialise +	panic("Unimplemented!") +} +func (replacement OutputRuneLiteral) buildRunes(store Store) walk.RuneList { +	return walk.RuneList {replacement.rune}  }  // An OutputContent which is a slot that is loaded from  type OutputLoad struct {  	slot int  } -func (replacement OutputLoad) build(store Store) walk.ValueList { +func (replacement OutputLoad) buildValues(store Store) walk.ValueList {  	values, isValues := store[replacement.slot].(walk.ValueList)  	if !isValues {  		panic("Tried to output non-values list")  	}  	return values  } +func (replacement OutputLoad) buildRunes(store Store) walk.RuneList { +	runes, isRunes := store[replacement.slot].(walk.RuneList) +	if !isRunes { +		panic("Tried to output non-runes as runes") +	} +	return runes +}  // Don't read in anything, just output the series of data and slots specified  type SubexOutputState struct { @@ -155,7 +180,7 @@ type SubexOutputState struct {  func (state SubexOutputState) build(store Store) walk.ValueList {  	var result walk.ValueList  	for _, part := range state.content { -		result = append(result, part.build(store)...) +		result = append(result, part.buildValues(store)...)  	}  	return result  } | 
