<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/subex
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2024-03-30 21:09:32 +0000
committerCharlie Stanton <charlie@shtanton.xyz>2024-03-30 21:09:32 +0000
commitfd79fd18c6c32884e757e91b8629c87af4cbf34e (patch)
tree50e6b49c64b1c6cf230c80ce83a925ec98fd2387 /subex
parent976d96af62945178f3a3ab572620026df75003cf (diff)
downloadstred-go-fd79fd18c6c32884e757e91b8629c87af4cbf34e.tar
Add map destructure
Diffstat (limited to 'subex')
-rw-r--r--subex/filter.go6
-rw-r--r--subex/main_test.go35
-rw-r--r--subex/parse.go13
-rw-r--r--subex/subexast.go29
-rw-r--r--subex/subexstate.go25
5 files changed, 108 insertions, 0 deletions
diff --git a/subex/filter.go b/subex/filter.go
index dce0f0e..ae4b8ab 100644
--- a/subex/filter.go
+++ b/subex/filter.go
@@ -38,6 +38,12 @@ func (_ anyArrayFilter) valueFilter(value walk.Value) bool {
return isArray
}
+type anyMapFilter struct {}
+func (_ anyMapFilter) valueFilter(value walk.Value) bool {
+ _, isMap := value.(walk.MapValue)
+ return isMap
+}
+
type anyStringFilter struct {}
func (_ anyStringFilter) valueFilter(value walk.Value) bool {
_, isString := value.(walk.StringValue)
diff --git a/subex/main_test.go b/subex/main_test.go
index 673b807..9c1819a 100644
--- a/subex/main_test.go
+++ b/subex/main_test.go
@@ -293,6 +293,41 @@ func TestSubexMain(t *testing.T) {
},
},
},
+ {
+ subex: "#(.(.$_){-0}):",
+ input: []walk.Value {
+ walk.MapValue {
+ {
+ Key: "a",
+ Value: walk.NullValue{},
+ },
+ {
+ Key: "b",
+ Value: walk.NumberValue(4),
+ },
+ {
+ Key: "c",
+ Value: walk.StringValue("hello"),
+ },
+ },
+ },
+ expected: []walk.Value {
+ walk.ArrayValue {
+ {
+ Index: 0,
+ Value: walk.StringValue("a"),
+ },
+ {
+ Index: 0,
+ Value: walk.StringValue("b"),
+ },
+ {
+ Index: 0,
+ Value: walk.StringValue("c"),
+ },
+ },
+ },
+ },
}
for i, test := range tests {
diff --git a/subex/parse.go b/subex/parse.go
index 98821fd..1e17bb3 100644
--- a/subex/parse.go
+++ b/subex/parse.go
@@ -35,6 +35,7 @@ const (
StringStructure
ArrayStructure
ArrayValuesStructure
+ MapStructure
)
func (s Structure) String() string {
switch s {
@@ -46,6 +47,8 @@ func (s Structure) String() string {
return "@"
case ArrayValuesStructure:
return ":"
+ case MapStructure:
+ return "#"
default:
panic("Invalid structure")
}
@@ -329,6 +332,9 @@ func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs Sub
case ArrayValuesStructure:
innerInType = ValueType
expectedInType = ValueType
+ case MapStructure:
+ innerInType = ValueType
+ expectedInType = ValueType
default:
panic("Invalid structure")
}
@@ -356,6 +362,9 @@ func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs Sub
case ':':
structure = ArrayValuesStructure
expectedInnerOutType = ValueType
+ case '#':
+ structure = MapStructure
+ expectedInnerOutType = ValueType
default:
panic("Missing matching destructure")
}
@@ -371,6 +380,8 @@ func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs Sub
outType = ValueType
case ArrayValuesStructure:
outType = ValueType
+ case MapStructure:
+ outType = ValueType
}
lhs = SubexASTDestructure {
@@ -400,6 +411,8 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType
lhs, outType = parseDestructure(l, ArrayStructure, inType)
case ':':
lhs, outType = parseDestructure(l, ArrayValuesStructure, inType)
+ case '#':
+ lhs, outType = parseDestructure(l, MapStructure, inType)
// TODO
// case '[':
// rangeParts := parseRangeSubex(l)
diff --git a/subex/subexast.go b/subex/subexast.go
index a2c3675..2685925 100644
--- a/subex/subexast.go
+++ b/subex/subexast.go
@@ -489,6 +489,11 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
construct = &SubexConstructArrayValuesState {
next: next,
}
+ case MapStructure:
+ innerOutType = ValueType
+ construct = &SubexConstructMapState {
+ next: next,
+ }
default:
panic("Invalid ast structure")
}
@@ -523,6 +528,14 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
next: construct,
},
}
+ case MapStructure:
+ innerInType = ValueType
+ destructFooter = &SubexDiscardTerminalState {
+ terminal: walk.MapEnd,
+ next: &SubexDecrementNestState {
+ next: construct,
+ },
+ }
default:
panic("Invalid ast destructure")
}
@@ -550,6 +563,10 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
beginConstruct = &SubexCaptureBeginState {
next: inner,
}
+ case MapStructure:
+ beginConstruct = &SubexCaptureBeginState {
+ next: inner,
+ }
default:
panic("Invalid ast structure")
}
@@ -593,6 +610,18 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
},
},
}
+ case MapStructure:
+ return &SubexCaptureBeginState {
+ next: &SubexCopyState {
+ filter: anyMapFilter{},
+ next: &SubexDiscardState {
+ next: &SubexIncrementNestState {
+ keys: true,
+ next: beginConstruct,
+ },
+ },
+ },
+ }
default:
panic("Invalid destructure in ast")
}
diff --git a/subex/subexstate.go b/subex/subexstate.go
index 45b5d00..26d7347 100644
--- a/subex/subexstate.go
+++ b/subex/subexstate.go
@@ -373,6 +373,31 @@ func (state SubexConstructArrayValuesState) epsilon(aux auxiliaryState) []SubexB
}}
}
+type SubexConstructMapState struct {
+ next SubexState
+}
+func (state SubexConstructMapState) epsilon(aux auxiliaryState) []SubexBranch {
+ values, aux := aux.popOutput()
+ var m walk.MapValue
+ if len(values) % 2 != 0 {
+ panic("Tried to construct array with odd length input")
+ }
+ for i := 0; i < len(values); i += 2 {
+ key, isNum := values[i].(walk.StringValue)
+ if !isNum {
+ panic("Tried to construct array with non-numeric index")
+ }
+ m = append(m, walk.MapElement {
+ Key: string(key),
+ Value: values[i + 1],
+ })
+ }
+ return []SubexBranch {{
+ state: state.next,
+ aux: aux.topAppend([]walk.Value {m}),
+ }}
+}
+
type SubexConstructStringState struct {
next SubexState
}