diff options
Diffstat (limited to 'subex/parse.go')
| -rw-r--r-- | subex/parse.go | 203 | 
1 files changed, 140 insertions, 63 deletions
| diff --git a/subex/parse.go b/subex/parse.go index 179cc01..01a747b 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -144,62 +144,128 @@ func parseInt(l RuneReader) (output int) {  	return output  } -func parseNumberFilter(l RuneReader, minPower int) SubexASTNumberFilter { -	var lhs SubexASTNumberFilter -	r := l.Next() -	switch r { -	case eof: -		panic("Missing matching ]") -	case 'c': -		count := parseInt(l) -		lhs = SubexASTNumberFilterCount {count} -	case 'p': -		var subset NumberSubset -		if l.Next() == 'i' { -			subset = NumberSubsetPositiveInteger -		} else { -			subset = NumberSubsetPositiveReal -			l.Rewind() -		} -		lhs = SubexASTNumberFilterSubset { -			subset: subset, -		} -	default: +// Parse a number literal in a number expression +func parseNumberLiteral(l RuneReader) NumberExprLiteral { +	var builder strings.Builder +	for { +		r := l.Next()  		if !isNumericRune(r) { -			panic("Invalid character in numeric []") +			l.Rewind() +			break  		} - -		var builder strings.Builder  		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 NumberExprLiteral { +		Value: number, +	} +} + +// Parse a numeric expression +func parseNumberExpression(l RuneReader, minPower int) NumberExpr { +	var lhs NumberExpr +	switch l.Next() { +	case '(': +		lhs = parseNumberExpression(l, 0) +		if !accept(l, ")") { +			panic("Missing closing )")  		} -		numberString := builder.String() -		number, err := strconv.ParseFloat(numberString, 64) -		if err != nil { -			panic("Invalid number literal") +	case 'n': +		lhs = NumberExprVariable{} +	case '-': +		lhs = NumberExprLiteral{0} +		l.Rewind() +	case '!': +		lhs = NumberExprNot { +			Right: parseNumberExpression(l, 13),  		} - -		lhs = SubexASTNumberFilterLiteral {number} +	default: +		l.Rewind() +		lhs = parseNumberLiteral(l)  	}  	loop: for {  		r := l.Next()  		switch { -		case r == '+' && minPower <= 10: -			lhs = SubexASTNumberFilterAdd { -				lhs: lhs, -				rhs: parseNumberFilter(l, 11), +		case r == '|' && minPower <= 8: +			lhs = NumberExprOr { +				Left: lhs, +				Right: parseNumberExpression(l, 9),  			} -		case r == '*' && minPower <= 20: -			lhs = SubexASTNumberFilterMultiply { -				lhs: lhs, -				rhs: parseNumberFilter(l, 21), +		case r == '&' && minPower <= 10: +			lhs = NumberExprAnd { +				Left: lhs, +				Right: parseNumberExpression(l, 11), +			} +		case r == '<' && minPower <= 20: +			if accept(l, "=") { +				lhs = NumberExprAtMost { +					Left: lhs, +					Right: parseNumberExpression(l, 21), +				} +			} else { +				lhs = NumberExprLessThan { +					Left: lhs, +					Right: parseNumberExpression(l, 21), +				} +			} +		case r == '>' && minPower <= 20: +			if accept(l, "=") { +				lhs = NumberExprAtLeast { +					Left: lhs, +					Right: parseNumberExpression(l, 21), +				} +			} else { +				lhs = NumberExprGreaterThan { +					Left: lhs, +					Right: parseNumberExpression(l, 21), +				} +			} +		case r == '=' && minPower <= 20: +			lhs = NumberExprEqual { +				Left: lhs, +				Right: parseNumberExpression(l, 21), +			} +		case r == '~' && minPower <= 20: +			lhs = NumberExprNot { +				Right: NumberExprEqual { +					Left: lhs, +					Right: parseNumberExpression(l, 21), +				}, +			} +		case r == '+' && minPower <= 30: +			lhs = NumberExprAdd { +				Left: lhs, +				Right: parseNumberExpression(l, 31), +			} +		case r == '-' && minPower <= 30: +			lhs = NumberExprSubtract { +				Left: lhs, +				Right: parseNumberExpression(l, 31), +			} +		case r == '*' && minPower <= 36: +			lhs = NumberExprMultiply { +				Left: lhs, +				Right: parseNumberExpression(l, 37), +			} +		case r == '/' && minPower <= 36: +			lhs = NumberExprDivide { +				Left: lhs, +				Right: parseNumberExpression(l, 37), +			} +		case r == '%' && minPower <= 36: +			lhs = NumberExprMod { +				Left: lhs, +				Right: parseNumberExpression(l, 37), +			} +		case r == '^' && minPower <= 40: +			lhs = NumberExprExponent { +				Left: lhs, +				Right: parseNumberExpression(l, 41),  			}  		default:  			l.Rewind() @@ -210,6 +276,34 @@ func parseNumberFilter(l RuneReader, minPower int) SubexASTNumberFilter {  	return lhs  } +// Having just read a [ in a value subex, parse the number mapping contents up +// to but not including the closing ] +func parseNumberMapping(l RuneReader) SubexAST { +	numRange := parseNumberExpression(l, 0) +	var numReplace []NumberExpr +	if accept(l, ":") { +		if !accept(l, "]") { +			for { +				numReplace = append( +					numReplace, +					parseNumberExpression(l, 0), +				) +				if !accept(l, ",") { +					break +				} +			} +		} else { +			l.Rewind() +		} +	} else { +		numReplace = []NumberExpr{NumberExprVariable{}} +	} +	return SubexASTNumberMapping { +		Range: numRange, +		Replace: numReplace, +	} +} +  // Having just read {, read in and parse the range contents  func parseRepeatRange(l RuneReader) (output []ConvexRange) {  	loop: for { @@ -717,9 +811,7 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType  	case '[':  		switch inType {  		case ValueType: -			lhs = SubexASTCopyNumberFilter { -				filter: parseNumberFilter(l, 0), -			} +			lhs = parseNumberMapping(l)  			if !accept(l, "]") {  				panic("Missing matching ]")  			} @@ -748,21 +840,6 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType  		default:  			panic("Invalid inType")  		} -	case 'r': -		switch inType { -		case ValueType: -			outType = inType -			lhs = SubexASTCopyNumberFilter { -				filter: SubexASTNumberFilterSubset { -					subset: NumberSubsetReal, -				}, -			} -		case RuneType: -			outType = inType -			lhs = SubexASTCopyRune {'r'} -		default: -			panic("Invalid inType") -		}  	case '?':  		outType = inType  		lhs = SubexASTCopyBool{} | 
