diff options
Diffstat (limited to 'subex/parse.go')
-rw-r--r-- | subex/parse.go | 162 |
1 files changed, 87 insertions, 75 deletions
diff --git a/subex/parse.go b/subex/parse.go index 35baaa2..fa98ecc 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -6,6 +6,12 @@ import ( "strings" ) +type Type int +const ( + ValueType Type = iota + RuneType +) + type RuneReader interface { Next() rune Rewind() @@ -45,24 +51,24 @@ func parseScalarLiteral(l RuneReader) (walk.Scalar, bool) { if err != nil { panic("Invalid number literal") } - return walk.NumberScalar(number), true + return walk.NumberValue(number), true } switch r { case 'n': if accept(l, "u") && accept(l, "l") && accept(l, "l") { - return walk.NullScalar{}, true + return walk.NullValue{}, true } else { panic("Invalid literal") } case 't': if accept(l, "r") && accept(l, "u") && accept(l, "e") { - return walk.BoolScalar(true), true + return walk.BoolValue(true), true } else { panic("Invalid literal") } case 'f': if accept(l, "a") && accept(l, "l") && accept(l, "s") && accept(l, "e") { - return walk.BoolScalar(false), true + return walk.BoolValue(false), true } else { panic("Invalid literal") } @@ -133,34 +139,53 @@ func parseRepeatRange(l RuneReader) (output []ConvexRange) { return output } -// 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) { +func parseValueReplacement(l RuneReader) (output []OutputValueAST) { // TODO escaping // TODO add arrays, maps and strings loop: for { r := l.Next() switch r { - case eof: - panic("Missing closing `") - case '`': - break loop - case '$': - slot := l.Next() - if slot == eof { - panic("Missing slot character") - } - output = append(output, OutputLoadAST{slot: slot}) - default: - 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}) - } + case eof: + panic("Missing closing `") + case ' ': + case '`': + break loop + case '$': + slot := l.Next() + if slot == eof { + panic("Missing slot character") + } + output = append(output, OutputValueLoadAST {slot: slot}) + default: + l.Rewind() + scalar, ok := parseScalarLiteral(l) + if !ok { + panic("Invalid scalar literal") + } + output = append(output, OutputValueLiteralAST {scalar}) + } + } + return output +} + +func parseRuneReplacement(l RuneReader) (output []OutputRuneAST) { + // TODO escaping + // TODO add arrays, maps and strings + loop: for { + r := l.Next() + switch r { + case eof: + panic("Missing closing `") + case '`': + break loop + case '$': + slot := l.Next() + if slot == eof { + panic("Missing slot character") + } + output = append(output, OutputRuneLoadAST {slot: slot}) + default: + output = append(output, OutputRuneLiteralAST {r}) } } return output @@ -245,17 +270,29 @@ func parseReplacement(l RuneReader, runic bool) (output []OutputContentAST) { // return parts // } -func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { +func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST { var lhs SubexAST r := l.Next() switch r { case eof: return nil case '(': - lhs = parseSubex(l, 0, runic) + lhs = parseSubex(l, 0, inType, outType) if !accept(l, ")") { panic("Missing matching )") } + case '~': + if !accept(l, "(") { + panic("Missing ( after ~") + } + lhs = parseSubex(l, 0, RuneType, RuneType) + if !accept(l, ")") { + panic("Missing matching )") + } + if !accept(l, "~") { + panic("Missing matching ~") + } + lhs = SubexASTEnterString {lhs} // TODO // case '[': // rangeParts := parseRangeSubex(l) @@ -278,7 +315,10 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { // ) // lhs = SubexASTOutput {replacement} case '.': - if runic { + if inType != outType { + panic("Copying value changes type!") + } + if inType == RuneType { lhs = SubexASTCopyAnyRune{} } else { lhs = SubexASTCopyAnyValue{} @@ -287,32 +327,8 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { lhs = SubexASTCopyBool{} case '%': lhs = SubexASTCopyNumber{} - case ':': - if runic { - lhs = SubexASTCopyRune {':'} - } else { - if !accept(l, "[") { - panic("Missing [ after :") - } - lhs = SubexASTEnterArray {parseSubex(l, 0, runic)} - if !accept(l, "]") { - panic("Missing matching ]") - } - } case '`': - lhs = SubexASTOutput {parseReplacement(l, runic)} - case '~': - if runic { - lhs = SubexASTCopyRune {'~'} - } else { - if !accept(l, "\"") { - panic("Missing \" after ~") - } - lhs = SubexASTEnterString {parseSubex(l, 0, true)} - if !accept(l, "\"") { - panic("Missing matching \"") - } - } + lhs = SubexASTOutputValues {parseValueReplacement(l)} // TODO // case '_': // lhs = SubexASTCopyStringAtom{} @@ -322,15 +338,11 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { // lhs = SubexASTCopyValue{} // case '"': // lhs = SubexASTCopyScalar {walk.NewAtomStringTerminal()} - // case '~': - // literals := parseNonStringLiteral(l) - // var replacement []OutputContentAST - // for _, literal := range literals { - // replacement = append(replacement, OutputValueLiteralAST {literal}) - // } - // lhs = SubexASTOutput {replacement} default: - if runic { + if inType != outType { + panic("inType and outType don't match in copy") + } + if inType == RuneType { lhs = SubexASTCopyRune {r} } else { l.Rewind() @@ -343,7 +355,7 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { } loop: for { if minPower <= 20 { - next := parseSubex(l, 21, runic) + next := parseSubex(l, 21, inType, outType) if next != nil && (next != SubexASTEmpty{}) { lhs = SubexASTConcat{lhs, next} continue loop @@ -362,12 +374,12 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { lhs = SubexASTProduct {lhs} case r == '-' && minPower <= 4: lhs = SubexASTNegate {lhs} - case r == '/' && minPower <= 4: - lhs = SubexASTReciprocal {lhs} + // case r == '/' && minPower <= 4: + // lhs = SubexASTReciprocal {lhs} case r == '!' && minPower <= 4: lhs = SubexASTNot {lhs} - case r == '=' && minPower <= 4: - lhs = SubexASTEqual {lhs} + // case r == '=' && minPower <= 4: + // lhs = SubexASTEqual {lhs} case r == '$' && minPower <= 4: slot := l.Next() if slot == eof { @@ -376,26 +388,26 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { if slot == '_' { lhs = SubexASTDiscard {lhs} } else { - lhs = SubexASTStore{ + lhs = SubexASTStoreValues { Match: lhs, Slot: slot, } } case r == '|' && minPower <= 8: - rhs := parseSubex(l, 9, runic) + rhs := parseSubex(l, 9, inType, outType) if rhs == nil { panic("Missing subex after |") } lhs = SubexASTOr{lhs, rhs} - case r == ';' && minPower <= 10: - rhs := parseSubex(l, 11, runic) + /*case r == ';' && minPower <= 10: + rhs := parseSubex(l, 11, inType, outType) if rhs == nil { panic("Missing subex after ;") } - lhs = SubexASTJoin{ + lhs = SubexASTJoin { Content: lhs, Delimiter: rhs, - } + }*/ default: l.Rewind() break loop @@ -405,7 +417,7 @@ func parseSubex(l RuneReader, minPower int, runic bool) SubexAST { } func Parse(l RuneReader) SubexAST { - ast := parseSubex(l, 0, false) + ast := parseSubex(l, 0, ValueType, ValueType) if ast == nil { return SubexASTEmpty{} } |