From 8ac12c99fc59b01da40c2939cb4a7b72d32d2153 Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Sat, 27 Apr 2024 09:29:46 +0100 Subject: Add iterating destructures --- main/main_test.go | 7 +++++ subex/parse.go | 61 +++++++++++++++++++++-------------------- subex/subexstate.go | 78 ----------------------------------------------------- 3 files changed, 37 insertions(+), 109 deletions(-) diff --git a/main/main_test.go b/main/main_test.go index b745202..8512b62 100644 --- a/main/main_test.go +++ b/main/main_test.go @@ -86,6 +86,13 @@ func TestMain(t *testing.T) { input: miscInput, expected: `["Charlie Johnson","Tom Johnson","Charlie Chaplin","John Johnson"]`, }, + { + name: "Get full names with merge full command", + program: "s/#(\"people\"$_ :(): )-/p M/#( \"people\" @( . #()# )@ )#/{ s/#( \"people\"$_ @( . #[ \"first_name\" \".{-0}$a\" | \"last_name\" \".{-0}$b\" | .. $_]- `\"$a $b\"` )@ )-/p }", + quiet: true, + input: miscInput, + expected: `["Charlie Johnson","Tom Johnson","Charlie Chaplin","John Johnson"]`, + }, { name: "Verbose concat array values", program: "as/#( \"array\"$_ :(): )-/{ :s N/#( .$_ . )-/{ es/.{-0}:():/be mbs } :em s/:( -( ~(.{-0}` `)-{-0} ~(.{-0})- )~ )-/p }", diff --git a/subex/parse.go b/subex/parse.go index b6bf2f6..9a7a75c 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -55,6 +55,12 @@ func (s Structure) String() string { } } +type DestructureMethod int +const ( + Normal DestructureMethod = iota + Iterate +) + type RuneReader interface { Next() rune Rewind() @@ -361,8 +367,14 @@ func parseRuneReplacement(l RuneReader, end rune) (output SubexAST) { // } func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs SubexAST, outType Type) { - if !accept(l, "(") { - panic("Missing ( after destructure start") + var method rune + switch l.Next() { + case '(': + method = ')' + case '[': + method = ']' + default: + panic("Missing ( or [ after destructure start") } var innerInType Type @@ -390,8 +402,22 @@ func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs Sub resolveTypes(inType, expectedInType) lhs, innerOutType := parseSubex(l, 0, innerInType) - if !accept(l, ")") { - panic("Missing matching )") + if !accept(l, string(method)) { + panic("Missing matching ) or ]") + } + + switch method { + case ')': + case ']': + lhs = SubexASTRepeat { + Content: lhs, + Acceptable: []ConvexRange{{ + Start: -1, + End: 0, + }}, + } + default: + panic("Invalid method") } var structure Structure @@ -487,20 +513,6 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType case ')', ']', '|', ';', '{', '+', '*', '/', '!', '=', '$': l.Rewind() return SubexASTEmpty{}, inType - // case '=': - // replacement := parseReplacement(l) - // lhs = SubexASTOutput{replacement} - // case '^': - // replacement := parseReplacement(l) - // replacement = append( - // []OutputContentAST{OutputValueLiteralAST {walk.NewAtomStringTerminal()}}, - // replacement... - // ) - // replacement = append( - // replacement, - // OutputValueLiteralAST {walk.NewAtomStringTerminal()}, - // ) - // lhs = SubexASTOutput {replacement} case '.': outType = inType if inType == RuneType { @@ -569,14 +581,10 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType lhs = SubexASTProduct {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: slot := l.Next() if slot == eof { @@ -608,15 +616,6 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType panic("Missing subex after |") } lhs = SubexASTOr{lhs, rhs} - /*case r == ';' && minPower <= 10: - rhs := parseSubex(l, 11, inType, outType) - if rhs == nil { - panic("Missing subex after ;") - } - lhs = SubexASTJoin { - Content: lhs, - Delimiter: rhs, - }*/ default: l.Rewind() break loop diff --git a/subex/subexstate.go b/subex/subexstate.go index 1e1e94e..8f27a10 100644 --- a/subex/subexstate.go +++ b/subex/subexstate.go @@ -146,84 +146,6 @@ func (state SubexStoreRunesEndState) epsilon(aux auxiliaryState) []SubexBranch { }} } -/* -// 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 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 a Value literal -type OutputValueLiteral struct { - value walk.Value -} -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) 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 { - content []OutputContent - next SubexState -} -// Given a store, return what is outputted by an epsilon transition from this state -// TODO: separate into buildValues and buildRunes -func (state SubexOutputState) build(store Store) walk.ValueList { - var result walk.ValueList - for _, part := range state.content { - result = append(result, part.buildValues(store)...) - } - return result -} -func (state SubexOutputState) eat(aux auxiliaryState, char walk.Value) []SubexBranch { - content := state.build(aux.store) - nextStates := state.next.eat(aux.topAppend(content), char) - return nextStates -} -func (state SubexOutputState) accepting(aux auxiliaryState) []OutputStack { - content := state.build(aux.store) - outputStacks := state.next.accepting(aux.topAppend(content)) - return outputStacks -} -*/ - type SubexOutputValueLiteralState struct { literal walk.Scalar next SubexState -- cgit v1.2.3