diff options
author | Charlie Stanton <charlie@shtanton.xyz> | 2025-10-18 09:41:50 +0100 |
---|---|---|
committer | Charlie Stanton <charlie@shtanton.xyz> | 2025-10-18 09:41:50 +0100 |
commit | b2ce005d227a10a9b8a6f5362c87a0e34ee07acc (patch) | |
tree | 454c7dcafc02759b90d083ab1d72c0bbfb65f578 /subex/parse.go | |
parent | 62aa738be03845f96c40edde087ea39693b27e4e (diff) | |
download | stred-go-b2ce005d227a10a9b8a6f5362c87a0e34ee07acc.tar |
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{} |