From 9d82785f46949151b783d83648b39ce9ba40c615 Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Sat, 30 Mar 2024 09:42:00 +0000 Subject: Add none structures and allow mismatched destructuring --- subex/parse.go | 175 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 42 deletions(-) (limited to 'subex/parse.go') diff --git a/subex/parse.go b/subex/parse.go index 9602a4b..f1565f5 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -8,10 +8,44 @@ import ( type Type int const ( - ValueType Type = iota + AnyType Type = iota + ValueType RuneType ) +func resolveTypes(t1 Type, t2 Type) Type { + if t1 == AnyType { + return t2 + } + + if t2 == AnyType { + return t1 + } + + if t1 == t2 { + return t1 + } + + panic("Types don't match in parser") +} + +type Structure int +const ( + NoneStructure Structure = iota + StringStructure + ArrayStructure +) +func (s Structure) innerType() Type { + switch s { + case StringStructure: + return RuneType + case ArrayStructure: + return ValueType + default: + panic("Invalid structure") + } +} + type RuneReader interface { Next() rune Rewind() @@ -270,48 +304,94 @@ func parseRuneReplacement(l RuneReader) (output []OutputRuneAST) { // return parts // } -func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST { - var lhs SubexAST +func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs SubexAST, outType Type) { + if !accept(l, "(") { + panic("Missing ( after destructure start") + } + + var innerInType Type + var expectedInType Type + switch destructure { + case NoneStructure: + innerInType = inType + expectedInType = inType + case StringStructure: + innerInType = RuneType + expectedInType = ValueType + case ArrayStructure: + innerInType = ValueType + expectedInType = ValueType + default: + panic("Invalid structure") + } + + resolveTypes(inType, expectedInType) + + lhs, innerOutType := parseSubex(l, 0, innerInType) + if !accept(l, ")") { + panic("Missing matching )") + } + + var structure Structure + var expectedInnerOutType Type + r := l.Next() + switch r { + case '-': + structure = NoneStructure + expectedInnerOutType = innerOutType + case '~': + structure = StringStructure + expectedInnerOutType = RuneType + case '@': + structure = ArrayStructure + expectedInnerOutType = ValueType + default: + panic("Missing matching destructure") + } + + innerOutType = resolveTypes(innerOutType, expectedInnerOutType) + + switch structure { + case NoneStructure: + outType = innerOutType + case StringStructure: + outType = ValueType + case ArrayStructure: + outType = ValueType + } + + lhs = SubexASTDestructure { + Destructure: destructure, + Structure: structure, + Content: lhs, + } + + return lhs, outType +} + +func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType Type) { r := l.Next() switch r { case eof: - return nil + return nil, inType case '(': - lhs = parseSubex(l, 0, inType, outType) + lhs, outType = parseSubex(l, 0, inType) if !accept(l, ")") { panic("Missing matching )") } + case '-': + lhs, outType = parseDestructure(l, NoneStructure, inType) 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} + lhs, outType = parseDestructure(l, StringStructure, inType) case '@': - if !accept(l, "(") { - panic("Missing ( after @") - } - lhs = parseSubex(l, 0, ValueType, ValueType) - if !accept(l, ")") { - panic("Missing matching )") - } - if !accept(l, "@") { - panic("Missing matching ~") - } - lhs = SubexASTEnterArray {lhs} + lhs, outType = parseDestructure(l, ArrayStructure, inType) // TODO // case '[': // rangeParts := parseRangeSubex(l) // lhs = SubexASTRange {rangeParts} - case ')', ']', '"', '|', ';', '{', '+', '-', '*', '/', '!', '=', '$': + case ')', ']', '"', '|', ';', '{', '+', '*', '/', '!', '=', '$': l.Rewind() - return SubexASTEmpty{} + return SubexASTEmpty{}, inType // case '=': // replacement := parseReplacement(l) // lhs = SubexASTOutput{replacement} @@ -327,19 +407,20 @@ func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST // ) // lhs = SubexASTOutput {replacement} case '.': - if inType != outType { - panic("Copying value changes type!") - } + outType = inType if inType == RuneType { lhs = SubexASTCopyAnyRune{} } else { lhs = SubexASTCopyAnyValue{} } case '?': + outType = inType lhs = SubexASTCopyBool{} case '%': + outType = inType lhs = SubexASTCopyNumber{} case '`': + outType = inType lhs = SubexASTOutputValues {parseValueReplacement(l)} // TODO // case '_': @@ -351,9 +432,7 @@ func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST // case '"': // lhs = SubexASTCopyScalar {walk.NewAtomStringTerminal()} default: - if inType != outType { - panic("inType and outType don't match in copy") - } + outType = inType if inType == RuneType { lhs = SubexASTCopyRune {r} } else { @@ -367,8 +446,9 @@ func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST } loop: for { if minPower <= 20 { - next := parseSubex(l, 21, inType, outType) + next, outType2 := parseSubex(l, 21, inType) if next != nil && (next != SubexASTEmpty{}) { + outType = resolveTypes(outType, outType2) lhs = SubexASTConcat{lhs, next} continue loop } @@ -382,14 +462,18 @@ func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST } case r == '+' && minPower <= 4: lhs = SubexASTSum {lhs} + resolveTypes(inType, ValueType) + outType = resolveTypes(outType, ValueType) case r == '*' && minPower <= 4: lhs = SubexASTProduct {lhs} - case r == '-' && minPower <= 4: - lhs = SubexASTNegate {lhs} + resolveTypes(inType, ValueType) + outType = resolveTypes(outType, ValueType) // case r == '/' && minPower <= 4: // lhs = SubexASTReciprocal {lhs} case r == '!' && minPower <= 4: lhs = SubexASTNot {lhs} + resolveTypes(inType, ValueType) + outType = resolveTypes(outType, ValueType) // case r == '=' && minPower <= 4: // lhs = SubexASTEqual {lhs} case r == '$' && minPower <= 4: @@ -398,15 +482,21 @@ func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST panic("Missing slot character") } if slot == '_' { - lhs = SubexASTDiscard {lhs} + lhs = SubexASTDiscard { + Content: lhs, + InnerOutType: outType, + } } else { + resolveTypes(inType, ValueType) lhs = SubexASTStoreValues { Match: lhs, Slot: slot, } } + outType = AnyType case r == '|' && minPower <= 8: - rhs := parseSubex(l, 9, inType, outType) + rhs, outType2 := parseSubex(l, 9, inType) + outType = resolveTypes(outType, outType2) if rhs == nil { panic("Missing subex after |") } @@ -425,11 +515,12 @@ func parseSubex(l RuneReader, minPower int, inType Type, outType Type) SubexAST break loop } } - return lhs + return lhs, outType } func Parse(l RuneReader) SubexAST { - ast := parseSubex(l, 0, ValueType, ValueType) + ast, outType := parseSubex(l, 0, ValueType) + outType = resolveTypes(outType, ValueType) if ast == nil { return SubexASTEmpty{} } -- cgit v1.2.3